IBM supports Reverse AJAX (AKA
Comet) in a form of
WebSphere Feature Pack for Web 2.0 for WebSphere Application Server 6.0+. It is shipped by default with the latest fix packs for WAS 6.1 and in all 7.0 versions. Actually this Feature Pack has a lot of nice Web 2.0 features, but at the moment we are interested in
Web Messaging Service only.
To install and enable FP you need to follow the
documentation, it won't take long. As the result you will get the ability to push data to your client's web browser by publishing it to the special JMS topics. As an alternative, you may also use a more specialized API for doing this, but the Publisher object is stored in the Servlet context, therefore it can be somewhat tricky to access it from an arbitrary part of your application, which is not true with JMS.
I assume that you have successfully installed and tested your Web Messaging, and now want to upgrade your
QuoteStreamer sample application to GWT. For example, you have a widget and want its methods to be called when some data arrives from the backend.
It is possible to use
JSNI to achieve exactly this goal. The idea is simple: when you need to push some object to the client (for example, out of your Message Driven Bean), you first serialize it using
GWT RPC mechanism, then put the resulting string to the appropriate JMS Topic. This string is wrapped to JSON envelope, sent to the client via WFP and unwrapped there via Dojo. Your JavaScript listener should be triggered at this moment. But as we use GWT for all our JavaScript programming, we should implement that listener as JSNI snippet:
private native void initCallbackAndSubscribe (String someParam) /*-{
if ($wnd.dojox) {
$wnd.dojox.cometd.subscribe("/test/" + someParam, this,
function (comet) {
module.@com.test.MyCoolWidget::onReverseAjax(Ljava/lang/String;)(comet.data)
}
);
}
}-*/;
// This is a normal Java code
public void
onReverseAjax
(String msg) throws
SerializationException
{
SerializationStreamReader r = ((SerializationStreamFactory) svc).createStreamReader(msg);
Object data =
r.readObject();
// Do something with that data arrived
}
As you see, the data is deserialized later in your normal Java code called by this listener. That's it. If you have initialized everything right (in your index.jsp or some similar place), this scheme should work fine. Now I will show you how to send data using GWT RPC. It should be simple via RPC.encodeResponseForSuccess method, but there is one problem - it requires SerializationPolicy object for the security reasons. This mechanism restricts serialized objects to the limited set described in *.gwt.rpc files, generated during compilation. Another problem is that there can be several such files. I haven't found a good way to get the SerializationPolicy other than by concatenating these files altogether:
public SerializationPolicy getSerializationPolicy() throws Exception {
String result = "";
for (File file : new File(PATH_TO_YOUR_GENERATED_STUFF).listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".rpc");
}
})) {
result += (FileUtils.readFileToString(file));
}
return SerializationPolicyLoader
.loadFromStream(new ByteArrayInputStream(result
.getBytes("UTF-8")), null);
}
public void publishObject(String topic, Object obj, Method method) throws Exception {
String r = RPC.encodeResponseForSuccess(method, obj, getSerializationPolicy());
publishStringViaJms(topic, r.toString());
}
I forgot to mention that RPC.encodeResponseForSuccess requires a Method instance, but it is used only to extract the return value type, so any method returning the object of the obj.getClass() type will do.
In general, there are some good things about such WebSphere + GWT + Comet integration, the major one is that it uses WFP, which is a pretty powerful Comet server, compared to some custom-built solutions. For example (at least on WAS 7.0), it uses connection multiplexing, nio and all that cool stuff you expect from the modern web server. Also, it naturally integrates into your ESB, so you can use it in your Process Server applications with ease. Some limitations are obvious too, for example it won't work in GWT Hosted Mode with Tomcat. Also it seems to me that this should be implemented somehow simpler, but I was unable to find it. So if you have any other ideas, please give me a clue.
P.S.: Actually, there are some alternatives to this approach. For example you can use
gwt-comet-streamhub to enable Reverse AJAX for your GWT applications. But it provides its own Comet server called
StreamHub which is definitely not a part of the IBM WebSphere product line :)
P.P.S.: The described method should work with other Comet-enabled J2EE servers like
Jetty with minor modifications.
P.P.P.S.: There is left one more issue with security. As for now, it is not really clear for me how to implement role-based security for Comet... I can think of using several update servlets at the same time, and it should allow me to use webapp security. But this issue definitely needs further investigation.