Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon

Using Transactions with Asynchronous Tasks in JavaEE [Tutorial]

Save for later
  • 5 min read
  • 31 Jul 2018

article-image
Threading is a common issue in most software projects, no matter which language or other technology is involved. When talking about enterprise applications, things become even more important and sometimes harder. Using asynchronous tasks could be a challenge: what if you need to add some spice and add a transaction to it?

Thankfully, the Java EE environment has some great features for dealing with this challenge, and this article will show you how.

This article is an extract from the book Java EE 8 Cookbook, authored by Elder Moraes.


Usually, a transaction means something like code blocking. Isn't it awkward to combine two opposing concepts? Well, it's not! They can work together nicely, as shown here.

Adding Java EE 8 dependency


Let's first add our Java EE 8 dependency:

        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>

  1. Let's first create a User POJO:

public class User {
private Long id;
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public User(Long id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "User{" + "id=" + id + ",
name=" + name + '}';
} 
}

  1. And here is a slow bean that will return User:

@Stateless
public class UserBean {
public User getUser(){
try {
TimeUnit.SECONDS.sleep(5);
long id = new Date().getTime();
return new User(id, "User " + id);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
long id = new Date().getTime();
return new User(id, "Error " + id);
}
}
}

  1. Now we create a task to be executed that will return User using some transaction stuff:

public class AsyncTask implements Callable<User> {
private UserTransaction userTransaction;
private UserBean userBean;

@Override
public User call() throws Exception {
performLookups();
try {
userTransaction.begin();
User user = userBean.getUser();
userTransaction.commit();
return user;
} catch (IllegalStateException | SecurityException | 
HeuristicMixedException | HeuristicRollbackException | 
NotSupportedException | RollbackException | 
SystemException e) {
userTransaction.rollback();
return null;
}
}

private void performLookups() throws NamingException{
userBean = CDI.current().select(UserBean.class).get();
userTransaction = CDI.current()
.select(UserTransaction.class).get();
} 
}

  1. And finally, here is the service endpoint that will use the task to write the result to a response:

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
@Path("asyncService")
@RequestScoped
public class AsyncService {
private AsyncTask asyncTask;

@Resource(name = "LocalManagedExecutorService")
private ManagedExecutorService executor;

@PostConstruct
public void init(){
asyncTask = new AsyncTask();
}

@GET
public void asyncService(@Suspended AsyncResponse response){

Future<User> result = executor.submit(asyncTask);

while(!result.isDone()){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
}
}

try {
response.resume(Response.ok(result.get()).build());
} catch (InterruptedException | ExecutionException ex) {
System.err.println(ex.getMessage());
response.resume(Response.status(Response
.Status.INTERNAL_SERVER_ERROR)
.entity(ex.getMessage()).build());
}

}
}


To try this code, just deploy it to GlassFish 5 and open this URL:

http://localhost:8080/ch09-async-transaction/asyncService

How the Asynchronous execution works


The magic happens in the AsyncTask class, where we will first take a look at the performLookups method:

    private void performLookups() throws NamingException{
        Context ctx = new InitialContext();
        userTransaction = (UserTransaction) 
        ctx.lookup("java:comp/UserTransaction");
        userBean = (UserBean) ctx.lookup("java:global/
        ch09-async-transaction/UserBean");
    }


It will give you the instances of both UserTransaction and UserBean from the application server. Then you can relax and rely on the things already instantiated for you.

As our task implements a Callabe<V> object that it needs to implement the call() method:

    @Override
    public User call() throws Exception {
        performLookups();
        try {
            userTransaction.begin();
            User user = userBean.getUser();
            userTransaction.commit();
            return user;
        } catch (IllegalStateException | SecurityException | 
                HeuristicMixedException | HeuristicRollbackException 
                | NotSupportedException | RollbackException | 
                SystemException e) {
            userTransaction.rollback();
            return null;
        }
    }


You can see Callable as a Runnable interface that returns a result.

Our transaction code lives here:

            userTransaction.begin();
            User user = userBean.getUser();
            userTransaction.commit();


And if anything goes wrong, we have the following:

        } catch (IllegalStateException | SecurityException | 
           HeuristicMixedException | HeuristicRollbackException 
           | NotSupportedException | RollbackException | 
           SystemException e) {
            userTransaction.rollback();
            return null;
        }


Now we will look at AsyncService. First, we have some declarations:

private AsyncTask asyncTask;
@Resource(name = "LocalManagedExecutorService")
private ManagedExecutorService executor;

@PostConstruct
public void init(){
asyncTask = new AsyncTask();
}


We are asking the container to give us an instance from ManagedExecutorService, which It is responsible for executing the task in the enterprise context.

Then we call an init() method, and the bean is constructed (@PostConstruct). This instantiates the task.

Now we have our task execution:

@GET
    public void asyncService(@Suspended AsyncResponse response){
Future<User> result = executor.submit(asyncTask);

while(!result.isDone()){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
}
}

try {
response.resume(Response.ok(result.get()).build());
} catch (InterruptedException | ExecutionException ex) {
System.err.println(ex.getMessage());
response.resume(Response.status(Response.
Status.INTERNAL_SERVER_ERROR)
.entity(ex.getMessage()).build());
}

}


Note that the executor returns Future<User>:

Future<User> result = executor.submit(asyncTask);


This means this task will be executed asynchronously. Then we check its execution status until it's done:

        while(!result.isDone()){
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException ex) {
                System.err.println(ex.getMessage());
            }
        }


And once it's done, we write it down to the asynchronous response:

response.resume(Response.ok(result.get()).build());


The full source code of this recipe is at Github.

So now, using Transactions with Asynchronous Tasks in JavaEE isn't such a daunting task, is it? If you found this tutorial helpful and would like to learn more, head on to this book Java EE 8 Cookbook.




Oracle announces a new pricing structure for Java

Design a RESTful web API with Java [Tutorial]

How to convert Java code into Kotlin