Ajax wizardry gets around the limitations of Portlet 1.0 The Portlet 1.0 specification has serious limitations when it comes to inter-portlet communication, but not everyone is ready to jump to Portlet 2.0. In this addition to the JavaWorld Portlet Packet, author Sandeep Tol shows you how to use the Dojo toolkit to enhance communication between portlets based on Portlet 1.0. Level: IntermediateIn the world of Java portlet programming, inter-portlet communication, also called portlet-to-portlet communication, allows multiple portlets to respond to events triggered by other portlets. For example, a product setup portlet and a product configuration portlet might communicate with each other to update data on a third portlet.Many portal platforms allow developers to code event handlers or JMS requests to achieve this. In addition, asynchronous content rendering for specific portlets is possible using portal server features. However, these techniques require coding and configuring on the server, and are tightly coupled to specific vendor implementations. They also require a server request for very event. Get started with Portlet 2.0The portlets described in this article have been built to the Portlet 1.0 specification. Portlet 2.0 standardizes communication among portlets, making some of the workarounds described here unnecessary. Whether you stick with Portlet 1.0 or switch to the newer spec now depends on your needs; factors include portability, compatibility with previous versions, ease of use, and performance. See these articles in the JavaWorld Portlet Packet to learn more about Portlet 2.0:“A quickstart guide to Portlet 2.0”“Inter-portlet communication in Portlet 2.0In contrast, with Ajax you can dynamically update the data displayed by a portlet without submitting an action request or refreshing the page. You can also enable client-side inter-portlet communication without relying on different server-side IPC implementations. By using the open source Dojo JavaScript toolkit, you can simplify coding, reduce lines of code, and make scripts easily to understand.This article will help you build portlets that can dynamically update the data displayed without submitting an action request or refreshing the page, and also enable client-side inter-portlet communication without relying on different server-side IPC implementations. This article describes how to use Ajax for client-side inter-portlet communication using the Dojo JavaScript toolkit. You’ll also learn how to render the data asynchronously on client side using Dojo.This article is intended for developers who are beginning to use Ajax with Java portlets. To get the most out of this article, you should have a good understanding of JavaScript programming, the Dojo toolkit, a Java portal server, and portlet development. About DojoDojo is an open source JavaScript library that offers numerous scripts, widgets, and the like for dynamic Web development. Most importantly, Dojo provides an wrapper for the XMLHttpRequest object. The sample discussed in this article uses the dojo.io.bind() function call to make an asynchronous call to dynamically update a portlet, and uses the dojo.event.topic.publish() and dojo.event.topic.subscribe() functions for inter-portlet communication.Dojo event handlingDojo includes an event communication system that allows you to handle application events. This article uses the event publish/subscribe system, known as topics, for communication between portlets.There are three functions that you need to understand to use Dojo’s topic system: dojo.publish(), dojo.subscribe(), and dojo.unsubscribe(). dojo.publish() calls any functions that are connected to a topic via dojo.subscribe(), passing to those subscribed functions arguments that are published. As you might expect, dojo.unsubscribe() will cause a previously subscribed function to no longer be called. Here’s the syntax for these functions:dojo.publish(Topic Name [string], Arguments to Pass to Subscribed Function [array])handle = dojo.subscribe (Topic Name [string], Context of Linked Method [string or null], Linked Method [string or function])dojo.unsubscribe (Handle [handle object])Dojo bind()Most of the magic of the dojo.io package is exposed through the bind() method. dojo.io.bind() is a generic asynchronous request API that wraps multiple transport layers (queues of iframes, XMLHTTP, mod_pubsub, LivePage, and more). Dojo will pick the best available transport mechanism for a request at runtime. To make a request that returns raw text from a URL, you would call bind() as illustrated in Listing 1.Listing 1. Calling bind()dojo.io.bind ({ url: "http://localhost:9080/Ipcdemo/myportlet", handle: function(type, data, evt) { /*do something w/ the data */ }, mimetype: "text/plain" }); That’s all the code you need to include! You provide the URL from which you want to get the data, and a callback function that you’d like to have called when you actually get the data back. You can also handle potential exceptions as shown in Listing 2. Listing 2. Exception handling in bind()dojo.io.bind({ url: "http://localhost:9080/Ipcdemo/myportlet", handle: function(type, data, evt) { if (type =="error") { /*print the error message */ }, /*do something w/ the error*/ }, mimetype: "text/plain" }); Building an inter-portlet communication exampleFigure 1 gives an overview of the example you’ll consider in this article. You can download the package that contains the complete code for this application, and follow along with it as you walk through the discussion that follows.Figure 1. The example application in action (click to enlarge)The Product Setup portlet displays a drop-down menu from which you can choose different configurations. Once the user selects a configuration, an event will be fired and a message sent to the listener of the application’s other portlet, which then displays the selected configuration. In the next few sections, you’ll walk through the code that performs this task.Publishing an eventBecause Dojo is a JavaScript API, you first must load dojo.js, as in Listing 3. Listing 3. Loading dojo.js<script type="text/JavaScript" src="<%= renderRequest.getContextPath() %>/dojo/dojo.js"> </script> For this code to work, you’ll need to ensure that the Dojo files are available in the server context path.The JavaScript in the following listings is taken from prodsetup.jsp, which will fire an event and notify listeners (subscribers) when the user presses the Publish button. How does this work? First, in Listing 4, you must import the required libraries for handling events.Listing 4. Importing event-handling libraries// Load Dojo's code relating to widget managing functions dojo.require("dojo.widget.*"); // Load Dojo's code relating to the Button widget dojo.require("dojo.widget.Button"); // Load Dojo's event handling functionality dojo.require("dojo.event.*"); dojo.require("dojo.event.topic.*"); Next, you must register the function that should be called on page load, as shown in Listing 5. The optional JSP tag <portlet: namespace> is replaced by an alphanumeric portlet namespace string during runtime to ensure that the JavaScript function name (the namespace plus the method name) is unique to the current portlet. If you don’t want to use this tag, give any name that can be uniquely identified, such as myportlet_initialize. Listing 5. Registering the function to be called on page load// Register our init function dojo.addOnLoad( <portlet:namespace />_initialize ); // initialize the string .this value will be passed to listeners var configuration = null; With Dojo, you connect functions to one another — creating a link that calls one function when another fires, for instance — using event.connect(). Your linked function is called when the event occurs; at that point, Dojo also passes a normalized event object to a linked function. The syntax for this function is shown in Listing 6.Listing 6. event.connect() syntaxdojo.event.connect (Event object Node, Event [string], Linked Method [string or function]) The _initialize() method, shown in Listing 7, will be called on page load. Now you need to register your events with Dojo to ensure that the required methods are called on event occurrences. In the sample app, the _onButton() method will be called when the user clicks the Publish button. To do this, you first need to get the node of the event-triggering object by calling dojo.widget.byId(id). (If you don’t use widgets, use the document.getElementById(id) or dojo.byid(id) methods.)Next, you need to indicate the event for which you want to call a method — onClick, for example, or OnFocus or OnChange Call the connect() method by passing the publishbutton node object, the event name (onClick, in this case), and the linked method name (_OnButton()). dojo.event.connect() takes a variety of arguments, depending on how you are planning to use it; refer to the dojo.connect API documentation for more details. The _initialize() method, shown in Listing 7, uses the connect() method from Listing 6.Listing 7. _initialize()// Init function for this portlet function <portlet:namespace />_initialize() { var pubButton = dojo.widget.byId("publishButton"); dojo.event.connect (pubButton,'onClick','<portlet:namespace />_onButton'); } Though you can call functions on an event by adding simple JavaScript (button onClick="foo()"..), there are many advantages of using the Dojo event API; for instance, you can call many functions on the same event; map events to any property, object, or element; and pass a set of predefined event attributes.In the _onButton() method, shown in Listing 8, you set the message value to the event and broadcast (publish) the event to subscribers. If you want to create a new event object and publish, you would use var myevent = {message: configuration}; this example uses the event object received from dojo.connect() and sets the message value to that object. Dojo offers support for anonymous publication and subscription of objects, via dojo.publish() and dojo.subcribe(). These methods allow a function to broadcast objects to any other function that has subscribed. This is Dojo’s topic system, and it makes it very easy to allow separate components to communicate without explicit knowledge of one another’s internals. The syntax for publish() is dojo.event.topic.publish(topic name, data object). The data object could be any string, array object, event or data. In Listing 8, you can see that the topic name for the sample application is "/ipcdemo/setupchange". Listing 8. _onButton// Button onClick handler function <portlet:namespace />_onButton(event) { //set the value to event message event.message = configuration; dojo.event.topic.publish("/ipcdemo/setupchange", event); } The code in Listing 9 will use the value selected from the application’s drop-down menu to set the variable named configuration. This function will be called when the drop-down menu’s onChange event is fired.Listing 9. Using values from a drop-down menu<select onChange="loadConfig(this.value);" function <portlet:namespace/>_loadConfig(config) { /* If an invoice was selected */ configuration = config; } Subscribing to the eventIn prodconfig.jsp, the application subscribes to the topic and handles the event. To begin, you must import the required APIs, as you did in Listing 4 above, and subscribe to the topic on page load. This is illustrated in Listing 10.Listing 10. Importing required APIs and subscribing to the topic<script type="text/javascript"> // Load Dojo's event handling functionality dojo.require("dojo.event.*"); dojo.require("dojo.event.topic.*"); dojo.require("dojo.lfx.html"); dojo.require("dojo.gfx.color"); dojo.require("dojo.io.*"); dojo.addOnLoad( <portlet:namespace/>_initialize ); //store the event message in this variable var message = null; The subscribe() method takes the context and name of the callback method; it notifies Dojo that whenever this event is published, this callback method should be called. This requires a little bit of knowledge of object-oriented JavaScript programming. Assume that function is the class object. You need to call the load() function, which is inside _listener(), using the _myListener object. By doing so, you will ensure that the topic subscription happens when the page is loaded. This is illustrated in Listing 11. Listing 11. Ensuring topic subscription// Init function for this portlet's Dojo components function <portlet:namespace/>_initialize() { <portlet:namespace/>_myListener = new <portlet:namespace/>_listener(); <portlet:namespace/>_myListener.load(); } In the _listener() method in Listing 12, handleEvent() is a callback method and load() is the method in which you subscribe to the topic. the this keyword is used to pass the context of the _listener() function. The syntax for subscribe() is dojo.event.topic.subscribe (topic name, context of a linked method, function name).Dojo calls the handleEvent() method on publish, and the event object is passed as an argument. In Listing 12, you dynamically insert the event.message value in the JSP to display it.Listing 12. _listener()function <portlet:namespace/>_listener() { // Event handler - override for each portlet this.handleEvent = function (args) { var countElement = document.getElementById("event_message"); countElement.innerHTML = args.message; //invoke the server request to refill the data message = args.message; After the dynamic insert, you next need to call another method, _loadconfig(), shown in Listing 13. This method will use Dojo to asynchronously render the page inside prodconfig.jsp. Listing 13. _loadconfig()<portlet:namespace/>_loadConfig(message); //optional graphics below } this.load = function() { dojo.event.topic.subscribe("/ipcdemo/setupchange", this, this.handleEvent); } } </script> Dynamically updating the portlet asynchronouslydojo.io.bind() is a generic asynchronous request API that wraps multiple transport layers (queues of iframes, XMLHTTP, mod_pubsub, LivePage, and so on). Dojo attempts to pick the best available transport for the request at hand; in the sample code, only XMLHTTP will ever be chosen, as no other transports are rolled in. You would call bind() as shown in Listing 14.Listing 14. Calling bind()dojo.io.bind({ url: "http://foo.bar.com/sampleData.txt", handle : function(type, data, evt){ /*do something w/ the data */}, mimetype: "text/plain" }); In Listing 14, url is the asynchronous request you are making and handle is the callback function that you’d like to have called when you actually do get the data. That’s all you need to do. There’s also an error handling mechanism if something goes wrong with the request.In the _loadConfig() method shown in Listing 15, there’s a new variable, bindArgs, to build an argument for passing to the bind() method. Listing 15. _loadConfig()function <portlet:namespace/>_loadConfig(config) { /* If an config was selected */ if (config != null && config != "null") { /* Put selectd config value on query string */ var querystring = new Array(); querystring['<%= Constants.config_key %>'] = config; /* Set up bind arguments: */ /* url: url for request */ /* method: http method */ /* content: key/value mapping of parameters sent with request */ /* handle: function to run when there is a response */ /* mimetype:mimetype of response */ var bindArgs = { url: "<%=response.encodeURL(request.getContextPath() + "/com/ibm/ipcdemo/dyndata/begin.do")%>", method: "POST", content: querystring, handle: function(type, data, evt) { if (type == "error") { /* Swap returned error message */ data = "<p style='color:red'>" + data.message + ""; } /* Swap returned data into div tag */ var div = document.getElementById("dyn_content"); div.innerHTML = ""; div.innerHTML = data; //<portlet:namespace/>_refresh(); }, mimetype: "text/html" }; dojo.io.bind(bindArgs); } }; In Listing 15, _loadConfig () checks to see if a value has been passed to the function. You load the config parameter (here, a constant string value) into a JavaScript array, which is passed to dojo.io.bind(). You then create the JavaScript object bindArgs with the following five parameters:url — The URL of the asynchronous request you are making.method — The HTTP method for the request. The default is GET.content — The request parameters to send with the request. They are required for POST requests only. The sample code will pass the querystring that is built in Listing 15.handle — The method invoked to handle the response and error.mimetype — The Multipurpose Internet Mail Extensions (MIME) type of the response.You pass the bindArgs object to dojo.io.bind(). The handle method specifies how to handle the response. If (type == "error"), set the data value to the error message; otherwise, directly insert the data returned from the response inside the portlet’s innerHTML property.To optimize performance, you should load Dojo.js in a JSP that is always available on the portlet. The other JSPs can thus simply make a Dojo call without loading Dojo.js again. Loading the same JavaScript multiple times in portlets weighs down the client and degrades performance. The code in Listing 16 will help to load the Dojo library if necessary. Listing 16. Loading the Dojo library<script type="text/JavaScript"> var path = "<%= request.getContextPath() %>/dojo/dojo.js"; if(typeof dojo == "undefined") { document.write('<S'); document.write('CRIPT type="text/JavaScript" src="'); document.write(path); document.write('" ></S'); document.write('CRIPT>'); } </script> Sample portlets in actionTo deploy the sample application, you can import SampleIPCPortlet.zip into your development environment, then either export it as a WAR file and deploy it on a portal of choice, or run the portlets in an integrated test environment. Navigate to the IPC_Demo_Portal.portal, run it on the server, and open the test app in your browser. You’ll see two portlets: the Product Setup portlet, and the Product Config portlet.Click Publish Configuration in the Product Setup portlet. You should see the screen in Figure 2; the value you select from the drop-down menu will appear in the Product Config portlet.Figure 2. Portlets after clicking Publish (click to enlarge)After you click Refresh you’ll see the screen in Figure 3.Figure 3. Portlets after clicking Refresh (click to enlarge)Each time you click the button in the Product Setup portlet, a message is communicated to the Product Config portlet. Apart from displaying the received messages, this portlet also makes an asynchronous call to the DynamicPortlet to retrieve the data. The sample code was built and tested on WebLogic Portal. However, it was written to the Portlet 1.0 standard (JSR-168), and thus can be easily ported to any compliant portal platform. You may need to make some changes in the path of Dojo.js, depending on where the library is located.In conclusionUsing Ajax in portlets can improve the user experience, with pages that refresh and content that’s ready to read more quickly. Users need not wait for separate applications to refresh before interacting with the complete page. Ajax-based applications will see a decrease in network bandwidth, as content is loaded on demand and HTML is produced locally, boosting the overall performance and speed of the Web applications. Ajax is based on open standards and hence is cross-browser compatible. You can also call Web services using Ajax XML messaging. Dojo further simplifies the Ajax interface by providing rich interfaces.Ajax currently has a few problems — trouble submitting HTML forms in portlets, for instance — but such issues should be overcome in the near future. Hopefully, the very simple sample here will help you get started working with Ajax in your own portal projects.Interested in the new Portlet spec? See “A quickstart guide to Portlet 2.0.”Sandeep Tol is an architect at Wipro Technologies in India. He has more than nine years of IT experience in designing and developing portals, Java EE apps, Web technologies, and SOAs. He’s part of a solution team with areas of expertise in portals, content management, and business integration. His current interests are in Web 2.0 and SOA design and methodologies. He is a certified TOGAF practitioner and a WebSphere commerce developer. Web DevelopmentOpen SourceSoftware DevelopmentJavaDevelopment Approaches