Monday May 31, 2010

TOTD #139: Asynchronous Request Processing using Servlets 3.0 and Java EE 6

Server resources are always valuable and should be used conservatively. Consider a Servlet that has to wait for a JDBC connection to be available from the pool, receiving a JMS message or reading a resource from the file system. Waiting for a "long running" process to completely blocks the thread - waiting, sitting and doing nothing - not an optimal usage of your server resources. Servlets 3.0 introduces the ability to asynchronously process requests such that the control (or thread) is returned back to the container to perform other tasks while waiting for the long running process to complete. The request processing continues in the same thread after after the response from the long running process is returned or or may be dispatched to a new resource from within the long running process. A typical use case for long running process is a Chat Application.

The asynchronous behavior need to be explicitly enabled on a Servlet. This is achieved by adding "asyncSupported" attribute on @WebServlet as shown below:

@WebServlet(name="AsyncServlet", urlPatterns={"/AsyncServlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {

The asynchronous processing can then be started in a separate thread using "startAsync" method on the request. This method returns "AsyncContext" which represents the execution context of the asynchronous request. The asynchronous request can then be completed by calling AsyncContext.complete (explicit) or dispatching to another resource (implicit). The container completes the invocation of asynchronous request in the later case.

Lets say the long running process is implemented as:

class MyAsyncService implements Runnable {
    AsyncContext ac;

    public MyAsyncService(AsyncContext ac) {
        this.ac = ac;
    }

    @Override
    public void run() {
        System.out.println("Some long running process in \\"MyAsyncService\\"");
    }
}

Note this is running in a separate thread. This service can be invoked from the original servlet as:
AsycnContext ac = request.startAsync();

The service may also be started as:

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
executor.execute(new MyAsyncService(ac));

The ScheduledThreadPoolExecutor usage is recommended as it makes the thread management easier.

A listener can be associated with the AsyncContext to get notified of when the async request is complete, timed out, or resulted in an error. This can be achieved as:

ac.addListener(new AsyncListener() {

    @Override
    public void onComplete(AsyncEvent event) throws IOException {
        System.out.println("onComplete.");
    }

    . . .
});

A more complete code for the above fragment is given below. The "onComplete" method is used to clean up resources created during async processing.

The asynchronous request processing can be either completed in "run" method of "MyAsyncService" by invoking "AsycnContext.complete" as shown below:

@Override
public void run() {
    System.out.println("Some long running process in \\"MyAsyncService\\"");
    ac.complete();
}

or dispatched to a different resource as:

@Override
public void run() {
    System.out.println("Some long running process in \\"MyAsyncService\\"");
    ac.dispatch("/response.jsp");
}

Lets take a look at our complete code:

@WebServlet(name="AsyncServlet", urlPatterns={"/AsyncServlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
   
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try {
            System.out.println("Starting doGet");
            AsyncContext ac = request.startAsync();
            ac.addListener(new AsyncListener() {

                @Override
                public void onComplete(AsyncEvent event) throws IOException {
                    System.out.println("onComplete");
                }

                @Override
                public void onTimeout(AsyncEvent event) throws IOException {
                    System.out.println("onTimeout");
                }

                @Override
                public void onError(AsyncEvent event) throws IOException {
                    System.out.println("onError");
                }

                @Override
                public void onStartAsync(AsyncEvent event) throws IOException {
                    System.out.println("onStartAsync");
                }
            });

            System.out.println("Do some stuff in doGet ...");


            // Start another service
            ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
            executor.execute(new MyAsyncService(ac));

            System.out.println("Some more stuff in doGet ...");
        } finally { 
        }
    }

    class MyAsyncService implements Runnable {
        AsyncContext ac;

        public MyAsyncService(AsyncContext ac) {
            this.ac = ac;
            System.out.println("Dispatched to " + "\\"MyAsyncService\\"");
        }

        @Override
        public void run() {
            System.out.println("Some long running process in \\"MyAsyncService\\"");
            ac.complete();
        }
    }
} 

Invoking the GET operation of this Servlet shows the following sequence of statements in GlassFish server log:

INFO: Starting doGet
INFO: Dispatched to "MyAsyncService"
INFO: Doing some stuff in doGet ...
INFO: Some more in doGet ...
INFO: Some long running process in "MyAsyncService"
INFO: onComplete

The first statement marks the beginning of "doGet", the second statement indicates that the request is dispatched to the long running
"MyAsyncService", the third and fourth statement indicates that the response returned back to "doGet" for normal request processing. Finally fifth and sixth statement shows that now the asynchronous request is getting processed and is completed.

Clean and simple!

A request may be dispatched from Asynchronous servlet to Synchronous but other way around is illegal. The section 2.3.3.3 in the Servlets 3.0 specification provides an introduction to the Asynchronous Processing in Servlets 3.0.

The asynchronous behavior is available in the Servlet Filter as well.

The complete code used in this blog can be downloaded here. A fully working sample of a Chat Application using Servlets 3.0 can be seen here.

Technorati: totd glassfish v3 servlets javaee asynchronous comet

Tuesday May 18, 2010

TOTD #137: Asynchronous EJB, a light-weight JMS solution - Feature-rich Java EE 6

One of the new features introduced in Enterprise Java Beans 3.1 (JSR 318)  is asynchronous invocation of a business method. This allows the control to return to the client before the container dispatches the instance to a bean. The asynchronous operations can return a "Future<V>" that allow the client to retrieve a result value, check for exceptions, or attempt to cancel any in-progress invocations.

The "@Asynchronous" annotation is used to mark a specific (method-level) or all (class-level) methods of the bean as asynchronous. Here is an example of a stateless session bean that is tagged as asynchronous at the class-level:

@Stateless
@Asynchronous
public class SimpleAsyncEJB {

    public Future<Integer> addNumbers(int n1, int n2) {
        Integer result;

        result = n1 + n2;
        try {
            // simulate JPA queries + reading file system
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

        return new AsyncResult(result);
    }
}

The method signature returns "Future<Integer>" and the return type is "AsyncResult(Integer)". The "AsyncResult" is a new class introduced in EJB 3.1 that wraps the result of an asynchronous method as a Future object. Under the covers, the value is retrieved and sent to the client. Adding any new methods to this class will automatically make them asynchronous as well.

The 2 second sleep simulates server side processing of the response which may involve querying the database or reading some information from the filesystem.

This EJB can be easily injected in a Servlet using the normal way:

@EJB SimpleAsyncEJB ejb;

This business method can be invoked in the "doGet" method of a Servlet as:

PrintWriter out = response.getWriter();
try {
    Future<Integer> future = ejb.addNumbers(10, 20);
    print(out, "Client is working ...");
    Thread.currentThread().sleep(1000);

    if (!future.isDone()) {
        print(out, "Response not ready yet ...");
    }

    print(out, "Client is working again ...");
    Thread.currentThread().sleep(1000);

    if (!future.isDone()) {
        print(out, "Response not ready yet ...");
    }

    print(out, "Client is still working ...");
    Thread.currentThread().sleep(1000);

    if (!future.isDone()) {
        print(out, "Response not ready yet ...");
    } else {
        print(out, "Response is now ready");
    }

    Integer result = future.get();

    print(out, "The result is: " + result);
} catch (InterruptedException ex) {
    ex.printStackTrace();
} catch (ExecutionException ex) {
    ex.printStackTrace();
} finally { 
    out.close();
}

The control is returned to the client right after the the EJB business method is invoked and does not wait for the business method execution to finish. The methods on "Future" API are used to query if the result is available. The "Thread.sleep()" for 1 second simulate that client can continue working and possibly check for results at a regular interval. The "print" is a convenience method that prints the string to "response.getWriter" and flushes the output so that it can be instantly displayed instead of getting buffered. Invoking this "doGet" shows the following output:

1274142978365: Client is working ...
1274142979365: Response not ready yet ...
1274142979365: Client is working again ...
1274142980366: Response not ready yet ...
1274142980366: Client is still working ...
1274142981366: Response is now ready
1274142981366: The result is: 30

The client transaction context does not and security context do propagate from the client to the asynchronous business method.

Up until now, any kind of asynchrony in the EJB required to use the Message Driven Beans which in turn required some JMS setup. Introduction of this feature allows you to easily incorporate asynchrony in your EJB applications.

Try this and other Java EE 6 features in GlassFish Server Open Source Edition 3 or Oracle GlassFish Server today!

The complete source code used in this blog can be downloaded here.

Technorati: totd javaee ejb asynchronous glassfish v3

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today