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);
+ }
+}