You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2012/04/09 10:06:28 UTC
svn commit: r1311157 - in
/camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel:
impl/DefaultCamelBeanPostProcessor.java util/ReflectionHelper.java
Author: davsclaus
Date: Mon Apr 9 08:06:28 2012
New Revision: 1311157
URL: http://svn.apache.org/viewvc?rev=1311157&view=rev
Log:
CAMEL-5149: The default injector should perform bean post processing to setup @Produce and the likes on the bean
Added:
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelBeanPostProcessor.java
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/util/ReflectionHelper.java
Added: camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelBeanPostProcessor.java
URL: http://svn.apache.org/viewvc/camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelBeanPostProcessor.java?rev=1311157&view=auto
==============================================================================
--- camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelBeanPostProcessor.java (added)
+++ camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelBeanPostProcessor.java Mon Apr 9 08:06:28 2012
@@ -0,0 +1,219 @@
+/**
+ * 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.Field;
+import java.lang.reflect.Method;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ReflectionHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A bean post processor which implements the <a href="http://camel.apache.org/bean-integration.html">Bean Integration</a>
+ * features in Camel. Features such as the <a href="http://camel.apache.org/bean-injection.html">Bean Injection</a> of objects like
+ * {@link org.apache.camel.Endpoint} and
+ * {@link org.apache.camel.ProducerTemplate} together with support for
+ * <a href="http://camel.apache.org/pojo-consuming.html">POJO Consuming</a> via the
+ * {@link org.apache.camel.Consume} annotation along with
+ * <a href="http://camel.apache.org/pojo-producing.html">POJO Producing</a> via the
+ * {@link org.apache.camel.Produce} annotation along with other annotations such as
+ * {@link org.apache.camel.DynamicRouter} for creating <a href="http://camel.apache.org/dynamicrouter-annotation.html">a Dynamic router via annotations</a>.
+ * {@link org.apache.camel.RecipientList} for creating <a href="http://camel.apache.org/recipientlist-annotation.html">a Recipient List router via annotations</a>.
+ * {@link org.apache.camel.RoutingSlip} for creating <a href="http://camel.apache.org/routingslip-annotation.html">a Routing Slip router via annotations</a>.
+ * <p/>
+ * Components such as <tt>camel-spring</tt>, and <tt>camel-blueprint</tt> can leverage this post processor to hook in Camel
+ * bean post processing into their bean processing framework.
+ */
+public class DefaultCamelBeanPostProcessor {
+
+ protected static final transient Logger LOG = LoggerFactory.getLogger(DefaultCamelBeanPostProcessor.class);
+ protected CamelPostProcessorHelper camelPostProcessorHelper;
+ protected CamelContext camelContext;
+
+ public DefaultCamelBeanPostProcessor() {
+ }
+
+ public DefaultCamelBeanPostProcessor(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ /**
+ * Apply this post processor to the given new bean instance <i>before</i> any bean
+ * initialization callbacks (like <code>afterPropertiesSet</code>
+ * or a custom init-method). The bean will already be populated with property values.
+ * The returned bean instance may be a wrapper around the original.
+ *
+ * @param bean the new bean instance
+ * @param beanName the name of the bean
+ * @return the bean instance to use, either the original or a wrapped one; if
+ * <code>null</code>, no subsequent BeanPostProcessors will be invoked
+ * @throws Exception is thrown if error post processing bean
+ */
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
+ LOG.trace("Camel bean processing before initialization for bean: {}", beanName);
+
+ // some beans cannot be post processed at this given time, so we gotta check beforehand
+ if (!canPostProcessBean(bean, beanName)) {
+ return bean;
+ }
+
+ injectFields(bean, beanName);
+ injectMethods(bean, beanName);
+
+ if (bean instanceof CamelContextAware && canSetCamelContext(bean, beanName)) {
+ CamelContextAware contextAware = (CamelContextAware)bean;
+ CamelContext context = getOrLookupCamelContext();
+ if (context == null) {
+ LOG.warn("No CamelContext defined yet so cannot inject into bean: " + beanName);
+ } else {
+ contextAware.setCamelContext(context);
+ }
+ }
+
+ return bean;
+ }
+
+ /**
+ * Apply this post processor to the given new bean instance <i>after</i> any bean
+ * initialization callbacks (like <code>afterPropertiesSet</code>
+ * or a custom init-method). The bean will already be populated with property values.
+ * The returned bean instance may be a wrapper around the original.
+ *
+ * @param bean the new bean instance
+ * @param beanName the name of the bean
+ * @return the bean instance to use, either the original or a wrapped one; if
+ * <code>null</code>, no subsequent BeanPostProcessors will be invoked
+ * @throws Exception is thrown if error post processing bean
+ */
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
+ LOG.trace("Camel bean processing after initialization for bean: {}", beanName);
+
+ // some beans cannot be post processed at this given time, so we gotta check beforehand
+ if (!canPostProcessBean(bean, beanName)) {
+ return bean;
+ }
+
+ if (bean instanceof DefaultEndpoint) {
+ DefaultEndpoint defaultEndpoint = (DefaultEndpoint) bean;
+ defaultEndpoint.setEndpointUriIfNotSpecified(beanName);
+ }
+
+ return bean;
+ }
+
+ /**
+ * Strategy to get the {@link CamelContext} to use.
+ */
+ public CamelContext getOrLookupCamelContext() {
+ return camelContext;
+ }
+
+ /**
+ * Strategy to get the {@link CamelPostProcessorHelper}
+ */
+ protected CamelPostProcessorHelper getPostProcessorHelper() {
+ if (camelPostProcessorHelper == null) {
+ camelPostProcessorHelper = new CamelPostProcessorHelper(getOrLookupCamelContext());
+ }
+ return camelPostProcessorHelper;
+ }
+
+ protected boolean canPostProcessBean(Object bean, String beanName) {
+ return bean != null;
+ }
+
+ protected boolean canSetCamelContext(Object bean, String beanName) {
+ if (bean instanceof CamelContextAware) {
+ CamelContextAware camelContextAware = (CamelContextAware) bean;
+ CamelContext context = camelContextAware.getCamelContext();
+ if (context != null) {
+ LOG.trace("CamelContext already set on bean with id [{}]. Will keep existing CamelContext on bean.", beanName);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * A strategy method to allow implementations to perform some custom JBI
+ * based injection of the POJO
+ *
+ * @param bean the bean to be injected
+ */
+ protected void injectFields(final Object bean, final String beanName) {
+ ReflectionHelper.doWithFields(bean.getClass(), new ReflectionHelper.FieldCallback() {
+ public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
+ EndpointInject endpointInject = field.getAnnotation(EndpointInject.class);
+ if (endpointInject != null && getPostProcessorHelper().matchContext(endpointInject.context())) {
+ injectField(field, endpointInject.uri(), endpointInject.ref(), bean, beanName);
+ }
+
+ Produce produce = field.getAnnotation(Produce.class);
+ if (produce != null && getPostProcessorHelper().matchContext(produce.context())) {
+ injectField(field, produce.uri(), produce.ref(), bean, beanName);
+ }
+ }
+ });
+ }
+
+ protected void injectField(Field field, String endpointUri, String endpointRef, Object bean, String beanName) {
+ ReflectionHelper.setField(field, bean, getPostProcessorHelper().getInjectionValue(field.getType(), endpointUri, endpointRef, field.getName(), bean, beanName));
+ }
+
+ protected void injectMethods(final Object bean, final String beanName) {
+ ReflectionHelper.doWithMethods(bean.getClass(), new ReflectionHelper.MethodCallback() {
+ public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
+ setterInjection(method, bean, beanName);
+ getPostProcessorHelper().consumerInjection(method, bean, beanName);
+ }
+ });
+ }
+
+ protected void setterInjection(Method method, Object bean, String beanName) {
+ EndpointInject endpointInject = method.getAnnotation(EndpointInject.class);
+ if (endpointInject != null && getPostProcessorHelper().matchContext(endpointInject.context())) {
+ setterInjection(method, bean, beanName, endpointInject.uri(), endpointInject.ref());
+ }
+
+ Produce produce = method.getAnnotation(Produce.class);
+ if (produce != null && getPostProcessorHelper().matchContext(produce.context())) {
+ setterInjection(method, bean, beanName, produce.uri(), produce.ref());
+ }
+ }
+
+ protected void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointRef) {
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes != null) {
+ if (parameterTypes.length != 1) {
+ LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
+ } else {
+ String propertyName = ObjectHelper.getPropertyName(method);
+ Object value = getPostProcessorHelper().getInjectionValue(parameterTypes[0], endpointUri, endpointRef, propertyName, bean, beanName);
+ ObjectHelper.invokeMethod(method, bean, value);
+ }
+ }
+ }
+
+}
Added: camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/util/ReflectionHelper.java
URL: http://svn.apache.org/viewvc/camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/util/ReflectionHelper.java?rev=1311157&view=auto
==============================================================================
--- camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/util/ReflectionHelper.java (added)
+++ camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/util/ReflectionHelper.java Mon Apr 9 08:06:28 2012
@@ -0,0 +1,125 @@
+/**
+ * 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.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Helper for working with reflection on classes.
+ * <p/>
+ * This code is based on org.apache.camel.spring.util.ReflectionUtils class.
+ */
+public final class ReflectionHelper {
+
+ private ReflectionHelper() {
+ // utility class
+ }
+
+ /**
+ * Callback interface invoked on each field in the hierarchy.
+ */
+ public interface FieldCallback {
+
+ /**
+ * Perform an operation using the given field.
+ *
+ * @param field the field to operate on
+ */
+ void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;
+ }
+
+ /**
+ * Action to take on each method.
+ */
+ public interface MethodCallback {
+
+ /**
+ * Perform an operation using the given method.
+ *
+ * @param method the method to operate on
+ */
+ void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
+ }
+
+ /**
+ * Invoke the given callback on all fields in the target class, going up the
+ * class hierarchy to get all declared fields.
+ * @param clazz the target class to analyze
+ * @param fc the callback to invoke for each field
+ */
+ public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException {
+ // Keep backing up the inheritance hierarchy.
+ Class<?> targetClass = clazz;
+ do {
+ Field[] fields = targetClass.getDeclaredFields();
+ for (Field field : fields) {
+ try {
+ fc.doWith(field);
+ } catch (IllegalAccessException ex) {
+ throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': " + ex);
+ }
+ }
+ targetClass = targetClass.getSuperclass();
+ }
+ while (targetClass != null && targetClass != Object.class);
+ }
+
+ /**
+ * Perform the given callback operation on all matching methods of the given
+ * class and superclasses (or given interface and super-interfaces).
+ *
+ * @param clazz class to start looking at
+ * @param mc the callback to invoke for each method
+ */
+ public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException {
+ // Keep backing up the inheritance hierarchy.
+ Method[] methods = clazz.getDeclaredMethods();
+ for (Method method : methods) {
+ try {
+ mc.doWith(method);
+ } catch (IllegalAccessException ex) {
+ throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() + "': " + ex);
+ }
+ }
+ if (clazz.getSuperclass() != null) {
+ doWithMethods(clazz.getSuperclass(), mc);
+ } else if (clazz.isInterface()) {
+ for (Class<?> superIfc : clazz.getInterfaces()) {
+ doWithMethods(superIfc, mc);
+ }
+ }
+ }
+
+ public static void setField(Field f, Object instance, Object value) {
+ try {
+ boolean oldAccessible = f.isAccessible();
+ boolean shouldSetAccessible = !Modifier.isPublic(f.getModifiers()) && !oldAccessible;
+ if (shouldSetAccessible) {
+ f.setAccessible(true);
+ }
+ f.set(instance, value);
+ if (shouldSetAccessible) {
+ f.setAccessible(oldAccessible);
+ }
+ } catch (Exception ex) {
+ throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + f);
+ }
+ }
+
+}