/** * 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.impl; import java.lang.reflect.Method; import java.util.Set; import javax.xml.bind.annotation.XmlTransient; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; import org.apache.camel.Consume; import org.apache.camel.Consumer; import org.apache.camel.ConsumerTemplate; import org.apache.camel.Endpoint; import org.apache.camel.FluentProducerTemplate; import org.apache.camel.IsSingleton; import org.apache.camel.NoSuchBeanException; import org.apache.camel.PollingConsumer; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.ProducerTemplate; import org.apache.camel.ProxyInstantiationException; import org.apache.camel.Service; import org.apache.camel.builder.DefaultFluentProducerTemplate; import org.apache.camel.component.bean.BeanInfo; import org.apache.camel.component.bean.BeanProcessor; import org.apache.camel.component.bean.ProxyHelper; import org.apache.camel.processor.CamelInternalProcessor; import org.apache.camel.processor.DeferServiceFactory; import org.apache.camel.processor.UnitOfWorkProducer; import org.apache.camel.util.CamelContextHelper; import org.apache.camel.util.IntrospectionSupport; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.ServiceHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A helper class for Camel based injector or post processing hooks which can be * reused by both the <a href="http://camel.apache.org/spring.html">Spring</a>, * <a href="http://camel.apache.org/guice.html">Guice</a> and * <a href="http://camel.apache.org/blueprint.html">Blueprint</a> support. * * @version */ public class CamelPostProcessorHelper implements CamelContextAware { private static final Logger LOG = LoggerFactory.getLogger(CamelPostProcessorHelper.class); @XmlTransient private CamelContext camelContext; public CamelPostProcessorHelper() { } public CamelPostProcessorHelper(CamelContext camelContext) { this.setCamelContext(camelContext); } public CamelContext getCamelContext() { return camelContext; } public void setCamelContext(CamelContext camelContext) { this.camelContext = camelContext; } /** * Does the given context match this camel context */ public boolean matchContext(String context) { if (ObjectHelper.isNotEmpty(context)) { if (!getCamelContext().getName().equals(context)) { return false; } } return true; } public void consumerInjection(Method method, Object bean, String beanName) { Consume consume = method.getAnnotation(Consume.class); if (consume != null && matchContext(consume.context())) { LOG.debug("Creating a consumer for: " + consume); subscribeMethod(method, bean, beanName, consume.uri(), consume.ref(), consume.property()); } } public void subscribeMethod(Method method, Object bean, String beanName, String endpointUri, String endpointName, String endpointProperty) { // lets bind this method to a listener String injectionPointName = method.getName(); Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointName, endpointProperty, injectionPointName, true); if (endpoint != null) { try { Processor processor = createConsumerProcessor(bean, method, endpoint); Consumer consumer = endpoint.createConsumer(processor); LOG.debug("Created processor: {} for consumer: {}", processor, consumer); startService(consumer, endpoint.getCamelContext(), bean, beanName); } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } } } /** * Stats the given service */ protected void startService(Service service, CamelContext camelContext, Object bean, String beanName) throws Exception { // defer starting the service until CamelContext has started all its initial services if (camelContext != null) { camelContext.deferStartService(service, true); } else { // mo CamelContext then start service manually ServiceHelper.startService(service); } boolean singleton = isSingleton(bean, beanName); if (!singleton) { LOG.debug("Service is not singleton so you must remember to stop it manually {}", service); } } /** * Create a processor which invokes the given method when an incoming * message exchange is received */ protected Processor createConsumerProcessor(final Object pojo, final Method method, final Endpoint endpoint) { BeanInfo info = new BeanInfo(getCamelContext(), method); BeanProcessor answer = new BeanProcessor(pojo, info); // must ensure the consumer is being executed in an unit of work so synchronization callbacks etc is invoked CamelInternalProcessor internal = new CamelInternalProcessor(answer); internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(null)); return internal; } public Endpoint getEndpointInjection(Object bean, String uri, String name, String propertyName, String injectionPointName, boolean mandatory) { if (ObjectHelper.isEmpty(uri) && ObjectHelper.isEmpty(name)) { // if no uri or ref, then fallback and try the endpoint property return doGetEndpointInjection(bean, propertyName, injectionPointName); } else { return doGetEndpointInjection(uri, name, injectionPointName, mandatory); } } private Endpoint doGetEndpointInjection(String uri, String name, String injectionPointName, boolean mandatory) { return CamelContextHelper.getEndpointInjection(getCamelContext(), uri, name, injectionPointName, mandatory); } /** * Gets the injection endpoint from a bean property. * * @param bean the bean * @param propertyName the property name on the bean */ private Endpoint doGetEndpointInjection(Object bean, String propertyName, String injectionPointName) { // fall back and use the method name if no explicit property name was given if (ObjectHelper.isEmpty(propertyName)) { propertyName = injectionPointName; } // we have a property name, try to lookup a getter method on the bean with that name using this strategy // 1. first the getter with the name as given // 2. then the getter with Endpoint as postfix // 3. then if start with on then try step 1 and 2 again, but omit the on prefix try { Object value = IntrospectionSupport.getOrElseProperty(bean, propertyName, null); if (value == null) { // try endpoint as postfix value = IntrospectionSupport.getOrElseProperty(bean, propertyName + "Endpoint", null); } if (value == null && propertyName.startsWith("on")) { // retry but without the on as prefix propertyName = propertyName.substring(2); return doGetEndpointInjection(bean, propertyName, injectionPointName); } if (value == null) { return null; } else if (value instanceof Endpoint) { return (Endpoint) value; } else { String uriOrRef = getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value); return getCamelContext().getEndpoint(uriOrRef); } } catch (Exception e) { throw new IllegalArgumentException("Error getting property " + propertyName + " from bean " + bean + " due " + e.getMessage(), e); } } /** * Creates the object to be injected for an * {@link org.apache.camel.EndpointInject} or * {@link org.apache.camel.Produce} injection point */ public Object getInjectionValue(Class<?> type, String endpointUri, String endpointRef, String endpointProperty, String injectionPointName, Object bean, String beanName) { return getInjectionValue(type, endpointUri, endpointRef, endpointProperty, injectionPointName, bean, beanName, true); } /** * Creates the object to be injected for an * {@link org.apache.camel.EndpointInject} or * {@link org.apache.camel.Produce} injection point */ public Object getInjectionValue(Class<?> type, String endpointUri, String endpointRef, String endpointProperty, String injectionPointName, Object bean, String beanName, boolean binding) { if (type.isAssignableFrom(ProducerTemplate.class)) { return createInjectionProducerTemplate(endpointUri, endpointRef, endpointProperty, injectionPointName, bean); } else if (type.isAssignableFrom(FluentProducerTemplate.class)) { return createInjectionFluentProducerTemplate(endpointUri, endpointRef, endpointProperty, injectionPointName, bean); } else if (type.isAssignableFrom(ConsumerTemplate.class)) { return createInjectionConsumerTemplate(endpointUri, endpointRef, endpointProperty, injectionPointName); } else { Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointRef, endpointProperty, injectionPointName, true); if (endpoint != null) { if (type.isInstance(endpoint)) { return endpoint; } else if (type.isAssignableFrom(Producer.class)) { return createInjectionProducer(endpoint, bean, beanName); } else if (type.isAssignableFrom(PollingConsumer.class)) { return createInjectionPollingConsumer(endpoint, bean, beanName); } else if (type.isInterface()) { // lets create a proxy try { return ProxyHelper.createProxy(endpoint, binding, type); } catch (Exception e) { throw createProxyInstantiationRuntimeException(type, endpoint, e); } } else { throw new IllegalArgumentException("Invalid type: " + type.getName() + " which cannot be injected via @EndpointInject/@Produce for: " + endpoint); } } return null; } } public Object getInjectionPropertyValue(Class<?> type, String propertyName, String propertyDefaultValue, String injectionPointName, Object bean, String beanName) { try { // enforce a properties component to be created if none existed CamelContextHelper.lookupPropertiesComponent(getCamelContext(), true); String key; String prefix = getCamelContext().getPropertyPrefixToken(); String suffix = getCamelContext().getPropertySuffixToken(); if (!propertyName.contains(prefix)) { // must enclose the property name with prefix/suffix to have it resolved key = prefix + propertyName + suffix; } else { // key has already prefix/suffix so use it as-is as it may be a compound key key = propertyName; } String value = getCamelContext().resolvePropertyPlaceholders(key); if (value != null) { return getCamelContext().getTypeConverter().mandatoryConvertTo(type, value); } else { return null; } } catch (Exception e) { if (ObjectHelper.isNotEmpty(propertyDefaultValue)) { try { return getCamelContext().getTypeConverter().mandatoryConvertTo(type, propertyDefaultValue); } catch (Exception e2) { throw ObjectHelper.wrapRuntimeCamelException(e2); } } throw ObjectHelper.wrapRuntimeCamelException(e); } } public Object getInjectionBeanValue(Class<?> type, String name) { if (ObjectHelper.isEmpty(name)) { Set<?> found = getCamelContext().getRegistry().findByType(type); if (found == null || found.isEmpty()) { throw new NoSuchBeanException(name, type.getName()); } else if (found.size() > 1) { throw new NoSuchBeanException("Found " + found.size() + " beans of type: " + type + ". Only one bean expected."); } else { // we found only one return found.iterator().next(); } } else { return CamelContextHelper.mandatoryLookup(getCamelContext(), name, type); } } /** * Factory method to create a {@link org.apache.camel.ProducerTemplate} to * be injected into a POJO */ protected ProducerTemplate createInjectionProducerTemplate(String endpointUri, String endpointRef, String endpointProperty, String injectionPointName, Object bean) { // endpoint is optional for this injection point Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointRef, endpointProperty, injectionPointName, false); CamelContext context = endpoint != null ? endpoint.getCamelContext() : getCamelContext(); ProducerTemplate answer = new DefaultProducerTemplate(context, endpoint); // start the template so its ready to use try { // no need to defer the template as it can adjust to the endpoint at runtime startService(answer, context, bean, null); } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } return answer; } /** * Factory method to create a * {@link org.apache.camel.FluentProducerTemplate} to be injected into a * POJO */ protected FluentProducerTemplate createInjectionFluentProducerTemplate(String endpointUri, String endpointRef, String endpointProperty, String injectionPointName, Object bean) { // endpoint is optional for this injection point Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointRef, endpointProperty, injectionPointName, false); CamelContext context = endpoint != null ? endpoint.getCamelContext() : getCamelContext(); FluentProducerTemplate answer = new DefaultFluentProducerTemplate(context); answer.setDefaultEndpoint(endpoint); // start the template so its ready to use try { // no need to defer the template as it can adjust to the endpoint at runtime startService(answer, context, bean, null); } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } return answer; } /** * Factory method to create a {@link org.apache.camel.ConsumerTemplate} to * be injected into a POJO */ protected ConsumerTemplate createInjectionConsumerTemplate(String endpointUri, String endpointRef, String endpointProperty, String injectionPointName) { ConsumerTemplate answer = new DefaultConsumerTemplate(getCamelContext()); // start the template so its ready to use try { startService(answer, null, null, null); } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } return answer; } /** * Factory method to create a started * {@link org.apache.camel.PollingConsumer} to be injected into a POJO */ protected PollingConsumer createInjectionPollingConsumer(Endpoint endpoint, Object bean, String beanName) { try { PollingConsumer consumer = endpoint.createPollingConsumer(); startService(consumer, endpoint.getCamelContext(), bean, beanName); return consumer; } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } } /** * A Factory method to create a started {@link org.apache.camel.Producer} to * be injected into a POJO */ protected Producer createInjectionProducer(Endpoint endpoint, Object bean, String beanName) { try { Producer producer = DeferServiceFactory.createProducer(endpoint); return new UnitOfWorkProducer(producer); } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } } protected RuntimeException createProxyInstantiationRuntimeException(Class<?> type, Endpoint endpoint, Exception e) { return new ProxyInstantiationException(type, endpoint, e); } /** * Implementations can override this method to determine if the bean is * singleton. * * @param bean the bean * @return <tt>true</tt> if its singleton scoped, for prototype scoped * <tt>false</tt> is returned. */ protected boolean isSingleton(Object bean, String beanName) { if (bean instanceof IsSingleton) { IsSingleton singleton = (IsSingleton) bean; return singleton.isSingleton(); } return true; } }