You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by ke...@apache.org on 2011/05/19 19:01:09 UTC

svn commit: r1124995 - in /openejb/trunk/openejb3/server: openejb-client/src/main/java/org/apache/openejb/client/ openejb-client/src/test/java/org/apache/openejb/client/ openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/

Author: kevan
Date: Thu May 19 17:01:09 2011
New Revision: 1124995

URL: http://svn.apache.org/viewvc?rev=1124995&view=rev
Log:
OPENEJB-1165 Got Ivan's original remote patch building. Have not done any additional testing

Modified:
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBMetaDataImpl.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBResponse.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/RequestMethodConstants.java
    openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Response.java
    openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/EJBRequestTest.java
    openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java
    openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/ClientObjectFactory.java
    openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/EjbRequestHandler.java
    openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/JndiRequestHandler.java

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java Thu May 19 17:01:09 2011
@@ -68,14 +68,21 @@ public class Client {
 
     public static Response request(Request req, Response res, ServerMetaData server) throws RemoteException {
         try {
-            return client.processRequest(req, res, server);
+            return client.processRequest(req, res, server, null);
         } finally {
             failed.remove();
         }
     }
 
-    protected Response processRequest(Request req, Response res, ServerMetaData server) throws RemoteException {
-//        System.out.println("req = " + req);
+    public static Response request(Request req, Response res, ServerMetaData server, URI preferedServerURI) throws RemoteException {
+        try {
+            return client.processRequest(req, res, server, preferedServerURI);
+        } finally {
+            failed.remove();
+        }
+    }
+
+    protected Response processRequest(Request req, Response res, ServerMetaData server, URI preferredServerURI) throws RemoteException {
         if (server == null)
             throw new IllegalArgumentException("Server instance cannot be null");
 
@@ -87,9 +94,16 @@ public class Client {
 
         Connection conn = null;
         try {
-            conn = ConnectionManager.getConnection(cluster, server, req);
+            if (preferredServerURI != null) {
+                conn = ConnectionManager.getConnection(preferredServerURI);
+            } else {
+                conn = ConnectionManager.getConnection(cluster, server, req);
+            }
+            if (res instanceof EJBResponse) {
+                ((EJBResponse) res).setResponseServerURI(conn.getURI());
+            }
         } catch (IOException e) {
-            throw new RemoteException("Unable to connect",e);
+            throw new RemoteException("Unable to connect", e);
         }
 
         try {
@@ -278,9 +292,11 @@ public class Client {
             Set<URI> failed = getFailed();
             failed.add(conn.getURI());
             conn.discard();
-            if (e instanceof RetryException || getRetry()){
+            //If the preferred server URI is configured, we will not try to fail over to other servers
+            //Currently, while calling Future.cancel method remotely, the initial business method invocation server URI should be used.
+            if ((e instanceof RetryException || getRetry()) && preferredServerURI == null) {
                 try {
-                    processRequest(req, res, server);
+                    processRequest(req, res, server, null);
                 } catch (RemoteFailoverException re) {
                     throw re;
                 } catch (RemoteException re) {

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java Thu May 19 17:01:09 2011
@@ -140,6 +140,18 @@ public abstract class EJBInvocationHandl
         return response;
     }
 
+    protected EJBResponse request(EJBRequest req, EJBResponse res) throws Exception {
+        req.setClientIdentity(getClientIdentity());
+
+        req.setServerHash(server.buildHash());
+
+        Client.request(req, res, server);
+        if (null != res.getServer()) {
+            server.merge(res.getServer());
+        }
+        return res;
+    }
+
     protected Object getClientIdentity() {
         if (client != null) {
             Object identity = client.getClientIdentity();

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBMetaDataImpl.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBMetaDataImpl.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBMetaDataImpl.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBMetaDataImpl.java Thu May 19 17:01:09 2011
@@ -16,15 +16,18 @@
  */
 package org.apache.openejb.client;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
-import java.io.ByteArrayOutputStream;
-import java.io.ByteArrayInputStream;
-import java.util.List;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Properties;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
 
 import javax.ejb.EJBHome;
 
@@ -54,6 +57,8 @@ public class EJBMetaDataImpl implements 
 
     protected transient Class mainInterface;
 
+    protected final transient Set<String> asynchronousMethods = new HashSet<String>();
+
     protected final transient Properties properties = new Properties();
 
     protected transient Class keyClass;
@@ -69,9 +74,9 @@ public class EJBMetaDataImpl implements 
 
     }
 
-    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, String typeOfBean, InterfaceType interfaceType, List<Class> businessInterfaces) {
+    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, String typeOfBean, InterfaceType interfaceType, List<Class> businessInterfaces, Set<String> asynchronousMethodSignatures) {
         this.interfaceType = interfaceType;
-        
+
         if ("STATEFUL".equalsIgnoreCase(typeOfBean)){
             this.type = STATEFUL;
         } else if ("STATELESS".equalsIgnoreCase(typeOfBean)){
@@ -88,22 +93,25 @@ public class EJBMetaDataImpl implements 
         if (businessInterfaces != null){
             this.businessClasses.addAll(businessInterfaces);
         }
+        if (asynchronousMethodSignatures != null) {
+            this.asynchronousMethods.addAll(asynchronousMethodSignatures);
+        }
     }
 
-    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, Class primaryKeyClass, String typeOfBean, InterfaceType interfaceType, List<Class> businessInterfaces) {
-        this(homeInterface, remoteInterface, typeOfBean, interfaceType, businessInterfaces);
+    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, Class primaryKeyClass, String typeOfBean, InterfaceType interfaceType, List<Class> businessInterfaces, Set<String> asynchronousMethodSignatures) {
+        this(homeInterface, remoteInterface, typeOfBean, interfaceType, businessInterfaces, asynchronousMethodSignatures);
         if (type == CMP_ENTITY || type == BMP_ENTITY) {
             this.keyClass = primaryKeyClass;
         }
     }
 
-    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, Class primaryKeyClass, String typeOfBean, String deploymentID, InterfaceType interfaceType, List<Class> businessInterfaces) {
-        this(homeInterface, remoteInterface, primaryKeyClass, typeOfBean, interfaceType, businessInterfaces);
+    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, Class primaryKeyClass, String typeOfBean, String deploymentID, InterfaceType interfaceType, List<Class> businessInterfaces, Set<String> asynchronousMethodSignatures) {
+        this(homeInterface, remoteInterface, primaryKeyClass, typeOfBean, interfaceType, businessInterfaces, asynchronousMethodSignatures);
         this.deploymentID = deploymentID;
     }
 
-    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, Class primaryKeyClass, String typeOfBean, String deploymentID, int deploymentCode, InterfaceType interfaceType, List<Class> businessInterfaces, Class mainInterface) {
-        this(homeInterface, remoteInterface, primaryKeyClass, typeOfBean, deploymentID, interfaceType, businessInterfaces);
+    public EJBMetaDataImpl(Class homeInterface, Class remoteInterface, Class primaryKeyClass, String typeOfBean, String deploymentID, int deploymentCode, InterfaceType interfaceType, List<Class> businessInterfaces, Set<String> asynchronousMethodSignatures) {
+        this(homeInterface, remoteInterface, primaryKeyClass, typeOfBean, deploymentID, interfaceType, businessInterfaces, asynchronousMethodSignatures);
         this.deploymentCode = deploymentCode;
     }
 
@@ -143,6 +151,17 @@ public class EJBMetaDataImpl implements 
         return (type == STATEFUL || type == STATELESS || type == SINGLETON);
     }
 
+    public boolean isAsynchronousMethod(Method method) {
+        if(asynchronousMethods.size() == 0) {
+            return false;
+        }
+        return asynchronousMethods.contains(generateMethodSignature(method));
+    }
+
+    public void addAsynchronousMethod(Method method) {
+        asynchronousMethods.add(generateMethodSignature(method));
+    }
+
     protected void setEJBHomeProxy(EJBHomeProxy home) {
         ejbHomeProxy = home;
     }
@@ -194,8 +213,14 @@ public class EJBMetaDataImpl implements 
             out.writeObject(primaryKey);
         }
         out.writeObject(mainInterface);
+
         out.writeByte(interfaceType.ordinal());
 
+        out.writeInt(asynchronousMethods.size());
+        for (String asynchronousMethod : asynchronousMethods) {
+            out.writeObject(asynchronousMethod);
+        }
+ 
         if (properties.size() == 0) {
             out.writeBoolean(false);
         } else {
@@ -235,6 +260,9 @@ public class EJBMetaDataImpl implements 
             byte typeIndex = in.readByte();
             interfaceType = InterfaceType.values()[typeIndex];
         }
+        for (int i = in.readInt(); i > 0; i--) {
+            asynchronousMethods.add((String)in.readObject());
+        }
 
         final boolean hasProperties = in.readBoolean();
         if (hasProperties) {
@@ -280,4 +308,12 @@ public class EJBMetaDataImpl implements 
             }
         }
     }
+
+    private String generateMethodSignature(Method method) {
+        StringBuilder buffer = new StringBuilder(method.getName());
+        for(Class<?> parameterType : method.getParameterTypes()) {
+            buffer.append(parameterType.getName());
+        }
+        return buffer.toString();
+    }
 }

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java Thu May 19 17:01:09 2011
@@ -16,14 +16,28 @@
  */
 package org.apache.openejb.client;
 
-import org.apache.openejb.client.proxy.ProxyManager;
-
-import javax.ejb.EJBException;
-import javax.ejb.EJBObject;
 import java.lang.reflect.Method;
 import java.rmi.RemoteException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.ejb.EJBException;
+import javax.ejb.EJBObject;
+
+import org.apache.openejb.client.proxy.ProxyManager;
 
 public abstract class EJBObjectHandler extends EJBInvocationHandler {
 
@@ -35,11 +49,16 @@ public abstract class EJBObjectHandler e
 
     protected static final Method GETHANDLER = getMethod(EJBObjectProxy.class, "getEJBObjectHandler", null);
 
+    protected static final Method CANCEL = getMethod(Future.class, "cancel", boolean.class);
+
+    //TODO figure out how to configure and manage the thread pool on the client side
+    protected static final BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>();
+    protected static final ExecutorService executorService = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, blockingQueue);
     /*
     * The registryId is a logical identifier that is used as a key when placing EntityEJBObjectHandler into
     * the BaseEjbProxyHanlder's liveHandleRegistry.  EntityEJBObjectHandlers that represent the same
     * bean identity (keyed by the registry id) will be stored together so that they can be removed together
-    * when the EJBInvocationHandler.invalidateAllHandlers is invoked. The EntityEJBObjectHandler uses a 
+    * when the EJBInvocationHandler.invalidateAllHandlers is invoked. The EntityEJBObjectHandler uses a
     * compound key composed of the entity bean's primary key, deployment id, and
     * container id.  This uniquely identifies the bean identity that is proxied by this handler allowing it
     * to be removed with other handlers bound to the same registry id.
@@ -209,10 +228,39 @@ public abstract class EJBObjectHandler e
 
     protected Object businessMethod(Method method, Object[] args, Object proxy) throws Throwable {
 
+        if (ejb.isAsynchronousMethod(method)) {
+            try {
+                String requestId = UUID.randomUUID().toString();
+                EJBResponse response = new EJBResponse();
+                AsynchronousCall asynchronousCall = new AsynchronousCall(method, args, proxy, requestId, response);
+                return new FutureAdapter(executorService.submit(asynchronousCall), response, requestId);
+            } catch (RejectedExecutionException e) {
+                throw new EJBException("failed to allocate internal resource to execute the target task", e);
+            }
+        } else {
+            return _businessMethod(method, args, proxy, null);
+        }
+    }
+
+    private Object _businessMethod(Method method, Object[] args, Object proxy, String requestId) throws Throwable {
         EJBRequest req = new EJBRequest(RequestMethodConstants.EJB_OBJECT_BUSINESS_METHOD, ejb, method, args, primaryKey);
 
+        //Currently, we only set the requestId while the asynchronous invocation is called
+        req.getBody().setRequestId(requestId);
         EJBResponse res = request(req);
+        return _handleBusinessMethodResponse(res);
+    }
 
+    private Object _businessMethod(Method method, Object[] args, Object proxy, String requestId, EJBResponse response) throws Throwable {
+        EJBRequest req = new EJBRequest(RequestMethodConstants.EJB_OBJECT_BUSINESS_METHOD, ejb, method, args, primaryKey);
+
+        //Currently, we only set the request while the asynchronous invocation is called
+        req.getBody().setRequestId(requestId);
+        EJBResponse res = request(req, response);
+        return _handleBusinessMethodResponse(res);
+    }
+
+    private Object _handleBusinessMethodResponse(EJBResponse res) throws Throwable{
         switch (res.getResponseCode()) {
             case ResponseCodes.EJB_ERROR:
                 throw new SystemError((ThrowableArtifact) res.getResult());
@@ -227,4 +275,133 @@ public abstract class EJBObjectHandler e
         }
     }
 
+    private class AsynchronousCall implements Callable {
+
+        private Method method;
+
+        private Object[] args;
+
+        private Object proxy;
+
+        private String requestId;
+
+        private EJBResponse response;
+
+        public AsynchronousCall(Method method, Object[] args, Object proxy, String requestId, EJBResponse response) {
+            this.method = method;
+            this.args = args;
+            this.proxy = proxy;
+            this.requestId = requestId;
+            this.response = response;
+        }
+
+        @Override
+        public Object call() throws Exception {
+            try {
+                return _businessMethod(method, args, proxy, requestId, response);
+            } catch (Exception e) {
+                throw e;
+            } catch (Throwable error) {
+                throw new SystemException(error);
+            }
+        }
+    }
+
+    private class FutureAdapter<T> implements Future<T> {
+
+        private Future<T> target;
+
+        private String requestId;
+
+        private EJBResponse response;
+
+        private volatile boolean canceled;
+
+        private AtomicBoolean lastMayInterruptIfRunningValue = new AtomicBoolean(false);
+
+        public FutureAdapter(Future<T> target, EJBResponse response, String requestId) {
+            this.target = target;
+            this.requestId = requestId;
+            this.response = response;
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            /* In EJB 3.1 spec 3.4.8.1.1
+             * a. If a client calls cancel on its Future object, the container will attempt to cancel 
+             *    the associated asynchronous invocation only if that invocation has not already been dispatched.
+             *    There is no guarantee that an asynchronous invocation can be cancelled, regardless of how quickly 
+             *    cancel is called after the client receives its Future object.
+             *    If the asynchronous invocation can not be cancelled, the method must return false.
+             *    If the asynchronous invocation is successfully cancelled, the method must return true.
+             *
+             * b. The meaning of parameter mayInterruptIfRunning is changed.
+             *
+             *  So, we should never call cancel(true), or the underlying Future object will try to interrupt the target thread.
+            */
+
+            /**
+             * We use our own flag canceled to identify whether the task is canceled successfully.
+             */
+            if (canceled) {
+                return true;
+            }
+            if (blockingQueue.remove(target)) {
+                // We successfully remove the task from the queue
+                canceled = true;
+                return true;
+            } else {
+                // Did not find the task in the queue, the status might be ran/canceled or running
+                // Future.isDone() will return true when the task has been ran or canceled,
+                // Since we never call the Future.cancel method, the isDone method will only return true when the task has ran
+                if (!target.isDone()) {
+                    //The task is in the running state
+                    if (lastMayInterruptIfRunningValue.getAndSet(mayInterruptIfRunning) == mayInterruptIfRunning) {
+                        return false;
+                    }
+                    EJBRequest req = new EJBRequest(RequestMethodConstants.FUTURE_CANCEL, ejb, CANCEL, new Object[] { Boolean.valueOf(mayInterruptIfRunning) }, primaryKey);
+                    req.getBody().setRequestId(requestId);
+                    try {
+                        EJBResponse res = request(req);
+                        if(res.getResponseCode() != ResponseCodes.EJB_OK) {
+                            //TODO how do we notify the user that we fail to configure the value ?
+                        }
+                    } catch (Exception e) {
+                        //TODO how to handle
+                        return false;
+                    }
+                }
+                return false;
+            }
+        }
+
+        @Override
+        public T get() throws InterruptedException, ExecutionException {
+            if (canceled) {
+                throw new CancellationException();
+            }
+            return target.get();
+        }
+
+        @Override
+        public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            if (canceled) {
+                throw new CancellationException();
+            }
+            return target.get(timeout, unit);
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return canceled;
+        }
+
+        @Override
+        public boolean isDone() {
+            if (canceled) {
+                return false;
+            }
+            return target.isDone();
+        }
+    }
 }

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java Thu May 19 17:01:09 2011
@@ -20,13 +20,14 @@ import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.lang.reflect.Method;
-
 import java.rmi.Remote;
+import java.util.UUID;
+
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.rmi.PortableRemoteObject;
-import javax.rmi.CORBA.Tie;
 import javax.rmi.CORBA.Stub;
+import javax.rmi.CORBA.Tie;
 
 import org.omg.CORBA.ORB;
 
@@ -117,12 +118,13 @@ public class EJBRequest implements Clust
         private transient ORB orb; 
         private transient Method methodInstance;
         private transient Class interfaceClass;
-//        private transient Class methodClass;
         private transient String methodName;
         private transient Class[] methodParamTypes;
         private transient Object[] methodParameters;
         private transient Object primaryKey;
 
+        private transient String requestId;
+
         public Body(EJBMetaDataImpl ejb) {
             this.ejb = ejb;
         }
@@ -190,15 +192,25 @@ public class EJBRequest implements Clust
             this.primaryKey = primaryKey;
         }
 
+        public String getRequestId() {
+            return requestId;
+        }
+
+        public void setRequestId(String requestId) {
+            this.requestId = requestId;
+        }
+
         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
             byte version = in.readByte(); // future use
 
+            requestId = null;
             ClassNotFoundException result = null;
             primaryKey = null;
 //            methodClass = null;
             methodName = null;
             methodInstance = null;
             try {
+                requestId = (String)in.readObject();
                 primaryKey = in.readObject();
                 interfaceClass = (Class) in.readObject();
 //                methodClass = (Class) in.readObject();
@@ -228,6 +240,8 @@ public class EJBRequest implements Clust
             // write out the version of the serialized data for future use
             out.writeByte(1);
 
+            out.writeObject(requestId);
+
             out.writeObject(primaryKey);
 
             out.writeObject(interfaceClass);
@@ -470,49 +484,51 @@ public class EJBRequest implements Clust
     }
 
     public String toString() {
-        StringBuffer s = null;
+        StringBuilder s = null; 
         switch (requestMethod) {
             case RequestMethodConstants.EJB_HOME_GET_EJB_META_DATA:
-                s = new StringBuffer("EJB_HOME.GET_EJB_META_DATA");
+                s = new StringBuilder("EJB_HOME.GET_EJB_META_DATA");
                 break;
             case RequestMethodConstants.EJB_HOME_GET_HOME_HANDLE:
-                s = new StringBuffer("EJB_HOME.GET_HOME_HANDLE");
+                s = new StringBuilder("EJB_HOME.GET_HOME_HANDLE");
                 break;
             case RequestMethodConstants.EJB_HOME_REMOVE_BY_HANDLE:
-                s = new StringBuffer("EJB_HOME.REMOVE_BY_HANDLE");
+                s = new StringBuilder("EJB_HOME.REMOVE_BY_HANDLE");
                 break;
             case RequestMethodConstants.EJB_HOME_REMOVE_BY_PKEY:
-                s = new StringBuffer("EJB_HOME.REMOVE_BY_PKEY");
+                s = new StringBuilder("EJB_HOME.REMOVE_BY_PKEY");
                 break;
             case RequestMethodConstants.EJB_HOME_FIND:
-                s = new StringBuffer("EJB_HOME.FIND");
+                s = new StringBuilder("EJB_HOME.FIND");
                 break;
             case RequestMethodConstants.EJB_HOME_CREATE:
-                s = new StringBuffer("EJB_HOME.CREATE");
+                s = new StringBuilder("EJB_HOME.CREATE");
                 break;
             case RequestMethodConstants.EJB_HOME_METHOD:
-                s = new StringBuffer("EJB_HOME.HOME_METHOD");
+                s = new StringBuilder("EJB_HOME.HOME_METHOD");
                 break;
             case RequestMethodConstants.EJB_OBJECT_GET_EJB_HOME:
-                s = new StringBuffer("EJB_OBJECT.GET_EJB_HOME");
+                s = new StringBuilder("EJB_OBJECT.GET_EJB_HOME");
                 break;
             case RequestMethodConstants.EJB_OBJECT_GET_HANDLE:
-                s = new StringBuffer("EJB_OBJECT.GET_HANDLE");
+                s = new StringBuilder("EJB_OBJECT.GET_HANDLE");
                 break;
             case RequestMethodConstants.EJB_OBJECT_GET_PRIMARY_KEY:
-                s = new StringBuffer("EJB_OBJECT.GET_PRIMARY_KEY");
+                s = new StringBuilder("EJB_OBJECT.GET_PRIMARY_KEY");
                 break;
             case RequestMethodConstants.EJB_OBJECT_IS_IDENTICAL:
-                s = new StringBuffer("EJB_OBJECT.IS_IDENTICAL");
+                s = new StringBuilder("EJB_OBJECT.IS_IDENTICAL");
                 break;
             case RequestMethodConstants.EJB_OBJECT_REMOVE:
-                s = new StringBuffer("EJB_OBJECT.REMOVE");
+                s = new StringBuilder("EJB_OBJECT.REMOVE");
                 break;
             case RequestMethodConstants.EJB_OBJECT_BUSINESS_METHOD:
-                s = new StringBuffer("EJB_OBJECT.BUSINESS_METHOD");
+                s = new StringBuilder("EJB_OBJECT.BUSINESS_METHOD");
                 break;
+            case RequestMethodConstants.FUTURE_CANCEL:
+                s = new StringBuilder("FUTURE.CANCEL");
             default:
-                s = new StringBuffer("EJB_UKNOWN."+requestMethod);
+                s = new StringBuilder("EJB_UKNOWN."+requestMethod);
         }
         s.append(':').append(deploymentId);
         if (body != null) {

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBResponse.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBResponse.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBResponse.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBResponse.java Thu May 19 17:01:09 2011
@@ -19,6 +19,7 @@ package org.apache.openejb.client;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.net.URI;
 
 public class EJBResponse implements ClusterableResponse {
 
@@ -26,6 +27,8 @@ public class EJBResponse implements Clus
     private transient Object result;
     private transient ServerMetaData server;
 
+    private transient URI responseServerURI;
+
     public EJBResponse() {
 
     }
@@ -55,7 +58,7 @@ public class EJBResponse implements Clus
     public ServerMetaData getServer() {
         return server;
     }
-    
+
     public String toString() {
         StringBuffer s = null;
         switch (responseCode) {
@@ -94,6 +97,14 @@ public class EJBResponse implements Clus
         return s.toString();
     }
 
+    public URI getResponseServerURI() {
+        return responseServerURI;
+    }
+
+    public void setResponseServerURI(URI responseServerURI) {
+        this.responseServerURI = responseServerURI;
+    }
+
     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
         byte version = in.readByte(); // future use
 

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/RequestMethodConstants.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/RequestMethodConstants.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/RequestMethodConstants.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/RequestMethodConstants.java Thu May 19 17:01:09 2011
@@ -50,5 +50,7 @@ public class RequestMethodConstants {
     public static final int JNDI_LIST = 28;
     public static final int JNDI_LIST_BINDINGS = 29;
 
+    public static final int FUTURE_CANCEL = 35;
+
 }
 

Modified: openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Response.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Response.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Response.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/Response.java Thu May 19 17:01:09 2011
@@ -26,5 +26,6 @@ public interface Response extends Extern
     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
 
     public void writeExternal(ObjectOutput out) throws IOException;
+
 }
 

Modified: openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/EJBRequestTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/EJBRequestTest.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/EJBRequestTest.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/EJBRequestTest.java Thu May 19 17:01:09 2011
@@ -38,7 +38,7 @@ public class EJBRequestTest extends Test
 
 
     protected void setUp() throws Exception {
-        ejb = new EJBMetaDataImpl(FooHome.class, FooObject.class, Integer.class, "BMP_ENTITY", "FooBeanID", InterfaceType.BUSINESS_REMOTE, null);
+        ejb = new EJBMetaDataImpl(FooHome.class, FooObject.class, Integer.class, "BMP_ENTITY", "FooBeanID", InterfaceType.BUSINESS_REMOTE, null, null);
     }
 
     public void testEJBHomeCreate() throws Exception {

Modified: openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java (original)
+++ openejb/trunk/openejb3/server/openejb-client/src/test/java/org/apache/openejb/client/LoginTestUtil.java Thu May 19 17:01:09 2011
@@ -17,9 +17,10 @@
  */
 package org.apache.openejb.client;
 
-import java.rmi.RemoteException;
+import java.net.URI;
 import java.net.URL;
 import java.net.URLDecoder;
+import java.rmi.RemoteException;
 
 public class LoginTestUtil {
     public static Request serverRequest;
@@ -36,7 +37,8 @@ public class LoginTestUtil {
         }
 
         Client.setClient(new Client() {
-            protected Response processRequest(Request req, Response res, ServerMetaData server) throws RemoteException {
+            @Override
+            protected Response processRequest(Request req, Response res, ServerMetaData server, URI preferredServerURI) throws RemoteException {
                 serverRequest = req;
                 return serverResponse;
             }

Modified: openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/ClientObjectFactory.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/ClientObjectFactory.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/ClientObjectFactory.java (original)
+++ openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/ClientObjectFactory.java Thu May 19 17:01:09 2011
@@ -146,7 +146,7 @@ class ClientObjectFactory implements org
                 idCode,
                 convert(info.getInterfaceType()),
                 info.getInterfaces(),
-                info.getInterface());
+                beanContext.getAsynchronousMethodSignatures());
         eMetaData.loadProperties(beanContext.getProperties());
         
         Object primKey = info.getPrimaryKey();
@@ -190,17 +190,17 @@ class ClientObjectFactory implements org
         return hanlder.createEJBHomeProxy();
     }
 
-    private EJBMetaDataImpl buildEjbMetaData(ProxyInfo info, BeanContext deployment, int idCode) {
-        EJBMetaDataImpl eMetaData = new EJBMetaDataImpl(deployment.getHomeInterface(),
-                deployment.getRemoteInterface(),
-                deployment.getPrimaryKeyClass(),
-                deployment.getComponentType().toString(),
-                deployment.getDeploymentID().toString(),
+    private EJBMetaDataImpl buildEjbMetaData(ProxyInfo info, BeanContext beanContext, int idCode) {
+        EJBMetaDataImpl eMetaData = new EJBMetaDataImpl(beanContext.getHomeInterface(),
+                beanContext.getRemoteInterface(),
+                beanContext.getPrimaryKeyClass(),
+                beanContext.getComponentType().toString(),
+                beanContext.getDeploymentID().toString(),
                 idCode,
                 convert(info.getInterfaceType()),
                 info.getInterfaces(),
-                info.getInterface());
-        eMetaData.loadProperties(deployment.getProperties());
+                beanContext.getAsynchronousMethodSignatures());
+        eMetaData.loadProperties(beanContext.getProperties());
         return eMetaData;
     }
 }
\ No newline at end of file

Modified: openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/EjbRequestHandler.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/EjbRequestHandler.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/EjbRequestHandler.java (original)
+++ openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/EjbRequestHandler.java Thu May 19 17:01:09 2011
@@ -21,6 +21,10 @@ import java.io.ObjectOutputStream;
 import java.rmi.RemoteException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.openejb.BeanContext;
 import org.apache.openejb.ProxyInfo;
@@ -32,6 +36,7 @@ import org.apache.openejb.client.EJBResp
 import org.apache.openejb.client.RequestMethodConstants;
 import org.apache.openejb.client.ResponseCodes;
 import org.apache.openejb.client.ThrowableArtifact;
+import org.apache.openejb.core.ThreadContext;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.spi.SecurityService;
 import org.apache.openejb.util.LogCategory;
@@ -45,9 +50,11 @@ class EjbRequestHandler {
 
     private final ClusterableRequestHandler clusterableRequestHandler;
 
+    private Map<String, AtomicBoolean> asynchronousInvocationCancelMap = new ConcurrentHashMap<String, AtomicBoolean>();
+
     EjbRequestHandler(EjbDaemon daemon) {
         this.daemon = daemon;
-        
+
         clusterableRequestHandler = newClusterableRequestHandler();
     }
 
@@ -73,7 +80,6 @@ class EjbRequestHandler {
 
         CallContext call = null;
         BeanContext di = null;
-        RpcContainer c = null;
 
         try {
             di = this.daemon.getDeployment(req);
@@ -190,6 +196,10 @@ class EjbRequestHandler {
                 case RequestMethodConstants.EJB_HOME_REMOVE_BY_PKEY:
                     doEjbHome_REMOVE_BY_PKEY(req, res);
                     break;
+
+                case RequestMethodConstants.FUTURE_CANCEL:
+                    doFUTURE_CANCEL_METHOD(req, res);
+                    break;
             }
 
         } catch (org.apache.openejb.InvalidateReferenceException e) {
@@ -230,18 +240,47 @@ class EjbRequestHandler {
         clusterableRequestHandler.updateServer(beanContext, req, res);
     }
 
+    protected void doFUTURE_CANCEL_METHOD(EJBRequest req, EJBResponse res) throws Exception {
+        AtomicBoolean invocationCancelTag = asynchronousInvocationCancelMap.get(req.getBody().getRequestId());
+        if (invocationCancelTag == null) {
+            //TODO ?
+        } else {
+            invocationCancelTag.set((Boolean) req.getBody().getMethodParameters()[0]);
+            res.setResponse(ResponseCodes.EJB_OK, null);
+        }
+    }
+ 
     protected void doEjbObject_BUSINESS_METHOD(EJBRequest req, EJBResponse res) throws Exception {
 
         CallContext call = CallContext.getCallContext();
-        RpcContainer c = (RpcContainer) call.getBeanContext().getContainer();
-
-        Object result = c.invoke(req.getDeploymentId(),
-                req.getInterfaceClass(), req.getMethodInstance(),
-                req.getMethodParameters(),
-                req.getPrimaryKey()
-        );
-        
-        res.setResponse(ResponseCodes.EJB_OK, result);
+        BeanContext beanContext = (BeanContext)call.getBeanContext();
+        boolean asynchronous = beanContext.isAsynchronous(req.getMethodInstance());
+        try {
+            if (asynchronous) {
+                AtomicBoolean invocationCancelTag = new AtomicBoolean(false);
+                ThreadContext.initAsynchronousCancelled(invocationCancelTag);
+                asynchronousInvocationCancelMap.put(req.getBody().getRequestId(), invocationCancelTag);
+            }
+            RpcContainer c = (RpcContainer) call.getBeanContext().getContainer();
+            
+            Object result = c.invoke(req.getDeploymentId(),
+                                     req.getInterfaceClass(), req.getMethodInstance(),
+                                     req.getMethodParameters(),
+                                     req.getPrimaryKey()
+                                     );
+ 
+            //Pass the internal value to the remote client, as AsyncResult is not serializable
+            if(result != null && asynchronous) {
+                result = ((Future)result).get();
+            }
+ 
+            res.setResponse(ResponseCodes.EJB_OK, result);
+        } finally {
+            if (asynchronous) {
+                ThreadContext.removeAsynchronousCancelled();
+                asynchronousInvocationCancelMap.remove(req.getBody().getRequestId());
+            }
+        }
     }
 
     protected void doEjbHome_METHOD(EJBRequest req, EJBResponse res) throws Exception {

Modified: openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/JndiRequestHandler.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/JndiRequestHandler.java?rev=1124995&r1=1124994&r2=1124995&view=diff
==============================================================================
--- openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/JndiRequestHandler.java (original)
+++ openejb/trunk/openejb3/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/JndiRequestHandler.java Thu May 19 17:01:09 2011
@@ -371,7 +371,7 @@ class JndiRequestHandler {
                         -1,
                         convert(proxyInfo.getInterfaceType()),
                         null,
-                        proxyInfo.getInterface());
+                        beanContext.getAsynchronousMethodSignatures());
                 metaData.loadProperties(beanContext.getProperties());
                 log(metaData);
                 res.setResult(metaData);
@@ -393,7 +393,7 @@ class JndiRequestHandler {
                         -1,
                         convert(proxyInfo.getInterfaceType()),
                         proxyInfo.getInterfaces(),
-                        proxyInfo.getInterface());
+                        beanContext.getAsynchronousMethodSignatures());
                 metaData.setPrimaryKey(proxyInfo.getPrimaryKey());
                 metaData.loadProperties(beanContext.getProperties());