All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.cdi.CdiCamelExtension Maven / Gradle / Ivy

The newest version!
/**
 * 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.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.impl.DefaultCamelContext;
import org.apache.camel.management.event.AbstractExchangeEvent;
import org.apache.camel.model.RouteContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
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.DeploymentException;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.InjectionTargetFactory;
import javax.enterprise.inject.spi.ObserverMethod;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessBeanAttributes;
import javax.enterprise.inject.spi.ProcessInjectionPoint;
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.enterprise.inject.spi.WithAnnotations;
import javax.inject.Named;
import java.lang.annotation.Annotation;
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;

public class CdiCamelExtension implements Extension {

    private final Logger logger = LoggerFactory.getLogger(CdiCamelExtension.class);

    private final CdiCamelEnvironment environment = new CdiCamelEnvironment();

    private final Set> converters = newSetFromMap(new ConcurrentHashMap, Boolean>());

    private final Set> camelBeans = newSetFromMap(new ConcurrentHashMap, Boolean>());

    private final Set> eagerBeans = newSetFromMap(new ConcurrentHashMap, Boolean>());

    private final Map> cdiEventEndpoints = new ConcurrentHashMap<>();

    private final Map> contextBeans = new ConcurrentHashMap<>();

    private final Set contextQualifiers = newSetFromMap(new ConcurrentHashMap());

    private final Set eventQualifiers = newSetFromMap(new ConcurrentHashMap());

    ForwardingObserverMethod getObserverMethod(InjectionPoint ip) {
        return cdiEventEndpoints.get(ip);
    }

    Set getObserverEvents() {
        return eventQualifiers;
    }

    Bean getContextBean(Annotated annotated) {
        return contextBeans.get(annotated);
    }

    Set getContextQualifiers() {
        return contextQualifiers;
    }

    private void typeConverters(@Observes @WithAnnotations(Converter.class) ProcessAnnotatedType pat) {
        converters.add(pat.getAnnotatedType().getJavaClass());
    }

    private void camelContextAware(@Observes ProcessAnnotatedType pat) {
        camelBeans.add(pat.getAnnotatedType());
    }

    private void camelAnnotations(@Observes @WithAnnotations({BeanInject.class, Consume.class, EndpointInject.class, Produce.class, PropertyInject.class}) ProcessAnnotatedType pat) {
        camelBeans.add(pat.getAnnotatedType());
    }

    private void consumeBeans(@Observes @WithAnnotations(Consume.class) ProcessAnnotatedType pat) {
        eagerBeans.add(pat.getAnnotatedType());
    }

    private  void camelContextBeans(@Observes ProcessInjectionTarget pit, BeanManager manager) {
        pit.setInjectionTarget(environment.camelContextInjectionTarget(pit.getInjectionTarget(), pit.getAnnotatedType(), manager));
    }

    private  void camelContextProducers(@Observes ProcessProducer pp, BeanManager manager) {
        pp.setProducer(environment.camelContextProducer(pp.getProducer(), pp.getAnnotatedMember(), manager));
    }

    private  void camelBeansPostProcessor(@Observes ProcessInjectionTarget pit, BeanManager manager) {
        if (camelBeans.contains(pit.getAnnotatedType()))
            pit.setInjectionTarget(new CamelBeanInjectionTarget<>(pit.getInjectionTarget(), manager));
    }

    private void cdiEventEndpoints(@Observes ProcessInjectionPoint pip) {
        InjectionPoint ip = pip.getInjectionPoint();
        // FIXME: to be removed when OWB-1102 is fixed
        if (!CdiEventEndpoint.class.isAssignableFrom(CdiSpiHelper.getRawType(ip.getType())))
            return;
        // 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  void endpointProducers(@Observes ProcessBeanAttributes pba) {
        // Veto the bean as we first need to collect the metadata during the bean discovery phase. The bean attributes decoration is done after the bean discovery.
        if (pba.getAnnotated() instanceof AnnotatedMethod && CdiCamelFactory.class.equals(((AnnotatedMethod) pba.getAnnotated()).getDeclaringType().getJavaClass()))
            pba.veto();
    }

    private void producerTemplates(@Observes ProcessBeanAttributes pba) {
        // Veto the bean as we first need to collect the metadata during the bean discovery phase. The bean attributes decoration is done after the bean discovery.
        if (pba.getAnnotated() instanceof AnnotatedMethod && CdiCamelFactory.class.equals(((AnnotatedMethod) pba.getAnnotated()).getDeclaringType().getJavaClass()))
            pba.veto();
    }

    private  void camelEventNotifiers(@Observes ProcessObserverMethod 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  void camelContextBeans(@Observes ProcessBean pb) {
        processCamelContextBean(pb.getAnnotated(), pb.getBean());
    }

    private  void camelContextProducerFields(@Observes ProcessProducerField pb) {
        processCamelContextBean(pb.getAnnotated(), pb.getBean());
    }

    private  void camelContextProducerMethods(@Observes ProcessProducerMethod 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, BeanManager manager) {
        // Decorate the CDI Camel factory beans with the metadata gathered during the bean discovery phase
        Bean bean = (Bean) manager.resolve(manager.getBeans(CdiCamelFactory.class));
        for (AnnotatedMethod am : abd.getAnnotatedType(CdiCamelFactory.class, null).getMethods()) {
            if (!am.isAnnotationPresent(Produces.class))
                continue;
            // TODO: would be more correct to add a bean for each Camel context bean
            Class type = CdiSpiHelper.getRawType(am.getBaseType());
            if (CdiEventEndpoint.class.equals(type)) {
                Set qualifiers = new HashSet<>();
                for (InjectionPoint ip : cdiEventEndpoints.keySet())
                    qualifiers.addAll(ip.getQualifiers());
                abd.addBean(manager.createBean(new BeanAttributesDecorator<>(manager.createBeanAttributes(am), qualifiers), CdiCamelFactory.class, manager.getProducerFactory(am, bean)));
            } else if (Endpoint.class.isAssignableFrom(type) || ProducerTemplate.class.isAssignableFrom(type)) {
                abd.addBean(manager.createBean(new BeanAttributesDecorator<>(manager.createBeanAttributes(am), CdiSpiHelper.excludeElementOfTypes(contextQualifiers, Any.class, Default.class, Named.class)), CdiCamelFactory.class, manager.getProducerFactory(am, bean)));
            }
        }
    }

    private void addDefaultCamelContext(@Observes AfterBeanDiscovery abd, final BeanManager manager) {
        if (manager.getBeans(CamelContext.class, AnyLiteral.INSTANCE).isEmpty())
            abd.addBean(manager.createBean(new CamelContextBeanAttributes(manager), DefaultCamelContext.class, new InjectionTargetFactory() {
                @Override
                public InjectionTarget createInjectionTarget(Bean bean) {
                    return environment.camelContextInjectionTarget(new CamelContextDefaultProducer(), null, manager);
                }
            }));
    }

    private void addCdiEventObserverMethods(@Observes AfterBeanDiscovery abd) {
        for (ObserverMethod method : cdiEventEndpoints.values())
            abd.addObserverMethod(method);
    }

    private void createCamelContexts(@Observes AfterDeploymentValidation adv, BeanManager manager) {
        Collection 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> 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 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();
    }

    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 DeploymentException("Error adding routes of type [" + routeBean.getBeanClass().getName() + "] to Camel context [" + context.getName() + "]", cause));
            }
        } catch (Exception exception) {
            adv.addDeploymentProblem(exception);
        }
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy