A common goal of portal developers is to be able to build portlets that render independently of other content on the portal desktop. That way, the portlets are in effect mini-Web applications with which users can interact without having to refresh the entire portal desktop. Asynchronous JavaScript and XML (Ajax), a newly emerging technology, can help you achieve this goal.
This paper introduces the Ajax technology and demonstrates its applicability to portlets with a sample portlet that renders its content through Ajax.
Contents
Introducing Ajax
The acronym Ajax is relatively new, but the behind-the-scenes technologies are not. Recently, Ajax technologies have gained attention, thanks to popular implementations such as Gmail, Google Maps, and Google Suggest. The key enabler of Ajax is the XMLHttpRequest object,
first implemented by Microsoft as an ActiveX object in Internet Explorer 5 for Windows. A compatible native version was integrated into Mozilla 1.0, Netscape 7, and Apple's Safari 1.2 browsers.
Using the XMLHttpRequest Object
When used within a JavaScript on an HTML page, the XMLHttpRequest object can make asynchronous calls to the same HTTP server for other content by performing these two steps:
- Calling the
open() and send() methods on the object
- Providing handling for the
onreadystatechange event the request object will return when content becomes available
Let's take a look at a JavaScript function that goes through those steps.
var portletReq;
function asynchGet(updateURL){
if (window.XMLHttpRequest) {
portletReq = new XMLHttpRequest();
} else if (window.ActiveXObject) {
portletReq = new ActiveXObject("Microsoft.XMLHTTP");
}
portletReq.onreadystatechange = processReqChange;
portletReq.open("GET", updateURL, true);
portletReq.send(null);
}
|
In this function, a condition first checks which object we are creating for the asynchronous call. When the onreadystatechange event is triggered, the function calls processReqChange (in this example) and then calls the open() method with the following parameters:
- The HTTP method for the request, for example, "
GET"
- The URL for the connection, for example,
updateURL
- The asynchronous boolean flag, an important flag for
XMLHttpRequest, set to true (the default)
Now, because the flag in open() is set to true, instead of waiting for a response, processing of the script and the HTML page that contains it continues after XMLHttpRequest has called the send() method. Subsequently, when the onreadystatechange event is triggered, XMLHttpRequest can handle the response asynchronously.
Lastly, XMLHttpRequest calls the send() method, passing null content, that is, the request has no postable string.
In our application, the selectInvoice() function calls asynchGet() when a value in a select list is chosen, as follows:
function selectInvoice(evt) {
evt = (evt) ? evt : ((window.event) ? window.event : null);
if (evt) {
var select = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
if (select && select.options.length > 1) {
// When invoice is selected, call underlying web-app to return
// content.
// For this sample, the content is hard-coded HTML, but in a real
// deployment a call to servlet or JSP in the web-app can be
// used to return this content instead.
asynchGet("/AjaxPortlet/InvoiceServlet?invoice=" + select.value);
}
}
}
|
When a state change occurs in the request, the onreadystatechange event is triggered and the processReqChange function is called. Once readyState equals 4 (for example, "complete,") and the application has verified that the response status is valid (for example, 200), we can process the response, as follows:
function processReqChange() {
if (portletReq.readyState == 4) {
if (portletReq.status == 200) {
// process response
displayInvoice();
}
}
}
|
In our application, displayInvoice() processes the response and displays the requested content, like this:
function displayInvoice() {
// substitute new invoice HTML content into "portletcontent" div tag
var div = document.getElementById("portletcontent");
div.innerHTML = "";
div.innerHTML = portletReq.responseText;
}
|
The application calls getElementById on the document to get the div tag within the page that is labeled with the ID portletcontent. Here is the HTML snippet from the bottom of the page:
<div id="portletcontent"></div>
|
The innerHTML element of the div tag is populated with the responseText content from the request. At this point, the user sees the new content on the page.
Applying Ajax to Portlets
As demonstrated in the preceding sections, Ajax technologies are primarily JavaScript-related and involve manipulations with the Document Object Model (DOM) of the HTML page. How to apply this capability to portlets? Basically, you can place the above JavaScript examples in a portlet so that the portlet can asynchronously fetch content from its associated Web application.
In our example, AjaxPortlet uses a JavaServer Pages (JSP) page (view.jsp) to display its content when invoking doView() for the portlet. In fact, view.jsp contains all the JavaScript code outlined above. In addition, it displays a list of invoice numbers from which the user can choose. See the code below.
<%
// hard-coded invoice numbers
String[] invoiceList = {"439089", "439090", "439091", "439092"};
%>
<select onchange="selectInvoice(event)" size="1">
<option>Select an invoice...</option>
<%
for (int i=0; i < invoiceList.length; i++) {
%>
<option value="<%=invoiceList[i]%>"><%=invoiceList[i]%></option>
<%
}
%>
</select>
<div id="portletcontent"></div>
|
A selection triggers the call to selectInvoice(), which then makes an asynchronous request to the associated Web application for AjaxPortlet. In our sample, the request is made to a dummy servlet (InvoiceServlet) that simply returns hard-coded HTML content for an invoice. In a real deployment, the servlet or JSP page would be more robust and would likely retrieve the data from another back-end store. When the Web application responds, readyState is set to 4 and processReqChange invokes displayInvoice(), which then replaces the div tag's innerHTML with the content returned from the request.
Installing the AjaxPortlet Sample
To install the AjaxPortlet sample on Sun Java System Portal Server 6 (henceforth, Portal Server), follow these steps:
- Download the sample.
- Unzip the files.
- Deploy the portlet Web application. Run this
pdeploy command line to deploy the Web archive (WAR) file to Portal Server (all on one line):
% pdeploy deploy -u uid -w password
{-g|-d
dn} -p webcontainerpassword
WAR_filename
For example:
% /opt/SUNWps/bin/pdeploy deploy -u "uid=amAdmin,ou=People,dc=sesta,dc=com" -w admin -p sunjava -d "dc=sesta,dc=com" /tmp/AjaxPortlet/AjaxPortlet.war
For details on pdeploy, see Chapter 11 of the Sun Java System Portal Server 6 2005Q1 Technical Reference Guide.
- Run
dpadmin to update the display profile so as to create a channel for AjaxInvoiceViewerPortlet and add that channel to PortletSamplesTabPanelContainer:
% dpadmin modify -u uid -w password {-g|-d dn)} -m filename
For example:
% /opt/SUNWps/bin/dpadmin modify
-u "uid=amAdmin,ou=People,dc=sesta,dc=com" -w admin -m
-d "dc=sesta,dc=com" /tmp/AjaxPortlet/AjaxPortlet-dp.xml
For details on dpadmin, see Chapter 12 of the Sun Java System Portal Server 6 2005Q1 Technical Reference Guide.
Building the Sample
The AjaxPortlet sample includes a build.xml file for building the Web application with Apache Ant.
You can also open the sample source in either of these ways:
- From Sun Java Studio Enterprise 7 2004Q4, choose Local Directory from the New Filesystem menu.
- From Netbeans 4.1, choose Web Project with Existing Ant Script from the New Project menu.
Simply select the build.xml file and the appropriate targets.
Conclusion
The tricks to asynchronously rendering portal content are more related to JavaScript and XMLHttpRequest than to portlet technology. However, portlet developers can use those technologies in tandem to provide a valuable user experience.
References
About the Authors
Greg Ziebold, a staff engineer at Sun, has been a member of the Sun Java System Portal Server development team since July 2000. He's working on resolving issues that relate to portlet development.
Jai Suri is a member of the Sun Java System Portal Server development team. Currently, he's focusing on integrating groupware into the portal and on building tools to simplify portal development.
Marina Sum is a staff writer for Sun Developer Network. She has been writing for Sun for 15 years, mostly in the technical arena.
|
|