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.
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>
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 + '}';
}
}
@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);
}
}
}
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();
}
}
@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
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