11 Replies Latest reply on Nov 14, 2008 2:28 PM by Andrew Rubinger

    Reintroducing Asynchronous Support

    Andrew Rubinger Master

      In writing EJB3 Proxy, and to prioritize compliance w/ TCK, we've dropped support for asynchronous invocations. I'm now putting it back.

      The previous implementation worked like:

      * Get a traditional Proxy from JNDI
      * Cast that to an interface implemented by all Proxies, "JBossProxy"
      * Call Object myAsync = JBossProxy.getAsynchronousProxy();
      * Cast myAsync to the the Business Interface to invoke
      * Cast myAsync to AsynchProvider (org.jboss.aspects.asynch.AsynchProvider) to get the Future (org.jboss.aspects.asynch.Future)

      ...it looks like:

      StatefulRemote tester =
       (StatefulRemote) getInitialContext().lookup("StatefulBean/remote");
       assertEquals("Wrong return for stateful remote", 31, tester.method(31));
      
       StatefulRemote asynchTester = (StatefulRemote)((JBossProxy)tester).getAsynchronousProxy();
       assertEquals("Wrong return value for stateful remote", 0, asynchTester.method(32));
       AsynchProvider ap = (AsynchProvider) asynchTester;
       Future future = ap.getFuture();
       int ret = (Integer) future.get();
       assertEquals("Wrong async return value for stateful remote", ret, 32);


      I want to change the following:

      * Proxies should not be equipped with the facilities to return an asynchronous version of themselves. I'd rather see another component take an existing proxy and async-ize it. So async becomes a true add-on optional capability.
      * Remove use of org.jboss.aspects.asynch.Future in favor of java.util.concurrent.Future

      One hitch I see with Bill's Future implementations is that they directly support remoting via the AOP Dispatcher. I want to simplify the whole thing by making the async client-side interceptor act like a regular client which carries the invocation as a j.u.c.FutureTask.

      We'll see if I'm missing some reason why things were built as they were.

      S,
      ALR

        • 1. Re: Reintroducing Asynchronous Support
          Andrew Rubinger Master

          In addition to the simplicity argument, another motivation I have is to make the APIs between our support and that defined by EJB3.1 as close as possible.

          Even though EJB3.1 spec says that the decision to invoke async or not lies w/ the bean provider; I say it should be the caller's choice. So that's why we'll keep our implementation as well.

          S,
          ALR

          • 2. Re: Reintroducing Asynchronous Support
            David Lloyd Master

            Does EJB 3.1 specify use of java.util.concurrent.Future? I only ask because I've discovered a couple of problems with it for remove invocation use that I ran into when working on Remoting:

            1) It is not entirely clear if .cancel() should block until it knows whether or not the cancel succeeded. The designers clearly did not expect there to be any time lag between the invocation of the .cancel() method and the ability to determine the result. With remote invocation one wishes to be able to cancel asynchronously; you could always return false but the docs clearly state that false means "the task could not be cancelled, typically because it has already completed normally".

            2) It should not be the client's decision whether or not the server interrupts the task thread. Nevertheless, .cancel() accepts a boolean parameter which indicates whether the task should be cancelled. It's clear that this interface was meant to be used from code that has authority over the thread pool which is executing the task.

            3) The API itself lends very little power to the end-user (no methods to await interruptibly, no way to receive failure or cancellation notifications other than catching exceptions).

            4) There is no mechanism for users to register a callback which can be invoked when the operation is done.

            For Remoting and XNIO I ended up creating a new future interface (within XNIO) specifically designed for I/O and remote invocation operations:

            http://anonsvn.jboss.org/repos/xnio/xnio-base/trunk/api/src/main/java/org/jboss/xnio/IoFuture.java

            In addition I provide abstract implementations, and a utility method to create a java.util.concurrent.Future for interfaces which require it. Semantically, the wrapper Future will ignore the "interruptIfRunning" parameter to .cancel(), and .cancel() blocks until it is known whether the operation was successfully cancelled.

            I'm not saying you should introduce an XNIO or Remoting 3 dependency - well, unless you want to :) - but I think it's safe to say that java.util.concurrent.Future isn't an acceptable mechanism for getting the result of a remote invocation. Been there, tried that. :-)

            • 3. Re: Reintroducing Asynchronous Support
              David Lloyd Master

              In #2 above I mean "cancel() accepts a boolean parameter which indicates whether the task should be interrupted", not cancelled...

              • 4. Re: Reintroducing Asynchronous Support
                David Lloyd Master

                ...and for #3 I mean "uninterruptibly", not "interruptibly".... :-|

                • 6. Re: Reintroducing Asynchronous Support
                  Andrew Rubinger Master

                   

                  "david.lloyd@jboss.com" wrote:
                  Does EJB 3.1 specify use of java.util.concurrent.Future?


                  Yes.

                  "EJB 3.1 Core Specification 4.5.2" wrote:
                  The client return type of an asynchronous method is either void or java.util.concurrent.Future<V>, where V is the result value type.


                  "david.lloyd@jboss.com" wrote:
                  I only ask because I've discovered a couple of problems with it for remove invocation use that I ran into when working on Remoting...


                  Clearly I'd like to take advantage of your experience here, but not at the expense of confusing users with more nonstandard APIs. So perhaps I can take a hybrid approach.

                  "david.lloyd@jboss.com" wrote:
                  1) It is not entirely clear if .cancel() should block until it knows whether or not the cancel succeeded. The designers clearly did not expect there to be any time lag between the invocation of the .cancel() method and the ability to determine the result. With remote invocation one wishes to be able to cancel asynchronously; you could always return false but the docs clearly state that false means "the task could not be cancelled, typically because it has already completed normally".


                  Though not explicitly stated, to fulfill the JavaDoc's requirements of j.u.c.Future, we'd have to block until we knew whether the operation would be cancelled.

                  In the case of EJB, a call to "cancel" is a request by the client. The bean must be written to support cancellation, and "mayInterruptIfRunning" would have to be ignored as that context is not available to the bean provider to check.

                  "EJB 3.1 Core Spec, 4.5.2.2" wrote:
                  A client can request that an asynchronous invocation be cancelled by calling the Future<v>.cancel(boolean mayInterruptIfRunning) method. A Bean Developer can check whether the client has requested cancellation by calling the SessionContext.isCancelled() method within the context of the asynchronous method.


                  Now, if the bean provider chooses to honor the request to cancel, there's a missing mechanism to let the client to know whether the request was fulfilled. So what should be the proper return value of "cancel()"?

                  I'll pose this question to the JSR318 EG.

                  "david.lloyd@jboss.com" wrote:
                  2) It should not be the client's decision whether or not the server interrupts the task thread. Nevertheless, .cancel() accepts a boolean parameter which indicates whether the task should be cancelled. It's clear that this interface was meant to be used from code that has authority over the thread pool which is executing the task.


                  From the spec above, this flag is lost and not available in SessionContext anyway.

                  "david.lloyd@jboss.com" wrote:
                  3) The API itself lends very little power to the end-user (no methods to await interruptibly, no way to receive failure or cancellation notifications other than catching exceptions).

                  4) There is no mechanism for users to register a callback which can be invoked when the operation is done.


                  True story. The implementation I'm looking to replace/simplify also provides blocking to "get" via the Oswego concurrent utils. And only the XNIO impl provides for callbacks.

                  At first glance I'd approach this by using a XNIO Future impl under the hood, and wrapping w/ j.u.c.Future to return to the client. This would also pave the way to provide for more than one async interface to the client, who could choose his view (XNIO, j.u.c, etc). I'm weary of drawing in another dependency unless it's already brought into AS somewhere?

                  Then again, eventually we're gonna need Remoting 3 which depends on XNIO, so worst case is we'd be tacking on this library prematurely...

                  S,
                  ALR

                  • 7. Re: Reintroducing Asynchronous Support
                    David Lloyd Master

                     

                    "ALRubinger" wrote:
                    Now, if the bean provider chooses to honor the request to cancel, there's a missing mechanism to let the client to know whether the request was fulfilled. So what should be the proper return value of "cancel()"?

                    I'll pose this question to the JSR318 EG.


                    Yeah, it seems to me there's really no gray area in the javadoc for Future - "true" means cancelled, "false" means not cancelled. There's no answer that means "outcome unknown" - you pretty much would have to throw a RuntimeException in that case to strictly fulfill the contract (though in practice, returning "false" is probably good enough... that's what I do with my wrapper class anyway). Not only that but the method is (implicitly) blocking but not interruptible. Blah.

                    A good client use case that I measure against is a Swing client. For a long-running task, they'll want to have a "cancel" button for sure. Also, in a Swing client you don't want to block the EDT. Using a non-blocking .cancel() method is perfect for this case. The close button can map directly to the .cancel() method, with no intervening logic, and the future's callback (or a separate waiting thread) can be used to retrieve the result. Using j.u.c.Future you'd need to execute the .cancel() in another thread just in case it blocks (and manually prevent multiple button presses as well). I think that the AJAX world will also see similar behavior.

                    "ALRubinger" wrote:
                    At first glance I'd approach this by using a XNIO Future impl under the hood, and wrapping w/ j.u.c.Future to return to the client. This would also pave the way to provide for more than one async interface to the client, who could choose his view (XNIO, j.u.c, etc). I'm weary of drawing in another dependency unless it's already brought into AS somewhere?


                    It will be, for Remoting to start with, and other projects going forward.



                    • 8. Re: Reintroducing Asynchronous Support
                      Andrew Rubinger Master

                      A note as I go through this:

                      Previous implementation was not Thread-safe, as it backed the Future result in a ThreadLocal. So if a client cached a Proxy and used it in a multithreaded environment, the wrong results could be obtained.

                      S,
                      ALR

                      • 9. Re: Reintroducing Asynchronous Support
                        Andrew Rubinger Master

                         

                        "ALRubinger" wrote:
                        Previous implementation was not Thread-safe, as it backed the Future result in a ThreadLocal.


                        Attention everyone paying attention:

                        The above is clearly the dumbest thing I've ever said. From now on, no more working while sick and on NyQuil.

                        S,
                        ALR

                        • 10. Re: Reintroducing Asynchronous Support
                          Anil Saldanha Master

                           

                          "ALRubinger" wrote:
                          "ALRubinger" wrote:
                          Previous implementation was not Thread-safe, as it backed the Future result in a ThreadLocal.


                          Attention everyone paying attention:

                          The above is clearly the dumbest thing I've ever said. From now on, no more working while sick and on NyQuil.

                          S,
                          ALR


                          Maybe you intended to take "Dayquil". Nyquil would have tucked you in bed.

                          • 11. Re: Reintroducing Asynchronous Support
                            Andrew Rubinger Master

                            This is done as part of https://jira.jboss.org/jira/browse/EJBTHREE-1591.

                            The underlying mechanism now decouples async invocations from jboss-ejb3-proxy; the client can add async support at runtime via a j.l.f.Proxy mixin. So as the Unit Tests in jboss-ejb3-common show, this isn't at all specific to EJB3.

                            S,
                            ALR