Saturday, 21 September 2013

Some new JEE6 Features: Asynchronous EJBs, Singletons and Futures

Asynchronous EJB Methods, Singletons and Futures

In this post I want to explore some new JEE6 features concerning EJBs and run them on the Raspberry Pi.
These features are

  • Singleton EJBs
  • Asynchronous EJB calls
  • Asynchronous EJB calls returning Futures

What are these elements?

Singleton EJB
As the name indicates, a Singleton EJB is a bean that will be instantiated exactly once in your application. Does not sound very spectacular but in fact it is. Whenever you will look up this bean you will always get the same instance. This goes for every client of the application.

Asynchronous EJB calls
It is now possible to code EJB methods that can be called asynchronously. You call the method and it returns immediately while it performs its task in the background. This is calling the method “Send-and-Forget” style. Just like sending a message via JMS.
If you want to keep in touch with the method, your method needs to return a Future object.

Asynchronous EJB calls returning Futures
When an asynchronous method returns a Future, you can keep in touch. The Future is your link to the running method. It offers an API to determine whether the method’s result is already available or not. If a value is ready to be retrieved you can fetch it.
Futures are very handy concept for certain applications. You can trigger a couple of calculations running concurrently, collect the Futures and scan them for results subsequently trickling in.  
There is one little drawback with using Futures in the context of EJBs: Future is just an interface and the Java default implementation AsyncResult is not serializable. An AsyncResult cannot be passed over Remote EJB interfaces. Using Future and AsyncResult requires you to carefully choose between local and remote interfaces and to modularize your EJBs properly.

Demo Application

Let’s have a look on the demo application.
The next picture will give an overview:


The whole thing consists of two JEE applications. A servlet and an ejb-module. Both are separate deployment units:
  • Servlet: LocalAsyncClient
  • Ejb-module: MyAsyncExample
All the new features are coded within the ejb.module. The servlet is only for triggering the action and for displaying the results.

Coding

The application is best described inside out, so let’s start with the heart of the matter, the ejb that offers asynchronous methods.
This bean is called RandomLengthTask. Here is the local interface:
package com.mtag.ejbs.async;
import java.util.concurrent.Future;
import javax.ejb.Asynchronous;
import javax.ejb.Local;

/**
 * EJB’s local interface.
 */
@Local
public interface RandomLengthTaskLocal {
    /**
     * This annotation is all it takes to make this function asynchronous.
     * When it is called it will return the Future immediately.
     */
    @Asynchronous
       public Future<string> performRandomTask();
      
    /**
     * Asynchronous but not returning anything.
     * Will return immediately.
     */
    @Asynchronous
    public void sendAndForget(long duration);
}
As can be seen, turning a regular method into an asynchronous one does not take much. It’s just one annotation.

Returning a Future is notable. The method returns it directly and only later fills in the value. The client can question the Future and eventually extract the result.
The Future is the reason for providing a local interface for our EJB. We cannot pass a Future over a remote connection. We need to structure our application accordingly. More on this structure later.
Let'ts take a look at the EJB's implementation:
package com.mtag.ejbs.async;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Random;
import java.util.concurrent.Future;
import java.util.logging.Logger;

import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;

/**
 * Session Bean implementation class RandomLengthTask
 */
@Stateless
public class RandomLengthTask implements RandomLengthTaskLocal {
 private static final Logger LOGGER = Logger.getLogger(RandomLengthTask.class.getName());
    /**
     * Default constructor. 
     */
    public RandomLengthTask() {
        // TODO Auto-generated constructor stub
    }
    
    /**
     * 
     * @param duration
     * @return void
     * Asynchronous Method returning immediately without a result value
     */
    @Asynchronous
    public void sendAndForget(long duration) {
     LOGGER.info("Send and Forget will take: " + duration + "ms");
     try {
      Thread.sleep(duration);
      LOGGER.info("Send and Forget: Waking up. Slept " + duration + "ms");
     } catch (InterruptedException e) {
      LOGGER.info("Send and Forget: Sleep was interrupted!");
     }
     LOGGER.info("Send and Forget has finished");
    }
    
    /**
     * Will return a Future immediately.
     * Eventually the .
     * The length of the task will be randomly generated between 1 second and 1 minute.
     */
    @Asynchronous
    public Future<String> performRandomTask() {
     // record starting time...
     Calendar cal = Calendar.getInstance();
     SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
     String started = sdf.format(cal.getTime());
     LOGGER.info("RandomTask started at: "+started);
     try {
      Thread.sleep(new Random(System.currentTimeMillis()).nextInt(60000)+1000);
     } catch (InterruptedException e) {
      LOGGER.info("RandomTask was interrupted...");
     }
     cal = Calendar.getInstance();
     LOGGER.info("RandomTask finished at: "+sdf.format(cal.getTime()));
     //
     // AsyncResult is the default implementation of the Future interface...
        return new AsyncResult<String>("Random Task Start: "+started+" / finished: "+sdf.format(cal.getTime()));
    }
}
The life of the sendAndForget method is a bit hard to monitor. After calling it lives a life of its own. Best we can do is to log its activities. Later, when running the servlet, we have to take a look at the console output once in a while.
The method takes the duration it is supposed to run as a parameter. We will set this duration from the servlet later.

The performRandomTask is a bit more interesting. It not only returns a Future (in fact it returns an AsyncResult which is the default implementation of Future) but generates its own duration randomly.
When finishing, it returns its start and finish time stamps as a result into the Future. This information can of course be accessed by our servlet.

We now have a bean that offers two types of asynchronous methods: a send-and-forget type returning void immediately and one returning a Future immediately supplying a value into that Future in the future :-).
The bean has a local interface because we cannot transport the Future over a remote call.
We will now write a Decorator bean wrapping the local interface and offering a remote one to the public.
This bean is called InBetweenBean as can be seen in the picture above.
Here is the code of its remote interface:
package com.mtag.ejbs.async;

import java.util.List;
import javax.ejb.Remote;

@Remote
public interface InBetweenBeanRemote {
    /**
     * Calls an asynchronous method and saves the result, a Future, in the
     * SessionManager Bean.
     */
 public void callLenghyTask(String sID);

    /**
     * Calls an asynchronous send-and-forget method with an instruction of how long to 
     * sleep (duration)
     */
 public void sendAndForgetTask(long duration);

     /**
     * Queries the SessionManagerBean for the results asspciated with a giben client (sid)
     * and returns a list of results.
     * @param sid
     * @return
     */
    public List<string> getResultList(String sid);
}

The method "sendAndForgetTask" is easy. It calls our asynchronous send-and-forget method returning void immediately. The amount of time this method shall run must be supplied by the client.
"callLegthyTask" is more interesting a we will see in a minute. It takes a session-id for a parameter which indicates to some session state that has to be handled.
The same goes for the "getResultList" method. It will return a session specific list of results.

Let's have a look at the implementation of this interface.
package com.mtag.ejbs.async;

import java.util.List;
import java.util.concurrent.Future;
import java.util.logging.Logger;

import javax.ejb.EJB;
import javax.ejb.Stateless;

import com.mtag.ejbs.async.SessionManagerLocal;

/**
 * Session Bean implementation class InBetweenBean
 */
@Stateless
public class InBetweenBean implements InBetweenBeanRemote {
 private static final Logger LOGGER = Logger.getLogger(InBetweenBeanRemote.class.getName());
 
 @EJB(mappedName = "java:global/MyAsyncExample/RandomLengthTask!com.mtag.ejbs.async.RandomLengthTaskLocal")
 RandomLengthTaskLocal rltBean;
 
 @EJB(mappedName = "java:global/MyAsyncExample/SessionManager!com.mtag.ejbs.async.SessionManagerLocal")
 SessionManagerLocal sessionManager;
    /**
     * Default constructor. 
     */
    public InBetweenBean() {
        // TODO Auto-generated constructor stub
    }
    
    /**
     * Calls an asynchronous task returning a Future.
     * This Futute is stored within the SessinManager Singleton EJB
     */
    public void callLenghyTask(String sID) {
     LOGGER.info("calling RLTask...");
     Future<String> result = rltBean.performRandomTask();
     //
     // Get SessionState for this Client...
     SessionState ss = sessionManager.getSessionState(sID);
     // Add the Future...
     ss.addResult(result);
     // Give StateObjekt back to SessionManager...
     sessionManager.addSessionState(sID, ss);
     LOGGER.info("RLTask has finished...");
    }
    
    /**
     * Calls a send-and-forget task...
     */
    public void sendAndForgetTask(long duration) {
     LOGGER.info("calling SendAndForget-Task...");
     rltBean.sendAndForget(duration);
     LOGGER.info("SendAndForgetTask finished...");
    }
    
    /**
     * Returns the SessionState of a given client as String-List.
     * A String-List is serializable...
     * @param sid
     * @return
     */
    public List<String> getResultList(String sid) {
     SessionState ss = sessionManager.getSessionState(sid);
     return ss.getResultsAsStringList();
    }
    
}

The first thing I'd like to mention here, are the two @EJB-annotations.
They are injecting EJB references into the respective local variables. Do you still remember how this had to be done in the early days of J2EE? You had to maintain monstrous xml-files describing your beans and including all the references form one to the other. Now one annotation does it all. I don't even have an xml-file to maintain explicitly in this project (though I'm not quite sure if I can keep this up during the course of this blog). Anyway, JEE has come a long way.
In case you ask yourself where theses JNDI-names come from, the JEE specification defines some of those "global" ones. Others are defined by JBoss. One sure way to have them right is to deploy the beans you already implemented. JBoss publishes the names on the console and wrights them to the log-files.

Ok, now we have a reference on the bean that implements the asynchronous methods and a reference on on a bean called SessionManager.
We'll discuss the SessionManager bean later in more detail, because it is another new JEE feature.
For now just take a look at the implementation of the "callLengthyTask" method.
First we call an asynchronous method receiving a Future.
Then we call the Session Manager for the session state of the current client, identified by its session id.
Next we add the Future to this session state and store the session state back to the Session Manager.
Eventually the Future we've just given away to the Session Manager will be supplied with a value from the concurrently running asynchronous method.
To see these values, a client can call the "getResultList" method of our InBetweenBean.
The getResultList-method calls the Session Manager for this client's session state and returns this state as a serializable list of strings. This list can be transported over a remote connection like a remote EJB call.
As you can see, InBetweenBean does not much but wraps functionality supplied by others.

Taking a look at the SessionManager bean and its little POJO helper SessionState wraps up the ejb-module.

Here are interface and implementation of the SessionManager EJB:
package com.mtag.ejbs.async;

import javax.ejb.Local;

@Local
public interface SessionManagerLocal {
    /**
     * @param sessionId
     * @return SessionState
     */
    public SessionState getSessionState(String sessionId);
    
    /**
     * Adds a SessionState to the Hashmap of 
     * @param sid
     * @param ss
     */
    public void addSessionState(String sid, SessionState ss);
}

package com.mtag.ejbs.async;

import java.util.HashMap;
import javax.ejb.Singleton;
import com.mtag.ejbs.async.SessionState;

/**
 * Manages a Hashmap of SessionState objects.
 * Key to the hashmap is a client's session id.  
 * 
 */
@Singleton
public class SessionManager implements SessionManagerLocal {
 private HashMap<String, SessionState> sessions = new HashMap<String, SessionState>();
  
    /**
     * Default constructor. 
     */
    public SessionManager() {
        // TODO Auto-generated constructor stub
    }
    
    /**
     * Store SessionState in the Hashmap
     * @param sid clients session id
     * @param ss  SessionState object
     */
    public void addSessionState(String sid, SessionState ss) {
     sessions.put(sid, ss);
    }
    
    /**
     * Retrive the session state of a given client.
     * If this is the first call a clien, a session state object will
     * created, stored and returned.
     * @param sessionId
     * @return SessionState
     */
    public SessionState getSessionState(String sessionId){
     SessionState state = null;
     state = sessions.get(sessionId);
        if (state == null) {
         state = new SessionState();
         sessions.put(sessionId, state);
        }
     return state;
    }
}
And finally the SessionState class: a POJO maintained by the SessionManager:
package com.mtag.ejbs.async;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * 
 * @author vkoster
 *
 * Store for Session State, both
 * - Session State and
 * - Resource State
 * Substitutes a little database 
 */
public class SessionState implements java.io.Serializable {
 private static final long serialVersionUID = 1L;
 private List<Future<String>> myList = new ArrayList<Future<String>>();
 
 public void addResult(Future<String> f) {
  myList.add(f);
 }
 
 /**
  * Transform a list of Futures into a list of strings
  * @return
  */
 public List<String> getResultsAsStringList() {
  List<String> rl = new ArrayList();
  
  for (Future<String> f : myList) {
   try {
    if (f.isDone()){
     rl.add(f.get());
    } else {
     rl.add("No reuslt yet!");
    } 
   } catch (CancellationException e) {
    rl.add("Calculatioin cancled!");
   } catch (ExecutionException e) {
    rl.add("Execution error!");
   } catch (InterruptedException e)  {
    rl.add("Calculation interrupted!");
   }
  }
  return rl;
 }
}

The SessionManager bean is annotated as @Singleton.
This is all it takes to accomplish the following:
  • The EJB is a singleton (obviously)
  • There will only be one instance of this bean, regardless of how many clients will access it
  • All clients will get exactly this one instance when they call this bean
  • The bean is comparable to a process with identity and addressability
Who is responsible for managing all this concurrency?
If you don't say otherwise, the ejb-container will do it for you. This is called "Container Managed Concurrency". If you prefer to take things into your own hands, there are annotations for this too.
I can do with container managed concurrency in this example.

A Singleton comes in handy for our purposes. It stores the session state of all the clients concurrently using out asynchronous ejb-module.
Whenever a client uses our ejb-module by calling a method that returns a Future, we add this Future to the session state of this client and store it within the Singleton. All the client has to do is to supply a Session ID to identify itself in subsequent calls.

The Session State itself is a simple List of Futures.
Remember: these Futures will receive their results eventually.
SessionState offers to translate the current state of this list of Futures into a String-list.
The String-list can be sent to remote clients which then can visualize the current state of the session.

Deployment



...to be continued...

No comments:

Post a Comment