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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5e72019  OWB-1299 ensure jar with an extension can be skipped in terms 
of scanning, org.apache.webbeans.scanExtensionJars = true new property, default 
stays consistent with our previous behavior
5e72019 is described below

commit 5e72019d22bc49860ef33e9bf134d712b44221c1
Author: Romain Manni-Bucau <rmannibu...@apache.org>
AuthorDate: Sat Oct 5 17:53:02 2019 +0200

    OWB-1299 ensure jar with an extension can be skipped in terms of scanning, 
org.apache.webbeans.scanExtensionJars = true new property, default stays 
consistent with our previous behavior
---
 .../webbeans/config/OpenWebBeansConfiguration.java |  23 +++
 .../corespi/scanner/AbstractMetaDataDiscovery.java |  22 ++-
 .../webbeans/portable/events/ExtensionLoader.java  |  74 ++++++++-
 .../META-INF/openwebbeans/openwebbeans.properties  |   7 +
 .../scanner/AbstractMetaDataDiscoveryTest.java     | 174 +++++++++++++++++++++
 .../org/apache/webbeans/test/AbstractUnitTest.java |  14 +-
 6 files changed, 306 insertions(+), 8 deletions(-)

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 968afd0..187f665 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
@@ -181,6 +181,13 @@ public class OpenWebBeansConfiguration
     public static final String IGNORED_EXTENSIONS = 
"org.apache.webbeans.ignoredExtensions";
 
     /**
+     * A boolean to enable CDI 1.1 behavior to not scan extension jar.
+     *
+     * IMPORTANT: this can break CDI 1.0 extensions.
+     */
+    public static final String SCAN_EXTENSION_JARS = 
"org.apache.webbeans.scanExtensionJars";
+
+    /**
      * By default we do _not_ force session creation in our 
WebBeansConfigurationListener. We only create the
      * Session if we really need the SessionContext. E.g. when we create a 
Contextual Instance in it.
      * Sometimes this creates a problem as the HttpSession can only be created 
BEFORE anything got written back
@@ -226,6 +233,11 @@ public class OpenWebBeansConfiguration
     private Set<String> ignoredExtensions;
 
     /**
+     * @see #SCAN_EXTENSION_JARS
+     */
+    private Boolean scanExtensionJars;
+
+    /**
      * All configured lists per key.
      *
      * For a single key the following configuration sources will get parsed:
@@ -450,6 +462,17 @@ public class OpenWebBeansConfiguration
         return ignoredExtensions;
     }
 
+    public synchronized boolean getScanExtensionJars()
+    {
+        if (scanExtensionJars == null)
+        {
+            final String property = getProperty(SCAN_EXTENSION_JARS);
+            // default must stay true for backward compatibility
+            scanExtensionJars = property == null || 
Boolean.parseBoolean(property.trim());
+        }
+        return scanExtensionJars;
+    }
+
     private Set<String> getPropertyList(String configKey)
     {
         String configValue = getProperty(configKey);
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 4fded1f..44febe9 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
@@ -19,6 +19,8 @@
 package org.apache.webbeans.corespi.scanner;
 
 
+import static java.util.stream.Collectors.toMap;
+
 import org.apache.webbeans.config.OWBLogConst;
 import org.apache.webbeans.config.OpenWebBeansConfiguration;
 import org.apache.webbeans.config.WebBeansContext;
@@ -105,13 +107,27 @@ public abstract class AbstractMetaDataDiscovery 
implements BdaScannerService
             return finder;
         }
 
+        final WebBeansContext webBeansContext = webBeansContext();
         if (beanArchiveService == null)
         {
-            beanArchiveService = webBeansContext().getBeanArchiveService();
+            beanArchiveService = webBeansContext.getBeanArchiveService();
         }
 
-        Filter userFilter = webBeansContext().getService(Filter.class);
-        archive = new CdiArchive(beanArchiveService, 
WebBeansUtil.getCurrentClassLoader(), getBeanDeploymentUrls(), userFilter, 
getAdditionalArchive());
+        final Filter userFilter = webBeansContext.getService(Filter.class);
+        Map<String, URL> beanDeploymentUrls = getBeanDeploymentUrls();
+        if 
(!webBeansContext.getOpenWebBeansConfiguration().getScanExtensionJars())
+        {
+            webBeansContext.getExtensionLoader().loadExtensionServices();
+
+            final Set<URL> extensionJars = 
webBeansContext.getExtensionLoader().getExtensionJars();
+            beanDeploymentUrls = extensionJars.isEmpty() ? beanDeploymentUrls 
: beanDeploymentUrls.entrySet().stream()
+                    .filter(it -> !extensionJars.contains(it.getValue()))
+                    .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+            extensionJars.clear(); // no more needed
+        }
+        archive = new CdiArchive(
+                beanArchiveService, WebBeansUtil.getCurrentClassLoader(),
+                beanDeploymentUrls, userFilter, getAdditionalArchive());
         finder = new OwbAnnotationFinder(archive);
 
         return finder;
diff --git 
a/webbeans-impl/src/main/java/org/apache/webbeans/portable/events/ExtensionLoader.java
 
b/webbeans-impl/src/main/java/org/apache/webbeans/portable/events/ExtensionLoader.java
index e331baf..b3b9913 100644
--- 
a/webbeans-impl/src/main/java/org/apache/webbeans/portable/events/ExtensionLoader.java
+++ 
b/webbeans-impl/src/main/java/org/apache/webbeans/portable/events/ExtensionLoader.java
@@ -18,9 +18,16 @@
  */
 package org.apache.webbeans.portable.events;
 
+import static java.util.stream.Collectors.toSet;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.logging.Logger;
@@ -37,6 +44,7 @@ import org.apache.webbeans.exception.WebBeansException;
 import org.apache.webbeans.logger.WebBeansLoggerFacade;
 import org.apache.webbeans.util.ExceptionUtil;
 import org.apache.webbeans.util.WebBeansUtil;
+import org.apache.xbean.finder.archive.FileArchive;
 
 /**
  * Loads META-INF/services/javax.enterprise.inject.spi.Extension
@@ -56,6 +64,8 @@ public class ExtensionLoader
     private final BeanManagerImpl manager;
 
     private final WebBeansContext webBeansContext;
+    private final Set<URL> extensionJars = new HashSet<>();
+    private boolean loaded;
 
     /**
      * Creates a new loader instance.
@@ -71,9 +81,13 @@ public class ExtensionLoader
     /**
      * Load extension services.
      */
-    public void loadExtensionServices()
+    public synchronized void loadExtensionServices()
     {
-        loadExtensionServices(WebBeansUtil.getCurrentClassLoader());
+        if (!loaded)
+        {
+            loadExtensionServices(WebBeansUtil.getCurrentClassLoader());
+            loaded = true;
+        }
     }
 
     /**
@@ -115,9 +129,61 @@ public class ExtensionLoader
                     throw new WebBeansException("Error occurred while reading 
Extension service list", e);
                 }
             }
-        }        
+        }
+
+        if 
(!webBeansContext.getOpenWebBeansConfiguration().getScanExtensionJars())
+        {
+            extensionJars.addAll(extensionClasses.stream()
+                    .map(clazz -> toJar(classLoader, clazz))
+                    .filter(Objects::nonNull)
+                    .collect(toSet()));
+        }
+    }
+
+    private URL toJar(final ClassLoader loader, final Class<?> clazz)
+    {
+        try
+        {
+            final String resource = clazz.getName().replace('.', '/') + 
".class";
+            Enumeration<URL> urls = loader.getResources(resource);
+            while (urls.hasMoreElements())
+            {
+                URL url = urls.nextElement();
+                switch (url.getProtocol())
+                {
+                    case "jar":
+                    {
+                        final String spec = url.getFile();
+                        int separator = spec.indexOf('!');
+                        if (separator == -1)
+                        {
+                            break;
+                        }
+                        url = new URL(spec.substring(0, separator));
+                        return new 
File(FileArchive.decode(url.getFile())).toURI().toURL();
+                    }
+                    case "file":
+                    {
+                        String path = url.getFile();
+                        path = path.substring(0, path.length() - 
resource.length());
+                        return new 
File(FileArchive.decode(path)).toURI().toURL();
+                    }
+                    default:
+                }
+            }
+        }
+        catch (final IOException ioe)
+        {
+            logger.warning(ioe.getMessage());
+        }
+        return null;
+    }
+
+    public Set<URL> getExtensionJars()
+    {
+        return extensionJars;
     }
-    
+
     /**
      * Returns service bean instance.
      * 
diff --git 
a/webbeans-impl/src/main/resources/META-INF/openwebbeans/openwebbeans.properties
 
b/webbeans-impl/src/main/resources/META-INF/openwebbeans/openwebbeans.properties
index f2818e7..fec5259 100644
--- 
a/webbeans-impl/src/main/resources/META-INF/openwebbeans/openwebbeans.properties
+++ 
b/webbeans-impl/src/main/resources/META-INF/openwebbeans/openwebbeans.properties
@@ -153,6 +153,13 @@ org.apache.webbeans.web.eagerSessionInitialisation=false
 # org.apache.webbeans.generator.javaVersion=1.6
 
################################################################################################
 
+############################# Are Extension jar scanned 
################################
+# In CDI 1.0 it was done but no more in next versions.
+# To avoid any impacting breaking change we still scan by default these jars
+# but you can enforce the spec behavior setting that property to false.
+# org.apache.webbeans.scanExtensionJars = true
+################################################################################################
+
 ############################# Ignored CDI Extension class names 
################################
 # A comma separated list of CDI Extension class names which should get ignored.
 # Each listed class name must be fully qualified.
diff --git 
a/webbeans-impl/src/test/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscoveryTest.java
 
b/webbeans-impl/src/test/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscoveryTest.java
index 5a62e2f..ca86eb0 100644
--- 
a/webbeans-impl/src/test/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscoveryTest.java
+++ 
b/webbeans-impl/src/test/java/org/apache/webbeans/corespi/scanner/AbstractMetaDataDiscoveryTest.java
@@ -18,15 +18,55 @@
  */
 package org.apache.webbeans.corespi.scanner;
 
+import static java.util.Collections.emptyEnumeration;
+import static java.util.Collections.emptyMap;
+import static org.apache.xbean.asm7.ClassWriter.COMPUTE_FRAMES;
+import static org.apache.xbean.asm7.Opcodes.ACC_PUBLIC;
+import static org.apache.xbean.asm7.Opcodes.ACC_SUPER;
+import static org.apache.xbean.asm7.Opcodes.ALOAD;
+import static org.apache.xbean.asm7.Opcodes.INVOKESPECIAL;
+import static org.apache.xbean.asm7.Opcodes.RETURN;
+import static org.apache.xbean.asm7.Opcodes.V1_8;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
 
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.Extension;
+
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.config.WebBeansFinder;
+import org.apache.webbeans.corespi.DefaultSingletonService;
+import org.apache.webbeans.spi.ContainerLifecycle;
+import org.apache.xbean.asm7.ClassWriter;
+import org.apache.xbean.asm7.MethodVisitor;
+import org.apache.xbean.asm7.Type;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
 public class AbstractMetaDataDiscoveryTest
 {
+    @Rule
+    public final TemporaryFolder temp = new TemporaryFolder();
+
     @Test
     public void isAnonymous() throws Exception
     {
@@ -45,4 +85,138 @@ public class AbstractMetaDataDiscoveryTest
         assertTrue(Boolean.class.cast(mtd.invoke(mock, 
AbstractMetaDataDiscoveryTest.class.getName() + "$1$2")));
         assertTrue(Boolean.class.cast(mtd.invoke(mock, 
AbstractMetaDataDiscoveryTest.class.getName() + "$15$222")));
     }
+
+    @Test
+    public void skipExtensionJarScanning() throws Exception
+    {
+        // we create a module with some scanned beans
+        final URL scannedModule = createScannedModule();
+
+        // we create another module with some elligible beans and an extension
+        final URL extensionModule = createExtensionModule();
+
+        final Thread thread = Thread.currentThread();
+        final ClassLoader oldLoader = thread.getContextClassLoader();
+        final URL[] urls = {scannedModule, extensionModule};
+        try (final URLClassLoader loader = new URLClassLoader(urls, new 
ClassLoader() {
+            @Override
+            public Class<?> loadClass(final String name) throws 
ClassNotFoundException
+            {
+                return oldLoader.loadClass(name);
+            }
+
+            @Override
+            public URL getResource(final String name)
+            {
+                return oldLoader.getResource(name);
+            }
+
+            @Override
+            public Enumeration<URL> getResources(final String name) throws 
IOException
+            {
+                if ("META-INF".equals(name) || "".equals(name)) // scanning
+                {
+                    return emptyEnumeration();
+                }
+                return oldLoader.getResources(name);
+            }
+        })
+        {
+            @Override
+            public URL[] getURLs()
+            {
+                return urls;
+            }
+        })
+        {
+            thread.setContextClassLoader(loader);
+
+            // we disable extension jar scanning and start then
+            // we start the container and check we scanned only first module
+            final Properties config = new Properties();
+            config.setProperty("org.apache.webbeans.scanExtensionJars", 
"false");
+            config.setProperty("org.apache.webbeans.scanExclusionPaths", 
"/classes,/test-classes," +
+                    
"/xbean,/ham,/junit-,/junit5-,/debugger,/idea,/openwebbeans,/geronimo");
+            final WebBeansContext context = new WebBeansContext(emptyMap(), 
config);
+            final DefaultSingletonService singletonService = 
DefaultSingletonService.class.cast(
+                    WebBeansFinder.getSingletonService());
+            singletonService.register(loader, context);
+            final ContainerLifecycle lifecycle = 
context.getService(ContainerLifecycle.class);
+            lifecycle.startApplication(null);
+            try
+            {
+                final BeanManager manager = context.getBeanManagerImpl();
+
+                final Set<Bean<?>> foos = manager.getBeans(
+                        
loader.loadClass("org.apache.openwebbeans.generated.test.Foo"));
+                assertEquals(1, foos.size());
+
+                final Set<Bean<?>> bars = manager.getBeans(
+                        
loader.loadClass("org.apache.openwebbeans.generated.test.Bar"));
+                assertTrue(bars.isEmpty());
+
+                final Object myExtension = context.getExtensionLoader()
+                        
.getExtension(loader.loadClass("org.apache.openwebbeans.generated.test.MyExtension"));
+                assertNotNull(myExtension);
+            }
+            finally
+            {
+                lifecycle.stopApplication(null);
+                singletonService.clear(loader);
+            }
+        }
+        finally
+        {
+            thread.setContextClassLoader(oldLoader);
+        }
+    }
+
+    private URL createScannedModule() throws IOException
+    {
+        final File file = temp.newFile("test-scanned.jar");
+        try (final JarOutputStream outputStream = new JarOutputStream(new 
FileOutputStream(file)))
+        {
+            createBean(outputStream, 
"org/apache/openwebbeans/generated/test/Foo.class", null);
+            outputStream.putNextEntry(new JarEntry("META-INF/beans.xml"));
+            outputStream.closeEntry();
+        }
+        return file.toURI().toURL();
+    }
+
+    private URL createExtensionModule() throws IOException
+    {
+        final File file = temp.newFile("test-extension.jar");
+        try (final JarOutputStream outputStream = new JarOutputStream(new 
FileOutputStream(file)))
+        {
+            createBean(outputStream, 
"org/apache/openwebbeans/generated/test/Bar.class", null);
+            createBean(outputStream, 
"org/apache/openwebbeans/generated/test/MyExtension.class", Extension.class);
+            outputStream.putNextEntry(new JarEntry("META-INF/services/" + 
Extension.class.getName()));
+            
outputStream.write("org.apache.openwebbeans.generated.test.MyExtension".getBytes(StandardCharsets.UTF_8));
+            outputStream.closeEntry();
+        }
+        return file.toURI().toURL();
+    }
+
+    private void createBean(final JarOutputStream outputStream, final String 
resource, final Class<?> itf)
+            throws IOException
+    {
+        outputStream.putNextEntry(new JarEntry(resource));
+        final ClassWriter writer = new ClassWriter(COMPUTE_FRAMES);
+        // make it count for annotated mode
+        writer.visitAnnotation(Type.getDescriptor(ApplicationScoped.class), 
true).visitEnd();
+        writer.visit(V1_8, ACC_PUBLIC + ACC_SUPER,
+                resource.substring(0, resource.length() - ".class".length()), 
null,
+                Type.getInternalName(Object.class), itf == null ? null : new 
String[]{ Type.getInternalName(itf) });
+        writer.visitSource(resource.replace(".class", ".java"), null);
+        final MethodVisitor constructor = writer.visitMethod(ACC_PUBLIC, 
"<init>", "()V", null, null);
+        constructor.visitCode();
+        constructor.visitVarInsn(ALOAD, 0);
+        constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", 
"<init>", "()V", false);
+        constructor.visitInsn(RETURN);
+        constructor.visitMaxs(1, 1);
+        constructor.visitEnd();
+        writer.visitEnd();
+        outputStream.write(writer.toByteArray());
+        outputStream.closeEntry();
+    }
 }
diff --git 
a/webbeans-impl/src/test/java/org/apache/webbeans/test/AbstractUnitTest.java 
b/webbeans-impl/src/test/java/org/apache/webbeans/test/AbstractUnitTest.java
index dee3a11..054f1af 100644
--- a/webbeans-impl/src/test/java/org/apache/webbeans/test/AbstractUnitTest.java
+++ b/webbeans-impl/src/test/java/org/apache/webbeans/test/AbstractUnitTest.java
@@ -59,6 +59,7 @@ public abstract class AbstractUnitTest
 {
     private StandaloneLifeCycle testLifecycle;
     private Map<Class<?>, Object> services = new HashMap<>();
+    private Properties configuration = new Properties();
     private List<Extension>  extensions = new ArrayList<>();
     private List<Class<?>> interceptors = new ArrayList<Class<?>>();
     private List<Class<?>> decorators = new ArrayList<Class<?>>();
@@ -75,6 +76,8 @@ public abstract class AbstractUnitTest
         extensions.clear();
         interceptors.clear();
         decorators.clear();
+        services.clear();
+        configuration.clear();
     }
 
     /**
@@ -148,7 +151,7 @@ public abstract class AbstractUnitTest
 
             final Properties properties = wbcAwareServices.entrySet().stream()
                     .collect(Collector.of(
-                        Properties::new,
+                            () -> configuration == null ? new Properties() : 
configuration,
                         (p, e) -> p.setProperty(e.getKey().getName(), 
Class.class.cast(e.getValue()).getName()),
                         (properties1, properties2) -> {
                             properties1.putAll(properties2);
@@ -322,6 +325,15 @@ public abstract class AbstractUnitTest
         this.services.put(type, instance);
     }
 
+    protected void addConfiguration(final String key, final String value)
+    {
+        if (configuration == null)
+        {
+            configuration = new Properties();
+        }
+        configuration.setProperty(key, value);
+    }
+
     /**
      * @param packageName package of the beans.xml file
      * @param fileName name of the beans xml file, without '.xml'

Reply via email to