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 }