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 */ 017 package org.apache.camel.impl; 018 019 import java.lang.reflect.Field; 020 import java.lang.reflect.Method; 021 022 import org.apache.camel.CamelContext; 023 import org.apache.camel.CamelContextAware; 024 import org.apache.camel.EndpointInject; 025 import org.apache.camel.Produce; 026 import org.apache.camel.util.ObjectHelper; 027 import org.apache.camel.util.ReflectionHelper; 028 import org.slf4j.Logger; 029 import org.slf4j.LoggerFactory; 030 031 /** 032 * A bean post processor which implements the <a href="http://camel.apache.org/bean-integration.html">Bean Integration</a> 033 * features in Camel. Features such as the <a href="http://camel.apache.org/bean-injection.html">Bean Injection</a> of objects like 034 * {@link org.apache.camel.Endpoint} and 035 * {@link org.apache.camel.ProducerTemplate} together with support for 036 * <a href="http://camel.apache.org/pojo-consuming.html">POJO Consuming</a> via the 037 * {@link org.apache.camel.Consume} annotation along with 038 * <a href="http://camel.apache.org/pojo-producing.html">POJO Producing</a> via the 039 * {@link org.apache.camel.Produce} annotation along with other annotations such as 040 * {@link org.apache.camel.DynamicRouter} for creating <a href="http://camel.apache.org/dynamicrouter-annotation.html">a Dynamic router via annotations</a>. 041 * {@link org.apache.camel.RecipientList} for creating <a href="http://camel.apache.org/recipientlist-annotation.html">a Recipient List router via annotations</a>. 042 * {@link org.apache.camel.RoutingSlip} for creating <a href="http://camel.apache.org/routingslip-annotation.html">a Routing Slip router via annotations</a>. 043 * <p/> 044 * Components such as <tt>camel-spring</tt>, and <tt>camel-blueprint</tt> can leverage this post processor to hook in Camel 045 * bean post processing into their bean processing framework. 046 */ 047 public class DefaultCamelBeanPostProcessor { 048 049 protected static final transient Logger LOG = LoggerFactory.getLogger(DefaultCamelBeanPostProcessor.class); 050 protected CamelPostProcessorHelper camelPostProcessorHelper; 051 protected CamelContext camelContext; 052 053 public DefaultCamelBeanPostProcessor() { 054 } 055 056 public DefaultCamelBeanPostProcessor(CamelContext camelContext) { 057 this.camelContext = camelContext; 058 } 059 060 /** 061 * Apply this post processor to the given new bean instance <i>before</i> any bean 062 * initialization callbacks (like <code>afterPropertiesSet</code> 063 * or a custom init-method). The bean will already be populated with property values. 064 * The returned bean instance may be a wrapper around the original. 065 * 066 * @param bean the new bean instance 067 * @param beanName the name of the bean 068 * @return the bean instance to use, either the original or a wrapped one; if 069 * <code>null</code>, no subsequent BeanPostProcessors will be invoked 070 * @throws Exception is thrown if error post processing bean 071 */ 072 public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception { 073 LOG.trace("Camel bean processing before initialization for bean: {}", beanName); 074 075 // some beans cannot be post processed at this given time, so we gotta check beforehand 076 if (!canPostProcessBean(bean, beanName)) { 077 return bean; 078 } 079 080 injectFields(bean, beanName); 081 injectMethods(bean, beanName); 082 083 if (bean instanceof CamelContextAware && canSetCamelContext(bean, beanName)) { 084 CamelContextAware contextAware = (CamelContextAware)bean; 085 CamelContext context = getOrLookupCamelContext(); 086 if (context == null) { 087 LOG.warn("No CamelContext defined yet so cannot inject into bean: " + beanName); 088 } else { 089 contextAware.setCamelContext(context); 090 } 091 } 092 093 return bean; 094 } 095 096 /** 097 * Apply this post processor to the given new bean instance <i>after</i> any bean 098 * initialization callbacks (like <code>afterPropertiesSet</code> 099 * or a custom init-method). The bean will already be populated with property values. 100 * The returned bean instance may be a wrapper around the original. 101 * 102 * @param bean the new bean instance 103 * @param beanName the name of the bean 104 * @return the bean instance to use, either the original or a wrapped one; if 105 * <code>null</code>, no subsequent BeanPostProcessors will be invoked 106 * @throws Exception is thrown if error post processing bean 107 */ 108 public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception { 109 LOG.trace("Camel bean processing after initialization for bean: {}", beanName); 110 111 // some beans cannot be post processed at this given time, so we gotta check beforehand 112 if (!canPostProcessBean(bean, beanName)) { 113 return bean; 114 } 115 116 if (bean instanceof DefaultEndpoint) { 117 DefaultEndpoint defaultEndpoint = (DefaultEndpoint) bean; 118 defaultEndpoint.setEndpointUriIfNotSpecified(beanName); 119 } 120 121 return bean; 122 } 123 124 /** 125 * Strategy to get the {@link CamelContext} to use. 126 */ 127 public CamelContext getOrLookupCamelContext() { 128 return camelContext; 129 } 130 131 /** 132 * Strategy to get the {@link CamelPostProcessorHelper} 133 */ 134 protected CamelPostProcessorHelper getPostProcessorHelper() { 135 if (camelPostProcessorHelper == null) { 136 camelPostProcessorHelper = new CamelPostProcessorHelper(getOrLookupCamelContext()); 137 } 138 return camelPostProcessorHelper; 139 } 140 141 protected boolean canPostProcessBean(Object bean, String beanName) { 142 return bean != null; 143 } 144 145 protected boolean canSetCamelContext(Object bean, String beanName) { 146 if (bean instanceof CamelContextAware) { 147 CamelContextAware camelContextAware = (CamelContextAware) bean; 148 CamelContext context = camelContextAware.getCamelContext(); 149 if (context != null) { 150 LOG.trace("CamelContext already set on bean with id [{}]. Will keep existing CamelContext on bean.", beanName); 151 return false; 152 } 153 } 154 155 return true; 156 } 157 158 159 /** 160 * A strategy method to allow implementations to perform some custom JBI 161 * based injection of the POJO 162 * 163 * @param bean the bean to be injected 164 */ 165 protected void injectFields(final Object bean, final String beanName) { 166 ReflectionHelper.doWithFields(bean.getClass(), new ReflectionHelper.FieldCallback() { 167 public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { 168 EndpointInject endpointInject = field.getAnnotation(EndpointInject.class); 169 if (endpointInject != null && getPostProcessorHelper().matchContext(endpointInject.context())) { 170 injectField(field, endpointInject.uri(), endpointInject.ref(), bean, beanName); 171 } 172 173 Produce produce = field.getAnnotation(Produce.class); 174 if (produce != null && getPostProcessorHelper().matchContext(produce.context())) { 175 injectField(field, produce.uri(), produce.ref(), bean, beanName); 176 } 177 } 178 }); 179 } 180 181 protected void injectField(Field field, String endpointUri, String endpointRef, Object bean, String beanName) { 182 ReflectionHelper.setField(field, bean, getPostProcessorHelper().getInjectionValue(field.getType(), endpointUri, endpointRef, field.getName(), bean, beanName)); 183 } 184 185 protected void injectMethods(final Object bean, final String beanName) { 186 ReflectionHelper.doWithMethods(bean.getClass(), new ReflectionHelper.MethodCallback() { 187 public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { 188 setterInjection(method, bean, beanName); 189 getPostProcessorHelper().consumerInjection(method, bean, beanName); 190 } 191 }); 192 } 193 194 protected void setterInjection(Method method, Object bean, String beanName) { 195 EndpointInject endpointInject = method.getAnnotation(EndpointInject.class); 196 if (endpointInject != null && getPostProcessorHelper().matchContext(endpointInject.context())) { 197 setterInjection(method, bean, beanName, endpointInject.uri(), endpointInject.ref()); 198 } 199 200 Produce produce = method.getAnnotation(Produce.class); 201 if (produce != null && getPostProcessorHelper().matchContext(produce.context())) { 202 setterInjection(method, bean, beanName, produce.uri(), produce.ref()); 203 } 204 } 205 206 protected void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointRef) { 207 Class<?>[] parameterTypes = method.getParameterTypes(); 208 if (parameterTypes != null) { 209 if (parameterTypes.length != 1) { 210 LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method); 211 } else { 212 String propertyName = ObjectHelper.getPropertyName(method); 213 Object value = getPostProcessorHelper().getInjectionValue(parameterTypes[0], endpointUri, endpointRef, propertyName, bean, beanName); 214 ObjectHelper.invokeMethod(method, bean, value); 215 } 216 } 217 } 218 219 }