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 }