Developing Rich Internet Applications for WebSphere Application Server Community Edition

Using Dojo, Tomcat Comet support and Direct Web Remoting (DWR) to enable your application for Ajax

This article briefly lists some of the Ajax functionality and frameworks included in WebSphere® Community Edition 2.1, and walks you through developing a sample Web 2.0 application that uses these frameworks to make development easier.

Manu T. George (mageorge@in.ibm.com), Staff Software Engineer, IBM

author photoManu T. George is a Staff Software Engineer at IBM India Software Labs in Bangalore, India. He is a committer on Apache Geronimo and Apache OpenEJB projects, and is a part of the IBM WebSphere Application Server Community Edition Level 3 Support Team. He received his Bachelor of Technology in Applied Electronics from the College of Engineering Trivandrum in the year 2001.


developerWorks Contributing author
        level

Vamsavardhana Reddy Chillakuru (vamsic007@in.ibm.com), Advisory Software Engineer, WSO2 Inc

Author photoVamsavardhana Reddy Chillakuru (a.k.a Vamsi) is an Advisory Software Engineer at IBM India Software Labs in Bangalore, India. He is a committer on Apache Geronimo and Apache Tuscany projects, a member of the Apache Geronimo Project Management Committee, and is part of the IBM WebSphere Application Server Community Edition Level 3 Support Team. He received his Bachelor of Statistics (Hons.) and Master of Statistics degrees from the Indian Statistical Institute, Kolkata, India in the years 1994 and 1996, respectively.


developerWorks Contributing author
        level

24 September 2008

Also available in Chinese Vietnamese

Introduction

Ajax (Asynchronous JavaScript and XML) is a term coined to represent a set of technologies that enable creation of Rich Internet Applications. Using these technologies you can create Web applications that have responsive and rich user interfaces similar to desktop applications. They enable your Web applications to retrieve data asynchronously in the background without affecting the page being displayed, as well as just requesting the data and not the entire HTML page. You use the XmlHttpRequest or equivalent object that modern browsers provide for this asynchronous background communication.

IBM WebSphere Application Server Community Edition v2.1.x (hereafter referred to as Community Edition) comes packaged with a few popular frameworks for developing and hosting Ajax-enabled applications. In this article we investigate how to configure and use three of these frameworks, namely:

  • The Dojo Toolkit
  • Direct Web Remoting (DWR)
  • Tomcat Comet Support

We also develop a simple Web application that uses these technologies to provide an improved user experience. To follow along you need WebSphere Application Server Community Edition v2.1.x.


The Dojo Toolkit

Dojo is an open-source DHTML toolkit written in JavaScript. Dojo solves some of the problems that are associated with using JavaScript, including handling browser-specific behaviors. These behaviors have been abstracted from the user via the Dojo toolkit.

It also provides a set of configurable widgets which can be used to rapidly develop dynamic and browser independent Web pages. The Dojo toolkit comprises of a set of JavaScript APIs, and is included in WebSphere Application Server Community Edition v 2.1. It can be accessed via the context root /dojo. Dojo also provides APIs for making asynchronous XMLHttpRequest calls for fetching data from the server without refreshing the page.


Direct Web Remoting (DWR)

DWR allows a developer to expose server-side Java™ objects to the client via JavaScript proxies. It creates proxies for all the exposed Java™ objects that can then be invoked from the client. In turn DWR invokes the corresponding Java methods in the server and returns the response back to the invoker script in JSON (JavaScript Object Notation) format.

You can configure DWR to call the server synchronously or asynchronously in the background. Therefore, it can avoid the page refreshes associated with the normal HTTP request-response model.

DWR also provides Reverse Ajax, which is a mechanism to send information asynchronously from the server to a browser. This lets the server publish responses to clients at periodic intervals, in the following ways:

  • Polling: Client keeps polling the server at regular intervals.
  • Comet: When the client makes a request, the server keeps a handle to the response and into which it keeps writing. In this case the client need not poll.
  • Piggy back: Responses are clubbed and sent with the response to the next request that the client makes.

Tomcat Comet Support

Comet is a term coined by Alex Russel of the Dojo Foundation to describe an event-driven, server-push mechanism over the HTTP protocol. In normal HTTP communication, the client always initiates the data transfer by opening a connection to the server and sending a request. The server processes this request, sends a response on the same connection, and then closes the connection. Thus the connection is relatively short lived. In Comet, the server keeps that connection open and keeps on writing data whenever a relevant event occurs. Tomcat provides this support via the NIO and APR connectors. The BIO connector does not provide Comet support.


Creating an NIO Connector

By default Community Edition does not come with a pre-installed NIO connector. We need to create and start a new NIO connector from the administrative console before we can run applications that use the Comet protocol. We will first delete the BIO connector that runs on port 8080, and then create a new NIO connector to run on the same port.

Follow these steps to create the NIO connector:

  1. Start Community Edition, and open https://localhost:8443/console/ in your browser.
  2. Enter system for the user name and manager for the password. Click Login, which brings up the Welcome page in the administrative console.
  3. In the left navigation pane, click the Web Server link to bring up the Web Server Manager page, as Figure 1 shows:
    Figure 1. Web Server Manager
    Screen shot of the Web Server Manager, showing the list of network listeners
  4. Delete the connector named TomcatWebConnector.

  5. In the Add New section, click the Tomcat NIO Connector link, which brings up the screen shown in Figure 2.
    Figure 2. Adding a new Tomcat NIO connector
    Screen shot of window for adding a new NIO connector
  6. Fill in the uniqueName field with the value TomcatNIOConnector and click Save, which creates and starts the NIO connector. Your window should now look like Figure 3.
    Figure 3. TomcatNIOConnector added to network listeners
    Screen shot showing the new NIO connector in the list of network listeners

Developing the Web Application

We are now all set to develop the Web application. It will be a stock ticker application that updates every 5 seconds with the latest quotes. We will use DWR's Reverse Ajax functionality over the Comet implementation provided by Tomcat. To display the quotes we will use a ready-made Dojo widget from the Dojo toolkit, the dojox.Grid widget. We will also write a servlet that uses the Comet support in Tomcat to print the stock information.

Writing the Comet Servlet

Apache Tomcat provides the org.apache.catalina.CometProcessor interface, which all the servlets that you write can implement to use Comet support. This interface defines a single method, namely public void event(CometEvent event)to implement in our servlet. Listing 1 shows the two main methods of this servlet:

Listing 1: Comet Servlet
	public void init() throws ServletException {
	    super.init();
	    Runnable r = new Runnable() {
	        public void run() {
	            while (!stop) {
                      synchronized (openConnections){
	                List<Stock> stocks = new StockService().getStocks();
		         for (HttpServletResponse response : openConnections) {
		             try {
		               PrintWriter pw = response.getWriter();
		               for (Stock stock : stocks) {
			            pw.println(stock.getSymbol() + ":"
			                + stock.getPrice());
		               }
		               pw.flush();
		             } catch (IOException e) {			
		                e.printStackTrace(System.out);
		             }
                          }
		         }
		         try {
		             Thread.sleep(5000);
		         } catch (InterruptedException e) {
		             e.printStackTrace(System.out);
		         }
		     }
	         }
             };
	     Thread stockThread = new Thread(r);
	     stockThread.setDaemon(true);
	     stockThread.start();
	}

	public void event(CometEvent event) throws IOException, ServletException {
		HttpServletRequest request = event.getHttpServletRequest();
		HttpServletResponse response = event.getHttpServletResponse();

		if (event.getEventType() == CometEvent.EventType.BEGIN) {
                   synchronized (openConnections){
			openConnections.add(response);
                   }
		} else if (event.getEventType() == CometEvent.EventType.ERROR) {
                   synchronized (openConnections){
			openConnections.remove(response);
                   }
		} else if (event.getEventType() == CometEvent.EventType.END) {
                   synchronized (openConnections){
			openConnections.remove(response);
                              }
		} else if (event.getEventType() == CometEvent.EventType.READ) {

		}
          }

In the init method we instantiate a thread that gets the stock quotes and writes it to all the clients that are listening, i.e all the response objects in the openConnections list. The event method is triggered when any of the four events (BEGIN, ERROR, END, READ) occurs. We get the response from the org.apache.catalina.CometEventobject that is passed as a parameter. For the BEGIN event, add the response to the collection. For the ERROR and END events, we remove the response from the list.

The BEGIN event represents the beginning of processing for that connection, so we add the response to the list of responses. The END and ERROR events represent the end of the connection or the occurrence of an error. For more information on what these events mean, see the Apache Tomcat documentation .

Configuring DWR

The application has a hard-coded set of stocks that it displays. The values of the stocks are randomly updated by the com.dev.trade.service.StockQuoteGenerator class. Listing 2 shows the relevant part of the source code for this class.

Listing 2: Class that generates stock prices
package com.dev.trade.service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Collections;
import com.dev.trade.bo.Stock;

public class StockQuoteGenerator {
	private StockService ss = new StockService();
	private List<Stock>  stocks = null;
	private static StockQuoteGenerator generator = new StockQuoteGenerator();
	private Set<QuoteListener>  
                listeners = Collections.synchronizedSet(new HashSet<QuoteListener> ());
	private volatile boolean contextDestroyed = false;

	private StockQuoteGenerator() {
                Runnable r = new Runnable() {
                    public void run() {
                        try {
                            while (!contextDestroyed) {
                                update(generateStockQuotes());
                                Thread.sleep(5000);
                            }
                        }catch (Exception e) {
                            e.printStackTrace(System.out);
                        }
                    }
                };
                Thread t = new Thread(r);
                t.setDaemon(true);
                t.start();
            }

	public static StockQuoteGenerator getInstance() {
		return generator;
	}

	public void setContextDestroyed(boolean cd) {
		this.contextDestroyed = cd;
	}

	public void addListener(QuoteListener listener) {	
	    listeners.add(listener);	
	}

	private void update(List<Stock>  stocks) {
	    for (QuoteListener listener : listeners) {
	        listener.updateStockQuotes(stocks);
	    }
	}

	private List<Stock>  generateStockQuotes() {
		stocks = ss.getStocks();		
		return stocks;
	}

}

In the constructor of StockQuoteGenerator class, which is a singleton class, we are starting a new thread and assigning a Runnable object to that thread. So the run method of this object keeps on executing until the contextDestroyed variable is set to true. This variable is set to true only when the application context is going to be destroyed. For this we will be registering a context listener in the web.xml , namely com.dev.trade.listener.ServletContextListener.

The generateStockQuotes method invokes the getStocks method of StockService to get the generated stock quotes . You use the addListener method to add listeners. These listeners implement the com.dev.trade.service.QuoteListener interface, and thus the method updateStockQuotes.

The listeners are all instances of com.dev.trade.service.StockQuoteListenerImpl. This class in its constructor initializes an instance of the org.directwebremoting.WebContext class provided by DWR, and then registers itself with StockQuoteGenerator's list of listeners. Listing 3 shows the updateStockQuotes method of this class.

Listing 3: Code for pushing data to different clients
	public void updateStockQuotes(List<Stock> stocks) {

		ScriptBuffer script = new ScriptBuffer();
		script.appendScript("updateStocks(").appendData(stocks).appendScript(
				");");			
		Collection<ScriptSession> sessions = wctx.getAllScriptSessions();
                              for (ScriptSession session : sessions) {
			if(!session.isInvalidated()){
			    session.addScript(script);
			} else {
				wctx.getAllScriptSessions().remove(session);
			}
		}
	}

This method propagates a JavaScript function call to all the open clients. It calls the function updateStocks and passes the values of stocks. This JavaScript method actually updates the model of the Dojo grid widget that we will be displaying and then calls its update method so that the grid displays with the new data. DWR creates script sessions for each open stock ticker page.

Note that for this to work the StockQuoteListenerImpl class's constructor should be called by the same thread DWR code is executing in. For this we will need to configure DWR to instantiate the objects of StockQuoteListenerImpl. For this configuration we create a dwr.xml file that we place in the WEB-INF directory of the application. Listing 4 shows this file.

Listing 4: dwr.xml
<dwr>
  <allow>
    <create creator="new" javascript="Ticker" scope="application">
      <param name="class" value="com.dev.trade.service.StockQuoteListenerImpl"/>
    </create>
    <create creator="new" javascript="StockService" scope="application">
      <param name="class" value="com.dev.trade.service.StockService"/>
    </create>    
    <convert converter="bean" match="com.dev.trade.bo.Stock"/>    
  </allow>
</dwr>

Note that the StockQuoteListenerImpl has been exposed in the dwr.xml via a creator. We set the scope of this object to application, as only one instance is needed per application. The other class that we expose to JavaScript is the com.dev.trade.service.StockService, which provides us with a method getStocks to get the list of stocks. We also need to add the DWRServlet to the web.xml of our application so that DWR is initialized and exposes proxies of the above configured objects to JavaScript. Listing 5 shows the web.xmlfile.

Listing 5: web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" version="2.4" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>brokerage</display-name>
<listener>
    <listener-class>
     com.dev.trade.listener.DwrContextListener
    </listener-class>
</listener>
<servlet>
  <display-name>DWR Servlet</display-name>
  <servlet-name>dwr-invoker</servlet-name>
  <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
  <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
  </init-param>
   <init-param>
    <param-name>activeReverseAjaxEnabled</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>initApplicationScopeCreatorsAtStartup</param-name>
    <param-value>true</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>  
</servlet>

<servlet-mapping>
  <servlet-name>dwr-invoker</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>


<welcome-file-list>
	<welcome-file>/stocks.html</welcome-file>
</welcome-file-list>

</web-app>

The activeReverseAjaxEnabled property is set to true so that DWR uses Comet for enabling Reverse Ajax. If it is set to false, then DWR will be using the piggy-back option.

The initApplicationScopeCreatorsAtStartup is for initializing all the application scoped objects at startup of DWR.

Now at the client side we need to import two script files: the DWR client-side engine and the JavaScript proxy that DWR creates for the exposed object com.dev.trade.service.StockService. Listing 6 shows the code for this import.

Listing 6: Accessing on the client
<script type="text/javascript" src="dwr/engine.js"></script>
<script type='text/javascript'
	src='/brokerage/dwr/interface/StockService.js'></script>

We also need to configure the DWR client side engine to use active Reverse Ajax, so we need to call dwr.engine.setActiveReverseAjax(true).

We can invoke the getStocks method of StockService to initially populate the Dojo grid widget that we will be displaying, using StockService.getStocks({callback:getModel,async:false}). The parameter getModel refers to a JavaScript method that will be called with the result of this call passed as a method parameter.

The method shown in listing 7 returns the model for the dojo grid that we will be displaying.

Listing 7: Web Remoting
	function getModel(stocks) {		
		var data = new Array();
		for ( var i = 0; i < stocks.length; i++) {
			var subData = new Array();
			subData[0] = stocks[i].symbol;
			subData[1] = stocks[i].companyName
			subData[2] = stocks[i].price
			subData[3] = stocks[i].eps
			data[i] = subData;
		}
		updatedModel = new dojox.grid.data.Table(null, data);
		return updatedModel;
	}

Configuring Dojo

Community Edition comes bundled with the Dojo JavaScript library. It uses Dojo for some of the administrative console portlets. Dojo is bundled as a Web application that is available at the context root /dojo. To use Dojo in our HTML client page, we need to import the dojo.js script, using the scripts shown in listing 8.

Listing 8: Import dojo.js
<script type="text/javascript" src="/dojo/dojo/dojo.js"
	djConfig="isDebug:false, parseOnLoad: true"></script>

When the Dojo script is imported we can import the remaining dependencies via dojo.require calls. The dojox.Grid widget that we are going to create needs a model and a structure. The model represents the data to be displayed, and the structure represents how we are going to display it. The model is actually an array of arrays. Listing 9 shows the structure definition:

Listing 9: Model definition
var view1 = {
		noscroll :false,
		cells : [ [ {
			name :'Symbol',
			width :'auto'
		}, {
			name :'Company Name',
			width :'auto'
		}, {
			name :'Price',
			width :'auto'
		}, {
			name :'Earnings Per Share',
			width :'auto'
		} ] ]
	};

var gridLayout = [ {
		type :'dojox.GridRowView',
		width :'20px'
	}, view1 ];

<div class="tundra" id="stock_grid" dojoType="dojox.Grid" model="updatedModel"
	structure="gridLayout" elasticView="1" defaultHeight="37em"></div>

In this listing we see that the div with id="stock_grid" encapsulates the actual grid. The structure is defined by the gridLayout object, which is an array that contains view1.

The view1 variable is also an array, containing the cells property that provides the details of how all the columns should display in the grid.


Build and deploy the Web Application

To build the application you will need Apache Maven2. Install Maven2 and follow these steps to build the application:

  1. Extract the ticker.zip archive, which creates a directory called ticker.
  2. Open the command shell and change the current directory to ticker.
  3. Run mvn install. This will build the application and place the output WAR file in target directory under ticker. The name of the WAR file is ticker.war.
  4. Open the Community Edition console in your browser and navigate to the Deploy New portlet.
  5. In the Archive text box, enter the path to the generated ticker.war and click Install.

Testing the Web Application

  1. Open http://localhost:8080/ticker in your Web browser to bring up the stock ticker, as shown in Figure 4.
    Figure 4. Stock ticker running in the browser
    Screen shot showing the running application
  2. Open http://localhost:8080/ticker/comet in your Web browser to see the output of the Comet servlet, as shown in Figure 5.
    Figure 5. Comet servlet output
    Screen shot showing the output of the Comet servlet in the browser

Conclusion

We created and configured the NIO connector of the Tomcat instance that is embedded in Community Edition via the administrative console. We also developed a Java EE Web application that uses DWR's Reverse Ajax functionality to push data from the server to the browser over the Comet support built into the NIO connector. Finally, we saw how to implement Tomcat's CometProcessor interface to write a servlet that exposes its Comet functionality.

Acknowledgements

Thanks to Phani Madgula and Ashish Jain for reviewing this material.


Download

DescriptionNameSize
Sample source codeticker.zip8KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Open source
ArticleID=340076
ArticleTitle=Developing Rich Internet Applications for WebSphere Application Server Community Edition
publish-date=09242008