Persistent EJB Timers in Payara Micro
Originally published on 31 Aug 2016
Last updated on 19 Mar 2020
Payara Micro is packed with most of the features and APIs that come with Payara Server Full Profile even though it doesn't entirely support whole Jakarta EE Full Profile. As an example, Payara Micro supports persistent EJB Timers, which are only required by the Jakarta EE Full Profile and not by the Web Profile. In Payara Micro, it's possible to use persistent EJB Timers, which are stored across your micro instances inside the distributed data grid as long as at least one instance in the data grid is up and running.
The persisted timer information is replicated within the whole data grid in multiple copies. That means even more resilience and flexibility in production compared to a store backed by a relational database. On the other hand, it also means that the timers are persisted only while some members of the cluster are up and running. This is highly desirable in modern resilient systems and can easily be ensured with multiple data grid members. Payara Micro makes it easy to provision a huge cluster of instances in the same data grid and scale it dynamically! We encourage you to take advantage of it as much as possible to make your architecture more robust and resilient to failures.
In order to make use of a persistent timer in Payara Micro, an application must declare a timer to be persistent, as defined by the EJB specification. This is actually the default, unless timers are specified as non-persistent. One more thing to do is to specify a name for your Payara Micro instances when you run them and you’re ready to go!
How to define persistent timers
Let’s demonstrate with a simple example. This code snippet defines a timer:
@Resource
private TimerService timerService;
@Timeout
public void run() {
logger.info("Timer triggered at " + new Date());
}
In the same managed bean, we will create a persistent interval timer programmatically with the following code:
timerService.createTimer(10000, 10000, "triggers every 10 seconds");
Let’s put the pieces together into a REST resource to make it possible to start the timer externally:
@Path("timers")
@Stateless
public class TimerResource {
private static final Logger logger = Logger.getLogger(TimerResource.class.getName());
@Resource
private TimerService timerService;
@Timeout
public void run() {
logger.info("Timer triggered at " + new Date());
}
@POST
public String startTimer() {
timerService.createTimer(10000, 10000, "triggers every 10 seconds");
logger.info("Timer scheduled to fire every 10 seconds");
return "Timer scheduled to fire every 10 seconds";
}
}
We also need to turn our REST resource into a managed bean so that the TimerService
is injected. In the above example, we use @Stateless
to turn the object into an EJB but we might have turned it into a CDI bean as well.
And don’t forget to define a JAX-RS application's configuration class to expose the REST resource. We’ll go with the following, exposing the resource at http://localhost:8080/persistentTimers/timers:
@ApplicationPath("/")
public class TimersApplication extends Application {
}
Finally, we’ll build the web application to get persistentTimers.war
and we’re ready to run it.
How to run an application with persistent timers
Our persistentTimers.war
is just a usual web application and can be executed using Payara Micro as is. However, in order to restore the timers from any previous execution, we have to give our Payara Micro instance a name so that the old timers are paired with the new application instance upon startup. This is because persistent timers are only executed on a single instance to avoid executing it multiple times within the cluster.
Therefore, we will run our application as follows, specifying "payara1" as the instance name:
java -jar payara-micro.jar --name payara1 --deploy persistentTimers.war
If all went well, the REST resource should be available at http://localhost:8080/persistentTimers/timers using the POST method. Once we access it, the request will trigger a timer scheduled to fire every 10 seconds. After a while, you should be able to see messages like these in the console output:
… Timer scheduled to fire every 10 seconds … Timer triggered at Tue Aug 23 10:49:52 CEST 2016 … Timer triggered at Tue Aug 23 10:50:01 CEST 2016
At this stage, there is one step missing to make the timers persistent. Can you guess what it is?
Yes, we need to run at least one more Payara Micro instance to form a cluster with the first one. Without other members in the cluster, any persistent timer would be forgotten after a restart.
Therefore, we need to execute the following command to bring up one more Payara Micro instance and bind it automatically to a free HTTP port:
java -jar payara-micro.jar --name payara2 --autoBindHttp --deploy persistentTimers.war
In fact, it is not necessary that our application is deployed on these additional instances, nor it is needed to give them a name. Therefore, the following command would be enough to provide the persistent storage for the Hazelcast based persistent timer:
java -jar payara-micro.jar –-autoBindHttp |
Now, we are ready to demonstrate that the timers are really being persisted. When we shutdown and restart our first instance of Payara Micro with the name payara1
, we should see in the console output that the previously scheduled timer is being scheduled automatically:
… ==> Restoring Timers ...
… EJB Timers owned by this server will be restored when timeout beans are loaded
… <== ... Timers Restored
… Timer triggered at Tue Aug 23 11:21:41 CEST 2016
… Timer triggered at Tue Aug 23 11:21:51 CEST 2016
Note that Payara Micro doesn't support all of the features of persistent timers. It only ensures that the timer is persisted for a single instance that created it until it's restarted under the same name. Timers are not replicated on other instances and are not coordinated across a Payara Micro cluster.
Conclusion
Hazelcast integration into Payara Micro provides means to build many useful features easily and reliably. Besides providing persistence for EJB Timers, it also powers HTTP and EJB session replication and distributed cache services using JCache API. Hazelcast is also at the core of theCDI event bus.
Support for persistent timers still doesn't mean that persistent timers will be coordinated across the Payara Micro cluster. But it does mean there is no additional dependency required to provide the persistent storage. And this is one of the reasons we could finally remove the embedded Derby database from Payara Micro and reduce its size and footprint even more!
Related Posts
The Payara Monthly Catch - October 2024
Published on 30 Oct 2024
by Chiara Civardi
0 Comments
Can You Futureproof Your Enterprise Java Apps or Are They Doomed to Fall Behind?
Published on 16 Oct 2024
by Chiara Civardi
0 Comments