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/06/14 18:49:19 UTC

[openwebbeans] branch master updated (412d080 -> 7b170d0)

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

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


    from 412d080  Merge pull request #28 from philippkunz/cdi-parameter-resolver
     new 1cde3d9  [OWB-1330] extract Cdi parameter resolver in its own extension
     new 7b170d0  [OWB-1331] @Scopes junit5 extension to veto or start scopes

The 2 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.


Summary of changes:
 .../java/org/apache/openwebbeans/junit5/Cdi.java   |  26 ++--
 .../openwebbeans/junit5/CdiMethodParameters.java   |  27 ++--
 .../org/apache/openwebbeans/junit5/Scopes.java     |  31 ++---
 .../org/apache/openwebbeans/junit5/SkipInject.java |  24 ++--
 .../openwebbeans/junit5/internal/CdiExtension.java |  63 +--------
 .../internal/CdiParametersResolverExtension.java   | 129 ++++++++++++++++++
 .../junit5/internal/ScopesExtension.java           | 116 ++++++++++++++++
 .../junit5/CdiParameterResolversTest.java          |  96 +++++++++++++
 .../org/apache/openwebbeans/junit5/ScopesTest.java | 118 ++++++++++++++++
 .../openwebbeans/junit5/extension/DummyScoped.java |  11 +-
 .../openwebbeans/junit5/extension/MyScope.java     |  44 +++---
 .../junit5/parameter/ParameterResolutionTest.java  |  22 ++-
 .../junit5/perclass/PerMethodTest.java             | 148 ++++++++++++++-------
 .../services/javax.enterprise.inject.spi.Extension |   4 +-
 14 files changed, 657 insertions(+), 202 deletions(-)
 copy samples/guess/src/main/java/org/apache/webbeans/sample/guess/Highest.java => webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/CdiMethodParameters.java (75%)
 copy samples/guess/src/main/java/org/apache/webbeans/sample/guess/Highest.java => webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Scopes.java (71%)
 copy webbeans-impl/src/test/java/org/apache/webbeans/test/injection/generics/GenericQualifier.java => webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/SkipInject.java (78%)
 create mode 100644 webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiParametersResolverExtension.java
 create mode 100644 webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/ScopesExtension.java
 create mode 100644 webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/CdiParameterResolversTest.java
 create mode 100644 webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/ScopesTest.java
 copy webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/lifecycle/LifecycleBinding.java => webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/DummyScoped.java (83%)
 copy webbeans-impl/src/test/java/org/apache/webbeans/test/configurator/DefaultQualifierTest.java => webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/MyScope.java (51%)
 copy samples/standalone-sample/src/main/resources/login.properties => webbeans-junit5/src/test/resources/META-INF/services/javax.enterprise.inject.spi.Extension (94%)


[openwebbeans] 01/02: [OWB-1330] extract Cdi parameter resolver in its own extension

Posted by rm...@apache.org.
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

commit 1cde3d95b6407f8bfd553cfd25baf9a236760edf
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Sun Jun 14 20:48:16 2020 +0200

    [OWB-1330] extract Cdi parameter resolver in its own extension
---
 .../java/org/apache/openwebbeans/junit5/Cdi.java   |  26 ++---
 .../openwebbeans/junit5/CdiMethodParameters.java   |  39 +++++++
 .../org/apache/openwebbeans/junit5/SkipInject.java |  36 ++++++
 .../openwebbeans/junit5/internal/CdiExtension.java |  63 +---------
 .../internal/CdiParametersResolverExtension.java   | 129 +++++++++++++++++++++
 .../junit5/CdiParameterResolversTest.java          |  96 +++++++++++++++
 6 files changed, 316 insertions(+), 73 deletions(-)

diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Cdi.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Cdi.java
index c6f6f2d..c5c7d7b 100644
--- a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Cdi.java
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Cdi.java
@@ -23,14 +23,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
 
 import java.io.Closeable;
 import java.lang.annotation.Annotation;
-import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 import java.util.function.Supplier;
-import javax.inject.Qualifier;
 
 import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 /**
@@ -79,20 +76,14 @@ public @interface Cdi
     Class<?>[] recursivePackages() default {};
 
     /**
-     * @return if the automatic scanning must be disabled.
+     * @return SeContainer properties.
      */
-    boolean disableDiscovery() default false;
+    Property[] properties() default {};
 
     /**
-     * When present on a test method parameter, it will <em>not</em> be attempted to be resolved with a CDI bean.
+     * @return if the automatic scanning must be disabled.
      */
-    @Qualifier
-    @Target(PARAMETER)
-    @Retention(RUNTIME)
-    @Documented
-    @interface DontInject
-    {
-    }
+    boolean disableDiscovery() default false;
 
     /**
      * @return an array of callback to call before the container starts.
@@ -113,4 +104,13 @@ public @interface Cdi
     interface OnStart extends Supplier<Closeable>
     {
     }
+
+    /**
+     * A property set in SeContainer (as String).
+     */
+    @interface Property
+    {
+        String name();
+        String value();
+    }
 }
diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/CdiMethodParameters.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/CdiMethodParameters.java
new file mode 100644
index 0000000..89e07f1
--- /dev/null
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/CdiMethodParameters.java
@@ -0,0 +1,39 @@
+/*
+ * 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.junit5;
+
+import org.apache.openwebbeans.junit5.internal.CdiParametersResolverExtension;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Enables method parameter injection using CDI lookups.
+ */
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+@ExtendWith(CdiParametersResolverExtension.class)
+public @interface CdiMethodParameters
+{
+}
diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/SkipInject.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/SkipInject.java
new file mode 100644
index 0000000..b9b1af3
--- /dev/null
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/SkipInject.java
@@ -0,0 +1,36 @@
+/*
+ * 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.junit5;
+
+import javax.inject.Qualifier;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Qualifier you can put in method parameters to prevent the injection (adding an unresolved qualifier).
+ */
+@Qualifier
+@Target(PARAMETER)
+@Retention(RUNTIME)
+public @interface SkipInject
+{
+}
diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiExtension.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiExtension.java
index 0dfe9ed..c5852c0 100644
--- a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiExtension.java
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiExtension.java
@@ -24,33 +24,24 @@ import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.api.extension.ParameterContext;
-import org.junit.jupiter.api.extension.ParameterResolutionException;
-import org.junit.jupiter.api.extension.ParameterResolver;
 import org.junit.platform.commons.util.AnnotationUtils;
 
 import javax.enterprise.context.spi.CreationalContext;
 import javax.enterprise.inject.se.SeContainer;
 import javax.enterprise.inject.se.SeContainerInitializer;
 import javax.enterprise.inject.spi.AnnotatedType;
-import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.InjectionTarget;
 import java.io.Closeable;
 import java.io.IOException;
-import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Parameter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Objects;
-import java.util.Set;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 // todo: enhance the setup to be thread safe? see Meecrowave ClassLoaderLock class and friends
-public class CdiExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver
+public class CdiExtension extends CdiParametersResolverExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback
 {
     private static SeContainer reusableContainer;
 
@@ -93,6 +84,7 @@ public class CdiExtension implements BeforeAllCallback, AfterAllCallback, Before
                 Stream.of(config.packages()).map(Class::getPackage).toArray(Package[]::new));
         initializer.addPackages(true,
                 Stream.of(config.recursivePackages()).map(Class::getPackage).toArray(Package[]::new));
+        Stream.of(config.properties()).forEach(property -> initializer.addProperty(property.name(), property.value()));
         onStop = Stream.of(config.onStarts())
                 .map(it ->
                 {
@@ -110,7 +102,6 @@ public class CdiExtension implements BeforeAllCallback, AfterAllCallback, Before
                     }
                 })
                 .peek(Supplier::get)
-                .filter(Objects::nonNull)
                 .toArray(Closeable[]::new);
         SeContainer container = initializer.initialize();
         if (reusable)
@@ -160,6 +151,7 @@ public class CdiExtension implements BeforeAllCallback, AfterAllCallback, Before
     @Override
     public void afterEach(final ExtensionContext extensionContext)
     {
+        super.afterEach(extensionContext);
         if (!creationalContexts.isEmpty())
         {
             creationalContexts.forEach(CreationalContext::release);
@@ -194,53 +186,4 @@ public class CdiExtension implements BeforeAllCallback, AfterAllCallback, Before
             return reusableContainer;
         }
     }
-
-    @Override
-    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
-            throws ParameterResolutionException
-    {
-        final SeContainer container = getContainer();
-        if (container == null)
-        {
-            return false;
-        }
-
-        Bean<?> bean = resolveParameterBean(container, parameterContext, extensionContext);
-        return bean != null;
-    }
-
-    @Override
-    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
-            throws ParameterResolutionException
-    {
-        final SeContainer container = getContainer();
-        if (container == null)
-        {
-            return false;
-        }
-
-        Bean<?> bean = resolveParameterBean(container, parameterContext, extensionContext);
-        BeanManager beanManager = container.getBeanManager();
-        CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean);
-        creationalContexts.add(creationalContext);
-        return beanManager.getReference(bean, parameterContext.getParameter().getType(), creationalContext);
-    }
-
-    private Bean<?> resolveParameterBean(SeContainer container, ParameterContext parameterContext, ExtensionContext extensionContext)
-    {
-        BeanManager beanManager = container.getBeanManager();
-        Set<Bean<?>> beans = beanManager.getBeans(
-                parameterContext.getParameter().getType(),
-                getQualifiers(parameterContext.getParameter()));
-        return beanManager.resolve(beans);
-    }
-
-    private Annotation[] getQualifiers(Parameter parameter)
-    {
-        final BeanManager beanManager = getContainer().getBeanManager();
-        return Arrays.stream(parameter.getAnnotations())
-                .filter(annotation -> beanManager.isQualifier(annotation.annotationType()))
-                .toArray(Annotation[]::new);
-    }
-
 }
diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiParametersResolverExtension.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiParametersResolverExtension.java
new file mode 100644
index 0000000..5bd9c62
--- /dev/null
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/CdiParametersResolverExtension.java
@@ -0,0 +1,129 @@
+/*
+ * 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.junit5.internal;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.CDI;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class CdiParametersResolverExtension implements ParameterResolver, AfterEachCallback
+{
+    private static final ExtensionContext.Namespace NAMESPACE =
+            ExtensionContext.Namespace.create(CdiParametersResolverExtension.class.getName());
+
+    @Override
+    public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext)
+            throws ParameterResolutionException
+    {
+        final Instances instances = extensionContext.getStore(NAMESPACE).getOrComputeIfAbsent(
+                Instances.class, k -> new Instances(), Instances.class);
+        if (instances != null && instances.instances != null &&
+                instances.instances.containsKey(parameterContext.getParameter()))
+        {
+            return false; // already handled
+        }
+        try
+        {
+            final Parameter parameter = parameterContext.getParameter();
+            final BeanManager bm = CDI.current().getBeanManager();
+            final Bean<?> bean = resolveParameterBean(bm, parameter);
+            if (bean == null)
+            {
+                return false;
+            }
+            final CreationalContext<Object> creationalContext = bm.createCreationalContext(null);
+            final Object instance = bm.getReference(bean, parameter.getType(), creationalContext);
+            if (instances.instances == null)
+            {
+                instances.instances = new HashMap<>();
+            }
+            instances.instances.put(parameter, new Instance(instance, creationalContext));
+            return true;
+        }
+        catch (final IllegalStateException ise) // no cdi container
+        {
+            return false;
+        }
+    }
+
+    @Override
+    public void afterEach(final ExtensionContext context)
+    {
+        final Instances instances = context.getStore(NAMESPACE).get(Instances.class, Instances.class);
+        if (instances != null && instances.instances != null)
+        {
+            instances.instances.values().stream().map(i -> i.creationalContext).forEach(CreationalContext::release);
+        }
+    }
+
+    @Override
+    public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext)
+            throws ParameterResolutionException
+    {
+        final Instances instances = extensionContext.getStore(NAMESPACE).get(Instances.class, Instances.class);
+        if (instances == null || instances.instances == null)
+        {
+            throw new ParameterResolutionException("No matching parameter: " + parameterContext.getParameter());
+        }
+        return instances.instances.get(parameterContext.getParameter()).instance;
+    }
+
+    private Bean<?> resolveParameterBean(final BeanManager beanManager, final Parameter parameter)
+    {
+        final Set<Bean<?>> beans = beanManager.getBeans(parameter.getType(), getQualifiers(beanManager, parameter));
+        return beanManager.resolve(beans);
+    }
+
+    private Annotation[] getQualifiers(final BeanManager beanManager, final Parameter parameter)
+    {
+        return Arrays.stream(parameter.getAnnotations())
+                .filter(annotation -> beanManager.isQualifier(annotation.annotationType()))
+                .toArray(Annotation[]::new);
+    }
+
+    private static class Instance
+    {
+        private final Object instance;
+        private final CreationalContext<?> creationalContext;
+
+        private Instance(final Object instance, final CreationalContext<?> creationalContext)
+        {
+            this.instance = instance;
+            this.creationalContext = creationalContext;
+        }
+    }
+
+    private static class Instances
+    {
+        private Map<Parameter, Instance> instances;
+    }
+}
diff --git a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/CdiParameterResolversTest.java b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/CdiParameterResolversTest.java
new file mode 100644
index 0000000..73e2309
--- /dev/null
+++ b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/CdiParameterResolversTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.junit5;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.CDI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@Cdi(classes = CdiParameterResolversTest.SomeBean.class, disableDiscovery = true)
+class CdiParameterResolversTest
+{
+    @Test
+    void noParam()
+    {
+        assertNotNull(CDI.current().getBeanManager());
+    }
+
+    @Test
+    @CdiMethodParameters
+    void cdiParam(final SomeBean someBean)
+    {
+        assertNotNull(someBean);
+        assertEquals("yes", someBean.ok());
+        assertTrue(someBean.getClass().getName().contains("$$Owb")); // it is cdi proxy
+    }
+
+    @Test
+    @CdiMethodParameters
+    @ExtendWith(CustomParamResolver.class)
+    void mixedParams(final SomeBean cdi, @SkipInject final SomeBean notCdi)
+    {
+        assertNotNull(cdi);
+        assertEquals("yes", cdi.ok());
+        assertEquals("custom", notCdi.ok());
+    }
+
+    @ApplicationScoped
+    public static class SomeBean
+    {
+        public String ok()
+        {
+            return "yes";
+        }
+    }
+
+    public static class CustomParamResolver implements ParameterResolver
+    {
+
+        @Override
+        public boolean supportsParameter(final ParameterContext parameterContext,
+                                         final ExtensionContext extensionContext) throws ParameterResolutionException
+        {
+            return parameterContext.getIndex() == 1;
+        }
+
+        @Override
+        public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext)
+                throws ParameterResolutionException
+        {
+            return new SomeBean()
+            {
+                @Override
+                public String ok()
+                {
+                    return "custom";
+                }
+            };
+        }
+    }
+}


[openwebbeans] 02/02: [OWB-1331] @Scopes junit5 extension to veto or start scopes

Posted by rm...@apache.org.
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

commit 7b170d0dadcf4e613bd19ae0ba8755bb38785900
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Sun Jun 14 20:49:14 2020 +0200

    [OWB-1331] @Scopes junit5 extension to veto or start scopes
---
 .../org/apache/openwebbeans/junit5/Scopes.java     |  43 ++++++
 .../junit5/internal/ScopesExtension.java           | 116 ++++++++++++++++
 .../org/apache/openwebbeans/junit5/ScopesTest.java | 118 ++++++++++++++++
 .../openwebbeans/junit5/extension/DummyScoped.java |  31 +++++
 .../openwebbeans/junit5/extension/MyScope.java     |  55 ++++++++
 .../junit5/parameter/ParameterResolutionTest.java  |  22 ++-
 .../junit5/perclass/PerMethodTest.java             | 148 ++++++++++++++-------
 .../services/javax.enterprise.inject.spi.Extension |  17 +++
 8 files changed, 490 insertions(+), 60 deletions(-)

diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Scopes.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Scopes.java
new file mode 100644
index 0000000..804d510
--- /dev/null
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/Scopes.java
@@ -0,0 +1,43 @@
+/*
+ * 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.junit5;
+
+import org.apache.openwebbeans.junit5.internal.ScopesExtension;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Activates CDI scopes on a running OWB instance - you can use @{@link Cdi} for that.
+ */
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+@ExtendWith(ScopesExtension.class)
+public @interface Scopes
+{
+    /**
+     * @return classes to deploy.
+     */
+    Class<?>[] value() default {};
+}
diff --git a/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/ScopesExtension.java b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/ScopesExtension.java
new file mode 100644
index 0000000..fd54b20
--- /dev/null
+++ b/webbeans-junit5/src/main/java/org/apache/openwebbeans/junit5/internal/ScopesExtension.java
@@ -0,0 +1,116 @@
+/*
+ * 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.junit5.internal;
+
+import org.apache.openwebbeans.junit5.Scopes;
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.spi.ContextsService;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.util.AnnotationUtils;
+
+import javax.enterprise.context.ConversationScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+import javax.enterprise.context.spi.Context;
+import java.lang.annotation.Annotation;
+import java.util.stream.Stream;
+
+public class ScopesExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback
+{
+    private Runnable[] classCallbacks;
+    private Runnable[] methodCallbacks;
+
+    @Override
+    public void afterAll(final ExtensionContext context)
+    {
+        stop(classCallbacks);
+    }
+
+    @Override
+    public void afterEach(final ExtensionContext context)
+    {
+        stop(methodCallbacks);
+    }
+
+    @Override
+    public void beforeAll(final ExtensionContext context)
+    {
+        classCallbacks = start(context, true);
+    }
+
+    @Override
+    public void beforeEach(final ExtensionContext context)
+    {
+        methodCallbacks = start(context, false);
+    }
+
+    private Runnable[] start(final ExtensionContext context, final boolean canVetoScopes)
+    {
+        final Class<?>[] scopes = AnnotationUtils.findAnnotation(context.getElement(), Scopes.class)
+                .map(Scopes::value)
+                .orElse(null);
+        if (scopes == null || scopes.length == 0)
+        {
+            return null;
+        }
+        final WebBeansContext webBeansContext = WebBeansContext.currentInstance();
+        final ContextsService contextsService = webBeansContext.getContextsService();
+        if (canVetoScopes)
+        {
+            stopIfNeeded(scopes, contextsService, RequestScoped.class);
+            stopIfNeeded(scopes, contextsService, SessionScoped.class);
+            if (webBeansContext.getOpenWebBeansConfiguration().supportsConversation())
+            {
+                stopIfNeeded(scopes, contextsService, ConversationScoped.class);
+            }
+        }
+        return Stream.of(scopes)
+                .map(scope -> {
+                    // todo: endParam support, not needed in standalone but can be in web?
+                    final Class<? extends Annotation> scopeAnnot = (Class<? extends Annotation>) scope;
+                    contextsService.startContext(scopeAnnot, null);
+                    return (Runnable) () -> contextsService.endContext(scopeAnnot, null);
+                })
+                .toArray(Runnable[]::new);
+    }
+
+    private void stopIfNeeded(final Class<?>[] scopes, final ContextsService contextsService, final Class<? extends Annotation> scope)
+    {
+        if (Stream.of(scopes).noneMatch(it -> it == scope))
+        {
+            final Context currentReqScope = contextsService.getCurrentContext(scope);
+            if (currentReqScope != null && currentReqScope.isActive())
+            {
+                contextsService.endContext(scope, null);
+            }
+        }
+    }
+
+    private void stop(final Runnable[] destroyers)
+    {
+        if (destroyers != null)
+        {
+            Stream.of(destroyers).forEach(Runnable::run);
+        }
+    }
+}
diff --git a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/ScopesTest.java b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/ScopesTest.java
new file mode 100644
index 0000000..3c4ccee
--- /dev/null
+++ b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/ScopesTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.junit5;
+
+import org.apache.openwebbeans.junit5.extension.DummyScoped;
+import org.apache.openwebbeans.junit5.extension.MyScope;
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.context.AbstractContext;
+import org.apache.webbeans.corespi.se.StandaloneContextsService;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.context.ContextException;
+import javax.enterprise.context.ContextNotActiveException;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import java.lang.annotation.Annotation;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@Cdi(disableDiscovery = true, properties =
+    @Cdi.Property(
+            name = "org.apache.webbeans.spi.ContextsService",
+            value = "org.apache.openwebbeans.junit5.ScopesTest$ContextsService"))
+@Scopes(SessionScoped.class)
+class ScopesTest
+{
+    @Inject
+    private BeanManager beanManager;
+    private static BeanManager beanManagerRef;
+
+    @AfterEach
+    void captureBm() {
+        beanManagerRef = beanManager;
+    }
+
+    @AfterAll
+    static void after() {
+        assertThrows(ContextNotActiveException.class, () -> beanManagerRef.getContext(DummyScoped.class).isActive());
+    }
+
+    @Test
+    void classScopeStarted()
+    {
+        assertThrows(ContextNotActiveException.class, () -> beanManager.getContext(RequestScoped.class).isActive());
+        assertThrows(ContextNotActiveException.class, () -> beanManager.getContext(DummyScoped.class).isActive());
+        assertTrue(beanManager.getContext(SessionScoped.class).isActive());
+    }
+
+    @Test
+    @Scopes(DummyScoped.class)
+    void methodScopeStarted()
+    {
+        assertThrows(ContextNotActiveException.class, () -> beanManager.getContext(RequestScoped.class).isActive());
+        assertTrue(beanManager.getContext(SessionScoped.class).isActive());
+        assertTrue(beanManager.getContext(DummyScoped.class).isActive());
+    }
+
+    // not required but enables to control the activation as any built-in scope so good enough for this test
+    public static class ContextsService extends StandaloneContextsService
+    {
+        public ContextsService(final WebBeansContext webBeansContext)
+        {
+            super(webBeansContext);
+        }
+
+        private AbstractContext getDummyScope()
+        {
+            return webBeansContext.getBeanManagerImpl().getExtension(MyScope.class).getScope();
+        }
+
+        @Override
+        public void endContext(final Class<? extends Annotation> scopeType, final Object endParameters)
+        {
+            if (scopeType == DummyScoped.class)
+            {
+                getDummyScope().setActive(false);
+            }
+            else
+            {
+                super.endContext(scopeType, endParameters);
+            }
+        }
+
+        @Override
+        public void startContext(final Class<? extends Annotation> scopeType, final Object startParameter) throws ContextException
+        {
+            if (scopeType == DummyScoped.class)
+            {
+                getDummyScope().setActive(true);
+            }
+            else
+            {
+                super.startContext(scopeType, startParameter);
+            }
+        }
+    }
+}
diff --git a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/DummyScoped.java b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/DummyScoped.java
new file mode 100644
index 0000000..66b5147
--- /dev/null
+++ b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/DummyScoped.java
@@ -0,0 +1,31 @@
+/*
+ * 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.junit5.extension;
+
+import javax.enterprise.context.NormalScope;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@NormalScope
+public @interface DummyScoped {
+}
diff --git a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/MyScope.java b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/MyScope.java
new file mode 100644
index 0000000..cf4649a
--- /dev/null
+++ b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/extension/MyScope.java
@@ -0,0 +1,55 @@
+/*
+ * 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.junit5.extension;
+
+import org.apache.webbeans.context.AbstractContext;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.Extension;
+import java.util.concurrent.ConcurrentHashMap;
+
+// register a custom scope we can manipulate injecting the extension (getScope())
+public class MyScope implements Extension
+{
+    private AbstractContext scope;
+
+    public AbstractContext getScope()
+    {
+        return scope;
+    }
+
+    void addScope(@Observes final BeforeBeanDiscovery beforeBeanDiscovery)
+    {
+        beforeBeanDiscovery.addScope(DummyScoped.class, true, true);
+    }
+
+    void addScope(@Observes final AfterBeanDiscovery afterBeanDiscovery)
+    {
+        afterBeanDiscovery.addContext(scope = new AbstractContext(DummyScoped.class)
+        {
+            @Override
+            protected void setComponentInstanceMap()
+            {
+                componentInstanceMap = new ConcurrentHashMap<>();
+            }
+        });
+    }
+}
diff --git a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/parameter/ParameterResolutionTest.java b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/parameter/ParameterResolutionTest.java
index fa0071c..6593846 100644
--- a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/parameter/ParameterResolutionTest.java
+++ b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/parameter/ParameterResolutionTest.java
@@ -18,18 +18,8 @@
  */
 package org.apache.openwebbeans.junit5.parameter;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertSame;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import javax.inject.Inject;
-import javax.inject.Qualifier;
 import org.apache.openwebbeans.junit5.Cdi;
+import org.apache.openwebbeans.junit5.SkipInject;
 import org.apache.openwebbeans.junit5.bean.MyService;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -38,6 +28,12 @@ import org.junit.jupiter.api.extension.ParameterContext;
 import org.junit.jupiter.api.extension.ParameterResolutionException;
 import org.junit.jupiter.api.extension.ParameterResolver;
 
+import javax.inject.Inject;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
 @Cdi(classes = MyService.class)
 class ParameterResolutionTest
 {
@@ -72,14 +68,14 @@ class ParameterResolutionTest
 
     @Test
     @ExtendWith(OnlyFirstParameterResolver.class)
-    void testThatParameterDoesNotGetInjectedWithDontInject(@Cdi.DontInject MyService service)
+    void testThatParameterDoesNotGetInjectedWithDontInject(@SkipInject MyService service)
     {
         assertNull(service); // OnlyFirstParameterResolver.resolveParameter resolves service to null
     }
 
     @Test
     @ExtendWith(OnlyFirstParameterResolver.class)
-    void testMixedCdiAndOtherParameterResolver(@Cdi.DontInject MyService service1, MyService service2)
+    void testMixedCdiAndOtherParameterResolver(@SkipInject MyService service1, MyService service2)
     {
         // OnlyFirstParameterResolver.resolveParameter resolves service1 to null
         assertNull(service1);
diff --git a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/perclass/PerMethodTest.java b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/perclass/PerMethodTest.java
index 98aa912..a5c70b7 100644
--- a/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/perclass/PerMethodTest.java
+++ b/webbeans-junit5/src/test/java/org/apache/openwebbeans/junit5/perclass/PerMethodTest.java
@@ -46,13 +46,15 @@ import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
 
 @TestInstance(PER_CLASS)
-class PerMethodTest {
+class PerMethodTest
+{
     private BeanManager bm1;
 
     @Test
     @Order(1)
     @ExtendWith(CustomExtension.class)
-    void cdiRuns() {
+    void cdiRuns()
+    {
         bm1 = CDI.current().getBeanManager();
         assertNotNull(bm1);
     }
@@ -60,7 +62,8 @@ class PerMethodTest {
     @Test
     @Order(2)
     @ExtendWith(CustomExtension.class)
-    void cdiReRuns() {
+    void cdiReRuns()
+    {
         final BeanManager bm2 = CDI.current().getBeanManager();
         assertNotNull(bm2);
         assertNotEquals(bm1, bm2);
@@ -68,13 +71,17 @@ class PerMethodTest {
     }
 
     private BeanManager getRealBm(final BeanManager wrapper) {
-        try {
+        try
+        {
             final Field bm = InjectableBeanManager.class.getDeclaredField("bm");
-            if (!bm.isAccessible()) {
+            if (!bm.isAccessible())
+            {
                 bm.setAccessible(true);
             }
             return BeanManager.class.cast(bm.get(wrapper));
-        } catch (final Exception e) {
+        }
+        catch (final Exception e)
+        {
             return fail(e);
         }
     }
@@ -83,15 +90,21 @@ class PerMethodTest {
     // here we just virtually set @Cdi on the method and move the class lifecycle to the method
     // note 1: in real, this kind of impl is not "inline" but this tests the use case more than the impl
     // note 2: by itself this use case is not terrible but sometimes requires by another jupiter extension
-    public static class CustomExtension extends CdiExtension {
+    public static class CustomExtension extends CdiExtension
+    {
         @Override
-        public void beforeEach(final ExtensionContext extensionContext) {
-            super.beforeAll(new ExtensionContext() {
+        public void beforeEach(final ExtensionContext extensionContext)
+        {
+            super.beforeAll(new ExtensionContext()
+            {
                 @Override
-                public Optional<AnnotatedElement> getElement() {
-                    return of(new AnnotatedElement() {
+                public Optional<AnnotatedElement> getElement()
+                {
+                    return of(new AnnotatedElement()
+                    {
                         @Override
-                        public <T extends Annotation> T getAnnotation(final Class<T> annotationClass) {
+                        public <T extends Annotation> T getAnnotation(final Class<T> annotationClass)
+                        {
                             return Stream.of(getAnnotations())
                                     .filter(it -> it.annotationType() == annotationClass)
                                     .map(annotationClass::cast)
@@ -99,66 +112,86 @@ class PerMethodTest {
                         }
 
                         @Override
-                        public Annotation[] getAnnotations() {
+                        public Annotation[] getAnnotations()
+                        {
                             return getDeclaredAnnotations();
                         }
 
                         @Override
-                        public Annotation[] getDeclaredAnnotations() {
-                            return new Annotation[]{
+                        public Annotation[] getDeclaredAnnotations()
+                        {
+                            return new Annotation[]
+                            {
                                     new Cdi() {
                                         @Override
-                                        public Class<? extends Annotation> annotationType() {
+                                        public Class<? extends Annotation> annotationType()
+                                        {
                                             return Cdi.class;
                                         }
 
                                         @Override
-                                        public Class<?>[] classes() {
+                                        public Class<?>[] classes()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public Class<?>[] decorators() {
+                                        public Class<?>[] decorators()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public Class<?>[] interceptors() {
+                                        public Class<?>[] interceptors()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public Class<?>[] alternatives() {
+                                        public Class<?>[] alternatives()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public Class<? extends Annotation>[] alternativeStereotypes() {
+                                        public Class<? extends Annotation>[] alternativeStereotypes()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public Class<?>[] packages() {
+                                        public Class<?>[] packages()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public Class<?>[] recursivePackages() {
+                                        public Class<?>[] recursivePackages()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public boolean disableDiscovery() {
+                                        public Property[] properties()
+                                        {
+                                            return new Property[0];
+                                        }
+
+                                        @Override
+                                        public boolean disableDiscovery()
+                                        {
                                             return false;
                                         }
 
                                         @Override
-                                        public Class<? extends OnStart>[] onStarts() {
+                                        public Class<? extends OnStart>[] onStarts()
+                                        {
                                             return new Class[0];
                                         }
 
                                         @Override
-                                        public boolean reusable() {
+                                        public boolean reusable()
+                                        {
                                             return false;
                                         }
                                     }
@@ -168,102 +201,122 @@ class PerMethodTest {
                 }
 
                 @Override
-                public Optional<ExtensionContext> getParent() {
+                public Optional<ExtensionContext> getParent()
+                {
                     return extensionContext.getParent();
                 }
 
                 @Override
-                public ExtensionContext getRoot() {
+                public ExtensionContext getRoot()
+                {
                     return extensionContext.getRoot();
                 }
 
                 @Override
-                public String getUniqueId() {
+                public String getUniqueId()
+                {
                     return extensionContext.getUniqueId();
                 }
 
                 @Override
-                public String getDisplayName() {
+                public String getDisplayName()
+                {
                     return extensionContext.getDisplayName();
                 }
 
                 @Override
-                public Set<String> getTags() {
+                public Set<String> getTags()
+                {
                     return extensionContext.getTags();
                 }
 
                 @Override
-                public Optional<Class<?>> getTestClass() {
+                public Optional<Class<?>> getTestClass()
+                {
                     return extensionContext.getTestClass();
                 }
 
                 @Override
-                public Class<?> getRequiredTestClass() {
+                public Class<?> getRequiredTestClass()
+                {
                     return extensionContext.getRequiredTestClass();
                 }
 
                 @Override
-                public Optional<TestInstance.Lifecycle> getTestInstanceLifecycle() {
+                public Optional<TestInstance.Lifecycle> getTestInstanceLifecycle()
+                {
                     return extensionContext.getTestInstanceLifecycle();
                 }
 
                 @Override
-                public Optional<Object> getTestInstance() {
+                public Optional<Object> getTestInstance()
+                {
                     return extensionContext.getTestInstance();
                 }
 
                 @Override
-                public Object getRequiredTestInstance() {
+                public Object getRequiredTestInstance()
+                {
                     return extensionContext.getRequiredTestInstance();
                 }
 
                 @Override
-                public Optional<TestInstances> getTestInstances() {
+                public Optional<TestInstances> getTestInstances()
+                {
                     return extensionContext.getTestInstances();
                 }
 
                 @Override
-                public TestInstances getRequiredTestInstances() {
+                public TestInstances getRequiredTestInstances()
+                {
                     return extensionContext.getRequiredTestInstances();
                 }
 
                 @Override
-                public Optional<Method> getTestMethod() {
+                public Optional<Method> getTestMethod()
+                {
                     return extensionContext.getTestMethod();
                 }
 
                 @Override
-                public Method getRequiredTestMethod() {
+                public Method getRequiredTestMethod()
+                {
                     return extensionContext.getRequiredTestMethod();
                 }
 
                 @Override
-                public Optional<Throwable> getExecutionException() {
+                public Optional<Throwable> getExecutionException()
+                {
                     return extensionContext.getExecutionException();
                 }
 
                 @Override
-                public Optional<String> getConfigurationParameter(final String key) {
+                public Optional<String> getConfigurationParameter(final String key)
+                {
                     return extensionContext.getConfigurationParameter(key);
                 }
 
                 @Override
-                public void publishReportEntry(final Map<String, String> map) {
+                public void publishReportEntry(final Map<String, String> map)
+                {
                     extensionContext.publishReportEntry(map);
                 }
 
                 @Override
-                public void publishReportEntry(final String key, final String value) {
+                public void publishReportEntry(final String key, final String value)
+                {
                     extensionContext.publishReportEntry(key, value);
                 }
 
                 @Override
-                public void publishReportEntry(final String value) {
+                public void publishReportEntry(final String value)
+                {
                     extensionContext.publishReportEntry(value);
                 }
 
                 @Override
-                public Store getStore(final Namespace namespace) {
+                public Store getStore(final Namespace namespace)
+                {
                     return extensionContext.getStore(namespace);
                 }
             });
@@ -271,7 +324,8 @@ class PerMethodTest {
         }
 
         @Override
-        public void afterEach(final ExtensionContext extensionContext) {
+        public void afterEach(final ExtensionContext extensionContext)
+        {
             super.afterEach(extensionContext);
             super.afterAll(extensionContext);
         }
diff --git a/webbeans-junit5/src/test/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/webbeans-junit5/src/test/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..d7f1887
--- /dev/null
+++ b/webbeans-junit5/src/test/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1,17 @@
+#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.
+org.apache.openwebbeans.junit5.extension.MyScope