In previous chapters, we’ve been focusing primarily on Payara Micro’s implementation of the MicroProfile standard. The benefit of coding against a standard is that we are not tied to a specific implementation, as code written against the standard can be deployed to any implementation.
In this chapter, we will cover some Payara Micro specific features that, while very useful, are specific to Payara Micro and not portable across implementations.
Automatic Clustering
Clustering is done automatically in Payara Micro; when two instances of Payara Micro are started on the same network, they automatically cluster together, with absolutely no configuration needed.
If we don’t want Payara Micro instances to join a cluster, we can start Payara Micro with the --noCluster command-line argument
Payara Micro (and Payara Server, for that matter) has an in-memory data grid used to share data across instances in a cluster. Payara’s data grid is based on the popular open source Hazelcast in-memory data grid. For the most part, the data grid is transparent to us as application developers deploying applications to Payara Micro; however, it is used behind the scenes to implement Payara Micro specific clustering features, such as having application scoped CDI beans be shared across a cluster and allowing to fire CDI events to observer methods running on another instance of Payara Micro on the same cluster.
Clustered Application Scoped CDI Beans
A common use of application scoped CDI beans is to cache frequently used data so that we don’t have to hit a database every time we need to retrieve it. With standard application scoped CDI beans, we would have to have a copy of the bean in each node of a cluster; keeping those instances in sync would not be a trivial task. With Payara’s clustered CDI beans, there is a single instance shared across nodes.
As can be seen in the example, all we have to do to share an application scoped bean across nodes in a cluster is annotated with @Clustered and have it implement the Serializable interface; all the hard work to share the bean instance is done behind the scenes by Payara Micro.
Clustered application scoped CDI beans must be serializable; otherwise, our code will fail to deploy.
When using the Payara Maven Bill of Materials (BOM), dependency versions are specified in the BOM; therefore, we shouldn’t specify them in our pom.xml.
Notice that there is nothing special we need to do to use our clustered application scoped CDI bean; we simply inject it via the @Inject annotation as usual.
At this point, we have a simple but complete example we can use to illustrate clustered application scoped CDI beans; we simply package our application in a WAR file and deploy it to two separate instances of Payara Micro, which will automatically form a cluster.
We then use the same command on a different terminal window to deploy a second copy of our WAR file.
Since we used the --autoBindHttp command-line argument for Payara Micro, the second instance listens for HTTP connections on the next available port (8081).
Recall that Payara Micro’s clustering capabilities are implemented via an in-memory data grid (Hazelcast); the output on the first instance of Payara Micro is letting us know that a data grid instance was added to the cluster.
At this point, we added an entry to the Map in the clustered application scoped CDI bean with a key of foo and a value of bar. Since the bean is clustered, the change is reflected across all nodes in the cluster.
The text below the curl command is the response from the second instance; as we can see, we got the expected value, which was set by sending an HTTP PUT request to the first instance in the cluster.
Remote CDI Events
The preceding RESTful web service code fires two types of country events: one when a country is updated and another when a country is deleted. Recall from Chapter 4 that we can use qualifiers to distinguish between events of the same type; in our example, the @Updated and @Deleted qualifiers are used to distinguish between these two events. In addition to our custom @Updated and @Deleted qualifiers, we annotated both events with the Payara-provided @Outbound qualifier; this qualifier is used to fire the event across the network so that event observer methods in other nodes can handle it. As a matter of fact, the only thing we have to do to convert a standard CDI event into a remote CDI event is to annotate the Event instance declaration with @Outbound.
For this example, we refactored the application so that the event observer is deployed in a separate WAR file; this way, we can deploy the observer to a different instance of Payara Micro.
The only difference between this example and the corresponding example in Chapter 4 is the addition of the @Inbound annotation to the observer method parameter, which is needed to handle events fired across the network.
Unsurprisingly, all we had to do to have this observer method listen for remote events is to annotate its parameter with the @Inbound annotation.
Since we didn’t package any RESTful web services in the second WAR file (only CDI beans), no endpoints are displayed on the output of the second Payara Micro instance.
As illustrated by our example, firing remote CDI events in Payara Micro takes very little work on our part; we simply annotate the event to be fired with @Outbound and the corresponding event observer method parameters with @Inbound; all the hard work is done by Payara Micro and is transparent to us as application developers.
Uber Jars
Uber Jars are executable JAR files that bundle both our application code and the Payara Micro runtime. Creating an Uber Jar can potentially simplify executing our applications; all we have to do is run the executable WAR file, without having to pass any arguments to Payara Micro.
Creating Uber Jars
The --outputUberJar command-line option takes an argument specifying the name of the JAR file to be created.
Most command-line arguments we pass to Payara Micro are recorded so that they don’t need to be specified when executing the JAR file; in our example, the generated JAR file will execute Payara Micro with the --autoBindHttp option enabled by default. One notable exception is that the --contextRoot command-line argument is not recorded; the context root of the generated JAR file will always be the name of the deployed WAR file (cdi-remote-events-1.0-SNAPSHOT.war in our example).
If we wish to change the context root, we can rename the WAR file to have the context root name we want; for example, renaming cdi-remote-events-1.0-SNAPSHOT.war to cdiremoteevents.war will result in the application having a context root of /cdiremoteevents.
Embedding Payara Micro
The PayaraMicro class is a singleton; it has a static getInstance() method that returns the PayaraMicro instance; we can configure Payara Micro programmatically by invoking setter and getter methods; for instance, in our example, we are invoking setHttpAutoBind(true), which is equivalent to passing the --autoBind command-line argument to Payara Micro when running from the command line. The API to configure Payara Micro is very intuitive for those familiar with running Payara Micro from the command line; refer to the official Payara Micro API JavaDoc at https://javadoc.io/doc/fish.payara.extras/payara-micro/latest/index.html; of particular interest is the fish.payara.micro package, particularly the PayaraMicro class and the PayaraMicroRuntime interface.
Once we have configured the embedded Payara Micro instance, we invoke its bootstrap() method, which returns an implementation of the PayaraMicroRuntime interface; to deploy our WAR file, we invoke its deploy() method, which takes three arguments: a String defining a name for our application, a second String defining the context root for the WAR file, and an instance of java.io.File representing our WAR file.
would run our application, which in turn would start a Payara Micro instance; we can then send HTTP requests to this Payara Micro instance as usual.
Summary
In this chapter, we covered Payara Micro features that go beyond the Jakarta EE and MicroProfile standards. We explained how Payara Micro instances cluster automatically. We also covered how to share application scoped CDI beans across cluster nodes, as well as how to send CDI events across the network to listeners deployed to other Payara Micro instances on the cluster.
Additionally, we covered how to create so-called Uber Jars, which are executable JAR files that contain both our application code and the Payara Micro runtime bundled together, allowing us to run Payara Micro and deploy our code by running an executable JAR file.
We then covered how to embed Payara Micro in our Java applications and how we can configure and deploy our applications and start Payara Micro programmatically.