Skip to Content

/wp-content/uploads/2013/06/clock_229134.jpgWhen a user has finished with a transaction and the transaction data is going to be saved, it is a common design-pattern to save the execute all the different transactions that are parts of the save operation and to return the success or error message. However, when there are several steps in the save-logic that are executed on non-cloud systems (e.g. you on-premise R/3 instance), this process can take quite long because of latency between the cloud and the back-end, on top of latency between the user and the cloud. To prevent too much delay, you may want to consider running these tasks in the background and notifying the user that his request has been received well instead.

During the press release of the Enterprise Hana Cloud, Hasso Plattner mentioned that a basic requests should not take longer than 1 second to return, including network/web-latency. If you find that your app has requests take more than 500ms to complete during off-peak hours, then you should really consider speeding it up. Using background jobs may be a good option in many scenarios.

Background jobs are key to building truly scalable cloud apps as they transfer both time and computationally and network intensive tasks from the application layer to a background process outside the user request/response lifecycle. This ensures that requests can always return immediately and reduces compounding performance issues that occur when requests become backlogged.

Java EE 6

You may have noticed throuh Release Notes for SAP HANA Cloud Platform that the SAP Hana Cloud now supports Jave EE 6 web profile APIs. The cool part of this web profile API with regards to background tasks is the line on the APIs page that mentions “javax.ejb”, which includes package “javax.ejb.Asynchronous”. This package exposes a very simple mechanism for asynchronous invocation of methods.

Suppose you have a new order that you need to save, but don’t want to let the user wait for the entire process to finish, you could persist some or all of the data in a background task. To achieve that, you simply prefix the persist-method with @Asynchronous. Please find an example of a DAO, that contains both a synchronous as well as an asynchronous persist method:

@Singleton
public class OrderDAO {
          private static Logger logger = LoggerFactory.getLogger(OrderDAO.class);
          public void persist(Order order) {
                    logger.info("I'm running in sync and going to wait for 5 seconds");
                    try { Thread.sleep(5000); } catch (InterruptedException e) {}
                    logger.info("I'm a synchronous method, running in thread '{}' ",
                                        Thread.currentThread().getName());
          }
          @Asynchronous
          public void persistAsync(Order order) {
                    logger.info("I'm running async and going to wait for 5 seconds");
                    try { Thread.sleep(5000); } catch (InterruptedException e) {}
                    logger.info("I'm an asynchronous method, running in thread '{}'",
                                        Thread.currentThread().getName());
          }
}

This all seems very easy, but there is a catch. Instead of hard-wiring the orderDAO, but instantiating it through e.g. OrderDAO dao = new OrderDAO(), you need to inject it with the @Inject annotation (or by looking it up in the JNDI programmatically). The code in which OrderDAO is instantiated would like similar to this:

public class Main extends HttpServlet {
     private static final long serialVersionUID = 1L;
     private static Logger logger = LoggerFactory.getLogger(Main.class);
     @Inject OrderDAO orderDAO;
     /**
      * @see HttpServlet#HttpServlet()
      */
     public Main() {
          super();
     }
     /**
      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
      */
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
               throws ServletException, IOException {
          Order order = new Order();
          logger.info("Starting");
          // orderDAO is not null here, it has been injected by the @Inject annotation
          orderDAO.persist(order);
          orderDAO.persistAsync(order);
          response.getWriter().print("A response, just for the sake of it");
          logger.info("Done");
     }
}

When you run the application and invoke it by hitting the corresponding URL, you ‘ll see the following log-entries appear in the trace (provided you switch on the logs for the corresponding classes):

2013 06 10 16:13:46 - Starting
2013 06 10 16:13:46 - I'm running in sync and going to wait for 5 seconds
2013 06 10 16:13:51 - I'm a synchronous method, running in thread 'http-bio-8041-exec-3'
2013 06 10 16:13:51 - Done
2013 06 10 16:13:51 - I'm running async and going to wait for 5 seconds
2013 06 10 16:13:56 - I'm an asynchronous method, running in thread '@Asynchronous async-test - 2'

From the log-entries you can see that the asynchronous version of the persist method is actually running in the background (in a separate thread), and continues running when the servlet has already returned its response.

More goodies from asynchonous calls

Result value

You should be able to get the eventual result from an asynchronous call back using “Future” objects. Please find an example of how to define them below:

final Future<Boolean> persistResult;

Of course your asynchronous persist method should also return that value:

public Future<Boolean> persistAsync(Order order) 

When you are sure that the background task is completed, you can then retrieve the result of the background task easily with with persistResult.get();

Check the status of a background task and cancel it

If you have defined a Future, such as the one mentioned above, you can check it’s status by calling it’s .isDone method. The .isDone method returns true if the background task has completed.

By calling the cancel method of the Future value, you can cancel the background task. For this to work, you will have to implement some logic inside your asynchronous method as well though, similar to:

if (SessionContext.wasCancelled()) {
     // stop and clean up
} else {
     // continue processing
}

Spring

If you’re not developing for a JEE6 engine, you may want to use Spring instead to create background tasks. Spring’s method of running a method asynchronously, looks very much like JEE6, but uses slightly different annotations:

@Asynchronous -> @Async

@Inject -> @Autowired

When using Spring you will also have to add a line to your application context xml file, so that Spring knows that you want to autowire some classes, and where to find them:

<context:component-scan base-package="com.yourcompany" />
<task:annotation-driven />

It’s easy enough to get certain processes to run asynchronously. Try it! Your users will love you for speeding up their transactions!

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. Matthias Steiner

    Nice blog Jan – thanks for sharing!!!

    Hope you don’t curse me for doing so, but I have a few things I’d like to comment on…

    I know that you wanted to illustrate the asynchronous features of Java EE6, but I could not help, but notice that you’re performing a write operation in a HTTP GET METHOD, which of course gives me chills. 😉

    A couple of follow up questions and comments:

    • how would you handle the (unlikely) case that the asynchronously performed write operation would fail?
    • Why did you mark your DAO as a singleton using the @Singleton annotation? It should be stateless anyway, right?
    • From what I remember Spring does understand the @Inject annotation and would @Inject the referenced interface/(implementation) in case there’s a unique match within the scanned components. You could also use the @Service annotation in the implementation and setting it as the default auto-wired candidate there.

    Cheers,

    matthias

    (0) 

Leave a Reply