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);
+        }
+    }
+
+}