You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2020/12/05 20:12:32 UTC

[openwebbeans] branch rmannibucau/scan-mojo created (now 6863edf)

This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a change to branch rmannibucau/scan-mojo
in repository https://gitbox.apache.org/repos/asf/openwebbeans.git.


      at 6863edf  basic scan mojo

This branch includes the following new commits:

     new 6863edf  basic scan mojo

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[openwebbeans] 01/01: basic scan mojo

Posted by rm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch rmannibucau/scan-mojo
in repository https://gitbox.apache.org/repos/asf/openwebbeans.git

commit 6863edf69a3743b42b381a8e288d5141ab437f94
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Sat Dec 5 21:12:25 2020 +0100

    basic scan mojo
---
 pom.xml                                            |   2 +
 .../webbeans/config/OpenWebBeansConfiguration.java |   7 +
 .../corespi/scanner/AbstractMetaDataDiscovery.java |   3 +-
 .../webbeans/corespi/scanner/xbean/CdiArchive.java |  49 ++-
 .../InterceptorAnnotatedDiscoveryTest.java         |   4 +-
 webbeans-maven-plugin/pom.xml                      | 118 ++++++
 .../org/apache/openwebbeans/maven/ScanMojo.java    | 457 +++++++++++++++++++++
 .../src/main/resources/META-INF/MANIFEST.MF        |   1 +
 .../apache/webbeans/web/tests/WebBeansTest.java    |   4 +-
 9 files changed, 640 insertions(+), 5 deletions(-)

diff --git a/pom.xml b/pom.xml
index 3d672d1..c37574a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -504,6 +504,7 @@
                   </execution>
                 </executions>
                 <configuration>
+                    <excludes>**/HelpMojo.java</excludes>
                     <configLocation>openwebbeans/owb-checks-default.xml</configLocation>
                     <headerLocation>openwebbeans/owb-header.txt</headerLocation>
                     <consoleOutput>true</consoleOutput>
@@ -649,6 +650,7 @@
         <module>webbeans-se</module>
         <module>webbeans-junit5</module>
         <module>webbeans-slf4j</module>
+      <module>webbeans-maven-plugin</module>
     </modules>
 
     <dependencyManagement>
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/config/OpenWebBeansConfiguration.java b/webbeans-impl/src/main/java/org/apache/webbeans/config/OpenWebBeansConfiguration.java
index 7eab234..82486a5 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/config/OpenWebBeansConfiguration.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/config/OpenWebBeansConfiguration.java
@@ -556,4 +556,11 @@ public class OpenWebBeansConfiguration
 
         return generatorJavaVersion;
     }
+
+    public void cleanBuiltTimeScanning()
+    {
+        configProperties.stringPropertyNames().stream()
+                .filter(it -> it.startsWith("openwebbeans.buildtime.scanning."))
+                .forEach(configProperties::remove);
+    }
 }
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscovery.java b/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscovery.java
index 210556e..797f093 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscovery.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscovery.java
@@ -129,7 +129,8 @@ public abstract class AbstractMetaDataDiscovery implements BdaScannerService
         }
         archive = new CdiArchive(
                 beanArchiveService, WebBeansUtil.getCurrentClassLoader(),
-                beanDeploymentUrls, userFilter, getAdditionalArchive());
+                beanDeploymentUrls, userFilter, getAdditionalArchive(),
+                webBeansContext.getOpenWebBeansConfiguration());
         finder = new OwbAnnotationFinder(archive);
 
         return finder;
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/xbean/CdiArchive.java b/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/xbean/CdiArchive.java
index b77aaf0..1b7c199 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/xbean/CdiArchive.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/corespi/scanner/xbean/CdiArchive.java
@@ -18,14 +18,18 @@
  */
 package org.apache.webbeans.corespi.scanner.xbean;
 
+import org.apache.webbeans.config.OpenWebBeansConfiguration;
 import org.apache.webbeans.spi.BeanArchiveService;
 import org.apache.webbeans.spi.BeanArchiveService.BeanArchiveInformation;
 import org.apache.xbean.finder.archive.Archive;
+import org.apache.xbean.finder.archive.ClassesArchive;
 import org.apache.xbean.finder.archive.ClasspathArchive;
 import org.apache.xbean.finder.archive.CompositeArchive;
 import org.apache.xbean.finder.archive.FilteredArchive;
 import org.apache.xbean.finder.filter.Filter;
+import org.apache.xbean.finder.util.Files;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
@@ -35,6 +39,10 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.toList;
 
 /**
  * this delegate pattern is interesting
@@ -53,7 +61,7 @@ public class CdiArchive implements Archive
     private final Archive delegate;
 
     public CdiArchive(BeanArchiveService beanArchiveService, ClassLoader loader, Map<String, URL> urls,
-                      Filter userFilter, Archive customArchive)
+                      Filter userFilter, Archive customArchive, OpenWebBeansConfiguration config)
     {
         Collection<Archive> archives = new ArrayList<>();
         boolean customAdded = false;
@@ -64,7 +72,9 @@ public class CdiArchive implements Archive
             BeanArchiveInformation beanArchiveInfo = beanArchiveService.getBeanArchiveInformation(url);
             final boolean custom = "openwebbeans".equals(url.getProtocol());
             Archive archive = new FilteredArchive(
-                    custom ? customArchive : ClasspathArchive.archive(loader, url),
+                    custom ?
+                            customArchive :
+                            createArchive(loader, url, config),
                     new BeanArchiveFilter(beanArchiveInfo, urlClasses, userFilter));
             if (!customAdded && custom)
             {
@@ -78,9 +88,44 @@ public class CdiArchive implements Archive
         {
             archives.add(userFilter != null ? new FilteredArchive(customArchive, userFilter) : customArchive);
         }
+        if (config != null)
+        {
+            config.cleanBuiltTimeScanning();
+        }
         delegate = new CompositeArchive(archives);
     }
 
+    private Archive createArchive(final ClassLoader loader, final URL url, final OpenWebBeansConfiguration config)
+    {
+        if (config != null)
+        {
+            final File file = Files.toFile(url);
+            if (!file.isDirectory()) // see ScanMojo
+            {
+                final String classes = config.getProperty(
+                        "openwebbeans.buildtime.scanning." + file.getName() + ".classes");
+                if (classes != null)
+                {
+                    return new ClassesArchive(Stream.of(classes.split(","))
+                        .map(it ->
+                        {
+                            try
+                            {
+                                return loader.loadClass(it);
+                            }
+                            catch (final ClassNotFoundException e)
+                            {
+                                return null;
+                            }
+                        })
+                        .filter(Objects::nonNull)
+                        .collect(toList()));
+                }
+            }
+        }
+        return ClasspathArchive.archive(loader, url);
+    }
+
     public Map<String, FoundClasses> classesByUrl()
     {
         return classesByUrl;
diff --git a/webbeans-impl/src/test/java/org/apache/webbeans/test/discovery/InterceptorAnnotatedDiscoveryTest.java b/webbeans-impl/src/test/java/org/apache/webbeans/test/discovery/InterceptorAnnotatedDiscoveryTest.java
index e3f9602..a8f79cc 100644
--- a/webbeans-impl/src/test/java/org/apache/webbeans/test/discovery/InterceptorAnnotatedDiscoveryTest.java
+++ b/webbeans-impl/src/test/java/org/apache/webbeans/test/discovery/InterceptorAnnotatedDiscoveryTest.java
@@ -75,7 +75,9 @@ public class InterceptorAnnotatedDiscoveryTest extends AbstractUnitTest
                 }
 
                 super.initFinder();
-                archive = new CdiArchive(webBeansContext().getBeanArchiveService(), WebBeansUtil.getCurrentClassLoader(), emptyMap(), null, null)
+                archive = new CdiArchive(
+                        webBeansContext().getBeanArchiveService(), WebBeansUtil.getCurrentClassLoader(), emptyMap(),
+                        null, null, null)
                 {
                     @Override
                     public Map<String, FoundClasses> classesByUrl()
diff --git a/webbeans-maven-plugin/pom.xml b/webbeans-maven-plugin/pom.xml
new file mode 100644
index 0000000..f0e7104
--- /dev/null
+++ b/webbeans-maven-plugin/pom.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>openwebbeans</artifactId>
+    <groupId>org.apache.openwebbeans</groupId>
+    <version>2.0.21-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>openwebbeans-maven-plugin</artifactId>
+  <name>Maven Plugin</name>
+  <packaging>maven-plugin</packaging>
+  <description>Apache OpenWebBeans Maven Plugin</description>
+
+  <properties>
+    <mvn.version>3.6.3</mvn.version>
+    <meecrowave.build.name>${project.groupId}.maven</meecrowave.build.name>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>${mvn.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.enterprise</groupId>
+          <artifactId>cdi-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <version>${mvn.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.inject</groupId>
+          <artifactId>javax.inject</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <version>3.3</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-dependency-tree</artifactId>
+      <version>3.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-artifact-resolver</artifactId>
+      <version>1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.xbean</groupId>
+      <artifactId>xbean-finder-shaded</artifactId>
+      <version>${xbean.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>3.6.0</version>
+        <executions>
+          <execution>
+            <id>mojo-descriptor</id>
+            <goals>
+              <goal>descriptor</goal>
+              <goal>helpmojo</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <goalPrefix>openwebbeans</goalPrefix>
+          <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>3.6.0</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
\ No newline at end of file
diff --git a/webbeans-maven-plugin/src/main/java/org/apache/openwebbeans/maven/ScanMojo.java b/webbeans-maven-plugin/src/main/java/org/apache/openwebbeans/maven/ScanMojo.java
new file mode 100644
index 0000000..e62d10a
--- /dev/null
+++ b/webbeans-maven-plugin/src/main/java/org/apache/openwebbeans/maven/ScanMojo.java
@@ -0,0 +1,457 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openwebbeans.maven;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
+import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
+import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.DefaultDependencyResolutionRequest;
+import org.apache.maven.project.DependencyResolutionException;
+import org.apache.maven.project.DependencyResolutionRequest;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.apache.maven.project.ProjectDependenciesResolver;
+import org.apache.maven.repository.RepositorySystem;
+import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
+import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.archive.Archive;
+import org.apache.xbean.finder.archive.ClasspathArchive;
+import org.apache.xbean.finder.util.Files;
+import org.eclipse.aether.graph.DependencyVisitor;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static java.util.Arrays.asList;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
+
+/**
+ * Scan at build time the beans and generate an OWB configuration to use it.
+ */
+@Mojo(name = "scan", requiresDependencyResolution = RUNTIME_PLUS_SYSTEM)
+public class ScanMojo extends AbstractMojo
+{
+    private static final String DEFAULT_DEP_EXCLUDES = "org.apache.johnzon:johnzon-," +
+            "org.apache.xbean:," +
+            "org.apache.openwebbeans:," +
+            "org.apache.tomcat:tomcat-," +
+            "org.apache.openjpa:openjpa," +
+            "org.apache.geronimo.specs:," +
+            "org.apache.commons:commons-," +
+            "commons-.+:commons-.+," +
+            "javax\\..+:.+," +
+            "org.postgresql:," +
+            "com.h2database:h2," +
+            "org.checkerframework:," +
+            "serp," +
+            "slf4j-";
+
+    @Parameter(property = "openwebbeans.skip", defaultValue = "false")
+    private boolean skip;
+
+    @Parameter(property = "openwebbeans.scanFolders", defaultValue = "false") // can move more than jars
+    private boolean scanFolders;
+
+    @Parameter(property = "openwebbeans.scopes", defaultValue = "compile,runtime")
+    private Collection<String> scopes;
+
+    @Parameter(property = "openwebbeans.libs")
+    private Collection<String> libs;
+
+    @Parameter(property = "openwebbeans.dependencies.includes")
+    private Collection<String> dependenciesIncludes;
+
+    @Parameter(property = "openwebbeans.dependencies.excludes", defaultValue = DEFAULT_DEP_EXCLUDES)
+    private Collection<String> dependenciesExcludes;
+
+    @Parameter(property = "openwebbeans.classes.excludes")
+    private Collection<String> classesExcludes;
+
+    @Parameter(defaultValue = "${project.build.outputDirectory}")
+    private File outputDirectory;
+
+    @Component
+    private MavenProjectHelper projectHelper;
+
+    @Component
+    private RepositorySystem repositorySystem;
+
+    @Component
+    private ProjectDependenciesResolver dependenciesResolver;
+
+    @Component
+    private DependencyGraphBuilder graphBuilder;
+
+    @Parameter(defaultValue = "${project}", readonly = true)
+    private MavenProject project;
+
+    /**
+     * Used to extend properties set by default.
+     */
+    @Parameter
+    private Map<String, String> openwebbeansProperties;
+
+    @Parameter(defaultValue = "CLASSES")
+    private ScanMode mode;
+
+    private Map<String, Pattern> patterns = new HashMap<>();
+
+    @Override
+    public void execute() throws MojoExecutionException
+    {
+        if (skip)
+        {
+            getLog().warn(getClass().getSimpleName() + " skipped");
+            return;
+        }
+
+        final Collection<Path> files = new ArrayList<>();
+        if (dependenciesExcludes.contains("{defaults}"))
+        {
+            dependenciesExcludes.remove("{defaults}");
+            dependenciesExcludes.addAll(asList(DEFAULT_DEP_EXCLUDES.split(",")));
+        }
+
+        collectClasspath(files);
+        if (!scanFolders)
+        {
+            files.removeIf(it -> java.nio.file.Files.isDirectory(it));
+        }
+
+        final List<URL> urls = files.stream().map(it ->
+        {
+            try
+            {
+                return it.toUri().toURL();
+            }
+            catch (final MalformedURLException e)
+            {
+                throw new IllegalStateException(e);
+            }
+        }).collect(toList());
+
+        final Thread thread = Thread.currentThread();
+        final ClassLoader oldLoader = thread.getContextClassLoader();
+        final List<Scanned> scanned;
+        try (final URLClassLoader loader = new URLClassLoader(urls.toArray(new URL[0])))
+        {
+            thread.setContextClassLoader(loader);
+            scanned = urls.stream()
+                    .map(url -> doScan(url, loader))
+                    .collect(toList());
+        }
+        catch (final Exception e)
+        {
+            throw new MojoExecutionException(e.getMessage(), e);
+        }
+        finally
+        {
+            thread.setContextClassLoader(oldLoader);
+        }
+
+        final Properties properties = new Properties()
+        {
+            @Override // deterministic write, not elegant but simple, avoids to recode java.util.Properties.saveConvert
+            public synchronized Enumeration<Object> keys()
+            {
+                final List<String> keys = new ArrayList<String>(List.class.cast(Collections.list(super.keys())));
+                Collections.sort(keys);
+                return Collections.enumeration(new ArrayList<>(keys));
+            }
+
+            @Override // same for java 11 and not 8
+            public Set<Map.Entry<Object, Object>> entrySet()
+            {
+                return new LinkedHashSet<>(super.entrySet().stream()
+                        .sorted(comparing(it -> String.valueOf(it.getKey())))
+                        .collect(toList()));
+            }
+        };
+        // meecrowave uses 1000 so let's override it
+        // for now we don't override scanner since built-in ones are able to read these meta but later we could
+        properties.setProperty("configuration.ordinal", "2000");
+        switch (mode == null ? ScanMode.CLASSES : mode)
+        {
+            case CLASSES:
+                if (!scanned.isEmpty())
+                {
+                    scanned.forEach(meta -> properties.setProperty(
+                            "openwebbeans.buildtime.scanning." + meta.name + ".classes",
+                            String.join(",", meta.classes)));
+                }
+                break;
+            default:
+                getLog().error("Unsupported scan mode: " + mode);
+        }
+
+        if (openwebbeansProperties != null) // easy way to customize a docker image or so let's support it
+        {
+            openwebbeansProperties.forEach(properties::setProperty);
+        }
+
+        final Path output = outputDirectory.toPath().resolve("META-INF/openwebbeans/openwebbeans.properties");
+        try
+        {
+            java.nio.file.Files.createDirectories(output.getParent());
+            try (final Writer writer = new BufferedWriter(java.nio.file.Files.newBufferedWriter(output))
+            {
+                @Override
+                public void write(final String str) throws IOException
+                {
+                    if (!str.startsWith("#")) // skip date comment (and we don't care of other comments btw)
+                    {
+                        super.write(str);
+                    }
+                }
+            })
+            {
+                // don't use store() to ensure it is reproducible (no date)
+                properties.store(writer, "");
+            }
+        }
+        catch (final IOException ioe)
+        {
+            throw new IllegalStateException(ioe);
+        }
+    }
+
+    private Scanned doScan(final URL url, final ClassLoader loader)
+    {
+        final File file = Files.toFile(url);
+        final Archive archive = ClasspathArchive.archive(loader, url);
+        final Collection<String> classes = new AnnotationFinder(archive, false)
+                .getAnnotatedClassNames().stream()
+                .filter(it ->
+                {
+                    try
+                    {
+                        final Class<?> aClass = loader.loadClass(it);
+                        return !aClass.isAnonymousClass() &&
+                                !Modifier.isPrivate(aClass.getModifiers());
+                    }
+                    catch (final ClassNotFoundException | NoClassDefFoundError err)
+                    {
+                        return false;
+                    }
+                })
+                .filter(it -> classesExcludes == null || classesExcludes.stream().noneMatch(it::startsWith))
+                .sorted()
+                .collect(toList());
+        return new Scanned(file.getName(), classes);
+    }
+
+    protected void collectClasspath(final Collection<Path> files)
+    {
+        final Collection<String> includedArtifacts = project.getArtifacts().stream()
+                .filter(this::isIncluded)
+                .map(a ->
+                {
+                    files.add(a.getFile().toPath());
+                    return a.getArtifactId();
+                }).collect(toSet());
+        libs.forEach(l ->
+        {
+            final boolean transitive = l.endsWith("?transitive");
+            final String coords = transitive ? l.substring(0, l.length() - "?transitive".length()) : l;
+            final String[] c = coords.split(":");
+            if (c.length < 3 || c.length > 5)
+            {
+                throw new IllegalArgumentException("libs syntax is groupId:artifactId:version[:classifier][:type[?transitive]]");
+            }
+            if (!transitive)
+            {
+                files.add(resolve(c[0], c[1], c[2], c.length == 4 ? c[3] : ""));
+            }
+            else
+                {
+                addTransitiveDependencies(files, includedArtifacts, new Dependency()
+                {{
+                    setGroupId(c[0]);
+                    setArtifactId(c[1]);
+                    setVersion(c[2]);
+                    if (c.length == 4 && !"-".equals(c[3]))
+                    {
+                        setClassifier(c[3]);
+                    }
+                    if (c.length == 5)
+                    {
+                        setType(c[4]);
+                    }
+                }});
+            }
+        });
+    }
+
+    private void addTransitiveDependencies(final Collection<Path> aggregator,
+                                           final Collection<String> includedArtifacts, final Dependency dependency)
+    {
+        final DependencyResolutionRequest request = new DefaultDependencyResolutionRequest();
+        request.setMavenProject(new MavenProject()
+        {{
+            getDependencies().add(dependency);
+        }});
+        try
+        {
+            dependenciesResolver
+                    .resolve(request)
+                    .getDependencyGraph()
+                    .accept(new DependencyVisitor()
+                    {
+                        @Override
+                        public boolean visitEnter(final org.eclipse.aether.graph.DependencyNode node)
+                        {
+                            return true;
+                        }
+
+                        @Override
+                        public boolean visitLeave(final org.eclipse.aether.graph.DependencyNode node)
+                        {
+                            final org.eclipse.aether.artifact.Artifact artifact = node.getArtifact();
+                            if (artifact != null && includedArtifacts.add(artifact.getArtifactId()))
+                            {
+                                aggregator.add(artifact.getFile().toPath());
+                            }
+                            return true;
+                        }
+                    });
+        }
+        catch (final DependencyResolutionException e)
+        {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    private Path resolve(final String group, final String artifact, final String version, final String classifier)
+    {
+        final DefaultArtifact art = new DefaultArtifact(
+                group, artifact, version, "compile", "jar", classifier, new DefaultArtifactHandler());
+        final ArtifactResolutionRequest artifactRequest = new ArtifactResolutionRequest()
+                .setArtifact(art);
+        final ArtifactResolutionResult result = repositorySystem.resolve(artifactRequest);
+        if (!result.isSuccess())
+        {
+            throw new IllegalStateException("Can't find " + art + ", please add it to the pom.");
+        }
+        return result.getArtifacts().iterator().next().getFile().toPath();
+    }
+
+
+    private boolean isIncluded(final Artifact a)
+    {
+        return (!((scopes == null &&
+                !(Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope())))
+                || (scopes != null && !scopes.contains(a.getScope())))) &&
+                isExplicitlyIncluded(a);
+    }
+
+    private boolean isExplicitlyIncluded(final Artifact art)
+    {
+        if (dependenciesExcludes.isEmpty() && dependenciesIncludes.isEmpty())
+        {
+            return true;
+        }
+        final String coord = art.getGroupId() + ':' + art.getArtifactId();
+        final String artOnly = art.getArtifactId();
+        if (!dependenciesIncludes.isEmpty() && dependenciesExcludes.isEmpty())
+        {
+            return dependenciesIncludes.stream()
+                    .anyMatch(it -> compare(coord, artOnly, it));
+        }
+        if (dependenciesIncludes.isEmpty() && !dependenciesExcludes.isEmpty())
+        {
+            return dependenciesExcludes.stream()
+                    .noneMatch(it -> compare(coord, artOnly, it));
+        }
+        final boolean forced = dependenciesIncludes.stream()
+                .anyMatch(it -> compare(coord, artOnly, it));
+        if (forced)
+        {
+            return true;
+        }
+        final boolean notExcluded = dependenciesExcludes.stream()
+                .noneMatch(it -> compare(coord, artOnly, it));
+        return notExcluded;
+    }
+
+    private boolean compare(final String coord, final String artOnly, final String conf)
+    {
+        return coord.startsWith(conf) || artOnly.startsWith(conf) || patterns.computeIfAbsent(conf, k ->
+        {
+            try
+            {
+                return Pattern.compile(k);
+            }
+            catch (final Exception e)
+            {
+                // whatever, ignore pattern
+                return Pattern.compile("/\\\\");
+            }
+        }).matcher(coord).matches();
+    }
+
+    private static class Scanned
+    {
+        private final String name;
+        private final Collection<String> classes;
+
+        private Scanned(final String name, final Collection<String> classes)
+        {
+            this.name = name;
+            this.classes = classes;
+        }
+    }
+
+    // this will be used to add new mode like "FULLY_PRE_SCANNED"
+    // where we wouldn't use the classloader but only the meta to scan
+    public enum ScanMode
+    {
+        // means we map the list of classes per jar,
+        // it enables to speed up scanning and keep extensibility of CDI
+        CLASSES
+    }
+}
diff --git a/webbeans-maven-plugin/src/main/resources/META-INF/MANIFEST.MF b/webbeans-maven-plugin/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9d885be
--- /dev/null
+++ b/webbeans-maven-plugin/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+Manifest-Version: 1.0
diff --git a/webbeans-web/src/test/java/org/apache/webbeans/web/tests/WebBeansTest.java b/webbeans-web/src/test/java/org/apache/webbeans/web/tests/WebBeansTest.java
index 82d1828..cbbb1f0 100644
--- a/webbeans-web/src/test/java/org/apache/webbeans/web/tests/WebBeansTest.java
+++ b/webbeans-web/src/test/java/org/apache/webbeans/web/tests/WebBeansTest.java
@@ -79,7 +79,9 @@ public class WebBeansTest {
         @Override
         public void scan()
         {
-            archive = new CdiArchive(new DefaultBeanArchiveService(), Thread.currentThread().getContextClassLoader(), new HashMap<String, URL>(), null, null);
+            archive = new CdiArchive(
+                    new DefaultBeanArchiveService(), Thread.currentThread().getContextClassLoader(),
+                    new HashMap<String, URL>(), null, null, null);
             finder = new OwbAnnotationFinder(new ClassesArchive());
         }
     }