http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelContextNameStrategy.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelContextNameStrategy.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelContextNameStrategy.java new file mode 100644 index 0000000..4b14b9d --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelContextNameStrategy.java @@ -0,0 +1,45 @@ +/** + * 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; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.camel.impl.DefaultCamelContextNameStrategy; +import org.apache.camel.spi.CamelContextNameStrategy; + +/** + * A {@link CamelContextNameStrategy} for Camel contexts that are managed by Camel CDI. + * + * As opposed to {@link org.apache.camel.impl.DefaultCamelContextNameStrategy}, + * this implementation does not increment the suffix for proxies that are created + * each time a contextual reference to a normal-scoped bean is retrieved. + * + * It is used by Camel CDI for custom Camel context beans that do not override + * the context name nor the naming strategy. + * + * @see CamelContextNameStrategy + */ +@Vetoed +final class CdiCamelContextNameStrategy extends DefaultCamelContextNameStrategy implements CamelContextNameStrategy { + + private static final AtomicInteger CONTEXT_COUNTER = new AtomicInteger(0); + + @Override + public String getNextName() { + return "camel" + "-" + CONTEXT_COUNTER.incrementAndGet(); + } +}
http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelEnvironment.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelEnvironment.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelEnvironment.java new file mode 100644 index 0000000..b5cbf58 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelEnvironment.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; + +import javax.enterprise.inject.spi.Annotated; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.enterprise.inject.spi.Producer; + +import org.apache.camel.CamelContext; +import org.apache.camel.core.osgi.utils.BundleContextUtils; + +@Vetoed +final class CdiCamelEnvironment { + + private final boolean hasBundleContext; + + CdiCamelEnvironment() { + hasBundleContext = isCamelCoreOsgiPresent() && hasBundleContext(CdiCamelExtension.class); + } + + <T extends CamelContext> Producer<T> camelContextProducer(Producer<T> delegate, Annotated annotated, BeanManager manager, CdiCamelExtension extension) { + CamelContextProducer<T> producer = new CamelContextProducer<>(delegate, annotated, manager, extension); + return hasBundleContext ? new CamelContextOsgiProducer<>(producer) : producer; + } + + <T extends CamelContext> InjectionTarget<T> camelContextInjectionTarget(InjectionTarget<T> delegate, Annotated annotated, BeanManager manager, CdiCamelExtension extension) { + CamelContextProducer<T> producer = new CamelContextProducer<>(delegate, annotated, manager, extension); + return new CamelContextInjectionTarget<>(delegate, hasBundleContext ? new CamelContextOsgiProducer<>(producer) : producer); + } + + private static boolean isCamelCoreOsgiPresent() { + try { + getClassLoader(CdiCamelExtension.class).loadClass("org.apache.camel.core.osgi.OsgiCamelContextHelper"); + return true; + } catch (ClassNotFoundException cause) { + return false; + } + } + + private static boolean hasBundleContext(Class clazz) { + return BundleContextUtils.getBundleContext(clazz) != null; + } + + private static ClassLoader getClassLoader(Class<?> clazz) { + if (Thread.currentThread().getContextClassLoader() != null) { + return Thread.currentThread().getContextClassLoader(); + } else { + return clazz.getClassLoader(); + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/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 new file mode 100755 index 0000000..6951cc3 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelExtension.java @@ -0,0 +1,328 @@ +/** + * 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; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import static java.util.Collections.newSetFromMap; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Default; +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; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.ObserverMethod; +import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.spi.ProcessBean; +import javax.enterprise.inject.spi.ProcessInjectionTarget; +import javax.enterprise.inject.spi.ProcessObserverMethod; +import javax.enterprise.inject.spi.ProcessProducer; +import javax.enterprise.inject.spi.ProcessProducerField; +import javax.enterprise.inject.spi.ProcessProducerMethod; +import javax.inject.Named; + +import org.apache.camel.BeanInject; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.Consume; +import org.apache.camel.Converter; +import org.apache.camel.Endpoint; +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.PropertyInject; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.ServiceStatus; +import org.apache.camel.management.event.AbstractExchangeEvent; +import org.apache.camel.model.RouteContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.camel.cdi.CdiSpiHelper.hasAnnotation; + +public class CdiCamelExtension implements Extension { + + private final Logger logger = LoggerFactory.getLogger(CdiCamelExtension.class); + + private final CdiCamelEnvironment environment = new CdiCamelEnvironment(); + + private final Set<Class<?>> converters = newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>()); + + private final Set<AnnotatedType<?>> camelBeans = newSetFromMap(new ConcurrentHashMap<AnnotatedType<?>, Boolean>()); + + private final Set<AnnotatedType<?>> eagerBeans = newSetFromMap(new ConcurrentHashMap<AnnotatedType<?>, Boolean>()); + + private final Map<InjectionPoint, ForwardingObserverMethod<?>> cdiEventEndpoints = new ConcurrentHashMap<>(); + + private final Map<Annotated, Bean<?>> contextBeans = new ConcurrentHashMap<>(); + + private final Set<Annotation> contextQualifiers = newSetFromMap(new ConcurrentHashMap<Annotation, Boolean>()); + + private final Set<Annotation> eventQualifiers = newSetFromMap(new ConcurrentHashMap<Annotation, Boolean>()); + + private final Map<Method, Bean<?>> producerBeans = new ConcurrentHashMap<>(); + + private final Map<Method, Set<Annotation>> producerQualifiers = new ConcurrentHashMap<>(); + + ForwardingObserverMethod<?> getObserverMethod(InjectionPoint ip) { + return cdiEventEndpoints.get(ip); + } + + Set<Annotation> getObserverEvents() { + return eventQualifiers; + } + + Bean<?> getContextBean(Annotated annotated) { + return contextBeans.get(annotated); + } + + Set<Annotation> getContextQualifiers() { + return contextQualifiers; + } + + private void processAnnotatedType(@Observes ProcessAnnotatedType<?> pat) { + if (pat.getAnnotatedType().isAnnotationPresent(Vetoed.class)) { + pat.veto(); + } + if (hasAnnotation(pat.getAnnotatedType(), Converter.class)) { + converters.add(pat.getAnnotatedType().getJavaClass()); + } + if (hasAnnotation(pat.getAnnotatedType(), BeanInject.class, Consume.class, EndpointInject.class, Produce.class, PropertyInject.class)) { + camelBeans.add(pat.getAnnotatedType()); + } + if (hasAnnotation(pat.getAnnotatedType(), Consume.class)) { + eagerBeans.add(pat.getAnnotatedType()); + } + } + + 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)); + } + + private <T extends CamelContext> void camelContextProducers(@Observes ProcessProducer<?, T> pp, BeanManager manager) { + pp.setProducer(environment.camelContextProducer(pp.getProducer(), pp.getAnnotatedMember(), manager, this)); + } + + private <T> void camelBeansPostProcessor(@Observes ProcessInjectionTarget<T> pit, BeanManager manager) { + if (camelBeans.contains(pit.getAnnotatedType())) { + 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()))) { + continue; + } + // TODO: refine the key to the type and qualifiers instead of the whole injection point as it leads to registering redundant observers + if (ip.getType() instanceof ParameterizedType) { + cdiEventEndpoints.put(ip, new ForwardingObserverMethod<>(((ParameterizedType) ip.getType()).getActualTypeArguments()[0], ip.getQualifiers())); + } else if (ip.getType() instanceof Class) { + cdiEventEndpoints.put(ip, new ForwardingObserverMethod<>(Object.class, ip.getQualifiers())); + } + } + } + + private <T extends Endpoint> void endpointBeans(@Observes ProcessProducerMethod<T, CdiCamelFactory> ppm) { + producerBeans.put(ppm.getAnnotatedProducerMethod().getJavaMember(), ppm.getBean()); + } + + private void producerTemplateBeans(@Observes ProcessProducerMethod<ProducerTemplate, CdiCamelFactory> ppm) { + producerBeans.put(ppm.getAnnotatedProducerMethod().getJavaMember(), ppm.getBean()); + } + + private void camelFactoryProducers(@Observes ProcessAnnotatedType<CdiCamelFactory> pat, BeanManager manager) { + AnnotatedType<CdiCamelFactory> at = pat.getAnnotatedType(); + Set<AnnotatedMethod<? super CdiCamelFactory>> methods = new HashSet<>(); + for (AnnotatedMethod<? super CdiCamelFactory> am : pat.getAnnotatedType().getMethods()) { + if (!am.isAnnotationPresent(Produces.class)) { + continue; + } + Class<?> type = CdiSpiHelper.getRawType(am.getBaseType()); + if (Endpoint.class.isAssignableFrom(type) || ProducerTemplate.class.equals(type)) { + Set<Annotation> qualifiers = CdiSpiHelper.getQualifiers(am, manager); + producerQualifiers.put(am.getJavaMember(), qualifiers.isEmpty() ? Collections.<Annotation>singleton(DefaultLiteral.INSTANCE) : qualifiers); + Set<Annotation> annotations = new HashSet<>(am.getAnnotations()); + annotations.removeAll(qualifiers); + annotations.add(Excluded.INSTANCE); + methods.add(new AnnotatedMethodDelegate<>(am, annotations)); + } + } + pat.setAnnotatedType(new AnnotatedTypeDelegate<>(at, methods)); + } + + private <T extends EventObject> void camelEventNotifiers(@Observes ProcessObserverMethod<T, ?> pom) { + // Only activate Camel event notifiers for explicit Camel event observers, that is, an observer method for a super type won't activate notifiers. + Type type = pom.getObserverMethod().getObservedType(); + // Camel events are raw types + if (type instanceof Class && Class.class.cast(type).getPackage().equals(AbstractExchangeEvent.class.getPackage())) { + eventQualifiers.addAll(pom.getObserverMethod().getObservedQualifiers().isEmpty() ? Collections.singleton(AnyLiteral.INSTANCE) : pom.getObserverMethod().getObservedQualifiers()); + } + } + + private <T extends CamelContext> void camelContextBeans(@Observes ProcessBean<T> pb) { + processCamelContextBean(pb.getAnnotated(), pb.getBean()); + } + + private <T extends CamelContext> void camelContextProducerFields(@Observes ProcessProducerField<T, ?> pb) { + processCamelContextBean(pb.getAnnotated(), pb.getBean()); + } + + private <T extends CamelContext> void camelContextProducerMethods(@Observes ProcessProducerMethod<T, ?> pb) { + processCamelContextBean(pb.getAnnotated(), pb.getBean()); + } + + private void processCamelContextBean(Annotated annotated, Bean<?> bean) { + contextQualifiers.addAll(bean.getQualifiers()); + // Annotated must be wrapped because of OWB-1099 + contextBeans.put(new AnnotatedWrapper(annotated), bean); + } + + private void cdiCamelFactoryProducers(@Observes AfterBeanDiscovery abd) { + for (Map.Entry<Method, Bean<?>> producer : producerBeans.entrySet()) { + Bean<?> bean = producer.getValue(); + Set<Annotation> qualifiers = new HashSet<>(producerQualifiers.get(producer.getKey())); + Class<?> type = producer.getKey().getReturnType(); + if (CdiEventEndpoint.class.equals(type)) { + for (InjectionPoint ip : cdiEventEndpoints.keySet()) { + qualifiers.addAll(ip.getQualifiers()); + } + } else { + if (Endpoint.class.isAssignableFrom(type) || ProducerTemplate.class.isAssignableFrom(type)) { + qualifiers.addAll(CdiSpiHelper.excludeElementOfTypes(contextQualifiers, Any.class, Default.class, Named.class)); + } + } + // TODO: would be more correct to add a bean for each Camel context bean + abd.addBean(new BeanDelegate<>(bean, qualifiers)); + } + } + + private void addDefaultCamelContext(@Observes AfterBeanDiscovery abd, BeanManager manager) { + if (contextBeans.isEmpty()) { + abd.addBean(new CdiCamelContextBean(manager, environment.camelContextInjectionTarget(new CamelContextDefaultProducer(), null, manager, CdiCamelExtension.this))); + } + } + + private void addCdiEventObserverMethods(@Observes AfterBeanDiscovery abd) { + for (ObserverMethod method : cdiEventEndpoints.values()) { + abd.addObserverMethod(method); + } + } + + private void createCamelContexts(@Observes AfterDeploymentValidation adv, BeanManager manager) { + Collection<CamelContext> contexts = new ArrayList<>(); + for (Bean<?> context : manager.getBeans(CamelContext.class, AnyLiteral.INSTANCE)) { + contexts.add(BeanManagerHelper.getReference(manager, CamelContext.class, context)); + } + + // Add type converters to Camel contexts + CdiTypeConverterLoader loader = new CdiTypeConverterLoader(); + for (Class<?> converter : converters) { + for (CamelContext context : contexts) { + loader.loadConverterMethods(context.getTypeConverterRegistry(), converter); + } + } + + // Add routes to Camel contexts + boolean deploymentException = false; + Set<Bean<?>> routes = new HashSet<>(manager.getBeans(RoutesBuilder.class, AnyLiteral.INSTANCE)); + routes.addAll(manager.getBeans(RouteContainer.class, AnyLiteral.INSTANCE)); + for (Bean<?> context : manager.getBeans(CamelContext.class, AnyLiteral.INSTANCE)) { + for (Bean<?> route : routes) { + Set<Annotation> qualifiers = new HashSet<>(context.getQualifiers()); + qualifiers.retainAll(route.getQualifiers()); + if (qualifiers.size() > 1) { + deploymentException |= !addRouteToContext(route, context, manager, adv); + } + } + } + // Let's return to avoid starting misconfigured contexts + if (deploymentException) { + return; + } + + // Trigger eager beans instantiation + for (AnnotatedType<?> type : eagerBeans) { + // Calling toString is necessary to force the initialization of normal-scoped beans + BeanManagerHelper.getReferencesByType(manager, type.getJavaClass(), AnyLiteral.INSTANCE).toString(); + } + + // Start Camel contexts + for (CamelContext context : contexts) { + if (ServiceStatus.Started.equals(context.getStatus())) { + continue; + } + logger.info("Camel CDI is starting Camel context [{}]", context.getName()); + try { + context.start(); + } catch (Exception exception) { + adv.addDeploymentProblem(exception); + } + } + + // Clean-up + converters.clear(); + camelBeans.clear(); + eagerBeans.clear(); + producerBeans.clear(); + producerQualifiers.clear(); + } + + private boolean addRouteToContext(Bean<?> routeBean, Bean<?> contextBean, BeanManager manager, AfterDeploymentValidation adv) { + try { + CamelContext context = BeanManagerHelper.getReference(manager, CamelContext.class, contextBean); + try { + Object route = BeanManagerHelper.getReference(manager, Object.class, routeBean); + if (route instanceof RoutesBuilder) { + context.addRoutes((RoutesBuilder) route); + } else if (route instanceof RouteContainer) { + context.addRouteDefinitions(((RouteContainer) route).getRoutes()); + } else { + throw new IllegalArgumentException("Invalid routes type [" + routeBean.getBeanClass().getName() + "], must be either of type RoutesBuilder or RouteContainer!"); + } + return true; + } catch (Exception cause) { + adv.addDeploymentProblem(new InjectionException("Error adding routes of type [" + routeBean.getBeanClass().getName() + "] to Camel context [" + context.getName() + "]", cause)); + } + } catch (Exception exception) { + adv.addDeploymentProblem(exception); + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelFactory.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelFactory.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelFactory.java new file mode 100755 index 0000000..cdb2336 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelFactory.java @@ -0,0 +1,211 @@ +/** + * 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; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import javax.enterprise.event.Event; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.InjectionException; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.Typed; +import javax.enterprise.inject.UnsatisfiedResolutionException; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.util.TypeLiteral; + +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.TypeConverter; +import org.apache.camel.component.mock.MockEndpoint; + +final class CdiCamelFactory { + + @Produces + private static TypeConverter typeConverter(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension) { + return selectContext(ip, instance, extension).getTypeConverter(); + } + + @Uri("") + @Produces + // Qualifiers are dynamically added in CdiCamelExtension + private static ProducerTemplate producerTemplate(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension) { + Uri uri = CdiSpiHelper.getQualifierByType(ip, Uri.class); + try { + CamelContext context = uri.context().isEmpty() ? selectContext(ip, instance, extension) : selectContext(uri.context(), instance); + ProducerTemplate producerTemplate = context.createProducerTemplate(); + // FIXME: avoid NPE caused by missing @Uri qualifier when injection point is @ContextName qualified + Endpoint endpoint = context.getEndpoint(uri.value(), Endpoint.class); + producerTemplate.setDefaultEndpoint(endpoint); + return producerTemplate; + } catch (Exception cause) { + throw new InjectionException("Error injecting producer template annotated with " + uri + " into " + ip, cause); + } + } + + @Produces + @Typed(MockEndpoint.class) + // Qualifiers are dynamically added in CdiCamelExtension + private static MockEndpoint mockEndpointFromMember(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension) { + String uri = "mock:" + ip.getMember().getName(); + try { + return selectContext(ip, instance, extension).getEndpoint(uri, MockEndpoint.class); + } catch (Exception cause) { + throw new InjectionException("Error injecting mock endpoint into " + ip, cause); + } + } + + @Uri("") + @Produces + @Typed(MockEndpoint.class) + // Qualifiers are dynamically added in CdiCamelExtension + private static MockEndpoint mockEndpointFromUri(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension) { + Uri uri = CdiSpiHelper.getQualifierByType(ip, Uri.class); + try { + CamelContext context = uri.context().isEmpty() ? selectContext(ip, instance, extension) : selectContext(uri.context(), instance); + return context.getEndpoint(uri.value(), MockEndpoint.class); + } catch (Exception cause) { + throw new InjectionException("Error injecting mock endpoint annotated with " + uri + + " into " + ip, cause); + } + } + + // Maintained for backward compatibility reason though this is redundant with @Uri + // see https://issues.apache.org/jira/browse/CAMEL-5553?focusedCommentId=13445936&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13445936 + @Mock("") + @Produces + @Typed(MockEndpoint.class) + // Qualifiers are dynamically added in CdiCamelExtension + private static MockEndpoint createMockEndpoint(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension) { + Mock mock = CdiSpiHelper.getQualifierByType(ip, Mock.class); + try { + CamelContext context = mock.context().isEmpty() ? selectContext(ip, instance, extension) : selectContext(mock.context(), instance); + return context.getEndpoint(mock.value(), MockEndpoint.class); + } catch (Exception cause) { + throw new InjectionException("Error injecting mock endpoint annotated with " + mock + " into " + ip, cause); + } + } + + @Uri("") + @Produces + // Qualifiers are dynamically added in CdiCamelExtension + private static Endpoint endpoint(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension) { + Uri uri = CdiSpiHelper.getQualifierByType(ip, Uri.class); + try { + CamelContext context = uri.context().isEmpty() ? selectContext(ip, instance, extension) : selectContext(uri.context(), instance); + return context.getEndpoint(uri.value(), Endpoint.class); + } catch (Exception cause) { + throw new InjectionException("Error injecting endpoint annotated with " + uri + " into " + ip, cause); + } + } + + @Produces + @SuppressWarnings("unchecked") + // Qualifiers are dynamically added in CdiCamelExtension + private static <T> CdiEventEndpoint<T> cdiEventEndpoint(InjectionPoint ip, @Any Instance<CamelContext> instance, CdiCamelExtension extension, @Any Event<Object> event) throws Exception { + CamelContext context = selectContext(ip, instance, extension); + Type type = Object.class; + if (ip.getType() instanceof ParameterizedType) { + type = ((ParameterizedType) ip.getType()).getActualTypeArguments()[0]; + } + String uri = eventEndpointUri(type, ip.getQualifiers()); + if (context.hasEndpoint(uri) == null) { + // FIXME: to be replaced once event firing with dynamic parameterized type is properly supported (see https://issues.jboss.org/browse/CDI-516) + TypeLiteral<T> literal = new TypeLiteral<T>() { + }; + for (Field field : TypeLiteral.class.getDeclaredFields()) { + if (field.getType().equals(Type.class)) { + field.setAccessible(true); + field.set(literal, type); + break; + } + } + context.addEndpoint(uri, + new CdiEventEndpoint<>( + event.select(literal, ip.getQualifiers().toArray(new Annotation[ip.getQualifiers().size()])), uri, context, (ForwardingObserverMethod<T>) extension.getObserverMethod(ip))); + } + return context.getEndpoint(uri, CdiEventEndpoint.class); + } + + private static <T extends CamelContext> T selectContext(String name, Instance<T> instance) { + for (T context : instance) { + if (name.equals(context.getName())) { + return context; + } + } + throw new UnsatisfiedResolutionException("No Camel context with name [" + name + "] is deployed!"); + } + + private static <T extends CamelContext> T selectContext(InjectionPoint ip, Instance<T> instance, CdiCamelExtension extension) { + Collection<Annotation> qualifiers = new HashSet<>(ip.getQualifiers()); + qualifiers.retainAll(extension.getContextQualifiers()); + if (qualifiers.isEmpty() && !instance.select(DefaultLiteral.INSTANCE).isUnsatisfied()) { + return instance.select(DefaultLiteral.INSTANCE).get(); + } + return instance.select(qualifiers.toArray(new Annotation[qualifiers.size()])).get(); + } + + private static String eventEndpointUri(Type type, Set<Annotation> qualifiers) { + String uri = "cdi-event://" + authorityFromType(type); + StringBuilder parameters = new StringBuilder(); + Iterator<Annotation> it = qualifiers.iterator(); + while (it.hasNext()) { + parameters.append(it.next().annotationType().getCanonicalName()); + if (it.hasNext()) { + parameters.append("%2C"); + } + } + if (parameters.length() > 0) { + uri += "?qualifiers=" + parameters.toString(); + } + return uri; + } + + private static String authorityFromType(Type type) { + if (type instanceof Class) { + return Class.class.cast(type).getName(); + } + if (type instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) type; + StringBuilder builder = new StringBuilder(authorityFromType(pt.getRawType())); + Iterator<Type> it = Arrays.asList(pt.getActualTypeArguments()).iterator(); + builder.append("%3C"); + while (it.hasNext()) { + builder.append(authorityFromType(it.next())); + if (it.hasNext()) { + builder.append("%2C"); + } + } + builder.append("%3E"); + return builder.toString(); + } + if (type instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType) type; + return authorityFromType(arrayType.getGenericComponentType()) + "%5B%5D"; + } + throw new IllegalArgumentException("Cannot create URI authority for event type [" + type + "]"); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java new file mode 100755 index 0000000..471b7f0 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelInjector.java @@ -0,0 +1,48 @@ +/** + * 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; + +import javax.enterprise.inject.spi.BeanManager; + +import org.apache.camel.spi.Injector; + +final class CdiCamelInjector implements Injector { + + private final Injector injector; + + private final BeanManager manager; + + CdiCamelInjector(Injector injector, BeanManager manager) { + this.injector = injector; + this.manager = manager; + } + + @Override + public <T> T newInstance(Class<T> type) { + T instance = BeanManagerHelper.getReferenceByType(manager, type); + if (instance != null) { + return instance; + } else { + return injector.newInstance(type); + } + } + + @Override + public <T> T newInstance(Class<T> type, Object instance) { + return injector.newInstance(type, instance); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelRegistry.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelRegistry.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelRegistry.java new file mode 100755 index 0000000..946fecf --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiCamelRegistry.java @@ -0,0 +1,98 @@ +/** + * 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; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; + +import org.apache.camel.component.properties.PropertiesComponent; +import org.apache.camel.spi.Registry; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link Registry} used by Camel to perform lookup into the CDI {@link BeanManager}. + */ +@Vetoed +final class CdiCamelRegistry implements Registry { + + private final Logger logger = LoggerFactory.getLogger(CdiCamelRegistry.class); + + private final BeanManager manager; + + CdiCamelRegistry(BeanManager manager) { + this.manager = manager; + } + + @Override + public Object lookupByName(String name) { + ObjectHelper.notEmpty(name, "name"); + logger.trace("Looking up bean with name [{}]", name); + // Work-around for WELD-2089 + if ("properties".equals(name) && findByTypeWithName(PropertiesComponent.class).containsKey("properties")) { + return BeanManagerHelper.getReferenceByName(manager, name, PropertiesComponent.class); + } + return BeanManagerHelper.getReferenceByName(manager, name, Object.class); + } + + @Override + public <T> T lookupByNameAndType(String name, Class<T> type) { + ObjectHelper.notEmpty(name, "name"); + ObjectHelper.notNull(type, "type"); + logger.trace("Looking up bean with name [{}] of type [{}]", name, type); + return BeanManagerHelper.getReferenceByName(manager, name, type); + } + + @Override + public <T> Map<String, T> findByTypeWithName(Class<T> type) { + ObjectHelper.notNull(type, "type"); + logger.trace("Looking up named beans of type [{}]", type); + Map<String, T> references = new HashMap<>(); + for (Bean<?> bean : manager.getBeans(type, AnyLiteral.INSTANCE)) { + if (bean.getName() != null) { + references.put(bean.getName(), BeanManagerHelper.getReference(manager, type, bean)); + } + } + return references; + } + + @Override + public <T> Set<T> findByType(Class<T> type) { + ObjectHelper.notNull(type, "type"); + logger.trace("Looking up beans of type [{}]", type); + return BeanManagerHelper.getReferencesByType(manager, type, AnyLiteral.INSTANCE); + } + + @Override + public Object lookup(String name) { + return lookupByName(name); + } + + @Override + public <T> T lookup(String name, Class<T> type) { + return lookupByNameAndType(name, type); + } + + @Override + public <T> Map<String, T> lookupByType(Class<T> type) { + return findByTypeWithName(type); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventComponent.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventComponent.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventComponent.java new file mode 100644 index 0000000..ffc4d89 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventComponent.java @@ -0,0 +1,53 @@ +/** + * 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; + +import java.util.Map; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; + +import org.apache.camel.Endpoint; +import org.apache.camel.impl.DefaultComponent; + +@Named("cdi-event") +@ApplicationScoped +/* package-private */ class CdiEventComponent extends DefaultComponent { + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) { + /* The CDI event endpoint URI follows the format hereafter: + + cdi-event://PayloadType<T1,...,Tn>[?qualifiers=QualifierType1[,...[,QualifierTypeN]...]] + + with the authority PayloadType (respectively the QualifierType) being the URI escaped fully + qualified name of the payload (respectively qualifier) raw type followed by the type parameters + section delimited by angle brackets for payload parameterized type. + + Which leads to unfriendly URIs, e.g.: + + cdi-event://org.apache.camel.cdi.se.pojo.EventPayload%3Cjava.lang.Integer%3E?qualifiers=org.apache.camel.cdi.se.qualifier.FooQualifier%2Corg.apache.camel.cdi.se.qualifier.BarQualifier + + From the conceptual standpoint, that shows the high impedance between the typesafe nature of CDI + and the dynamic nature of the Camel component model. + + From the implementation standpoint, that would prevent efficient binding between the endpoint + instances and observer methods as the CDI container doesn't have any ways of discovering the + Camel context model during the deployment phase. + */ + throw new UnsupportedOperationException("Creating CDI event endpoint isn't supported. Use @Inject " + CdiEventEndpoint.class.getSimpleName() + " instead"); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventConsumer.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventConsumer.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventConsumer.java new file mode 100644 index 0000000..f479163 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventConsumer.java @@ -0,0 +1,71 @@ +/** + * 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; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.RuntimeExchangeException; +import org.apache.camel.impl.DefaultConsumer; +import org.apache.camel.management.event.AbstractExchangeEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* package-private */ final class CdiEventConsumer<T> extends DefaultConsumer { + + private final Logger logger = LoggerFactory.getLogger(CdiEventConsumer.class); + + private final CdiEventEndpoint<T> endpoint; + + CdiEventConsumer(CdiEventEndpoint<T> endpoint, Processor processor) { + super(endpoint, processor); + this.endpoint = endpoint; + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + endpoint.registerConsumer(this); + } + + @Override + protected void doStop() throws Exception { + endpoint.unregisterConsumer(this); + super.doStop(); + } + + void notify(T event) { + logger.debug("Consuming CDI event [{}] with {}", event, this); + + Exchange exchange = getEndpoint().createExchange(); + // TODO: would that be possible to propagate the event metadata? + exchange.getIn().setBody(event); + + // Avoid infinite loop of exchange events + if (event instanceof AbstractExchangeEvent) { + exchange.setProperty(Exchange.NOTIFY_EVENT, Boolean.TRUE); + } + try { + getProcessor().process(exchange); + } catch (Exception cause) { + throw new RuntimeExchangeException("Error while processing CDI event", exchange, cause); + } finally { + if (event instanceof AbstractExchangeEvent) { + exchange.setProperty(Exchange.NOTIFY_EVENT, Boolean.FALSE); + } + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventEndpoint.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventEndpoint.java new file mode 100644 index 0000000..a003609 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventEndpoint.java @@ -0,0 +1,127 @@ +/** + * 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; + +import java.util.ArrayList; +import java.util.List; +import javax.enterprise.event.Event; + +import org.apache.camel.CamelContext; +import org.apache.camel.Consumer; +import org.apache.camel.Endpoint; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.impl.DefaultEndpoint; + +/** + * A Camel {@link Endpoint} that bridges the CDI events facility with Camel routes so that CDI events + * can be seamlessly observed / consumed (respectively produced / fired) from Camel consumers (respectively by Camel producers). + * <p/> + * The {@code CdiEventEndpoint<T>} bean can be used to observe / consume CDI events whose event type is {@code T}, for example: + * <pre><code> + * {@literal @}Inject + * CdiEventEndpoint{@literal <}String{@literal >} cdiEventEndpoint; + * + * from(cdiEventEndpoint).log("CDI event received: ${body}"); + * </code></pre> + * + * Conversely, the {@code CdiEventEndpoint<T>} bean can be used to produce / fire CDI events whose event type is {@code T}, for example: + * <pre><code> + * {@literal @}Inject + * CdiEventEndpoint{@literal <}String{@literal >} cdiEventEndpoint; + * + * from("direct:event").to(cdiEventEndpoint).log("CDI event sent: ${body}"); + * </code></pre> + * + * The type variable {@code T}, respectively the qualifiers, of a particular {@code CdiEventEndpoint<T>} injection point + * are automatically translated into the parameterized <i>event type</i>, respectively into the <i>event qualifiers</i>, e.g.: + * <pre><code> + * {@literal @}Inject + * {@literal @}FooQualifier + * CdiEventEndpoint{@literal <}List{@literal <}String{@literal >}{@literal >} cdiEventEndpoint; + * + * from("direct:event").to(cdiEventEndpoint); + * + * void observeCdiEvents({@literal @}Observes {@literal @}FooQualifier List{@literal <}String{@literal >} event) { + * logger.info("CDI event: {}", event); + * } + * </code></pre> + * + * When multiple Camel contexts exist in the CDI container, the {@code @ContextName} qualifier can be used + * to qualify the {@code CdiEventEndpoint<T>} injection points, e.g.: + * <pre><code> + * {@literal @}Inject + * {@literal @}ContextName("foo") + * CdiEventEndpoint{@literal <}List{@literal <}String{@literal >}{@literal >} cdiEventEndpoint; + * + * // Only observe / consume events having the {@literal @}ContextName("foo") qualifier + * from(cdiEventEndpoint).log("Camel context 'foo' > CDI event received: ${body}"); + * + * // Produce / fire events with the {@literal @}ContextName("foo") qualifier + * from("...").to(cdiEventEndpoint); + * + * void observeCdiEvents({@literal @}Observes {@literal @}ContextName("foo") List{@literal <}String{@literal >} event) { + * logger.info("Camel context 'foo' > CDI event: {}", event); + * } + * </code></pre> + */ +public final class CdiEventEndpoint<T> extends DefaultEndpoint { + + private final List<CdiEventConsumer<T>> consumers = new ArrayList<>(); + + private final Event<T> event; + + CdiEventEndpoint(Event<T> event, String endpointUri, CamelContext context, ForwardingObserverMethod<T> observer) { + super(endpointUri, context); + this.event = event; + observer.setObserver(this); + } + + public Consumer createConsumer(Processor processor) { + return new CdiEventConsumer<>(this, processor); + } + + @Override + public Producer createProducer() { + return new CdiEventProducer<>(this, event); + } + + @Override + public boolean isSingleton() { + return true; + } + + void registerConsumer(CdiEventConsumer<T> consumer) { + synchronized (consumers) { + consumers.add(consumer); + } + } + + void unregisterConsumer(CdiEventConsumer<T> consumer) { + synchronized (consumers) { + consumers.remove(consumer); + } + } + + void notify(T t) { + synchronized (consumers) { + for (CdiEventConsumer<T> consumer : consumers) { + consumer.notify(t); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventNotifier.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventNotifier.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventNotifier.java new file mode 100644 index 0000000..e9d27e7 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventNotifier.java @@ -0,0 +1,47 @@ +/** + * 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; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.EventObject; +import javax.enterprise.inject.spi.BeanManager; + +import org.apache.camel.support.EventNotifierSupport; + +final class CdiEventNotifier extends EventNotifierSupport { + + private final BeanManager manager; + + private final Annotation[] qualifiers; + + CdiEventNotifier(BeanManager manager, Collection<Annotation> qualifiers) { + this.manager = manager; + this.qualifiers = qualifiers.toArray(new Annotation[qualifiers.size()]); + // TODO: be more fine grained for the kind of events that are emitted depending on the observed event types + } + + @Override + public void notify(EventObject event) { + manager.fireEvent(event, qualifiers); + } + + @Override + public boolean isEnabled(EventObject event) { + return true; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventProducer.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventProducer.java new file mode 100644 index 0000000..11cb274 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiEventProducer.java @@ -0,0 +1,47 @@ +/** + * 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; + +import javax.enterprise.event.Event; +import org.apache.camel.Exchange; + +import org.apache.camel.impl.DefaultProducer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* package-private */ final class CdiEventProducer<T> extends DefaultProducer { + + private final Logger logger = LoggerFactory.getLogger(CdiEventProducer.class); + + private final Event<T> event; + + CdiEventProducer(CdiEventEndpoint<T> endpoint, Event<T> event) { + super(endpoint); + this.event = event; + } + + @Override + public void process(Exchange exchange) { + logger.debug("Firing CDI event [{}] with {}", event, this); + // TODO: leverage Camel type converter mechanism based on the endpoint type + // The EventMetadata injection point will be that of the event which is not very useful + // for the end user. Using BeanManager.fire would be a way to hide that internal though + // it will be necessary to check whether the exchange event type is assignable to the + // endpoint event type. + event.fire((T) exchange.getIn().getBody()); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiInjector.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiInjector.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiInjector.java deleted file mode 100644 index afb3a49..0000000 --- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiInjector.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * 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; - -import org.apache.camel.IsSingleton; -import org.apache.camel.spi.Injector; -import org.apache.camel.util.ReflectionInjector; -import org.apache.deltaspike.core.api.provider.BeanProvider; - -/** - * Injector implementation which performs injection with CDI bean provider. - */ -public class CdiInjector implements Injector { - - /** - * Fallback injector used when there is bean of given type registered in CDI. - */ - private Injector injector; - - public CdiInjector() { - this(new ReflectionInjector()); - } - - public CdiInjector(Injector parent) { - this.injector = parent; - } - - @Override - public <T> T newInstance(Class<T> type) { - T bean = BeanProvider.getContextualReference(type, true); - if (bean != null) { - return type.cast(bean); - } - return injector.newInstance(type); - } - - @Override - public <T> T newInstance(Class<T> type, Object instance) { - if (instance instanceof IsSingleton) { - boolean singleton = ((IsSingleton) instance).isSingleton(); - if (singleton) { - return type.cast(instance); - } - } - return newInstance(type); - } - -} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiSpiHelper.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..5bf852e --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiSpiHelper.java @@ -0,0 +1,143 @@ +/** + * 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; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import javax.enterprise.inject.spi.Annotated; +import javax.enterprise.inject.spi.AnnotatedConstructor; +import javax.enterprise.inject.spi.AnnotatedField; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionPoint; + +import org.apache.camel.util.ObjectHelper; + +@Vetoed +final class CdiSpiHelper { + + private CdiSpiHelper() { + } + + static <T extends Annotation> T getQualifierByType(InjectionPoint ip, Class<T> type) { + return getFirstElementOfType(ip.getQualifiers(), type); + } + + private static <E, T extends E> T getFirstElementOfType(Collection<E> collection, Class<T> type) { + for (E item : collection) { + if ((item != null) && type.isAssignableFrom(item.getClass())) { + return ObjectHelper.cast(type, item); + } + } + return null; + } + + @SafeVarargs + static <T> Set<T> excludeElementOfTypes(Set<T> annotations, Class<? extends T>... exclusions) { + Set<T> set = new HashSet<>(); + for (T qualifier : annotations) { + boolean exclude = false; + for (Class<? extends T> exclusion : exclusions) { + if (exclusion.isAssignableFrom(qualifier.getClass())) { + exclude = true; + break; + } + } + if (!exclude) { + set.add(qualifier); + } + } + return set; + } + + static Class<?> getRawType(Type type) { + if (type instanceof Class<?>) { + return Class.class.cast(type); + } else if (type instanceof ParameterizedType) { + return getRawType(ParameterizedType.class.cast(type).getRawType()); + } else if (type instanceof TypeVariable<?>) { + return getBound(TypeVariable.class.cast(type).getBounds()); + } else if (type instanceof WildcardType) { + return getBound(WildcardType.class.cast(type).getUpperBounds()); + } else if (type instanceof GenericArrayType) { + Class<?> rawType = getRawType(GenericArrayType.class.cast(type).getGenericComponentType()); + if (rawType != null) { + return Array.newInstance(rawType, 0).getClass(); + } + } + throw new UnsupportedOperationException("Unable to retrieve raw type for [" + type + "]"); + } + + private static Class<?> getBound(Type[] bounds) { + if (bounds.length == 0) { + return Object.class; + } else { + return getRawType(bounds[0]); + } + } + + @SafeVarargs + static boolean hasAnnotation(AnnotatedType<?> type, Class<? extends Annotation>... annotations) { + for (Class<? extends Annotation> annotation : annotations) { + if (hasAnnotation(type, annotation)) { + return true; + } + } + return false; + } + + static boolean hasAnnotation(AnnotatedType<?> type, Class<? extends Annotation> annotation) { + if (type.isAnnotationPresent(annotation)) { + return true; + } + for (AnnotatedMethod<?> method : type.getMethods()) { + if (method.isAnnotationPresent(annotation)) { + return true; + } + } + for (AnnotatedConstructor<?> constructor : type.getConstructors()) { + if (constructor.isAnnotationPresent(annotation)) { + return true; + } + } + for (AnnotatedField<?> field : type.getFields()) { + if (field.isAnnotationPresent(annotation)) { + return true; + } + } + return false; + } + + static Set<Annotation> getQualifiers(Annotated annotated, BeanManager manager) { + Set<Annotation> qualifiers = new HashSet<>(); + for (Annotation annotation : annotated.getAnnotations()) { + if (manager.isQualifier(annotation.annotationType())) { + qualifiers.add(annotation); + } + } + return qualifiers; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiTypeConverterLoader.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiTypeConverterLoader.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiTypeConverterLoader.java new file mode 100644 index 0000000..b8991e6 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/CdiTypeConverterLoader.java @@ -0,0 +1,33 @@ +/** + * 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; + +import org.apache.camel.impl.converter.AnnotationTypeConverterLoader; +import org.apache.camel.spi.TypeConverterRegistry; + +@Vetoed +final class CdiTypeConverterLoader extends AnnotationTypeConverterLoader { + + CdiTypeConverterLoader() { + super(null); + } + + @Override + protected void loadConverterMethods(TypeConverterRegistry registry, Class<?> type) { + super.loadConverterMethods(registry, type); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/ContextName.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/ContextName.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/ContextName.java index f34cb49..2006e71 100644 --- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/ContextName.java +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/ContextName.java @@ -1,40 +1,91 @@ -/** - * 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; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.apache.camel.CamelContext; -import org.apache.camel.builder.RouteBuilder; - -/** - * Used to bind objects to a named {@link CamelContext} instance - * such as {@link RouteBuilder} instances. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) -public @interface ContextName { - - /** - * Returns the name of the CamelContext to add the routes to. - * If no value is specified then the default CamelContext is used. - */ - String value() default ""; -} +/** + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + * CDI qualifier to be used for multi Camel contexts CDI deployment. + * {@code CamelContext} beans can be annotated with the {@code @ContextName} qualifier + * so that the Camel context is named accordingly, e.g.: + * + * <pre><code> + * {@literal @}ApplicationScoped + * {@literal @}ContextName("foo") + * public class FooCamelContext extends DefaultCamelContext { + * } + * </code></pre> + * + * Camel resources like route builders, endpoints and producer templates can be annotated with + * the {@code @ContextName} qualifier as well so that they are associated with the + * corresponding Camel context, e.g.: + * + * <pre><code> + * {@literal @}ContextName("foo") + * public class FooRouteBuilder extends RouteBuilder { + * + * {@literal @}Override + * public void configure() { + * from("direct:bar").to("mock:bar"); + * } + * } + * + * {@literal @}Inject + * {@literal @}ContextName("foo") + * {@literal @}Uri("direct:bar") + * ProducerTemplate barProducer; + * + * {@literal @}Inject + * {@literal @}ContextName("foo") + * {@literal @}Uri("mock:bar") + * MockEndpoint barMockEndpoint; + * </code></pre> + * + * @see org.apache.camel.CamelContext + * + */ +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +public @interface ContextName { + + /** + * Returns the name of the Camel context. + */ + String value(); + + final class Literal extends AnnotationLiteral<ContextName> implements ContextName { + + private static final long serialVersionUID = 1L; + + private final String name; + + public Literal(String name) { + this.name = name; + } + + @Override + public String value() { + return name; + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/DefaultLiteral.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/DefaultLiteral.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/DefaultLiteral.java new file mode 100644 index 0000000..c78d6c0 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/DefaultLiteral.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.camel.cdi; + +import javax.enterprise.inject.Default; +import javax.enterprise.util.AnnotationLiteral; + +@Vetoed +final class DefaultLiteral extends AnnotationLiteral<Default> implements Default { + + static final Default INSTANCE = new DefaultLiteral(); + + private static final long serialVersionUID = 1L; + + private DefaultLiteral() { + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateInjectionTarget.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateInjectionTarget.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateInjectionTarget.java new file mode 100644 index 0000000..9fef662 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateInjectionTarget.java @@ -0,0 +1,51 @@ +/** + * 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; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.enterprise.inject.spi.Producer; + +abstract class DelegateInjectionTarget<T> extends DelegateProducer<T> implements InjectionTarget<T> { + + private final InjectionTarget<T> delegate; + + DelegateInjectionTarget(InjectionTarget<T> delegate) { + super(delegate); + this.delegate = delegate; + } + + DelegateInjectionTarget(InjectionTarget<T> target, Producer<T> producer) { + super(producer); + this.delegate = target; + } + + @Override + public void inject(T instance, CreationalContext<T> ctx) { + delegate.inject(instance, ctx); + } + + @Override + public void postConstruct(T instance) { + delegate.postConstruct(instance); + } + + @Override + public void preDestroy(T instance) { + delegate.preDestroy(instance); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateProducer.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateProducer.java new file mode 100644 index 0000000..05f8516 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/DelegateProducer.java @@ -0,0 +1,51 @@ +/** + * 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; + +import java.util.Set; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.Producer; + +abstract class DelegateProducer<T> implements Producer<T> { + + private final Producer<T> delegate; + + DelegateProducer(Producer<T> delegate) { + this.delegate = delegate; + } + + @Override + public T produce(CreationalContext<T> ctx) { + return delegate.produce(ctx); + } + + @Override + public void dispose(T instance) { + delegate.dispose(instance); + } + + @Override + public Set<InjectionPoint> getInjectionPoints() { + return delegate.getInjectionPoints(); + } + + @Override + public String toString() { + return delegate.toString(); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/Excluded.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/Excluded.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/Excluded.java new file mode 100644 index 0000000..eccf446 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/Excluded.java @@ -0,0 +1,40 @@ +/** + * 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; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(value = RUNTIME) +@Target(value = { TYPE, METHOD, PARAMETER, FIELD }) +@interface Excluded { + + Excluded INSTANCE = new ExcludedLiteral(); + + final class ExcludedLiteral extends AnnotationLiteral<Excluded>implements Excluded { + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/components/camel-cdi/src/main/java/org/apache/camel/cdi/ForwardingObserverMethod.java ---------------------------------------------------------------------- diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/ForwardingObserverMethod.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/ForwardingObserverMethod.java new file mode 100644 index 0000000..3f2b565 --- /dev/null +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/ForwardingObserverMethod.java @@ -0,0 +1,78 @@ +/** + * 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; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import javax.enterprise.event.Reception; +import javax.enterprise.event.TransactionPhase; +import javax.enterprise.inject.spi.ObserverMethod; + +import org.apache.camel.CamelContext; + +/* package-private */ final class ForwardingObserverMethod<T> implements ObserverMethod<T> { + + // Should be replaced with the Java 8 functional interface Consumer<T> + private final AtomicReference<CdiEventEndpoint<T>> observer = new AtomicReference<>(); + + private final Type type; + + private final Set<Annotation> qualifiers; + + ForwardingObserverMethod(Type type, Set<Annotation> qualifiers) { + this.type = type; + this.qualifiers = qualifiers; + } + + void setObserver(CdiEventEndpoint<T> observer) { + this.observer.set(observer); + } + + @Override + public Class<?> getBeanClass() { + return CamelContext.class; + } + + @Override + public Type getObservedType() { + return type; + } + + @Override + public Set<Annotation> getObservedQualifiers() { + return qualifiers; + } + + @Override + public Reception getReception() { + return Reception.ALWAYS; + } + + @Override + public TransactionPhase getTransactionPhase() { + return TransactionPhase.IN_PROGRESS; + } + + @Override + public void notify(T event) { + if (observer.get() != null) { + observer.get().notify(event); + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/0421c24d/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 0532870..cd55010 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 @@ -17,37 +17,42 @@ package org.apache.camel.cdi; import java.util.HashMap; -import java.util.List; import java.util.Map; -import javax.xml.bind.JAXBContext; +import javax.enterprise.inject.UnsatisfiedResolutionException; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; -import org.apache.camel.impl.DefaultModelJAXBContextFactory; import org.apache.camel.main.MainSupport; -import org.apache.camel.spi.ModelJAXBContextFactory; -import org.apache.deltaspike.core.api.provider.BeanProvider; +import org.apache.deltaspike.cdise.api.CdiContainer; /** - * Allows Camel and CDI applications to be booted up on the command line as a Java Application + * Camel CDI boot integration. Allows Camel and CDI to be booted up on the command line as a JVM process. + * See http://camel.apache.org/camel-boot.html. */ -public abstract class Main extends MainSupport { // abstract to prevent cdi management - private static Main instance; - private JAXBContext jaxbContext; - private Object cdiContainer; // we don't want to need cdictrl API in OSGi +@Vetoed +public class Main extends MainSupport { - public Main() { - // add options... + static { + // Since version 2.3.0.Final and WELD-1915, Weld SE registers a shutdown hook that conflicts + // with Camel main support. See WELD-2051. The system property above is available starting + // Weld 2.3.1.Final to deactivate the registration of the shutdown hook. + System.setProperty("org.jboss.weld.se.shutdownHook", String.valueOf(Boolean.FALSE)); } + private static Main instance; + + private CdiContainer cdiContainer; + public static void main(String... args) throws Exception { - Main main = new Main() { }; + Main main = new Main(); instance = main; main.run(args); } /** - * Returns the currently executing main + * Returns the currently executing instance. * * @return the current running instance */ @@ -57,46 +62,42 @@ public abstract class Main extends MainSupport { // abstract to prevent cdi mana @Override protected ProducerTemplate findOrCreateCamelTemplate() { - ProducerTemplate answer = BeanProvider.getContextualReference(ProducerTemplate.class, true); - if (answer != null) { - return answer; + BeanManager manager = cdiContainer.getBeanManager(); + Bean<?> bean = manager.resolve(manager.getBeans(CamelContext.class)); + if (bean == null) { + throw new UnsatisfiedResolutionException("No default Camel context is deployed, cannot create default ProducerTemplate!"); } - if (getCamelContexts().isEmpty()) { - throw new IllegalArgumentException( - "No CamelContexts are available so cannot create a ProducerTemplate!"); - } - return getCamelContexts().get(0).createProducerTemplate(); + CamelContext context = (CamelContext) manager.getReference(bean, CamelContext.class, manager.createCreationalContext(bean)); + return context.createProducerTemplate(); } @Override protected Map<String, CamelContext> getCamelContextMap() { - List<CamelContext> contexts = BeanProvider.getContextualReferences(CamelContext.class, true); - Map<String, CamelContext> answer = new HashMap<String, CamelContext>(); - for (CamelContext context : contexts) { - String name = context.getName(); - answer.put(name, context); + BeanManager manager = cdiContainer.getBeanManager(); + Map<String, CamelContext> answer = new HashMap<>(); + for (Bean<?> bean : manager.getBeans(CamelContext.class, AnyLiteral.INSTANCE)) { + CamelContext context = (CamelContext) manager.getReference(bean, CamelContext.class, manager.createCreationalContext(bean)); + answer.put(context.getName(), context); } return answer; } - public ModelJAXBContextFactory getModelJAXBContextFactory() { - return new DefaultModelJAXBContextFactory(); - } - @Override protected void doStart() throws Exception { + // TODO: Use standard CDI Java SE support when CDI 2.0 becomes a prerequisite org.apache.deltaspike.cdise.api.CdiContainer container = org.apache.deltaspike.cdise.api.CdiContainerLoader.getCdiContainer(); container.boot(); container.getContextControl().startContexts(); cdiContainer = container; super.doStart(); + postProcessContext(); } @Override protected void doStop() throws Exception { super.doStop(); if (cdiContainer != null) { - ((org.apache.deltaspike.cdise.api.CdiContainer) cdiContainer).shutdown(); + cdiContainer.shutdown(); } } }