You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by cs...@apache.org on 2011/10/24 16:51:13 UTC

svn commit: r1188162 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/component/bean/ test/java/org/apache/camel/component/bean/pojomessage/

Author: cschneider
Date: Mon Oct 24 14:51:12 2011
New Revision: 1188162

URL: http://svn.apache.org/viewvc?rev=1188162&view=rev
Log:
CAMEL-4565 Code and tests for sending simple objects from a dynamic proxy

Added:
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java   (with props)
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java   (with props)
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java   (with props)
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java   (with props)
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java   (with props)
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java   (with props)
Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java

Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java?rev=1188162&view=auto
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java (added)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java Mon Oct 24 14:51:12 2011
@@ -0,0 +1,217 @@
+/**
+ * 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.component.bean;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExchangeException;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.Producer;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.impl.DefaultExchange;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractCamelInvocationHandler implements InvocationHandler {
+
+    private static final transient Logger LOG = LoggerFactory.getLogger(CamelInvocationHandler.class);
+    private static ExecutorService executorService;
+    protected final Endpoint endpoint;
+    protected final Producer producer;
+
+    public AbstractCamelInvocationHandler(Endpoint endpoint, Producer producer) {
+        super();
+        this.endpoint = endpoint;
+        this.producer = producer;
+    }
+
+    private static Object getBody(Exchange exchange, Class<?> type) throws InvalidPayloadException {
+        // get the body from the Exchange from either OUT or IN
+        if (exchange.hasOut()) {
+            if (exchange.getOut().getBody() != null) {
+                return exchange.getOut().getMandatoryBody(type);
+            } else {
+                return null;
+            }
+        } else {
+            if (exchange.getIn().getBody() != null) {
+                return exchange.getIn().getMandatoryBody(type);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    protected Object invokeWithbody(final Method method, Object body, final ExchangePattern pattern) throws InterruptedException, Throwable {
+        final Exchange exchange = new DefaultExchange(endpoint, pattern);
+        exchange.getIn().setBody(body);
+
+        // is the return type a future
+        final boolean isFuture = method.getReturnType() == Future.class;
+
+        // create task to execute the proxy and gather the reply
+        FutureTask<Object> task = new FutureTask<Object>(new Callable<Object>() {
+            public Object call() throws Exception {
+                // process the exchange
+                LOG.trace("Proxied method call {} invoking producer: {}", method.getName(), producer);
+                producer.process(exchange);
+
+                Object answer = afterInvoke(method, exchange, pattern, isFuture);
+                LOG.trace("Proxied method call {} returning: {}", method.getName(), answer);
+                return answer;
+            }
+        });
+
+        if (isFuture) {
+            // submit task and return future
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Submitting task for exchange id {}", exchange.getExchangeId());
+            }
+            getExecutorService(exchange.getContext()).submit(task);
+            return task;
+        } else {
+            // execute task now
+            try {
+                task.run();
+                return task.get();
+            } catch (ExecutionException e) {
+                // we don't want the wrapped exception from JDK
+                throw e.getCause();
+            }
+        }
+    }
+
+    protected Object afterInvoke(Method method, Exchange exchange, ExchangePattern pattern, boolean isFuture) throws Exception {
+        // check if we had an exception
+        Throwable cause = exchange.getException();
+        if (cause != null) {
+            Throwable found = findSuitableException(cause, method);
+            if (found != null) {
+                if (found instanceof Exception) {
+                    throw (Exception)found;
+                } else {
+                    // wrap as exception
+                    throw new CamelExchangeException("Error processing exchange", exchange, cause);
+                }
+            }
+            // special for runtime camel exceptions as they can be nested
+            if (cause instanceof RuntimeCamelException) {
+                // if the inner cause is a runtime exception we can throw it
+                // directly
+                if (cause.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException)((RuntimeCamelException)cause).getCause();
+                }
+                throw (RuntimeCamelException)cause;
+            }
+            // okay just throw the exception as is
+            if (cause instanceof Exception) {
+                throw (Exception)cause;
+            } else {
+                // wrap as exception
+                throw new CamelExchangeException("Error processing exchange", exchange, cause);
+            }
+        }
+
+        Class<?> to = isFuture ? getGenericType(exchange.getContext(), method.getGenericReturnType()) : method.getReturnType();
+
+        // do not return a reply if the method is VOID
+        if (to == Void.TYPE) {
+            return null;
+        }
+
+        return getBody(exchange, to);
+    }
+
+    protected static Class<?> getGenericType(CamelContext context, Type type) throws ClassNotFoundException {
+        if (type == null) {
+            // fallback and use object
+            return Object.class;
+        }
+
+        // unfortunately java dont provide a nice api for getting the generic
+        // type of the return type
+        // due type erasure, so we have to gather it based on a String
+        // representation
+        String name = ObjectHelper.between(type.toString(), "<", ">");
+        if (name != null) {
+            if (name.contains("<")) {
+                // we only need the outer type
+                name = ObjectHelper.before(name, "<");
+            }
+            return context.getClassResolver().resolveMandatoryClass(name);
+        } else {
+            // fallback and use object
+            return Object.class;
+        }
+    }
+
+    protected static synchronized ExecutorService getExecutorService(CamelContext context) {
+        // CamelContext will shutdown thread pool when it shutdown so we can
+        // lazy create it on demand
+        // but in case of hot-deploy or the likes we need to be able to
+        // re-create it (its a shared static instance)
+        if (executorService == null || executorService.isTerminated() || executorService.isShutdown()) {
+            // try to lookup a pool first based on id/profile
+            executorService = context.getExecutorServiceStrategy().lookup(CamelInvocationHandler.class, "CamelInvocationHandler", "CamelInvocationHandler");
+            if (executorService == null) {
+                executorService = context.getExecutorServiceStrategy().newDefaultThreadPool(CamelInvocationHandler.class, "CamelInvocationHandler");
+            }
+        }
+        return executorService;
+    }
+
+    /**
+     * Tries to find the best suited exception to throw.
+     * <p/>
+     * It looks in the exception hierarchy from the caused exception and matches
+     * this against the declared exceptions being thrown on the method.
+     * 
+     * @param cause the caused exception
+     * @param method the method
+     * @return the exception to throw, or <tt>null</tt> if not possible to find
+     *         a suitable exception
+     */
+    protected Throwable findSuitableException(Throwable cause, Method method) {
+        if (method.getExceptionTypes() == null || method.getExceptionTypes().length == 0) {
+            return null;
+        }
+
+        // see if there is any exception which matches the declared exception on
+        // the method
+        for (Class<?> type : method.getExceptionTypes()) {
+            Object fault = ObjectHelper.getException(type, cause);
+            if (fault != null) {
+                return Throwable.class.cast(fault);
+            }
+        }
+
+        return null;
+    }
+
+}

Propchange: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java?rev=1188162&r1=1188161&r2=1188162&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java Mon Oct 24 14:51:12 2011
@@ -18,213 +18,30 @@ package org.apache.camel.component.bean;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
 
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelExchangeException;
 import org.apache.camel.Endpoint;
-import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
-import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Producer;
-import org.apache.camel.RuntimeCamelException;
-import org.apache.camel.util.ObjectHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
- * An {@link java.lang.reflect.InvocationHandler} which invokes a
- * message exchange on a camel {@link Endpoint}
- *
- * @version 
+ * An {@link java.lang.reflect.InvocationHandler} which invokes a message
+ * exchange on a camel {@link Endpoint}
+ * 
+ * @version
  */
-public class CamelInvocationHandler implements InvocationHandler {
-    private static final transient Logger LOG = LoggerFactory.getLogger(CamelInvocationHandler.class);
-
-    // use a static thread pool to not create a new thread pool for each invocation
-    private static ExecutorService executorService;
-
-    private final Endpoint endpoint;
-    private final Producer producer;
+public class CamelInvocationHandler extends AbstractCamelInvocationHandler implements InvocationHandler {
     private final MethodInfoCache methodInfoCache;
 
     public CamelInvocationHandler(Endpoint endpoint, Producer producer, MethodInfoCache methodInfoCache) {
-        this.endpoint = endpoint;
-        this.producer = producer;
+        super(endpoint, producer);
         this.methodInfoCache = methodInfoCache;
     }
 
     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
         BeanInvocation invocation = new BeanInvocation(method, args);
         MethodInfo methodInfo = methodInfoCache.getMethodInfo(method);
-
         final ExchangePattern pattern = methodInfo != null ? methodInfo.getPattern() : ExchangePattern.InOut;
-        final Exchange exchange = endpoint.createExchange(pattern);
-        exchange.getIn().setBody(invocation);
-
-        // is the return type a future
-        final boolean isFuture = method.getReturnType() == Future.class;
-
-        // create task to execute the proxy and gather the reply
-        FutureTask task = new FutureTask<Object>(new Callable<Object>() {
-            public Object call() throws Exception {
-                // process the exchange
-                LOG.trace("Proxied method call {} invoking producer: {}", method.getName(), producer);
-                producer.process(exchange);
-
-                Object answer = afterInvoke(method, exchange, pattern, isFuture);
-                LOG.trace("Proxied method call {} returning: {}", method.getName(), answer);
-                return answer;
-            }
-        });
-
-        if (isFuture) {
-            // submit task and return future
-            if (LOG.isTraceEnabled()) {
-                LOG.trace("Submitting task for exchange id {}", exchange.getExchangeId());
-            }
-            getExecutorService(exchange.getContext()).submit(task);
-            return task;
-        } else {
-            // execute task now
-            try {
-                task.run();
-                return task.get();
-            } catch (ExecutionException e) {
-                // we don't want the wrapped exception from JDK
-                throw e.getCause();
-            }
-        }
-    }
-
-    protected Object afterInvoke(Method method, Exchange exchange, ExchangePattern pattern, boolean isFuture) throws Exception {
-        // check if we had an exception
-        Throwable cause = exchange.getException();
-        if (cause != null) {
-            Throwable found = findSuitableException(cause, method);
-            if (found != null) {
-                if (found instanceof Exception) {
-                    throw (Exception) found;
-                } else {
-                    // wrap as exception
-                    throw new CamelExchangeException("Error processing exchange", exchange, cause);
-                }
-            }
-            // special for runtime camel exceptions as they can be nested
-            if (cause instanceof RuntimeCamelException) {
-                // if the inner cause is a runtime exception we can throw it directly
-                if (cause.getCause() instanceof RuntimeException) {
-                    throw (RuntimeException) ((RuntimeCamelException) cause).getCause();
-                }
-                throw (RuntimeCamelException) cause;
-            }
-            // okay just throw the exception as is
-            if (cause instanceof Exception) {
-                throw (Exception) cause;
-            } else {
-                // wrap as exception
-                throw new CamelExchangeException("Error processing exchange", exchange, cause);
-            }
-        }
-
-        // do not return a reply if the method is VOID
-        Class<?> to = method.getReturnType();
-        if (to == Void.TYPE) {
-            return null;
-        }
-
-        // use type converter so we can convert output in the desired type defined by the method
-        // and let it be mandatory so we know wont return null if we cant convert it to the defined type
-        Object answer;
-        if (!isFuture) {
-            answer = getBody(exchange, to);
-        } else {
-            // if its a Future then we need to extract the class from the future type so we know
-            // which class to return the result as
-            Class<?> returnTo = getGenericType(exchange.getContext(), method.getGenericReturnType());
-            answer = getBody(exchange, returnTo);
-        }
-
-        return answer;
-    }
-
-    private static Object getBody(Exchange exchange, Class<?> type) throws InvalidPayloadException {
-        // get the body from the Exchange from either OUT or IN
-        if (exchange.hasOut()) {
-            if (exchange.getOut().getBody() != null) {
-                return exchange.getOut().getMandatoryBody(type);
-            } else {
-                return null;
-            }
-        } else {
-            if (exchange.getIn().getBody() != null) {
-                return exchange.getIn().getMandatoryBody(type);
-            } else {
-                return null;
-            }
-        }
-    }
-
-    protected static Class getGenericType(CamelContext context, Type type) throws ClassNotFoundException {
-        if (type == null) {
-            // fallback and use object
-            return Object.class;
-        }
-
-        // unfortunately java dont provide a nice api for getting the generic type of the return type
-        // due type erasure, so we have to gather it based on a String representation
-        String name = ObjectHelper.between(type.toString(), "<", ">");
-        if (name != null) {
-            if (name.contains("<")) {
-                // we only need the outer type
-                name = ObjectHelper.before(name, "<");
-            }
-            return context.getClassResolver().resolveMandatoryClass(name);
-        } else {
-            // fallback and use object
-            return Object.class;
-        }
-    }
-
-    /**
-     * Tries to find the best suited exception to throw.
-     * <p/>
-     * It looks in the exception hierarchy from the caused exception and matches this against the declared exceptions
-     * being thrown on the method.
-     *
-     * @param cause   the caused exception
-     * @param method  the method
-     * @return the exception to throw, or <tt>null</tt> if not possible to find a suitable exception
-     */
-    protected Throwable findSuitableException(Throwable cause, Method method) {
-        if (method.getExceptionTypes() == null || method.getExceptionTypes().length == 0) {
-            return null;
-        }
-
-        // see if there is any exception which matches the declared exception on the method
-        for (Class<?> type : method.getExceptionTypes()) {
-            Object fault = ObjectHelper.getException(type, cause);
-            if (fault != null) {
-                return Throwable.class.cast(fault);
-            }
-        }
-
-        return null;
-    }
-
-    protected static synchronized ExecutorService getExecutorService(CamelContext context) {
-        // CamelContext will shutdown thread pool when it shutdown so we can lazy create it on demand
-        // but in case of hot-deploy or the likes we need to be able to re-create it (its a shared static instance)
-        if (executorService == null || executorService.isTerminated() || executorService.isShutdown()) {
-            executorService = context.getExecutorServiceManager().newDefaultThreadPool(CamelInvocationHandler.class, "CamelInvocationHandler");
-        }
-        return executorService;
+        return invokeWithbody(method, invocation, pattern);
     }
 
 }
-

Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java?rev=1188162&view=auto
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java (added)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java Mon Oct 24 14:51:12 2011
@@ -0,0 +1,48 @@
+/**
+ * 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.component.bean;
+
+import java.lang.reflect.Method;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Producer;
+import org.apache.camel.RuntimeCamelException;
+
+/**
+ * Special InvocationHandler for methods that have only one parameter. This
+ * parameter is directly sent to as the body of the message. The idea is to use
+ * that as a very open message format especially when combined with e.g. JAXB
+ * serialization.
+ */
+public class PojoMessageInvocationHandler extends AbstractCamelInvocationHandler {
+    public PojoMessageInvocationHandler(Endpoint endpoint, Producer producer) {
+        super(endpoint, producer);
+    }
+
+    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+        int argsLength = (args == null) ? 0 : args.length;
+        if (argsLength != 1) {
+            throw new RuntimeCamelException(String.format("Error creating proxy for %s.%s Number of arguments must be 1 but is %d", 
+                                                          method.getDeclaringClass().getName(),
+                                                          method.getName(), argsLength));
+        }
+        final ExchangePattern pattern = method.getReturnType() != Void.TYPE ? ExchangePattern.InOut : ExchangePattern.InOnly;
+        return invokeWithbody(method, args[0], pattern);
+    }
+
+}

Propchange: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java?rev=1188162&view=auto
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java (added)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java Mon Oct 24 14:51:12 2011
@@ -0,0 +1,40 @@
+/**
+ * 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.component.bean;
+
+import java.lang.reflect.Proxy;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.Producer;
+import org.apache.camel.util.ServiceHelper;
+
+/**
+ * Create a dynamic proxy for a given interface and endpoint that sends the parameter object to the endpoint and optionally
+ * receives a reply. Unlike the ProxyHelper this works only with methods that have only one parameter.
+ */
+public final class PojoProxyHelper {
+    private PojoProxyHelper() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T createProxy(Endpoint endpoint, Class<?>... interfaceClasses) throws Exception {
+        Producer producer = endpoint.createProducer();
+        // ensure the producer is started
+        ServiceHelper.startService(producer);
+        return (T)Proxy.newProxyInstance(ProxyHelper.getClassLoader(interfaceClasses), interfaceClasses.clone(), new PojoMessageInvocationHandler(endpoint, producer));
+    }
+}

Propchange: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java?rev=1188162&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java Mon Oct 24 14:51:12 2011
@@ -0,0 +1,33 @@
+/**
+ * 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.component.bean.pojomessage;
+
+public class Person {
+    String name;
+
+    public Person() {
+    }
+
+    public Person(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+}
\ No newline at end of file

Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java?rev=1188162&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java Mon Oct 24 14:51:12 2011
@@ -0,0 +1,73 @@
+/**
+ * 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.component.bean.pojomessage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Endpoint;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.bean.PojoProxyHelper;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Test;
+
+public class PojoProxyHelperOneWayTest extends ContextTestSupport {
+
+    PersonReceiver receiver = new PersonReceiver();
+    
+    @Test
+    public void testOneWay() throws Exception {
+        Endpoint personEndpoint = context.getEndpoint("direct:person");
+        MockEndpoint result = (MockEndpoint)context.getEndpoint("mock:result");
+        Person person = new Person("Chris");
+        result.expectedBodiesReceived(person);
+        PersonHandler sender = PojoProxyHelper.createProxy(personEndpoint, PersonHandler.class);
+        
+        sender.onPerson(person);
+        
+        result.assertIsSatisfied();
+        Assert.assertEquals(1, receiver.receivedPersons.size());
+        Assert.assertEquals(person.getName(), receiver.receivedPersons.get(0).getName());
+    }
+    
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            
+            @Override
+            public void configure() throws Exception {
+                from("direct:person").to("mock:result").bean(receiver);
+            }
+        };
+    }
+    
+    public final class PersonReceiver implements PersonHandler {
+        public List<Person> receivedPersons = new ArrayList<Person>();
+
+        @Override
+        public void onPerson(Person person) {
+            receivedPersons.add(person);
+        }
+    }
+
+    public interface PersonHandler {
+        void onPerson(Person person);
+    }
+}

Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java?rev=1188162&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java Mon Oct 24 14:51:12 2011
@@ -0,0 +1,61 @@
+/**
+ * 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.component.bean.pojomessage;
+
+import junit.framework.Assert;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Endpoint;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.bean.PojoProxyHelper;
+import org.junit.Test;
+
+public class PojoProxyHelperRequestReplyTest extends ContextTestSupport {
+
+    PersonReceiver receiver = new PersonReceiver();
+    
+    @Test
+    public void testRequestReply() throws Exception {
+        Endpoint personEndpoint = context.getEndpoint("direct:person");
+        Person person = new Person("Chris");
+        PersonHandler sender = PojoProxyHelper.createProxy(personEndpoint, PersonHandler.class);
+        
+        Person resultPerson = sender.onPerson(person);
+        Assert.assertEquals(person.getName() + "1", resultPerson.getName());
+    }
+    
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() throws Exception {
+                from("direct:person").bean(receiver);
+            }
+        };
+    }
+    
+    public final class PersonReceiver implements PersonHandler {
+        @Override
+        public Person onPerson(Person person) {
+            return new Person(person.getName() + "1");
+        }
+    }
+
+    public interface PersonHandler {
+        Person onPerson(Person person);
+    }
+
+}

Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Re: svn commit: r1188162 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/component/bean/ test/java/org/apache/camel/component/bean/pojomessage/

Posted by Claus Ibsen <cl...@gmail.com>.
Hi

Nice refactor

Just a few suggestion for polishing
- the logger in the abstract class is using the other class name
- possible add a bit javadoc to the abstract class
- a method has xxxbody where body is with a little b, and not Body.


On Mon, Oct 24, 2011 at 4:51 PM,  <cs...@apache.org> wrote:
> Author: cschneider
> Date: Mon Oct 24 14:51:12 2011
> New Revision: 1188162
>
> URL: http://svn.apache.org/viewvc?rev=1188162&view=rev
> Log:
> CAMEL-4565 Code and tests for sending simple objects from a dynamic proxy
>
> Added:
>    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java   (with props)
>    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java   (with props)
>    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java   (with props)
>    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/
>    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java   (with props)
>    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java   (with props)
>    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java   (with props)
> Modified:
>    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java
>
> Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java?rev=1188162&view=auto
> ==============================================================================
> --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java (added)
> +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java Mon Oct 24 14:51:12 2011
> @@ -0,0 +1,217 @@
> +/**
> + * 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.component.bean;
> +
> +import java.lang.reflect.InvocationHandler;
> +import java.lang.reflect.Method;
> +import java.lang.reflect.Type;
> +import java.util.concurrent.Callable;
> +import java.util.concurrent.ExecutionException;
> +import java.util.concurrent.ExecutorService;
> +import java.util.concurrent.Future;
> +import java.util.concurrent.FutureTask;
> +
> +import org.apache.camel.CamelContext;
> +import org.apache.camel.CamelExchangeException;
> +import org.apache.camel.Endpoint;
> +import org.apache.camel.Exchange;
> +import org.apache.camel.ExchangePattern;
> +import org.apache.camel.InvalidPayloadException;
> +import org.apache.camel.Producer;
> +import org.apache.camel.RuntimeCamelException;
> +import org.apache.camel.impl.DefaultExchange;
> +import org.apache.camel.util.ObjectHelper;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +public abstract class AbstractCamelInvocationHandler implements InvocationHandler {
> +
> +    private static final transient Logger LOG = LoggerFactory.getLogger(CamelInvocationHandler.class);
> +    private static ExecutorService executorService;
> +    protected final Endpoint endpoint;
> +    protected final Producer producer;
> +
> +    public AbstractCamelInvocationHandler(Endpoint endpoint, Producer producer) {
> +        super();
> +        this.endpoint = endpoint;
> +        this.producer = producer;
> +    }
> +
> +    private static Object getBody(Exchange exchange, Class<?> type) throws InvalidPayloadException {
> +        // get the body from the Exchange from either OUT or IN
> +        if (exchange.hasOut()) {
> +            if (exchange.getOut().getBody() != null) {
> +                return exchange.getOut().getMandatoryBody(type);
> +            } else {
> +                return null;
> +            }
> +        } else {
> +            if (exchange.getIn().getBody() != null) {
> +                return exchange.getIn().getMandatoryBody(type);
> +            } else {
> +                return null;
> +            }
> +        }
> +    }
> +
> +    protected Object invokeWithbody(final Method method, Object body, final ExchangePattern pattern) throws InterruptedException, Throwable {
> +        final Exchange exchange = new DefaultExchange(endpoint, pattern);
> +        exchange.getIn().setBody(body);
> +
> +        // is the return type a future
> +        final boolean isFuture = method.getReturnType() == Future.class;
> +
> +        // create task to execute the proxy and gather the reply
> +        FutureTask<Object> task = new FutureTask<Object>(new Callable<Object>() {
> +            public Object call() throws Exception {
> +                // process the exchange
> +                LOG.trace("Proxied method call {} invoking producer: {}", method.getName(), producer);
> +                producer.process(exchange);
> +
> +                Object answer = afterInvoke(method, exchange, pattern, isFuture);
> +                LOG.trace("Proxied method call {} returning: {}", method.getName(), answer);
> +                return answer;
> +            }
> +        });
> +
> +        if (isFuture) {
> +            // submit task and return future
> +            if (LOG.isTraceEnabled()) {
> +                LOG.trace("Submitting task for exchange id {}", exchange.getExchangeId());
> +            }
> +            getExecutorService(exchange.getContext()).submit(task);
> +            return task;
> +        } else {
> +            // execute task now
> +            try {
> +                task.run();
> +                return task.get();
> +            } catch (ExecutionException e) {
> +                // we don't want the wrapped exception from JDK
> +                throw e.getCause();
> +            }
> +        }
> +    }
> +
> +    protected Object afterInvoke(Method method, Exchange exchange, ExchangePattern pattern, boolean isFuture) throws Exception {
> +        // check if we had an exception
> +        Throwable cause = exchange.getException();
> +        if (cause != null) {
> +            Throwable found = findSuitableException(cause, method);
> +            if (found != null) {
> +                if (found instanceof Exception) {
> +                    throw (Exception)found;
> +                } else {
> +                    // wrap as exception
> +                    throw new CamelExchangeException("Error processing exchange", exchange, cause);
> +                }
> +            }
> +            // special for runtime camel exceptions as they can be nested
> +            if (cause instanceof RuntimeCamelException) {
> +                // if the inner cause is a runtime exception we can throw it
> +                // directly
> +                if (cause.getCause() instanceof RuntimeException) {
> +                    throw (RuntimeException)((RuntimeCamelException)cause).getCause();
> +                }
> +                throw (RuntimeCamelException)cause;
> +            }
> +            // okay just throw the exception as is
> +            if (cause instanceof Exception) {
> +                throw (Exception)cause;
> +            } else {
> +                // wrap as exception
> +                throw new CamelExchangeException("Error processing exchange", exchange, cause);
> +            }
> +        }
> +
> +        Class<?> to = isFuture ? getGenericType(exchange.getContext(), method.getGenericReturnType()) : method.getReturnType();
> +
> +        // do not return a reply if the method is VOID
> +        if (to == Void.TYPE) {
> +            return null;
> +        }
> +
> +        return getBody(exchange, to);
> +    }
> +
> +    protected static Class<?> getGenericType(CamelContext context, Type type) throws ClassNotFoundException {
> +        if (type == null) {
> +            // fallback and use object
> +            return Object.class;
> +        }
> +
> +        // unfortunately java dont provide a nice api for getting the generic
> +        // type of the return type
> +        // due type erasure, so we have to gather it based on a String
> +        // representation
> +        String name = ObjectHelper.between(type.toString(), "<", ">");
> +        if (name != null) {
> +            if (name.contains("<")) {
> +                // we only need the outer type
> +                name = ObjectHelper.before(name, "<");
> +            }
> +            return context.getClassResolver().resolveMandatoryClass(name);
> +        } else {
> +            // fallback and use object
> +            return Object.class;
> +        }
> +    }
> +
> +    protected static synchronized ExecutorService getExecutorService(CamelContext context) {
> +        // CamelContext will shutdown thread pool when it shutdown so we can
> +        // lazy create it on demand
> +        // but in case of hot-deploy or the likes we need to be able to
> +        // re-create it (its a shared static instance)
> +        if (executorService == null || executorService.isTerminated() || executorService.isShutdown()) {
> +            // try to lookup a pool first based on id/profile
> +            executorService = context.getExecutorServiceStrategy().lookup(CamelInvocationHandler.class, "CamelInvocationHandler", "CamelInvocationHandler");
> +            if (executorService == null) {
> +                executorService = context.getExecutorServiceStrategy().newDefaultThreadPool(CamelInvocationHandler.class, "CamelInvocationHandler");
> +            }
> +        }
> +        return executorService;
> +    }
> +
> +    /**
> +     * Tries to find the best suited exception to throw.
> +     * <p/>
> +     * It looks in the exception hierarchy from the caused exception and matches
> +     * this against the declared exceptions being thrown on the method.
> +     *
> +     * @param cause the caused exception
> +     * @param method the method
> +     * @return the exception to throw, or <tt>null</tt> if not possible to find
> +     *         a suitable exception
> +     */
> +    protected Throwable findSuitableException(Throwable cause, Method method) {
> +        if (method.getExceptionTypes() == null || method.getExceptionTypes().length == 0) {
> +            return null;
> +        }
> +
> +        // see if there is any exception which matches the declared exception on
> +        // the method
> +        for (Class<?> type : method.getExceptionTypes()) {
> +            Object fault = ObjectHelper.getException(type, cause);
> +            if (fault != null) {
> +                return Throwable.class.cast(fault);
> +            }
> +        }
> +
> +        return null;
> +    }
> +
> +}
>
> Propchange: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/AbstractCamelInvocationHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java?rev=1188162&r1=1188161&r2=1188162&view=diff
> ==============================================================================
> --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java (original)
> +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/CamelInvocationHandler.java Mon Oct 24 14:51:12 2011
> @@ -18,213 +18,30 @@ package org.apache.camel.component.bean;
>
>  import java.lang.reflect.InvocationHandler;
>  import java.lang.reflect.Method;
> -import java.lang.reflect.Type;
> -import java.util.concurrent.Callable;
> -import java.util.concurrent.ExecutionException;
> -import java.util.concurrent.ExecutorService;
> -import java.util.concurrent.Future;
> -import java.util.concurrent.FutureTask;
>
> -import org.apache.camel.CamelContext;
> -import org.apache.camel.CamelExchangeException;
>  import org.apache.camel.Endpoint;
> -import org.apache.camel.Exchange;
>  import org.apache.camel.ExchangePattern;
> -import org.apache.camel.InvalidPayloadException;
>  import org.apache.camel.Producer;
> -import org.apache.camel.RuntimeCamelException;
> -import org.apache.camel.util.ObjectHelper;
> -import org.slf4j.Logger;
> -import org.slf4j.LoggerFactory;
>
>  /**
> - * An {@link java.lang.reflect.InvocationHandler} which invokes a
> - * message exchange on a camel {@link Endpoint}
> - *
> - * @version
> + * An {@link java.lang.reflect.InvocationHandler} which invokes a message
> + * exchange on a camel {@link Endpoint}
> + *
> + * @version
>  */
> -public class CamelInvocationHandler implements InvocationHandler {
> -    private static final transient Logger LOG = LoggerFactory.getLogger(CamelInvocationHandler.class);
> -
> -    // use a static thread pool to not create a new thread pool for each invocation
> -    private static ExecutorService executorService;
> -
> -    private final Endpoint endpoint;
> -    private final Producer producer;
> +public class CamelInvocationHandler extends AbstractCamelInvocationHandler implements InvocationHandler {
>     private final MethodInfoCache methodInfoCache;
>
>     public CamelInvocationHandler(Endpoint endpoint, Producer producer, MethodInfoCache methodInfoCache) {
> -        this.endpoint = endpoint;
> -        this.producer = producer;
> +        super(endpoint, producer);
>         this.methodInfoCache = methodInfoCache;
>     }
>
>     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
>         BeanInvocation invocation = new BeanInvocation(method, args);
>         MethodInfo methodInfo = methodInfoCache.getMethodInfo(method);
> -
>         final ExchangePattern pattern = methodInfo != null ? methodInfo.getPattern() : ExchangePattern.InOut;
> -        final Exchange exchange = endpoint.createExchange(pattern);
> -        exchange.getIn().setBody(invocation);
> -
> -        // is the return type a future
> -        final boolean isFuture = method.getReturnType() == Future.class;
> -
> -        // create task to execute the proxy and gather the reply
> -        FutureTask task = new FutureTask<Object>(new Callable<Object>() {
> -            public Object call() throws Exception {
> -                // process the exchange
> -                LOG.trace("Proxied method call {} invoking producer: {}", method.getName(), producer);
> -                producer.process(exchange);
> -
> -                Object answer = afterInvoke(method, exchange, pattern, isFuture);
> -                LOG.trace("Proxied method call {} returning: {}", method.getName(), answer);
> -                return answer;
> -            }
> -        });
> -
> -        if (isFuture) {
> -            // submit task and return future
> -            if (LOG.isTraceEnabled()) {
> -                LOG.trace("Submitting task for exchange id {}", exchange.getExchangeId());
> -            }
> -            getExecutorService(exchange.getContext()).submit(task);
> -            return task;
> -        } else {
> -            // execute task now
> -            try {
> -                task.run();
> -                return task.get();
> -            } catch (ExecutionException e) {
> -                // we don't want the wrapped exception from JDK
> -                throw e.getCause();
> -            }
> -        }
> -    }
> -
> -    protected Object afterInvoke(Method method, Exchange exchange, ExchangePattern pattern, boolean isFuture) throws Exception {
> -        // check if we had an exception
> -        Throwable cause = exchange.getException();
> -        if (cause != null) {
> -            Throwable found = findSuitableException(cause, method);
> -            if (found != null) {
> -                if (found instanceof Exception) {
> -                    throw (Exception) found;
> -                } else {
> -                    // wrap as exception
> -                    throw new CamelExchangeException("Error processing exchange", exchange, cause);
> -                }
> -            }
> -            // special for runtime camel exceptions as they can be nested
> -            if (cause instanceof RuntimeCamelException) {
> -                // if the inner cause is a runtime exception we can throw it directly
> -                if (cause.getCause() instanceof RuntimeException) {
> -                    throw (RuntimeException) ((RuntimeCamelException) cause).getCause();
> -                }
> -                throw (RuntimeCamelException) cause;
> -            }
> -            // okay just throw the exception as is
> -            if (cause instanceof Exception) {
> -                throw (Exception) cause;
> -            } else {
> -                // wrap as exception
> -                throw new CamelExchangeException("Error processing exchange", exchange, cause);
> -            }
> -        }
> -
> -        // do not return a reply if the method is VOID
> -        Class<?> to = method.getReturnType();
> -        if (to == Void.TYPE) {
> -            return null;
> -        }
> -
> -        // use type converter so we can convert output in the desired type defined by the method
> -        // and let it be mandatory so we know wont return null if we cant convert it to the defined type
> -        Object answer;
> -        if (!isFuture) {
> -            answer = getBody(exchange, to);
> -        } else {
> -            // if its a Future then we need to extract the class from the future type so we know
> -            // which class to return the result as
> -            Class<?> returnTo = getGenericType(exchange.getContext(), method.getGenericReturnType());
> -            answer = getBody(exchange, returnTo);
> -        }
> -
> -        return answer;
> -    }
> -
> -    private static Object getBody(Exchange exchange, Class<?> type) throws InvalidPayloadException {
> -        // get the body from the Exchange from either OUT or IN
> -        if (exchange.hasOut()) {
> -            if (exchange.getOut().getBody() != null) {
> -                return exchange.getOut().getMandatoryBody(type);
> -            } else {
> -                return null;
> -            }
> -        } else {
> -            if (exchange.getIn().getBody() != null) {
> -                return exchange.getIn().getMandatoryBody(type);
> -            } else {
> -                return null;
> -            }
> -        }
> -    }
> -
> -    protected static Class getGenericType(CamelContext context, Type type) throws ClassNotFoundException {
> -        if (type == null) {
> -            // fallback and use object
> -            return Object.class;
> -        }
> -
> -        // unfortunately java dont provide a nice api for getting the generic type of the return type
> -        // due type erasure, so we have to gather it based on a String representation
> -        String name = ObjectHelper.between(type.toString(), "<", ">");
> -        if (name != null) {
> -            if (name.contains("<")) {
> -                // we only need the outer type
> -                name = ObjectHelper.before(name, "<");
> -            }
> -            return context.getClassResolver().resolveMandatoryClass(name);
> -        } else {
> -            // fallback and use object
> -            return Object.class;
> -        }
> -    }
> -
> -    /**
> -     * Tries to find the best suited exception to throw.
> -     * <p/>
> -     * It looks in the exception hierarchy from the caused exception and matches this against the declared exceptions
> -     * being thrown on the method.
> -     *
> -     * @param cause   the caused exception
> -     * @param method  the method
> -     * @return the exception to throw, or <tt>null</tt> if not possible to find a suitable exception
> -     */
> -    protected Throwable findSuitableException(Throwable cause, Method method) {
> -        if (method.getExceptionTypes() == null || method.getExceptionTypes().length == 0) {
> -            return null;
> -        }
> -
> -        // see if there is any exception which matches the declared exception on the method
> -        for (Class<?> type : method.getExceptionTypes()) {
> -            Object fault = ObjectHelper.getException(type, cause);
> -            if (fault != null) {
> -                return Throwable.class.cast(fault);
> -            }
> -        }
> -
> -        return null;
> -    }
> -
> -    protected static synchronized ExecutorService getExecutorService(CamelContext context) {
> -        // CamelContext will shutdown thread pool when it shutdown so we can lazy create it on demand
> -        // but in case of hot-deploy or the likes we need to be able to re-create it (its a shared static instance)
> -        if (executorService == null || executorService.isTerminated() || executorService.isShutdown()) {
> -            executorService = context.getExecutorServiceManager().newDefaultThreadPool(CamelInvocationHandler.class, "CamelInvocationHandler");
> -        }
> -        return executorService;
> +        return invokeWithbody(method, invocation, pattern);
>     }
>
>  }
> -
>
> Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java?rev=1188162&view=auto
> ==============================================================================
> --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java (added)
> +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java Mon Oct 24 14:51:12 2011
> @@ -0,0 +1,48 @@
> +/**
> + * 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.component.bean;
> +
> +import java.lang.reflect.Method;
> +
> +import org.apache.camel.Endpoint;
> +import org.apache.camel.ExchangePattern;
> +import org.apache.camel.Producer;
> +import org.apache.camel.RuntimeCamelException;
> +
> +/**
> + * Special InvocationHandler for methods that have only one parameter. This
> + * parameter is directly sent to as the body of the message. The idea is to use
> + * that as a very open message format especially when combined with e.g. JAXB
> + * serialization.
> + */
> +public class PojoMessageInvocationHandler extends AbstractCamelInvocationHandler {
> +    public PojoMessageInvocationHandler(Endpoint endpoint, Producer producer) {
> +        super(endpoint, producer);
> +    }
> +
> +    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
> +        int argsLength = (args == null) ? 0 : args.length;
> +        if (argsLength != 1) {
> +            throw new RuntimeCamelException(String.format("Error creating proxy for %s.%s Number of arguments must be 1 but is %d",
> +                                                          method.getDeclaringClass().getName(),
> +                                                          method.getName(), argsLength));
> +        }
> +        final ExchangePattern pattern = method.getReturnType() != Void.TYPE ? ExchangePattern.InOut : ExchangePattern.InOnly;
> +        return invokeWithbody(method, args[0], pattern);
> +    }
> +
> +}
>
> Propchange: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoMessageInvocationHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java?rev=1188162&view=auto
> ==============================================================================
> --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java (added)
> +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java Mon Oct 24 14:51:12 2011
> @@ -0,0 +1,40 @@
> +/**
> + * 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.component.bean;
> +
> +import java.lang.reflect.Proxy;
> +
> +import org.apache.camel.Endpoint;
> +import org.apache.camel.Producer;
> +import org.apache.camel.util.ServiceHelper;
> +
> +/**
> + * Create a dynamic proxy for a given interface and endpoint that sends the parameter object to the endpoint and optionally
> + * receives a reply. Unlike the ProxyHelper this works only with methods that have only one parameter.
> + */
> +public final class PojoProxyHelper {
> +    private PojoProxyHelper() {
> +    }
> +
> +    @SuppressWarnings("unchecked")
> +    public static <T> T createProxy(Endpoint endpoint, Class<?>... interfaceClasses) throws Exception {
> +        Producer producer = endpoint.createProducer();
> +        // ensure the producer is started
> +        ServiceHelper.startService(producer);
> +        return (T)Proxy.newProxyInstance(ProxyHelper.getClassLoader(interfaceClasses), interfaceClasses.clone(), new PojoMessageInvocationHandler(endpoint, producer));
> +    }
> +}
>
> Propchange: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/PojoProxyHelper.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java?rev=1188162&view=auto
> ==============================================================================
> --- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java (added)
> +++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java Mon Oct 24 14:51:12 2011
> @@ -0,0 +1,33 @@
> +/**
> + * 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.component.bean.pojomessage;
> +
> +public class Person {
> +    String name;
> +
> +    public Person() {
> +    }
> +
> +    public Person(String name) {
> +        this.name = name;
> +    }
> +
> +    public String getName() {
> +        return name;
> +    }
> +
> +}
> \ No newline at end of file
>
> Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/Person.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java?rev=1188162&view=auto
> ==============================================================================
> --- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java (added)
> +++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java Mon Oct 24 14:51:12 2011
> @@ -0,0 +1,73 @@
> +/**
> + * 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.component.bean.pojomessage;
> +
> +import java.util.ArrayList;
> +import java.util.List;
> +
> +import junit.framework.Assert;
> +
> +import org.apache.camel.ContextTestSupport;
> +import org.apache.camel.Endpoint;
> +import org.apache.camel.builder.RouteBuilder;
> +import org.apache.camel.component.bean.PojoProxyHelper;
> +import org.apache.camel.component.mock.MockEndpoint;
> +import org.junit.Test;
> +
> +public class PojoProxyHelperOneWayTest extends ContextTestSupport {
> +
> +    PersonReceiver receiver = new PersonReceiver();
> +
> +    @Test
> +    public void testOneWay() throws Exception {
> +        Endpoint personEndpoint = context.getEndpoint("direct:person");
> +        MockEndpoint result = (MockEndpoint)context.getEndpoint("mock:result");
> +        Person person = new Person("Chris");
> +        result.expectedBodiesReceived(person);
> +        PersonHandler sender = PojoProxyHelper.createProxy(personEndpoint, PersonHandler.class);
> +
> +        sender.onPerson(person);
> +
> +        result.assertIsSatisfied();
> +        Assert.assertEquals(1, receiver.receivedPersons.size());
> +        Assert.assertEquals(person.getName(), receiver.receivedPersons.get(0).getName());
> +    }
> +
> +    @Override
> +    protected RouteBuilder createRouteBuilder() throws Exception {
> +        return new RouteBuilder() {
> +
> +            @Override
> +            public void configure() throws Exception {
> +                from("direct:person").to("mock:result").bean(receiver);
> +            }
> +        };
> +    }
> +
> +    public final class PersonReceiver implements PersonHandler {
> +        public List<Person> receivedPersons = new ArrayList<Person>();
> +
> +        @Override
> +        public void onPerson(Person person) {
> +            receivedPersons.add(person);
> +        }
> +    }
> +
> +    public interface PersonHandler {
> +        void onPerson(Person person);
> +    }
> +}
>
> Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperOneWayTest.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java
> URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java?rev=1188162&view=auto
> ==============================================================================
> --- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java (added)
> +++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java Mon Oct 24 14:51:12 2011
> @@ -0,0 +1,61 @@
> +/**
> + * 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.component.bean.pojomessage;
> +
> +import junit.framework.Assert;
> +
> +import org.apache.camel.ContextTestSupport;
> +import org.apache.camel.Endpoint;
> +import org.apache.camel.builder.RouteBuilder;
> +import org.apache.camel.component.bean.PojoProxyHelper;
> +import org.junit.Test;
> +
> +public class PojoProxyHelperRequestReplyTest extends ContextTestSupport {
> +
> +    PersonReceiver receiver = new PersonReceiver();
> +
> +    @Test
> +    public void testRequestReply() throws Exception {
> +        Endpoint personEndpoint = context.getEndpoint("direct:person");
> +        Person person = new Person("Chris");
> +        PersonHandler sender = PojoProxyHelper.createProxy(personEndpoint, PersonHandler.class);
> +
> +        Person resultPerson = sender.onPerson(person);
> +        Assert.assertEquals(person.getName() + "1", resultPerson.getName());
> +    }
> +
> +    @Override
> +    protected RouteBuilder createRouteBuilder() throws Exception {
> +        return new RouteBuilder() {
> +            public void configure() throws Exception {
> +                from("direct:person").bean(receiver);
> +            }
> +        };
> +    }
> +
> +    public final class PersonReceiver implements PersonHandler {
> +        @Override
> +        public Person onPerson(Person person) {
> +            return new Person(person.getName() + "1");
> +        }
> +    }
> +
> +    public interface PersonHandler {
> +        Person onPerson(Person person);
> +    }
> +
> +}
>
> Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/pojomessage/PojoProxyHelperRequestReplyTest.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
>
>



-- 
Claus Ibsen
-----------------
FuseSource
Email: cibsen@fusesource.com
Web: http://fusesource.com
Twitter: davsclaus, fusenews
Blog: http://davsclaus.blogspot.com/
Author of Camel in Action: http://www.manning.com/ibsen/