Java Development News:

Using DWR with TIBCO General Interface

By Joe Walker

01 Dec 2007 | TheServerSide.com

December 2007

Discussion

DWR enables Java methods on the server to be called via JavaScript in the browser, and vice versa, to push events and data from the server to the browser. You can get a copy of DWR here under an open source license along with instructions for installation, set up, and DWR basics www.directwebremoting.org . There’s also a TIBCO resource center for DWR that focuses on the interoperation of DWR with TIBCO technologies.

TIBCO General Interface is an Ajax toolkit recently awarded best open source Ajax toolkit by InfoWorld Labs for its visual tools and hundreds of controls and APIs. For download, docs, tutorials, and more visit developer.tibco.com/gi.

The OpenAjax Hub is an industry standard client-side Ajax event bus that enables JavaScript objects to communicate with each other in a publish-and-subscribe model. More info at http://www.openajax.org

Using DWR with TIBCO General Interface

This simple request/response cycle example demonstrates some of the basic utilities Direct Web Remoting (DWR) provides for making interactions between DWR and Ajax applications created with TIBCO General Interface™ easy.

Demo

View the live application
Get the source for this example

Use Case

In this example we’ll get data about some mythical corporations from the server and populate a TIBCO General Interface data grid with that information.

How It Works

On the Server

On the server, we have a simple JavaBean representing Corporation. The corporation has an ID, a name, a share price, a time the share price changed, how much it changed by, and the maximum/minimum price during this trading period.

 public class Corporation { private String jsxid; private String name; private BigDecimal last; private Date time; private BigDecimal change; private BigDecimal max; private BigDecimal min; // Getters and setters } public class Corporations { public List<Corporation> getCorporations() { // Make some stuff up return corporations; } }

DWR enables you to mark certain classes for export, so they can be converted and sent into a web browser, and to mark other classes as service classes. In the code above, you can see that we have the Corporations class, with a single method getCorporations, and a service class, which we export. This approach aids security, aids simplicity, and follows some very good design patterns of how to work with remote objects.

Notice that DWR isn't mentioned in the code. One of the great things about DWR is that it enables you to publish stuff without having great dependencies on DWR in your code. You can export virtually anything you like.

On the Web

This is the code you have on the web -- in the HTML page or the General Interface Ajax application.

 function giLoaded() { Corporations.getCorporations(function(corporations) { var cdf = dwr.gi.toCdfDocument(corporations, "jsxroot"); giApp.getCache().setDocument("corporations", cdf); giApp.getJSXByName('matrix').repaint(); }); }

One of the things you can do in General Interface is set up some code to be called when the interface has loaded. In this example, we've got a giLoaded function in JavaScript and it's going to ask the server for some data. Here's how it works:

  • Corporations.getCorporations is calling the Java code on the server with a callback function declared inline that says what we're going to do when that data arrives.
 Corporations.getCorporations(function(corporations) {
  • When the data arrives, the callback function creates a var cdf by converting the corporations object returned from the server via DWR into a Common Data Format (CDF) document. (CDF is the data model used by General Interface’s GUI controls. Learn more about CDF.) DWR features a utility, dwr.gi.toCdfDocument, that will take a Java object returned from a server via DWR as JavaScript and convert it into a ready-to-use CDF document.
 var cdf = dwr.gi.toCdfDocument(corporations, "jsxroot");
  • Next we put the new data set into the client applications’ data cache (an instance of GI’s jsx3.app.Cache class).
 giApp.getCache().setDocument("corporations", cdf);
  • The last line initiates an update to the view of the data grid called “matrix” which is already configured with a binding to the CDF document in the cache called “corporations”.
 giApp.getJSXByName('matrix').repaint();

As you see, you can use just a few lines of code to populate the data grid.

In the next example we’ll extend this demo so that we get real-time data pushed from the server using DWR 2.0’s ReverseAjax capabilities. [Example 2].

 Using DWR 2.0 with the OpenAjax Hub and TIBCO General Interface

In this example, you'll learn how to stream data to TIBCO General Interface™ using DWR 2.0's "Reverse Ajax" features and the publish and subscribe interfaces of the OpenAjax Hub.

Demo

View the live application
Get the source for this example

Use Case

This example builds on Example 1 where there was a data grid populated with data via a call to a method on a Java object on the server via DWR. In this example, we’ll extend that from request/response-based updates to real-time server-push updates (also called “Comet or Ajax Comet by some”). Thus we’ll see the stock prices of the mythical corporations in the example update with new values being streamed form the server to the client. DWR calls this Reverse Ajax.

How It Works

The interesting thing you’ll see in this example is that in the integration between DWR and General Interface there is no dependency between DWR and General Interface. They both know about the OpenAjax Hub -- DWR publishes data to a topic on the hub and General Interface subscribes to that topic on the hub -- that's it. Thus General Interface and DWR are “integrated” by the fact that they are not integrated at all. The intermediary OpenAjax Hub publish-and-subscribe system lets them work together without having to do deep integration. In this demo, in fact, you can remove DWR completely and replace it with TIBCO Ajax Message Service™ and you don't have to change the client implementation at all. This is in part why over 90 organizations pursuing Ajax standards have advanced the publish-and-subscribe concept in the OpenAjax Hub.

On the Web

 function giLoaded() { OpenAjax.subscribe("gidemo", "corporation", objectPublished); dwr.engine.setActiveReverseAjax(true); } function objectPublished(prefix, name, hd, corporation) { var matrix = giApp.getJSXByName("matrix"); var inserted = matrix.getRecordNode(corporation.jsxid); matrix.insertRecord(corporation, null, inserted == null); matrix.repaintData(); }
  • We have the giLoaded function on the web.
 function giLoaded() {
  • To get information for the table, we subscribe to a topic with the OpenAjax Hub. The application name is gidemo and the topic name is corporation. Whenever something gets published we call the objectPublished function.
 OpenAjax.subscribe("gidemo", "corporation", objectPublished);
  • Next we turn Reverse Ajax on. You don't want Reverse Ajax turned on in every page on your website because it would affect performance.
 dwr.engine.setActiveReverseAjax(true);
  • When information is published from the server, objectPublished is called, the OpenAjax Hub calls the subjectPublished, and the table is updated with that record.
 function objectPublished(prefix, name, hd, corporation) { var matrix = giApp.getJSXByName("matrix"); var inserted = matrix.getRecordNode(corporation.jsxid); matrix.insertRecord(corporation, null, inserted == null); matrix.repaintData();

On the Server

This is the Java code on the server.

 Corporation corp = corporations.getNextChangedCorporation(); Collection<ScriptSession> sessions = serverContext.getScriptSessionsByPage("demo.html"); ScriptProxy proxy = new ScriptProxy(sessions); proxy.addFunctionCall("OpenAjax.publish", "gidemo", "corporation", corp);

Here's how it works:

  • We go to the corporations object and call the getNextChangedCorporation function, which is going to invent some changes. In effect this is the business logic heart of the application; some code that waits for something to change so it can be published. The change could come from monitoring a message bus, or reading from some other input.
 Corporation corp = corporations.getNextChangedCorporation();
  • We get a collection of the web pages that are open at the current web page.
 Collection<ScriptSession> sessions = serverContext.getScriptSessionsByPage("demo.html");
  • A ScriptProxy enables us to send arbitary JavaScript to the browser. In this case we're using the OpenAjax library and we're going to publish to it.
 ScriptProxy proxy = new ScriptProxy(sessions);
  • We publish to the gidemo application using the corporation topic.
 proxy.addFunctionCall("OpenAjax.publish", "gidemo", "corporation", corp);
  • Then we publish the JavaBean. DWR takes care of everything else.

As data is published it appears in the General Interface table – not through requests and responses but by publishing events to the OpenAjax Hub from which the data grid gets the information and displays it.

What’s Next?

In the next example you’ll see a simple call center application created with TIBCO General Interface and DWR in which users pick callers from a call queue, but all users looking at the queue get instant updates pushed to them so that a user can see who is already handling what.

 Call Center Example using DWR 2.0 with TIBCO General Interface

In the previous examples we saw a basic request/response style application using DWR and TIBCO General Interface TM and an example of real-time Ajax, where DWR published real-time stock data to the OpenAjax Hub and General Interface subscribed to messages published by DWR. This example goes a bit further exploring a real-time multi-user collaborative application.

Demo

View the live application (hint – open it multiple times to simulate multiple users)
Get the source for this example

Use Case

This use case, simulating a call center, uses DWR 2.0 and General Interface for processing real-time messages that keep the state of the system up to date for all connected users.

In a call center environment, there are a number of things that are really annoying for customers. For example, if your call drops, you ring back and you have to start again with a new person. It would be very useful if the agent who was talking to you before could take that call out of the queue and pick up exactly where you left off. Or maybe you've got agents who are better at dealing with certain types of requests. Having some flexibility about who you pick from the queue is quite useful for a call center. With a plain web connection, that flexibility is difficult to provide, but with DWR 2.0 and General Interface it's quite simple.

In the call-center example, there are basically four things agents can do. They can pick up a call and begin working with a customer, book an event, call for a supervisor, or cancel an interaction.

An agent will generally pick the person who's been waiting the longest (at the top of the queue.) and get the form on the right where we can fill in contact information. The example simulates a part of a CRM system which attempts to remember contact details and give agents a heads-up on who is calling using caller-line identification. This form was created using TIBCO General Interface’s components.

For the purposes of this example, imagine that Fred is calling to sign up for an event, so an agent books it for him and gets payment information and so on. While this is happening, a wrench has appeared next to Fred's number for other agents, indicating that somebody is already handling this call. If the other agent tries to take the call, he'll be told it isn't available. That's the kind of real-time web interactivity you get with DWR 2.0’s Reverse Ajax capabilities. Agents can collaborate or call in a supervisor to provide a better customer experience.

You can both monitor your own work and the call center as a whole. This is enabled by an architecture that allows fast updates.

How it Works

From the server's perspective, there are basically two classes that are important: a public class and a service class. In the code on the server, Call is a plain JavaBean that contains all the information that's important for the call: when it started, notes associated with the call, whether a supervisor was involved, whether the call is being handled, and so forth. These are the bits of information that are stored on the server against each caller.

On the Server

 public class Call{ private Date callStarted = new Date(); private String notes = ""; private boolean supervisorAlert = false; private boolean isHandled = false; private String name; private String address; private String phoneNumber; private int id; // Getters and setters }

There's also a service class called call center and four methods -- beginHandling, alertSupervisor, cancelHandling, and completeHandling – that correspond to the things agents can do. (We won't go into implementation details, but this is very simple Java code. The entire implementation is well under 200 lines.) These are the primary methods exported by the server interface.

 /** Mark this call as being handled */ public String beginHandling(int id) ... /** Put the call back in queue with the supervisorAlert bit set */ public String alertSupervisor(int id, Call call) ... /** Mark this call as being handled */ public String cancelHandling(int id) ... /** Alert the back end systems and take this call off the queue */ public String completeHandling(int id, Call newCall) ...

We've also got an update function, which is the essence of this demo. When something on the server changes, this is what we do:

  • We gather a list of the script sessions or branches that are looking at this page. (We're using the ScriptProxy system, so we can talk to any bit of JavaScript in those browsers.)
  • We add a function call to update the list of callers, we parse the code list of callers, and publish that out to all the agents.
 private void update() { Collection<ScriptSession> sessions = serverContext.getScriptSessionsByPage("tc.html"); ScriptProxy proxy = new ScriptProxy(sessions); proxy.addFunctionCall("updateCallers", calls); }

On the Client

Each Ajax client application running has a updateCallers() function that receives the information pushed from DWR to the browser. That code on the browser side takes that list of objects and throws it into the call queue, an instance of the General Interface “matrix” type data grid named “listCallers”.

 function updateCallers(callers, now) { // Some tweaks so GI redraws everything without needing further explanation // We copy the id to jsxid, and ensure that the fields for the checkboxes are // 0 or 1 rather than true or false. We also check that any handled caller // is still around and has not hung up var caller; var handlingIdFound = false; for (var i = 0; i < callers.length; i++) { caller = callers[i]; caller.timePlain = now - caller.callStarted; caller.time = 10000 * Math.round(caller.timePlain / 10000); caller.jsxid = caller.id; caller.handled = caller.handled ? "JSXAPPS/ticketcenter/images/configure.png" : ""; caller.supervisorAlert = caller.supervisorAlert ? "JSXAPPS/ticketcenter/images/irkickflash.png" : ""; callerCache[caller.id] = caller; if (!caller.name) caller.name = "?"; if (caller.id == handlingId) { caller.handled = "JSXAPPS/ticketcenter/images/next.png"; handlingIdFound = true; } } // Convert the data into a CDF document and post to GI var cdf = dwr.gi.toCdfDocument(callers, "jsxroot"); ticketcenter.getCache().setDocument("callers", cdf); ticketcenter.getJSXByName('listCallers').repaint(); // Work out what to do if the caller we're working on has hung up if (!handlingIdFound && handlingId != null) { alert("It appears that this caller has hung up. Please select another."); deselect(); } }

Other aspects of the client-side code get the data for an individual caller in the queue and display that in the form.

This simple implementation enables DWR to keep all the General Interface Ajax applications running in the call center up-to-date with the current state of the application.

You can download the full server and client-side code starting here.

Related Resources