Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
jpenninkhof
Product and Topic Expert
Product and Topic Expert

When 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!

1 Comment
Labels in this area