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.component.bean;
018    
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Method;
021    
022    import org.apache.camel.CamelContext;
023    import org.apache.camel.Exchange;
024    import org.apache.camel.Message;
025    import org.apache.camel.Processor;
026    import org.apache.camel.impl.ServiceSupport;
027    import org.apache.camel.util.ObjectHelper;
028    import org.apache.camel.util.ServiceHelper;
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    
032    /**
033     * A {@link Processor} which converts the inbound exchange to a method
034     * invocation on a POJO
035     *
036     * @version $Revision: 905594 $
037     */
038    public class BeanProcessor extends ServiceSupport implements Processor {
039        private static final transient Log LOG = LogFactory.getLog(BeanProcessor.class);
040    
041        private boolean multiParameterArray;
042        private Method methodObject;
043        private String method;
044        private BeanHolder beanHolder;
045    
046        public BeanProcessor(Object pojo, BeanInfo beanInfo) {
047            this(new ConstantBeanHolder(pojo, beanInfo));
048        }
049    
050        public BeanProcessor(Object pojo, CamelContext camelContext, ParameterMappingStrategy parameterMappingStrategy) {
051            this(pojo, new BeanInfo(camelContext, pojo.getClass(), parameterMappingStrategy));
052        }
053    
054        public BeanProcessor(Object pojo, CamelContext camelContext) {
055            this(pojo, camelContext, BeanInfo.createParameterMappingStrategy(camelContext));
056        }
057    
058        public BeanProcessor(BeanHolder beanHolder) {
059            this.beanHolder = beanHolder;
060        }
061    
062        @Override
063        public String toString() {
064            String description = methodObject != null ? " " + methodObject : "";
065            return "BeanProcessor[" + beanHolder + description + "]";
066        }
067    
068        public void process(Exchange exchange) throws Exception {
069            // do we have an explicit method name we always should invoke
070            boolean isExplicitMethod = ObjectHelper.isNotEmpty(method);
071    
072            Object bean = beanHolder.getBean();
073            BeanInfo beanInfo = beanHolder.getBeanInfo();
074    
075            // do we have a custom adapter for this POJO to a Processor
076            // should not be invoked if an explicit method has been set
077            Processor processor = getProcessor();
078            if (!isExplicitMethod && processor != null) {
079                if (LOG.isTraceEnabled()) {
080                    LOG.trace("Using a custom adapter as bean invocation: " + processor);
081                }
082                processor.process(exchange);
083                return;
084            }
085    
086            Message in = exchange.getIn();
087    
088            // Now it gets a bit complicated as ProxyHelper can proxy beans which we later
089            // intend to invoke (for example to proxy and invoke using spring remoting).
090            // and therefore the message body contains a BeanInvocation object.
091            // However this can causes problem if we in a Camel route invokes another bean,
092            // so we must test whether BeanHolder and BeanInvocation is the same bean or not
093            BeanInvocation beanInvoke = in.getBody(BeanInvocation.class);
094            if (beanInvoke != null) {
095                if (LOG.isTraceEnabled()) {
096                    LOG.trace("Exchange IN body is a BeanInvocation instance: " + beanInvoke);
097                }
098                Class<?> clazz = beanInvoke.getMethod().getDeclaringClass();
099                boolean sameBean = clazz.isInstance(bean);
100                if (LOG.isTraceEnabled()) {
101                    LOG.debug("BeanHolder bean: " + bean.getClass() + " and beanInvocation bean: " + clazz + " is same instance: " + sameBean);
102                }
103                if (sameBean) {
104                    beanInvoke.invoke(bean, exchange);
105                    // propagate headers
106                    exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders());
107                    return;
108                }
109            }
110    
111            // set temporary header which is a hint for the bean info that introspect the bean
112            if (in.getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY) == null) {
113                in.setHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY, isMultiParameterArray());
114            }
115    
116            String prevMethod = null;
117            MethodInvocation invocation;
118            if (methodObject != null) {
119                invocation = beanInfo.createInvocation(methodObject, bean, exchange);
120            } else {
121                // we just override the bean's invocation method name here
122                if (isExplicitMethod) {
123                    prevMethod = in.getHeader(Exchange.BEAN_METHOD_NAME, String.class);
124                    in.setHeader(Exchange.BEAN_METHOD_NAME, method);
125                }
126                invocation = beanInfo.createInvocation(bean, exchange);
127            }
128            if (invocation == null) {
129                throw new IllegalStateException("No method invocation could be created, no matching method could be found on: " + bean);
130            }
131    
132            // remove temporary header
133            in.removeHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY);
134    
135            Object value = null;
136            try {
137                value = invocation.proceed();
138            } catch (InvocationTargetException e) {
139                // lets unwrap the exception
140                Throwable throwable = e.getCause();
141                if (throwable instanceof Exception) {
142                    Exception exception = (Exception)throwable;
143                    throw exception;
144                } else {
145                    Error error = (Error)throwable;
146                    throw error;
147                }
148            } finally {
149                if (isExplicitMethod) {
150                    in.setHeader(Exchange.BEAN_METHOD_NAME, prevMethod);
151                }
152            }
153    
154            // if the method returns something then set the value returned on the Exchange
155            if (!invocation.getMethod().getReturnType().equals(Void.TYPE) && value != Void.TYPE) {
156                if (exchange.getPattern().isOutCapable()) {
157                    // force out creating if not already created (as its lazy)
158                    if (LOG.isDebugEnabled()) {
159                        LOG.debug("Setting bean invocation result on the OUT message: " + value);
160                    }
161                    exchange.getOut().setBody(value);
162                    // propagate headers
163                    exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders());
164                } else {
165                    // if not out then set it on the in
166                    if (LOG.isDebugEnabled()) {
167                        LOG.debug("Setting bean invocation result on the IN message: " + value);
168                    }
169                    exchange.getIn().setBody(value);
170                }
171            }
172        }
173    
174        protected Processor getProcessor() {
175            return beanHolder.getProcessor();
176        }
177    
178        // Properties
179        // -----------------------------------------------------------------------
180    
181        public Method getMethodObject() {
182            return methodObject;
183        }
184    
185        public void setMethodObject(Method methodObject) {
186            this.methodObject = methodObject;
187        }
188    
189        public String getMethod() {
190            return method;
191        }
192    
193        public boolean isMultiParameterArray() {
194            return multiParameterArray;
195        }
196    
197        public void setMultiParameterArray(boolean mpArray) {
198            multiParameterArray = mpArray;
199        }
200    
201        /**
202         * Sets the method name to use
203         */
204        public void setMethod(String method) {
205            this.method = method;
206        }
207    
208        // Implementation methods
209        //-------------------------------------------------------------------------
210        protected void doStart() throws Exception {
211            ServiceHelper.startService(getProcessor());
212        }
213    
214        protected void doStop() throws Exception {
215            ServiceHelper.stopService(getProcessor());
216        }
217    }