You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2018/09/27 06:45:47 UTC

[camel] branch master updated: CAMEL-12833: Do CDI CamelContext creation with an apporpriate TCCL

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 345abf4  CAMEL-12833: Do CDI CamelContext creation with an apporpriate TCCL
345abf4 is described below

commit 345abf449e615c49e9affbb2761ff0763ffd7828
Author: James Netherton <ja...@gmail.com>
AuthorDate: Wed Sep 26 15:47:32 2018 +0100

    CAMEL-12833: Do CDI CamelContext creation with an apporpriate TCCL
---
 .../org/apache/camel/cdi/CamelContextProducer.java |  3 +-
 .../org/apache/camel/cdi/CdiCamelExtension.java    | 32 +++++++----
 .../java/org/apache/camel/cdi/CdiSpiHelper.java    | 50 ++++++++++++++++
 .../org/apache/camel/cdi/SyntheticAnnotated.java   | 17 +++++-
 .../org/apache/camel/cdi/XmlCdiBeanFactory.java    | 11 ++--
 .../org/apache/camel/cdi/test/NoTCCLSetTest.java   | 66 ++++++++++++++++++++++
 6 files changed, 162 insertions(+), 17 deletions(-)

diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CamelContextProducer.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CamelContextProducer.java
index 4dbe62efe4..e51d856 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CamelContextProducer.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CamelContextProducer.java
@@ -40,6 +40,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static org.apache.camel.cdi.AnyLiteral.ANY;
+import static org.apache.camel.cdi.CdiSpiHelper.createCamelContextWithTCCL;
 import static org.apache.camel.cdi.CdiSpiHelper.getRawType;
 import static org.apache.camel.cdi.CdiSpiHelper.isAnnotationType;
 import static org.apache.camel.cdi.DefaultLiteral.DEFAULT;
@@ -64,7 +65,7 @@ final class CamelContextProducer<T extends CamelContext> extends DelegateProduce
 
     @Override
     public T produce(CreationalContext<T> ctx) {
-        T context = super.produce(ctx);
+        T context = createCamelContextWithTCCL(() -> super.produce(ctx), annotated);
 
         // Do not override the name if it's been already set (in the bean constructor for example)
         if (context.getNameStrategy() instanceof DefaultCamelContextNameStrategy) {
diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelExtension.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelExtension.java
index 6535aa9..d01acb1 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelExtension.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelExtension.java
@@ -24,9 +24,12 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EventObject;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static java.util.Collections.newSetFromMap;
@@ -271,15 +274,22 @@ public class CdiCamelExtension implements Extension {
             .forEach(contextQualifiers::addAll);
 
         // From the @ContextName qualifiers on RoutesBuilder and RouteContainer beans
-        cdiBeans.stream()
+        List<Bean<?>> routeBeans = cdiBeans.stream()
             .filter(hasType(RoutesBuilder.class).or(hasType(RouteContainer.class)))
-            .map(Bean::getQualifiers)
-            .flatMap(Set::stream)
-            .filter(isAnnotationType(ContextName.class))
-            .filter(name -> !contextQualifiers.contains(name))
-            .peek(contextQualifiers::add)
-            .map(name -> camelContextBean(manager, ANY, name, APPLICATION_SCOPED))
-            .forEach(extraBeans::add);
+            .filter(bean -> bean.getQualifiers()
+                .stream()
+                .filter(isAnnotationType(ContextName.class).and(name -> !contextQualifiers.contains(name)))
+                .peek(contextQualifiers::add)
+                .count() > 0
+            )
+            .collect(Collectors.toList());
+
+        for (Bean<?> bean : routeBeans) {
+            Optional<Annotation> annotation = bean.getQualifiers()
+                .stream()
+                .filter(isAnnotationType(ContextName.class)).findFirst();
+            extraBeans.add(camelContextBean(manager, bean.getBeanClass(), ANY, annotation.get(), APPLICATION_SCOPED));
+        }
 
         Set<Bean<?>> allBeans = concat(cdiBeans.stream(), extraBeans.stream())
             .collect(toSet());
@@ -289,7 +299,7 @@ public class CdiCamelExtension implements Extension {
 
         if (contexts.size() == 0 && shouldDeployDefaultCamelContext(allBeans)) {
             // Add @Default Camel context bean if any
-            extraBeans.add(camelContextBean(manager, ANY, DEFAULT, APPLICATION_SCOPED));
+            extraBeans.add(camelContextBean(manager, null, ANY, DEFAULT, APPLICATION_SCOPED));
         } else if (contexts.size() == 1) {
             // Add the @Default qualifier if there is only one Camel context bean
             Bean<?> context = contexts.iterator().next();
@@ -360,9 +370,9 @@ public class CdiCamelExtension implements Extension {
             .anyMatch(isAnnotationType(Uri.class).or(isAnnotationType(Mock.class)).or(isEqual(DEFAULT)));
     }
 
-    private SyntheticBean<?> camelContextBean(BeanManager manager, Annotation... qualifiers) {
+    private SyntheticBean<?> camelContextBean(BeanManager manager, Class<?> beanClass, Annotation... qualifiers) {
         SyntheticAnnotated annotated = new SyntheticAnnotated(DefaultCamelContext.class,
-            manager.createAnnotatedType(DefaultCamelContext.class).getTypeClosure(), qualifiers);
+            manager.createAnnotatedType(DefaultCamelContext.class).getTypeClosure(), beanClass, qualifiers);
         return new SyntheticBean<>(manager, annotated, DefaultCamelContext.class,
             environment.camelContextInjectionTarget(
                 new SyntheticInjectionTarget<>(DefaultCamelContext::new), annotated, manager, this), bean ->
diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiSpiHelper.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiSpiHelper.java
index a69a1d8..023ed6b 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiSpiHelper.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiSpiHelper.java
@@ -31,6 +31,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import static java.security.AccessController.doPrivileged;
@@ -43,12 +44,14 @@ import static java.util.stream.Collectors.toSet;
 import javax.enterprise.inject.spi.Annotated;
 import javax.enterprise.inject.spi.AnnotatedConstructor;
 import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMember;
 import javax.enterprise.inject.spi.AnnotatedMethod;
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.util.Nonbinding;
 
+import org.apache.camel.CamelContext;
 import static org.apache.camel.cdi.AnyLiteral.ANY;
 import static org.apache.camel.cdi.DefaultLiteral.DEFAULT;
 
@@ -221,4 +224,51 @@ final class CdiSpiHelper {
                 StringJoiner::merge)
             .toString();
     }
+
+    /**
+     * Wraps creation of a {@link CamelContext} with the current thread context ClassLoader
+     * set with the ClassLoader associated to the {@link Annotated} java class.
+     */
+    static <T extends CamelContext> T createCamelContextWithTCCL(Supplier<T> supplier, Annotated annotated) {
+        ClassLoader oldTccl = Thread.currentThread().getContextClassLoader();
+        try {
+            ClassLoader classLoader = getClassLoader(annotated);
+            Thread.currentThread().setContextClassLoader(classLoader);
+            return supplier.get();
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldTccl);
+        }
+    }
+
+    private static ClassLoader getClassLoader(Annotated annotated) {
+        // Try to find a ClassLoader associated with the class containing AnnotatedMember
+        if (annotated instanceof AnnotatedMember) {
+            AnnotatedMember annotatedMember = (AnnotatedMember) annotated;
+            AnnotatedType type = annotatedMember.getDeclaringType();
+            return type.getJavaClass().getClassLoader();
+        }
+
+        // Try to find a ClassLoader associated with the annotated class
+        if (annotated instanceof AnnotatedType) {
+            AnnotatedType type = (AnnotatedType) annotated;
+            return type.getJavaClass().getClassLoader();
+        }
+
+        if (annotated instanceof SyntheticAnnotated) {
+            SyntheticAnnotated syntheticAnnotated = (SyntheticAnnotated) annotated;
+            Class<?> javaClass = syntheticAnnotated.getJavaClass();
+            if (javaClass != null) {
+                return javaClass.getClassLoader();
+            }
+        }
+
+        // Fallback to TCCL if available
+        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+        if (tccl != null) {
+            return tccl;
+        }
+
+        // If no TCCL is available, use the ClassLoader of this class
+        return CdiSpiHelper.class.getClassLoader();
+    }
 }
\ No newline at end of file
diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/SyntheticAnnotated.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/SyntheticAnnotated.java
index 9afa412..398f841 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/SyntheticAnnotated.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/SyntheticAnnotated.java
@@ -39,13 +39,24 @@ final class SyntheticAnnotated implements Annotated {
 
     private final Set<Annotation> annotations;
 
+    private final Class<?> javaClass;
+
     SyntheticAnnotated(Class<?> type, Set<Type> types, Annotation... annotations) {
-        this(type, types, asList(annotations));
+        this(type, types, null, asList(annotations));
     }
 
     SyntheticAnnotated(Class<?> type, Set<Type> types, Collection<Annotation> annotations) {
+        this(type, types, null, annotations);
+    }
+
+    SyntheticAnnotated(Class<?> type, Set<Type> types, Class<?> javaClass, Annotation... annotations) {
+        this(type, types, javaClass, asList(annotations));
+    }
+
+    SyntheticAnnotated(Class<?> type, Set<Type> types, Class<?> javaClass, Collection<Annotation> annotations) {
         this.type = type;
         this.types = types;
+        this.javaClass  = javaClass;
         this.annotations = new HashSet<>(annotations);
     }
 
@@ -83,4 +94,8 @@ final class SyntheticAnnotated implements Annotated {
     public boolean isAnnotationPresent(Class<? extends Annotation> type) {
         return annotations.stream().anyMatch(isAnnotationType(type));
     }
+
+    public Class<?> getJavaClass() {
+        return javaClass;
+    }
 }
diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/XmlCdiBeanFactory.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/XmlCdiBeanFactory.java
index 3ef81e2..549a322 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/XmlCdiBeanFactory.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/XmlCdiBeanFactory.java
@@ -62,6 +62,7 @@ import org.slf4j.LoggerFactory;
 
 import static org.apache.camel.cdi.AnyLiteral.ANY;
 import static org.apache.camel.cdi.ApplicationScopedLiteral.APPLICATION_SCOPED;
+import static org.apache.camel.cdi.CdiSpiHelper.createCamelContextWithTCCL;
 import static org.apache.camel.cdi.DefaultLiteral.DEFAULT;
 import static org.apache.camel.cdi.ResourceHelper.getResource;
 import static org.apache.camel.cdi.Startup.Literal.STARTUP;
@@ -109,7 +110,7 @@ final class XmlCdiBeanFactory {
                 ApplicationContextFactoryBean app = (ApplicationContextFactoryBean) node;
                 Set<SyntheticBean<?>> beans = new HashSet<>();
                 for (CamelContextFactoryBean factory : app.getContexts()) {
-                    SyntheticBean<?> bean = camelContextBean(factory, url);
+                    SyntheticBean<?> bean = camelContextBean(factory, url, annotatedType);
                     beans.add(bean);
                     beans.addAll(camelContextBeans(factory, bean, url));
                 }
@@ -137,7 +138,7 @@ final class XmlCdiBeanFactory {
             } else if (node instanceof CamelContextFactoryBean) {
                 CamelContextFactoryBean factory = (CamelContextFactoryBean) node;
                 Set<SyntheticBean<?>> beans = new HashSet<>();
-                SyntheticBean<?> bean = camelContextBean(factory, url);
+                SyntheticBean<?> bean = camelContextBean(factory, url, annotatedType);
                 beans.add(bean);
                 beans.addAll(camelContextBeans(factory, bean, url));
                 return beans;
@@ -152,7 +153,7 @@ final class XmlCdiBeanFactory {
         return emptySet();
     }
 
-    private SyntheticBean<?> camelContextBean(CamelContextFactoryBean factory, URL url) {
+    private SyntheticBean<?> camelContextBean(CamelContextFactoryBean factory, URL url, AnnotatedType annotatedType) {
         Set<Annotation> annotations = new HashSet<>();
         annotations.add(ANY);
         if (hasId(factory)) {
@@ -167,11 +168,13 @@ final class XmlCdiBeanFactory {
         annotations.add(APPLICATION_SCOPED);
         SyntheticAnnotated annotated = new SyntheticAnnotated(DefaultCamelContext.class,
             manager.createAnnotatedType(DefaultCamelContext.class).getTypeClosure(),
+            annotatedType.getJavaClass(),
             annotations);
+
         return new SyntheticBean<>(manager, annotated, DefaultCamelContext.class,
             environment.camelContextInjectionTarget(
                 new SyntheticInjectionTarget<>(() -> {
-                    DefaultCamelContext context = new DefaultCamelContext();
+                    DefaultCamelContext context = createCamelContextWithTCCL(DefaultCamelContext::new, annotated);
                     factory.setContext(context);
                     factory.setBeanManager(manager);
                     return context;
diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoTCCLSetTest.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoTCCLSetTest.java
new file mode 100644
index 0000000..a290362
--- /dev/null
+++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoTCCLSetTest.java
@@ -0,0 +1,66 @@
+/**
+ * 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.camel.cdi.test;
+
+import java.util.Set;
+
+import javax.enterprise.event.Observes;
+import javax.inject.Inject;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.cdi.CdiCamelConfiguration;
+import org.apache.camel.cdi.CdiCamelExtension;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+@RunWith(Arquillian.class)
+public class NoTCCLSetTest {
+
+    @Inject
+    CamelContext camelContext;
+
+    @Deployment
+    public static Archive<?> deployment() {
+        return ShrinkWrap.create(JavaArchive.class)
+            // Camel CDI
+            .addPackage(CdiCamelExtension.class.getPackage())
+            // Bean archive deployment descriptor
+            .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+    }
+
+    @Test
+    public void verifyNoTCCLFallbackClassLoader() {
+        assertThat(camelContext, is(notNullValue()));
+        Set<ClassLoader> classLoaders = camelContext.getPackageScanClassResolver().getClassLoaders();
+        assertThat(classLoaders.size(), is(1));
+        assertThat(classLoaders.iterator().next(), is(CamelContext.class.getClassLoader()));
+    }
+
+    private void configuration(@Observes CdiCamelConfiguration configuration) {
+        // Nullify TCCL before CamelContext creation
+        Thread.currentThread().setContextClassLoader(null);
+    }
+}