001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.impl; 018 019import java.lang.reflect.Method; 020import java.util.Set; 021import javax.xml.bind.annotation.XmlTransient; 022 023import org.apache.camel.CamelContext; 024import org.apache.camel.CamelContextAware; 025import org.apache.camel.Component; 026import org.apache.camel.Consume; 027import org.apache.camel.Consumer; 028import org.apache.camel.ConsumerTemplate; 029import org.apache.camel.Endpoint; 030import org.apache.camel.IsSingleton; 031import org.apache.camel.NoSuchBeanException; 032import org.apache.camel.PollingConsumer; 033import org.apache.camel.Processor; 034import org.apache.camel.Producer; 035import org.apache.camel.ProducerTemplate; 036import org.apache.camel.ProxyInstantiationException; 037import org.apache.camel.Service; 038import org.apache.camel.component.bean.BeanInfo; 039import org.apache.camel.component.bean.BeanProcessor; 040import org.apache.camel.component.bean.ProxyHelper; 041import org.apache.camel.processor.CamelInternalProcessor; 042import org.apache.camel.processor.UnitOfWorkProducer; 043import org.apache.camel.util.CamelContextHelper; 044import org.apache.camel.util.IntrospectionSupport; 045import org.apache.camel.util.ObjectHelper; 046import org.apache.camel.util.ServiceHelper; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050/** 051 * A helper class for Camel based injector or post processing hooks which can be reused by 052 * both the <a href="http://camel.apache.org/spring.html">Spring</a>, 053 * <a href="http://camel.apache.org/guice.html">Guice</a> and 054 * <a href="http://camel.apache.org/blueprint.html">Blueprint</a> support. 055 * 056 * @version 057 */ 058public class CamelPostProcessorHelper implements CamelContextAware { 059 private static final Logger LOG = LoggerFactory.getLogger(CamelPostProcessorHelper.class); 060 061 @XmlTransient 062 private CamelContext camelContext; 063 064 public CamelPostProcessorHelper() { 065 } 066 067 public CamelPostProcessorHelper(CamelContext camelContext) { 068 this.setCamelContext(camelContext); 069 } 070 071 public CamelContext getCamelContext() { 072 return camelContext; 073 } 074 075 public void setCamelContext(CamelContext camelContext) { 076 this.camelContext = camelContext; 077 } 078 079 /** 080 * Does the given context match this camel context 081 */ 082 public boolean matchContext(String context) { 083 if (ObjectHelper.isNotEmpty(context)) { 084 if (!getCamelContext().getName().equals(context)) { 085 return false; 086 } 087 } 088 return true; 089 } 090 091 public void consumerInjection(Method method, Object bean, String beanName) { 092 Consume consume = method.getAnnotation(Consume.class); 093 if (consume != null && matchContext(consume.context())) { 094 LOG.debug("Creating a consumer for: " + consume); 095 subscribeMethod(method, bean, beanName, consume.uri(), consume.ref(), consume.property()); 096 } 097 } 098 099 public void subscribeMethod(Method method, Object bean, String beanName, String endpointUri, String endpointName, String endpointProperty) { 100 // lets bind this method to a listener 101 String injectionPointName = method.getName(); 102 Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointName, endpointProperty, injectionPointName, true); 103 if (endpoint != null) { 104 try { 105 Processor processor = createConsumerProcessor(bean, method, endpoint); 106 Consumer consumer = endpoint.createConsumer(processor); 107 LOG.debug("Created processor: {} for consumer: {}", processor, consumer); 108 startService(consumer, bean, beanName); 109 } catch (Exception e) { 110 throw ObjectHelper.wrapRuntimeCamelException(e); 111 } 112 } 113 } 114 115 /** 116 * Stats the given service 117 */ 118 protected void startService(Service service, Object bean, String beanName) throws Exception { 119 if (isSingleton(bean, beanName)) { 120 getCamelContext().addService(service); 121 } else { 122 LOG.debug("Service is not singleton so you must remember to stop it manually {}", service); 123 ServiceHelper.startService(service); 124 } 125 } 126 127 /** 128 * Create a processor which invokes the given method when an incoming 129 * message exchange is received 130 */ 131 protected Processor createConsumerProcessor(final Object pojo, final Method method, final Endpoint endpoint) { 132 BeanInfo info = new BeanInfo(getCamelContext(), method); 133 BeanProcessor answer = new BeanProcessor(pojo, info); 134 // must ensure the consumer is being executed in an unit of work so synchronization callbacks etc is invoked 135 CamelInternalProcessor internal = new CamelInternalProcessor(answer); 136 internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(null)); 137 return internal; 138 } 139 140 public Endpoint getEndpointInjection(Object bean, String uri, String name, String propertyName, 141 String injectionPointName, boolean mandatory) { 142 if (ObjectHelper.isEmpty(uri) && ObjectHelper.isEmpty(name)) { 143 // if no uri or ref, then fallback and try the endpoint property 144 return doGetEndpointInjection(bean, propertyName, injectionPointName); 145 } else { 146 return doGetEndpointInjection(uri, name, injectionPointName, mandatory); 147 } 148 } 149 150 private Endpoint doGetEndpointInjection(String uri, String name, String injectionPointName, boolean mandatory) { 151 return CamelContextHelper.getEndpointInjection(getCamelContext(), uri, name, injectionPointName, mandatory); 152 } 153 154 /** 155 * Gets the injection endpoint from a bean property. 156 * @param bean the bean 157 * @param propertyName the property name on the bean 158 */ 159 private Endpoint doGetEndpointInjection(Object bean, String propertyName, String injectionPointName) { 160 // fall back and use the method name if no explicit property name was given 161 if (ObjectHelper.isEmpty(propertyName)) { 162 propertyName = injectionPointName; 163 } 164 165 // we have a property name, try to lookup a getter method on the bean with that name using this strategy 166 // 1. first the getter with the name as given 167 // 2. then the getter with Endpoint as postfix 168 // 3. then if start with on then try step 1 and 2 again, but omit the on prefix 169 try { 170 Object value = IntrospectionSupport.getOrElseProperty(bean, propertyName, null); 171 if (value == null) { 172 // try endpoint as postfix 173 value = IntrospectionSupport.getOrElseProperty(bean, propertyName + "Endpoint", null); 174 } 175 if (value == null && propertyName.startsWith("on")) { 176 // retry but without the on as prefix 177 propertyName = propertyName.substring(2); 178 return doGetEndpointInjection(bean, propertyName, injectionPointName); 179 } 180 if (value == null) { 181 return null; 182 } else if (value instanceof Endpoint) { 183 return (Endpoint) value; 184 } else { 185 String uriOrRef = getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value); 186 return getCamelContext().getEndpoint(uriOrRef); 187 } 188 } catch (Exception e) { 189 throw new IllegalArgumentException("Error getting property " + propertyName + " from bean " + bean + " due " + e.getMessage(), e); 190 } 191 } 192 193 /** 194 * Creates the object to be injected for an {@link org.apache.camel.EndpointInject} or {@link org.apache.camel.Produce} injection point 195 */ 196 public Object getInjectionValue(Class<?> type, String endpointUri, String endpointRef, String endpointProperty, 197 String injectionPointName, Object bean, String beanName) { 198 if (type.isAssignableFrom(ProducerTemplate.class)) { 199 return createInjectionProducerTemplate(endpointUri, endpointRef, endpointProperty, injectionPointName, bean); 200 } else if (type.isAssignableFrom(ConsumerTemplate.class)) { 201 return createInjectionConsumerTemplate(endpointUri, endpointRef, endpointProperty, injectionPointName); 202 } else { 203 Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointRef, endpointProperty, injectionPointName, true); 204 if (endpoint != null) { 205 if (type.isInstance(endpoint)) { 206 return endpoint; 207 } else if (type.isAssignableFrom(Producer.class)) { 208 return createInjectionProducer(endpoint, bean, beanName); 209 } else if (type.isAssignableFrom(PollingConsumer.class)) { 210 return createInjectionPollingConsumer(endpoint, bean, beanName); 211 } else if (type.isInterface()) { 212 // lets create a proxy 213 try { 214 return ProxyHelper.createProxy(endpoint, type); 215 } catch (Exception e) { 216 throw createProxyInstantiationRuntimeException(type, endpoint, e); 217 } 218 } else { 219 throw new IllegalArgumentException("Invalid type: " + type.getName() 220 + " which cannot be injected via @EndpointInject/@Produce for: " + endpoint); 221 } 222 } 223 return null; 224 } 225 } 226 227 public Object getInjectionPropertyValue(Class<?> type, String propertyName, String propertyDefaultValue, 228 String injectionPointName, Object bean, String beanName) { 229 try { 230 // enforce a properties component to be created if none existed 231 CamelContextHelper.lookupPropertiesComponent(getCamelContext(), true); 232 233 String key; 234 String prefix = getCamelContext().getPropertyPrefixToken(); 235 String suffix = getCamelContext().getPropertySuffixToken(); 236 if (!propertyName.contains(prefix)) { 237 // must enclose the property name with prefix/suffix to have it resolved 238 key = prefix + propertyName + suffix; 239 } else { 240 // key has already prefix/suffix so use it as-is as it may be a compound key 241 key = propertyName; 242 } 243 String value = getCamelContext().resolvePropertyPlaceholders(key); 244 if (value != null) { 245 return getCamelContext().getTypeConverter().mandatoryConvertTo(type, value); 246 } else { 247 return null; 248 } 249 } catch (Exception e) { 250 if (ObjectHelper.isNotEmpty(propertyDefaultValue)) { 251 try { 252 return getCamelContext().getTypeConverter().mandatoryConvertTo(type, propertyDefaultValue); 253 } catch (Exception e2) { 254 throw ObjectHelper.wrapRuntimeCamelException(e2); 255 } 256 } 257 throw ObjectHelper.wrapRuntimeCamelException(e); 258 } 259 } 260 261 public Object getInjectionBeanValue(Class<?> type, String name) { 262 if (ObjectHelper.isEmpty(name)) { 263 Set<?> found = getCamelContext().getRegistry().findByType(type); 264 if (found == null || found.isEmpty()) { 265 throw new NoSuchBeanException(name, type.getName()); 266 } else if (found.size() > 1) { 267 throw new NoSuchBeanException("Found " + found.size() + " beans of type: " + type + ". Only one bean expected."); 268 } else { 269 // we found only one 270 return found.iterator().next(); 271 } 272 } else { 273 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, type); 274 } 275 } 276 277 /** 278 * Factory method to create a {@link org.apache.camel.ProducerTemplate} to be injected into a POJO 279 */ 280 protected ProducerTemplate createInjectionProducerTemplate(String endpointUri, String endpointRef, String endpointProperty, 281 String injectionPointName, Object bean) { 282 // endpoint is optional for this injection point 283 Endpoint endpoint = getEndpointInjection(bean, endpointUri, endpointRef, endpointProperty, injectionPointName, false); 284 ProducerTemplate answer = new DefaultProducerTemplate(getCamelContext(), endpoint); 285 // start the template so its ready to use 286 try { 287 answer.start(); 288 } catch (Exception e) { 289 throw ObjectHelper.wrapRuntimeCamelException(e); 290 } 291 return answer; 292 } 293 294 /** 295 * Factory method to create a {@link org.apache.camel.ConsumerTemplate} to be injected into a POJO 296 */ 297 protected ConsumerTemplate createInjectionConsumerTemplate(String endpointUri, String endpointRef, String endpointProperty, 298 String injectionPointName) { 299 ConsumerTemplate answer = new DefaultConsumerTemplate(getCamelContext()); 300 // start the template so its ready to use 301 try { 302 answer.start(); 303 } catch (Exception e) { 304 throw ObjectHelper.wrapRuntimeCamelException(e); 305 } 306 return answer; 307 } 308 309 /** 310 * Factory method to create a started {@link org.apache.camel.PollingConsumer} to be injected into a POJO 311 */ 312 protected PollingConsumer createInjectionPollingConsumer(Endpoint endpoint, Object bean, String beanName) { 313 try { 314 PollingConsumer pollingConsumer = endpoint.createPollingConsumer(); 315 startService(pollingConsumer, bean, beanName); 316 return pollingConsumer; 317 } catch (Exception e) { 318 throw ObjectHelper.wrapRuntimeCamelException(e); 319 } 320 } 321 322 /** 323 * A Factory method to create a started {@link org.apache.camel.Producer} to be injected into a POJO 324 */ 325 protected Producer createInjectionProducer(Endpoint endpoint, Object bean, String beanName) { 326 try { 327 Producer producer = endpoint.createProducer(); 328 startService(producer, bean, beanName); 329 return new UnitOfWorkProducer(producer); 330 } catch (Exception e) { 331 throw ObjectHelper.wrapRuntimeCamelException(e); 332 } 333 } 334 335 protected RuntimeException createProxyInstantiationRuntimeException(Class<?> type, Endpoint endpoint, Exception e) { 336 return new ProxyInstantiationException(type, endpoint, e); 337 } 338 339 /** 340 * Implementations can override this method to determine if the bean is singleton. 341 * 342 * @param bean the bean 343 * @return <tt>true</tt> if its singleton scoped, for prototype scoped <tt>false</tt> is returned. 344 */ 345 protected boolean isSingleton(Object bean, String beanName) { 346 if (bean instanceof IsSingleton) { 347 IsSingleton singleton = (IsSingleton) bean; 348 return singleton.isSingleton(); 349 } 350 return true; 351 } 352}