Repository: camel Updated Branches: refs/heads/camel-2.17.x 1b47ca4b2 -> b340d6990
CAMEL-9904: Avoid creating an empty default Camel context in Camel CDI for empty deployments Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/b340d699 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/b340d699 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/b340d699 Branch: refs/heads/camel-2.17.x Commit: b340d69906c84370a7d6025e079c8fb7858037fd Parents: 1b47ca4 Author: Antonin Stefanutti <anto...@stefanutti.fr> Authored: Mon Apr 25 11:43:01 2016 +0200 Committer: Antonin Stefanutti <anto...@stefanutti.fr> Committed: Mon Apr 25 11:43:01 2016 +0200 ---------------------------------------------------------------------- .../org/apache/camel/cdi/CdiCamelExtension.java | 122 +++++++++++++++---- .../main/java/org/apache/camel/cdi/Main.java | 9 +- .../camel/cdi/test/NamedCamelBeanTest.java | 24 ++-- .../camel/cdi/test/NoCamelContextTest.java | 59 +++++++++ 4 files changed, 168 insertions(+), 46 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/b340d699/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelExtension.java ---------------------------------------------------------------------- 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 19e3eba..dcb1e07 100755 --- 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 @@ -28,6 +28,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; + +import static java.util.Collections.addAll; import static java.util.Collections.newSetFromMap; import javax.enterprise.event.Observes; import javax.enterprise.inject.Default; @@ -35,6 +37,7 @@ import javax.enterprise.inject.InjectionException; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.AfterDeploymentValidation; +import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; @@ -54,6 +57,7 @@ import javax.inject.Named; import org.apache.camel.BeanInject; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; +import org.apache.camel.Component; import org.apache.camel.Consume; import org.apache.camel.ConsumerTemplate; import org.apache.camel.Converter; @@ -69,6 +73,8 @@ import org.apache.camel.model.RouteContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.cdi.CdiSpiHelper.getFirstElementOfType; +import static org.apache.camel.cdi.CdiSpiHelper.getRawType; import static org.apache.camel.cdi.CdiSpiHelper.hasAnnotation; public class CdiCamelExtension implements Extension { @@ -85,6 +91,8 @@ public class CdiCamelExtension implements Extension { private final Map<InjectionPoint, ForwardingObserverMethod<?>> cdiEventEndpoints = new ConcurrentHashMap<>(); + private final Set<Bean<?>> cdiBeans = newSetFromMap(new ConcurrentHashMap<Bean<?>, Boolean>()); + private final Set<Annotation> contextQualifiers = newSetFromMap(new ConcurrentHashMap<Annotation, Boolean>()); private final Set<Annotation> eventQualifiers = newSetFromMap(new ConcurrentHashMap<Annotation, Boolean>()); @@ -93,8 +101,6 @@ public class CdiCamelExtension implements Extension { private final Map<Method, Set<Annotation>> producerQualifiers = new ConcurrentHashMap<>(); - private final Set<ContextName> contextNames = newSetFromMap(new ConcurrentHashMap<ContextName, Boolean>()); - ForwardingObserverMethod<?> getObserverMethod(InjectionPoint ip) { return cdiEventEndpoints.get(ip); } @@ -122,10 +128,6 @@ public class CdiCamelExtension implements Extension { } } - private void camelContextAware(@Observes ProcessAnnotatedType<? extends CamelContextAware> pat) { - camelBeans.add(pat.getAnnotatedType()); - } - private <T extends CamelContext> void camelContextBeans(@Observes ProcessInjectionTarget<T> pit, BeanManager manager) { pit.setInjectionTarget(environment.camelContextInjectionTarget(pit.getInjectionTarget(), pit.getAnnotatedType(), manager, this)); } @@ -140,6 +142,10 @@ public class CdiCamelExtension implements Extension { } } + private <T extends CamelContextAware> void camelContextAware(@Observes ProcessInjectionTarget<T> pit, BeanManager manager) { + pit.setInjectionTarget(new CamelBeanInjectionTarget<>(pit.getInjectionTarget(), manager)); + } + private void cdiEventEndpoints(@Observes ProcessBean<?> pb) { for (InjectionPoint ip : pb.getBean().getInjectionPoints()) { if (!CdiEventEndpoint.class.equals(CdiSpiHelper.getRawType(ip.getType()))) { @@ -197,43 +203,49 @@ public class CdiCamelExtension implements Extension { } } - private <T extends RoutesBuilder> void routeBuilderBeans(@Observes ProcessBean<T> pb) { - if (pb.getAnnotated().isAnnotationPresent(ContextName.class)) { - contextNames.add(pb.getAnnotated().getAnnotation(ContextName.class)); - } - } - - private <T extends CamelContext> void camelContextBeans(@Observes ProcessBean<T> pb) { - contextQualifiers.addAll(pb.getBean().getQualifiers()); + private void beans(@Observes ProcessProducerField<?, ?> pb) { + cdiBeans.add(pb.getBean()); } - private <T extends CamelContext> void camelContextProducerFields(@Observes ProcessProducerField<T, ?> pb) { - contextQualifiers.addAll(pb.getBean().getQualifiers()); + private void beans(@Observes ProcessProducerMethod<?, ?> pb) { + cdiBeans.add(pb.getBean()); } - private <T extends CamelContext> void camelContextProducerMethods(@Observes ProcessProducerMethod<T, ?> pb) { - contextQualifiers.addAll(pb.getBean().getQualifiers()); + private void beans(@Observes ProcessBean<?> pb) { + cdiBeans.add(pb.getBean()); } private void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager manager) { - // Missing @ContextName Camel context qualifiers + Set<ContextName> contextNames = new HashSet<>(); + for (Bean<?> bean : cdiBeans) { + if (bean.getTypes().contains(CamelContext.class)) { + contextQualifiers.addAll(bean.getQualifiers()); + } else if (bean.getTypes().contains(RoutesBuilder.class) + || bean.getTypes().contains(RouteContainer.class)) { + ContextName name = getFirstElementOfType(bean.getQualifiers(), ContextName.class); + if (name != null) { + contextNames.add(name); + } + } + } contextNames.removeAll(contextQualifiers); - if (contextQualifiers.isEmpty() && contextNames.isEmpty()) { + + if (contextQualifiers.isEmpty() && contextNames.isEmpty() && shouldDeployDefaultCamelContext(cdiBeans)) { // Add a @Default Camel context bean if any abd.addBean(camelContextBean(manager, AnyLiteral.INSTANCE, DefaultLiteral.INSTANCE)); } else if (contextQualifiers.isEmpty() && contextNames.size() == 1) { // Add a @ContextName and @Default Camel context bean if only one - abd.addBean(camelContextBean(manager, AnyLiteral.INSTANCE, DefaultLiteral.INSTANCE, contextNames.iterator().next())); + ContextName name = contextNames.iterator().next(); + abd.addBean(camelContextBean(manager, AnyLiteral.INSTANCE, DefaultLiteral.INSTANCE, name)); + addAll(contextQualifiers, AnyLiteral.INSTANCE, DefaultLiteral.INSTANCE, name); } else { // Add missing @ContextName Camel context beans for (ContextName name : contextNames) { abd.addBean(camelContextBean(manager, AnyLiteral.INSTANCE, name)); + addAll(contextQualifiers, AnyLiteral.INSTANCE, name); } } - // Update @ContextName Camel context qualifiers - contextQualifiers.addAll(contextNames); - // Then update the Camel producer beans for (Map.Entry<Method, Bean<?>> producer : producerBeans.entrySet()) { Bean<?> bean = producer.getValue(); @@ -260,6 +272,64 @@ public class CdiCamelExtension implements Extension { } } + private boolean shouldDeployDefaultCamelContext(Set<Bean<?>> beans) { + // Is there a Camel bean with the @Default qualifier? + for (Bean<?> bean : beans) { + if (bean.getBeanClass().getPackage().equals(getClass().getPackage())) { + continue; + } + if (bean.getTypes().contains(CamelContextAware.class) + || bean.getTypes().contains(Component.class) + || bean.getTypes().contains(RouteContainer.class) + || bean.getTypes().contains(RoutesBuilder.class)) { + if (bean.getQualifiers().contains(DefaultLiteral.INSTANCE)) { + return true; + } + } + } + // Or a bean with Camel annotations? + for (AnnotatedType<?> type : camelBeans) { + for (Annotated field : type.getFields()) { + if (field.isAnnotationPresent(Consume.class) && field.getAnnotation(Consume.class).context().isEmpty() + || field.isAnnotationPresent(BeanInject.class) && field.getAnnotation(BeanInject.class).context().isEmpty() + || field.isAnnotationPresent(EndpointInject.class) && field.getAnnotation(EndpointInject.class).context().isEmpty() + || field.isAnnotationPresent(Produce.class) && field.getAnnotation(Produce.class).context().isEmpty() + || field.isAnnotationPresent(PropertyInject.class) && field.getAnnotation(PropertyInject.class).context().isEmpty()) { + return true; + } + } + for (Annotated method : type.getMethods()) { + if (method.isAnnotationPresent(Consume.class) && method.getAnnotation(Consume.class).context().isEmpty() + || method.isAnnotationPresent(BeanInject.class) && method.getAnnotation(BeanInject.class).context().isEmpty() + || method.isAnnotationPresent(EndpointInject.class) && method.getAnnotation(EndpointInject.class).context().isEmpty() + || method.isAnnotationPresent(Produce.class) && method.getAnnotation(Produce.class).context().isEmpty() + || method.isAnnotationPresent(PropertyInject.class) && method.getAnnotation(PropertyInject.class).context().isEmpty()) { + return true; + } + } + } + // Or an injection point for Camel primitives? + for (Bean<?> bean : beans) { + if (bean.getBeanClass().getPackage().equals(getClass().getPackage())) { + continue; + } + for (InjectionPoint ip : bean.getInjectionPoints()) { + if (!getRawType(ip.getType()).getPackage().getName().startsWith("org.apache.camel")) { + continue; + } + for (Annotation qualifier : ip.getQualifiers()) { + if (qualifier.annotationType().equals(Uri.class) + || qualifier.annotationType().equals(Mock.class) + || qualifier.annotationType().equals(Default.class)) { + return true; + } + } + } + } + + return false; + } + private Bean<?> camelContextBean(BeanManager manager, Annotation... qualifiers) { CdiCamelContextAnnotated annotated = new CdiCamelContextAnnotated(manager, qualifiers); return new CdiCamelContextBean(annotated, environment.camelContextInjectionTarget(new CamelContextDefaultProducer(), annotated, manager, this)); @@ -317,12 +387,12 @@ public class CdiCamelExtension implements Extension { } // Clean-up - converters.clear(); camelBeans.clear(); + cdiBeans.clear(); + converters.clear(); eagerBeans.clear(); producerBeans.clear(); producerQualifiers.clear(); - contextNames.clear(); } private boolean addRouteToContext(Bean<?> routeBean, Bean<?> contextBean, BeanManager manager, AfterDeploymentValidation adv) { http://git-wip-us.apache.org/repos/asf/camel/blob/b340d699/components/camel-cdi/src/main/java/org/apache/camel/cdi/Main.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/Main.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/Main.java index ebdae5b..4700959 100644 --- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/Main.java +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/Main.java @@ -102,11 +102,10 @@ public class Main extends MainSupport { private void warnIfNoCamelFound() { BeanManager manager = cdiContainer.getBeanManager(); - Set<Bean<?>> contexts = manager.getBeans(CamelContext.class); - // Warn if the default CDI Camel context has no routes - if (contexts.size() == 1 && BeanManagerHelper.getReference(manager, CamelContext.class, contexts.iterator().next()).getRoutes().isEmpty()) { - LOG.warn("Camel CDI main has started with no Camel routes! " - + "You may add some RouteBuilder beans to your project."); + Set<Bean<?>> contexts = manager.getBeans(CamelContext.class, AnyLiteral.INSTANCE); + // Warn if there is no CDI Camel contexts + if (contexts.isEmpty()) { + LOG.warn("Camel CDI main has started with no Camel context!"); } } http://git-wip-us.apache.org/repos/asf/camel/blob/b340d699/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NamedCamelBeanTest.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NamedCamelBeanTest.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NamedCamelBeanTest.java index cb93c55..fbb2879 100644 --- a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NamedCamelBeanTest.java +++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NamedCamelBeanTest.java @@ -18,7 +18,6 @@ package org.apache.camel.cdi.test; import java.util.concurrent.TimeUnit; -import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.cdi.CdiCamelExtension; @@ -27,7 +26,6 @@ import org.apache.camel.cdi.bean.NamedCamelBean; import org.apache.camel.component.mock.MockEndpoint; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; -import org.jboss.arquillian.junit.InSequence; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; @@ -52,19 +50,8 @@ public class NamedCamelBeanTest { } @Test - @InSequence(1) - public void configureCamelContext(CamelContext context) throws Exception { - context.addRoutes(new RouteBuilder() { - @Override - public void configure() { - from("direct:inbound").bean("beanName").to("mock:outbound"); - } - }); - } - - @Test - @InSequence(2) - public void sendMessageToInbound(@Uri("direct:inbound") ProducerTemplate in, @Uri("mock:outbound") MockEndpoint out) throws InterruptedException { + public void sendMessageToInbound(@Uri("direct:inbound") ProducerTemplate in, + @Uri("mock:outbound") MockEndpoint out) throws InterruptedException { out.expectedMessageCount(1); out.expectedBodiesReceived("test-processed"); @@ -72,4 +59,11 @@ public class NamedCamelBeanTest { assertIsSatisfied(2L, TimeUnit.SECONDS, out); } + + private static class TestRoute extends RouteBuilder { + @Override + public void configure() { + from("direct:inbound").bean("beanName").to("mock:outbound"); + } + } } http://git-wip-us.apache.org/repos/asf/camel/blob/b340d699/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoCamelContextTest.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoCamelContextTest.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoCamelContextTest.java new file mode 100644 index 0000000..c1d85ad --- /dev/null +++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/NoCamelContextTest.java @@ -0,0 +1,59 @@ +/** + * 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 org.apache.camel.CamelContext; +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 javax.enterprise.inject.Any; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + + +@RunWith(Arquillian.class) +public class NoCamelContextTest { + + @Any + @Inject + private Instance<CamelContext> contexts; + + @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 verifyDeployment() { + assertThat("Camel context beans are deployed!", contexts.isUnsatisfied(), is(equalTo(true))); + } +}