Async
JavaLite Async is a lightweight system for processing asynchronous jobs. When developing a website, you often need to run some process while not slowing down user page refresh.
Async uses Apache Artemis under the hood, but makes it very easy to do so. While Apache Artemis is a JMS broker, the Async adds an abstraction layer based on a Command Pattern, which makes it trivial to add asynchronous processing:
Embedded broker instance
Setting up Apache Artemis requires substantial knowledge of JMS and this specific implementation. However, JavaLite Async makes it easy by configuring Apache Artemis with reasonable defaults
Async async = new Async("/opt/project1", false, new QueueConfig("email", new CommandListener(), 50));
async.start();
where /opt/project1
is a place to store persistent messages, email
is a name of a queue, and 50 is number of listeners (threads) to create for processing.
Writing a simple command
Lets write a command, which will simply print a message to console:
public class HelloCommand extends Command {
private String message;
public HelloCommand(String message) {
this.message = message;
}
public HelloCommand() {} //necessary to provide
@Override
public void execute() {
System.out.println(message);
} }
Processing a command
Lets instantiate and start the broker:
new Async(filePath, false, new QueueConfig("MESSAGES_QUEUE", new CommandListener(), 5));
Async async = start(); async.
after that, sending a command for asynchronous processing is a one line of code:
for(int i = 0; i < 100; i++){
send("MESSAGES_QUEUE", new HelloCommand("Hello, Dolly " + i));
async. }
as expected, the output of this process will be:
Hello, Dolly 0
Hello, Dolly 2
Hello, Dolly 1
Hello, Dolly 3
Hello, Dolly 4
...
In the example above, we allocated 5 threads for processing, therefore the order of execution of the comands will not necessarily be linear, since threads will process messages in parallel.
Creating multiple queues
The JavaLite Async
allows to create and configure multiple queues:
new Async(filePath, false,
Async async = new QueueConfig("MESSAGES_QUEUE", new CommandListener(), 5),
new QueueConfig("ERROR_QUEUE", new CommandListener(), 0));
The constructor accepts an array of QueueConfig
instances as a vararg.
Peeking into queues
Sometimes you need to peek into what is in the queue. Normally this is done in some administrative tools. Lets take a look at 3 top commands in the queue (the ones at the head of the queue):
List<Command> topCommands = async.getTopCommands(3, "ERROR_QUEUE");
IMPORTANT: peeking into a queue does not remove commands from a queue.
Reading synchronously
You can read and process commands from an individual queue one at the time without a listener. In some cases, such as DMQ, you do not want to process errors automatically. Here is how you can process one command at the time:
receiveCommand("ERROR_QUEUE");
ErrorCommand errorCommand = (ErrorCommand)// act on the information in your command.
Text vs Binary messages
Since the underlying technology is JMS - Java Messaging Service, the communication protocol is limited to the types of messages supported by JMS. JavaLite Async may use one of two: javax.jms.TextMessage
or javax.jms.BytesMessage
. In both cases, the serialization of a command is first done to XML with the use of XStream.
If your command has a tranient field that cannot/should not be serialized, use the XStream annotation to ignore it:
public class HelloCommand extends Command {
@XStreamOmitField
private Object ignoredElement;
.... }
In order to set Async to a binary mode, use this setter:
setBinaryMode(true); async.
Do not switch from mode to mode while having persistent messages stored in your queues.
Commands with DB access
In cases where your queue processing requires a database connetion, you can use a class DBCommandListener:
new Async(filePath, false, new QueueConfig("MESSAGES_QUEUE", new DBCommandListener("java:comp/env/jdbc/your_project"), 5)); Async async =
So long as you configure a JNDI connection with access string: java:comp/env/jdbc/yout_project
, the listener will find and open a database connection.
Look at the documentation of your container to learn how to configure a database connection pool and allocate a name to it. Here is an example from Tomcat JNDI datasource examples.
From the documentation of this class:
This class will open a new connection, start a new transaction and will execute the command. After that, the transaction will be committed. In case execution of a command fails, the transaction will be rolled back and command and exception wil be passed to onException(Command, Exception)
method, where a subclass can process them further. The connection will be closed regardless of outcome.
Dependency injection
Like any other parts of JavaLite, the Async integrates with Google Guice:
Async(dataDirectory, useLibAio, injector, queueConfigs); Async async =
The third parameter is an instance of a Guice Injector.
As long as your commands have an @Inject
annotation, they will be injected with services prior execution:
public class HelloCommand extends Command {
@Inject
PrintingService printingService;
private String message;
...
@Override
public void execute() {
println(message);
printingService.
} }
Access to Artemis Config
If you have a complex configuration, you can access and use the Artemis API directly
apache.activemq.artemis.core.config.Condifuration artemisCOnfig = async.getConfig(); org.
for more information, refer to Artemis documentation.
How to comment
The comment section below is to discuss documentation on this page.
If you have an issue, or discover bug, please follow instructions on the Support page