In this example, we create a conversational message which replies not just once, but replies continuously. Once every 100 milliseconds as it would turn out. The replyRepeating and replyDelayed, sendRepeating and sendDelayed methods all return an instance of AsyncTask, which is a handle on the task being performed. You can use this object to cancel the task.
@Service("TimeServer")
@RequireAuthentication
public class TimeDisplay implements MessageCallback {
private MessageBus bus;
@Inject
public TimeDisplay(MessageBus bus) {
this.bus = bus;
}
public void callback(final Message message) {
if (message.getCommandType() == null) return;
/**
* Create a local context to store state that is unique to this client instance. (not session wide).
*/
final LocalContext context = LocalContext.get(message);
/**
* Switch on the TimeServerCommand type provided
*/
switch (TimeServerCommands.valueOf(message.getCommandType())) {
case Start:
/**
* We want to start streaming.
*/
AsyncTask task = MessageBuilder.createConversation(message)
.toSubject("TimeChannel").signalling()
.withProvided(TimeServerParts.TimeString, new ResourceProvider<String>() {
public String get() {
return String.valueOf(System.currentTimeMillis());
}
}).noErrorHandling().replyRepeating(TimeUnit.MILLISECONDS, 100);
/**
* Store the task as an attribute uniquely identified by it's class type.
*/
context.setAttribute(AsyncTask.class, task);
/**
* Create a listener that will kill the task gracefully if the subject is unsubscribed. This
* isn't 100% necessary, as the task will be auto-killed ungracefully. But this provides
* and opportunity to clean up after ourselves.
*/
bus.addUnsubscribeListener(new UnsubscribeListener() {
public void onUnsubscribe(SubscriptionEvent event) {
if ("TimeChannel".equals(event.getSubject())) {
/**
* Delete this listener after this execution.
*/
event.setDisposeListener(true);
/**
* Stop the task from running.
*/
context.getAttribute(AsyncTask.class).cancel(true);
/**
* Destroy the local context. Sort of unnecessary, but helps reduce memory usage.
*/
context.destroy();
}
}
});
break;
case Stop:
/**
* Access our stored AsyncTask from this instance and cancel it.
*/
context.getAttribute(AsyncTask.class).cancel(true);
/**
* Destroy the local context. Sort of unnecessary, but helps reduce memory usage.
*/
context.destroy();
break;
}
}
}
That's all there is to it. It's pretty sweet. This API works on both the client and the server side. All the thread scheduling is all transparently managed by an executor service on the server, and by a simple Timer based implementation in the client. I'm hoping people will find this a welcome addition to Errai-land.
Mike.