Building an Ajax portlet for WebSphere Portal

In our previous article, Using Ajax with WebSphere Portal, we discussed some issues and design concerns when using Ajax in a portal application. In this tutorial, we will put our newfound knowledge to use and create an Ajax portlet application. To make things interesting, we decided to build a portlet that makes heavy use of Ajax and DHTML. This provides you a glimpse into what's possible with this technology, and to also give you a useful tool to browse your databases. While most of the application is already written, it is up to you to fill in the blanks to get things running.

Karl Bishop (kfbishop@us.ibm.com), Senior Software Engineer, IBM China

Photo: Karl BishopKarl Bishop is a Senior Software Engineer on the IBM Web Enablement and Support team. He works from the wilds of North Carolina. Karl works on various internal and external portal-based applications, as well as being a strong proponent of Linux based solutions.


developerWorks Contributing author
        level

Doug Phillips (dougep@us.ibm.com), Advisory Software Engineer, IBM China

Doug PhillipsDoug Phillips is an Advisory Software Engineer for IBM's elite group of skilled professionals known as IBM's Web Enablement and Support team. He has worked in many organizations within IBM and currently designs and develops both internal and external WebSphere Portal applications using IBM middleware and Linux. Doug is WebSphere and DB2 certified.



16 August 2006

Before you start

In this tutorial, you will write code for Ajax specific calls, review code that manipulates the DHTML of the page, and follow through the process of a complete round trip Ajax call from the browser to the server. The entire application is several hundred lines long, but we'll only ask you to code the important components while the rest will be provided. The steps are:

  • Write browser independent code for creating XMLHttpRequest (XHR) and processing XML document objects.
  • Send Ajax requests to the server and manipulate the returned results.
  • Retrieve and manipulate the servlet context from the portlet configuration for dynamically accessing the Ajax servlet included in the portlet .war file.
  • Enable the JavaScript events to process the actions and display or update data on the JSP page.
  • Deploy the portlet application onto WebSphere® Portal and see the results.

We will also review the code that manipulates the data for this particular application, including how to:

  • Enable and disable form elements during asynchronous Ajax calls.
  • Use DHML to update select boxes after new data is retrieved from the Ajax call.
  • Dynamically update HTML <div> area tags using innerHTML to replace the portions of the Web page.

Setting up

This tutorial assumes that you will be using the Rational® Application Developer (RAD), or one of its kin, but you are free to use whatever you like to edit and deploy the portal application. Nothing in this exercise is tool specific. You will also need to have WebSphere Portal v5.x or higher installed, and an available database, which we assume to be DB2® of course.

For all of these pieces to come together, you will need to import the application code into a RAD portal project and create at least one data source in WebSphere. The following steps will help you get started.

See Appendix A for the process of setting up a sample database and data source for use in this tutorial. You can use any available data source once everything is configured.


Configuring project and import files

  1. Create a JSR 168 Portlet project in RAD.
    1. Start RAD.
    2. Select File > New > Other, then locate and select Portal > Portlet Project (JSR 168). Note: Check Show All Wizards if you do not see the Portal section.
      Figure 1. JSR 168 portlet project
      JSR 168 portlet project
  2. Click Next.
  3. Enter the following information as shown below.
    • Name: Ajax_and_Portal
    • Project location: Accept the default.
    • WebSphere Portal version: Select your version of WebSphere Portal.
    • Create a portlet: Deselect this checkbox.
      Figure 2. New portlet project definition
      New portlet project definition
  4. Click Finished.
  5. Import the provided source files into the new project.
    1. Right-click on the top level of the "Ajax_and_Portal" project.
    2. Select Import... > Import ...
    3. Select Zip file.
    4. Click Next.
      Figure 3. Import from the zip file
      Import from the zip file
  6. On the zip file import page, do the following:
    • From Zip file: Locate and select the zip file you downloaded with this tutorial.
    • Into folder: Ensure that the Ajax_and_Portal project is selected.
    • Overwrite existing resources without warning: Select this checkbox.
    Figure 4. Select zip file to import screen
    Select zip file to import screen
  7. Click Finished.
  8. Your project structure should now look like Figure 5.
    Figure 5. Imported project structure
    Imported project structure

Initial deployment and test

The first thing to do is deploy the application in its current -- albeit lifeless -- state. Refer to Appendix B for instructions on deploying the portlet application into portal using RAD. Once you have the portlet deployed and placed on a page, we want to look at it.

Figure 6. Application web page
Application web page

Try to lookup a data source. Nothing happens! Not very exciting is it? By the end of this exercise, all of these items on the page will take action or display new data based on the Ajax calls made to the server, and the JavaScript code used to update the HTML tags.

Note that the Context Path is at the top of the portlet. You need this in just a minute to call the servlet directly from your browser. In our sample above, the context path is: /wps/PA_dsllabu.

It really is a portlet, no kidding

Yes, it's a portlet. And it completely confirms to the Java® Portlet (JSR168) specifications. However, Ajax itself is a separate technology that this exercise will demonstrate, and it happens to be using a portlet for displaying the data. This is important because we are also including a servlet that retrieves data for the application inside the portlet war file. WebSphere Portal is going to deploy this portlet, along with the servlet, as a Web application into IBM WebSphere Portal. This gives the portlet the ability to dynamically access the servlet that is included with its application. It does not have to be deployed separately in another J2EE application.

Let's first take a look at the portlet code.

  1. Locate the Project Explorer on the left of the page. Expand the Ajax_and_Portal project and keep expanding down to the DBExplorerPortlet.java file. Use Figure 7 to determine the location and tree structure of the file.
    Figure 7. DBExplorerPortlet.java location
    DBExplorerPortlet.java location
  2. Double click the file to open it in the editor view. Browse through the code and notice it does nothing but display the JSP page for the portlet. The doView() method is displayed below as an example of what you should see.
    public void doView(RenderRequest request, RenderResponse response)
     throws PortletException, IOException {	
     
    // ------------------------------------------------------------
    // Note: The portlet is just diplaying a JSP for the AJAX Lab. 
    // It does absolutely nothing else.
    // ------------------------------------------------------------
    
    // Set the MIME type for the render response
    response.setContentType(request.getResponseContentType());
    
    // Invoke the JSP to render
    PortletRequestDispatcher rd =
     getPortletContext().getRequestDispatcher(JSP_FOLDER + VIEW_JSP);
    rd.include(request,response);
    }

Ajax technology uses JavaScript and DHTML to use its functionality. This particular example does not require any pre-determined data for the JSP; therefore, the portlet itself has nothing to do other than display the JSP.


It's a normal servlet, I tell ya

Creating the servlet is a no-brainer. You've done it hundreds of times. Implement your service methods (doGet/doPost) and whatever business logic you require. Under normal circumstances, you will want to return an XML document to the caller, but you can return any textual information to the client. Let's look at the provided servlet code to understand where the data is coming from for the Ajax calls.

  1. Under the Project Explorer, expand the tree down to the code for the servlet. The file is called com.ibm.examples.Ajax.servlets > DBExplorerServlet.java. Double click this Java file to open it in the editor.
  2. Notice the doPost() method of the servlet and what it accomplishes. This particular code is not Ajax specific, but it is one way of getting a servlet to respond to the request by using parameters sent by the XMLHttpRequest object explained later in the exercise.
  3. Feel free to browse the code if you want to have a better understanding of what it does.

Each set of parameters requested to the servlet causes it to respond with a unique XML result. The browser-side JavaScript then parses the response data and renders it as needed. For example, if the parameter "LIST=SCHEMAS" is sent to the servlet, it returns the list of database schemas in an XML format. Let's try it, just to prove that there is nothing special happening with the servlet code.

  1. Enter the following URL into your browser window. An example is shown in Figure 8.
    http://localhost:9081/wps/PA_dsllabu/DBExplorerServlet?DS=jdbc/sample&LIST=SCHEMAS
    Figure 8. Results of calling the servlet directly
    Results of calling the servlet directlyNote: You need to adjust the actual URL to match your deployment. The /wps/PA_dsllabu portion of our sample URL is defined by the portal when you deployed your portlet application the first time. To see the context of your portal, look at the top of the Ajax portlet we deployed earlier.

This servlet is a simple J2EE servlet. You can access it via the browser like any other servlet normally deployed through the WebSphere Application Server interface. WebSphere Portal deploys WAR files with their own unique URI so that the portlets and other Web components can access files for that particular application, such as images, JavaScript, cascading style sheets and, in our example, servlets. Because it is deployed like any other Web application, the servlet is also deployed with the same Web context as the portlet application.

The only real trick when bundling a servlet with a portlet application is registering it into the web.xml file. You do this exactly as you would in a normal Web application.

Below is a fragment of the web.xml file from our exercise. The important item to notice is the Servlet-mapping > url-pattern. In this case, it's /DBExplorerServlet. This value is used when we define a JavaScript variable that our Ajax calls will use to communicate with the servlet. Go ahead and view the Deployment Descriptor of our project. A sample is shown in Figure 9.

Figure 9. Portlet web.xml file with bundled servlet definition
Portlet web.xml file with bundled servlet definition

Defining a JavaScript variable to address the servlet

For the application to know how to call our servlet dynamically (because you do not know what the URI context will be until it is deployed), you need to write some code to dynamically define the URL for the servlet.

  1. In the Project Explorer, open the following file: Ajax_and_Portal > WebContent > jsp > DBExplorer_View.jsp. Use Figure 10 to locate the file in the tree structure.
    Figure 10. View JSP file location
    View JSP file location
  2. Select the Source tab if not already selected, and locate the comment text "Insert JavaScript for the Ajax_SERVLET servlet URL here", and code the JavaScript section as shown below. Use the code sample below to determine the location of the inserted code and the actual code to type in the application.
    <%--------------------------------------------------------------------------------
    Set AJAX response servlet URL variable
    -------------------------------------------------------------------------------------
    Placing this variable in the JSP allows for runtime compile updates to the javascript
    We are using encodeURL() and getContextPath() to get the path of the web application
    deployed as a WAR file in Portal for accessing the servlet, stylesheet and javascript
    -----------------------------------------------------------------------------------%>
    <%--------------- Insert JavaScript for the AJAX_SERVLET URL here --------%>
    <script type="text/javascript">
      var AJAX_SERVLET = "<%=renderResponse.encodeURL(renderRequest.
        getContextPath())%>" + "/DBExplorerServlet";
    </script>
    <%------------------------------- End JavaScript insert ------------------%>

    You must place this code inside the JSP so that the portal runtime can use the portlet objects (renderResponse and renderRequest) to determine the servlet context generated when the portlet application was deployed. The Ajax functions in the JavaScript can now use this variable to make the calls to the servlet to retrieve the data needed for updating the page dynamically.
  3. Look through the rest of the JSP file. Here are a few key points to look for:
    1. None of the form fields have any event handlers (for example, onclick="event()") assigned to them. We will inject them at runtime using JavaScript.
    2. There is not an actual <form> tag, nor a way to submit the form input field contents to the server. Again, this will be handled by the Ajax calls.
    3. At the very end of the file there is a small Javascript block that calls an "init()" function. This function adds the event listeners to our input fields. In normal Web applications, this function is called as an "onload" event to the body element. But, since this is a portlet JSP fragment, we have no body tag to use. Therefore, this call needs to be made after the HTML content has been loaded into the DOM.
  4. Save your JSP changes by selecting File -> Save on the top menu or by pressing Ctrl-S.

So where's the Ajax stuff?

Good question! Let's get started coding the real thing. For Ajax to work properly, you need to retrieve and use the XMLHttpRequest object from any browser your client may be using. To do that, code a JavaScript function to get a browser independent XMLHttpRequest object. Different browsers instantiate this XHR object differently.

  1. In the Project Explorer, open the following file: Ajax_and_Portal > WebContent > js > DBExplorer.js.
  2. Locate the getXMLHttpRequest function. Insert the following code into the script at the lines indicated.
    function getXMLHttpRequest() {
      var xhr = null;
    // ---- Insert code here ----
      if ( window.XMLHttpRequest ) {
        // Mozilla/Safari
        xhr = new XMLHttpRequest();
      } else if ( typeofActiveXObject != "undefined" ) {
        // IE
        xhr = new ActiveXObject("MicroSoft.XMLHTTP");
      }
    // ---- End insert code ----
      return xhr;
    }

This function is the key to making the browser independent Ajax work because it determines if the browser supports the XMLHttpRequest or the ActiveXObject (Microsoft.XMLHTTP) object. This function returns the proper object, depending on the supported type of the browser. The API itself is the same once the object is created, but different browsers support different instantiations and require unique calls to create each object. This function takes care of most of the browsers.


Configuring the Ajax calls and retrieving data

Now that you can get the actual XMLHttpRequest object to make the Ajax call back to the server, you need to create functions that will implement this behavior.

To understand the code in the next section, you need to understand the different states specified for the XMLHttpRequest calls. In the JavaScript (at the top of the file), the code has already been added as variables to help later in the exercise. The code snippet below shows an example of what is being used in this exercise. You do not have to code this part of the exercise because it is already in the script. This is for reference only.

// -------------------------------------------------------------------------
// Set global ready state and HTTP variables to check AJAX status
// -------------------------------------------------------------------------
var READY_STATE_UNITIALIZED=0;
var READY_STATE_LOADING=1;
var READY_STATE_LOADED=2;
var READY_STATE_INTERACTIVE=3;
var READY_STATE_COMPLETE=4;
var HTTP_OK = 200;

Next, we need to create code that makes an XMLHttpRequest call to the server and requests specific data.

  1. Locate the event_setDataSource function. Insert the following code as indicated below.
    function event_setDataSource() {
      log("event_setDataSource: starting", "#660"); 
    
      // ... Existing code removed for brevity...
    
      // XHR for Schemas
      log("event_setDataSource: getting schemas", "#660"); 
    // ---- Insert code here ----
      xhr_getSchemas = getXMLHttpRequest();
      xhr_getSchemas.onreadystatechange = callback_getSchemas;
      xhr_getSchemas.open("POST",AJAX_SERVLET,true);
      xhr_getSchemas.setRequestHeader ("Content-Type", 
       "application/x-www-form-urlencoded");
      xhr_getSchemas.send("LIST=SCHEMAS&" + getFormDataParams() );
    // ---- End insert code ----
      
      log("event_setDataSource: finished", "#660");
    }

    This particular function is used to make a call to the Ajax servlet and requests a list of schemas from the database. There are several other functions in this script already coded for you that make similar calls, but you should focus on this particular one because of the scope of this exercise. This section of the code creates an XHR object, assigns a callback function, and opens a POST connection to the AJAX_SERVLET (defined in the JSP file earlier). Finally, it initiates the request passing in some parameters. Identify the following methods used with the XMLHttpRequest object and what they do.
    Method name Function
    .onreadystatechange Tells which JavaScript function to call if the state of the request changes. (You will be coding this in the next section.)
    .open Sets the request method (POST in our example), the URL and determines if the browser should make the call asynchronously (true) or wait for a result (false). This example does not need the browser to wait for a response, so we set it to true for asynchronous calls.
    .setRequestHeader Tells the servlet what type of content is being sent in the request. In this example, the call will contain HTTP form parameters.
    .send Initiates the call to the servlet and attaches the parameters to the request.
    The following functions for the other form fields are already completed for this exercise and do not need to be added. They use similar code that was added above to update other areas of the screen. It is for reference only:
    • event_setSchema
    • event_setTable
    • event_addColumn
    • event_deleteColumn
    • event_getTableData
  2. Since you told the XMLHttpRequest object you were going to use a state handler in the previous step, you need to code the callback function and have it take action when states change. Locate the callback_getSchemas function , and insert the following code at the area indicated.
    function callback_getSchemas()  {
      handlerLog("callback_getSchemas", xhr_getSchemas, "#00c"); 
    // ---- Insert code here ----
      // Show object is updating if the ready state is loading
      if (xhr_getSchemas.readyState == READY_STATE_LOADING) {
        showSelectUpdating("schema_select");
      }
      // Handle results if the ready state is complete
      if (xhr_getSchemas.readyState == READY_STATE_COMPLETE) {
        if (xhr_getSchemas.status == HTTP_OK) {
          fillSelect("schema_select", xhr_getSchemas.responseXML, true);
          formUnlockField("schema_select");
        } else {
          error("Failed to retrieve schema list:" + xhr_getSchemas.status);
        }
      }
    // ---- End insert code ----
    }

    Notice in this code there are two different sections. If the state on the request changes to "READY_STATE_LOADING" (or 1), it updates the data on the page indicating the request has been made and the data is being updated. This part of the application is already coded for you. It is a good idea to show data is being updated for Ajax-related calls because you do not know how fast the network connection will be or how long it will take to return the data. Setting an "updating" indicator gives the end user a sense of activity while the browser waits on a response. Without an indicator, the end user may think nothing is happening, especially if there is a slow response.

    The second section of this function will execute if the state on the request changes to "READY_STATE_COMPLETE" (or 4). This means the request is finished and typically ready for the results to be processed. This fillSelect function takes the resulting XML document and updates the HTML select tags with the returned data. The following functions for the other fields on this page are already included in the file and do not need to be added. They use similar code that was added above to update other areas of the screen. It is for reference only:
    • callback_getDatabaseProperties
    • callback_getTables
    • callback_getColumns
    • callback_getTableData
  3. Save the file before continuing by selecting File > Save or typing Ctrl-S.

Kick starting the code

Now that you have all of the Ajax code in place, it's not going to start by itself. You need to tell the browser that you actually want something to happen when an action is performed. You need to have some JavaScript events coded so the end user can kick off the Ajax calls when the user is interacting with the application. In this section, you will add the "onclick" event to the datasource lookup button.

  1. Locate the init() function in the DBExplorer.js file. Add an event handler for the load data source button by inserting the following code into the script at the lines indicated.
    document.getElementById("datasource_button").onclick =event_setDataSource;

    This tells the browser to execute this function when the button is clicked. We could have added these event listeners directly inside the form input tags, but that is not near as much fun. When adding event listeners using this method, you add the actual function object, and therefore, do not include quotes or the parenthesis. The events for the other fields have already been coded for you. The table below contains the other "onXXX" events that are already coded and need no action by you. You can review these areas of the code if you wish to have a better understanding.
    Input field Event Handler function Action performed
    Data source load button onclick event_setDataSource Retrieves database properties and schema list.
    Schema select onchange event_setSchema Retrieves the table list.
    Table select onchange event_setTable Retrieves available columns.
    Available columns select ondblclick event_addColumn Adds column to active columns list and retrieves table data.
    Active columns select ondblclick event_deleteColumn Removes column from active columns list and retrieves table data.
  2. Save your changes before continuing to the next step by selecting File -> Save from the menu or type Ctrl-S.

Deploying and testing the application

Now that you have coded the Ajax sections of the JavaScript and told the JSP to execute code upon lookup of a data source, you need to deploy the completed application onto the server. Refer to Appendix B on the procedures to deploy the portlet application.

Now that the portlet is deployed, you need to test the application.

  1. Point your browser to your portal page containing our portlet.
  2. Start by entering a data source name, or accepting the default of "jdbc/sample", and click on the Lookup button. Notice the other items start to dynamically changing the information based on the data source value and the onclick event coding that you put into the JSP. A log area shows the various activities taking place.

    So what exactly is happening? When you entered a data source and clicked the Lookup button, the onclick event was fired causing a call to the "event_setDataSource" function. This function initiates a couple of XHR Ajax processes to retrieve data from the server. Once the responses are received from the server, they are processed by the various "callback_XXX" functions. The received XML data is parsed and used to populate the respective areas of the document. All of this activity occurs asynchronously! There was no portal page refresh as shown by the timestamp at the top of the page, and multiple items were updated on the page. This is Ajax!
  3. Now select a schema such as DB2INST1. The fields automatically update to show only the tables defined for that particular schema.
  4. Select a table from the list of available tables. For example, if you chose DB2INST1 as the schema, you could choose EMPLOYEE as the table. As you do this, the columns become available for that particular table in the selection box below the table list.
  5. Double-click a few of the columns to add them to the selected columns list. If you chose the EMPLOYEE table, you could add EMPNO, FIRSTNAME, MIDINIT and LASTNAME to the columns. Notice how the data table is automatically updated to reflect the selected columns.
  6. Double-click entries in the Active columns list to remove them. The data table is updated to reflect the new selections. Remember, all this activity occurs without any page refresh or any portal interaction! The following is an example of the entire page filled in with data after making the calls using Ajax.
    Figure 11. Demo application in action
    Demo application in action

You may try and use different data sources and explore your data. Experiment, try different things. How can you enhance the application?


Troubleshooting

You've done it! You are now a master in the arts of Ajax and portal development. What? Something is not working? You have a few options.

  1. Go back and review all of your code updates. A cheat file is also provided in the Ajax_and_Portal zip file called solutions.txt. Compare the source code blocks included there with what you have in your code.
  2. Use the Mozilla Firefox browser along with the FireBug extention to debug the JavaScript. Firebug, written by Joe Hewitt, is easy to learn and will quickly help you track down any errors in the code. Spend 30 minutes using this amazing tool, and you'll save yourself hours, or even days, trying to figure out any JavaScript related bugs.
  3. Review the portal log files for error messages generated by the servlet. Common problems will be related to the data source connections to the database.
  4. As a last resort, you may also send the authors an email detailing what's happening, and we'll try our best to get you back on track.

The magic behind the screen updates

As far as Ajax is concerned, you have made the calls to get data and have updated the page with the data returned. That is Ajax in a nutshell. If you want to continue to explore some of the DHTML used in updating this data, you can continue the exercise. In the following sections, you will understand how the HTML objects are updated dynamically. These are just a few examples of what you can do once you retrieve the data. This part of the exercise is mostly DHTML examples and is optional. We cover:

  • Dynamically updating HTML select contents using DOM
  • Dynamically updating HTML DIV content using innerHTML
  • Disabling form fields and re-enabling them once the Ajax calls are complete
  • Getting the form data to send as parameters in the Ajax call
  • Logging area

Dynamically updating HTML select contents using DOM

Using the methods described previously, locate the DBExplorer.js file and find the function fillSelect().

// -------------------------------------------------------------------------------------
// Function to fill a specific select object from a properly formatted XML document
// -------------------------------------------------------------------------------------
function fillSelect( id, xmlResults, addAny) {
  clearSelect(id);
  selectObj = document.getElementById(id);
  if ( selectObj ) {
    if (addAny) {
      selectObj.options[0] = new Option("All Schemas", "any"); 
    }
    var xmlRoot = xmlResults.getElementsByTagName("htmlselect")[0];
    if (xmlRoot) {
      for(var i=0; i < xmlRoot.childNodes.length; i++) {
        child = xmlRoot.childNodes[i];
        if (child.nodeName == "select") {
          attrs = child.attributes;
          selectObj.options[selectObj.length] = new Option(
            child.firstChild.nodeValue, attrs.getNamedItem("value").value);
        }
      } //for
    }
    if (selectObj.length==0) {
      selectObj.options[0] = new Option("Not Available", "None");
      selectObj.disabled = true;
    }
    if (selectObj.options[0]) {
      selectObj.options[0].selected = true;
    }
  } // if obj
}

Notice how the code checks for the tag element htmlselect in the XML sent by the Ajax servlet and parses through select tags to get the data for updating the select options. If you accessed the servlet directly at the beginning of the exercise, you will notice the format of the XML returned is familiar. When a select tag is found in the XML, a new option is created on the select list. This allows for dynamic changes to the select boxes once the list is cleared using the clearSelect(id) method, which is also included in the JavaScript.

Dynamically updating HTML DIV contents using innerHTML

Locate the function updateDivHtml() in the DBExplorer.js file.

// -------------------------------------------------------------------------------------
// Function to update a specific DIV object from an XML Document
// -------------------------------------------------------------------------------------
function updateDivHtml(xmlDoc, divName) {
  var divHtml = "<table class='property_table'>";
  var xmlRoot = xmlDoc.getElementsByTagName("htmldiv");
  if (xmlRoot) {
    // Limit to only the first matching element.
    // Should only ever be one, but still need to state it
    xmlRoot = xmlRoot[0];
    if (xmlRoot.childNodes.length == 0) {
      divHtml = createDivRow("No data available","");
    } else {
      for(var i=0;i<xmlRoot.childNodes.length;i++) {
        textLine = xmlRoot.childNodes[i];
        if (textLine.nodeName=="textline") {
          attrs = textLine.attributes;
          if (attrs && textLine.firstChild) { 
            divHtml += createDivRow(attrs.getNamedItem("label").value,
              textLine.firstChild.nodeValue); 
          }
        }
      }
    } // if
  }
  divHtml += "</table>";
  document.getElementById(divName).innerHTML = divHtml;
}

The DIV tag is updated differently in this example. It is similar in that it parses the XML returned, but the XML is in a different format. It dynamically creates HTML in table format to insert into the DIV. Notice on the last line of the function that the DOM element's innerHTML attribute is updated with the table HTML created during the parsing. This completely replaces the current HTML inside the DIV with the newly formatted HTML dynamically, and updates the screen.

Disabling form fields and re-enabling them once the Ajax calls are complete

The following two functions located in the Ajax_and_Portal.js file enable and disable a form object on the page when an Ajax call is made. This reduces the number of problems that can occur if there are multiple items that can be changed during a single request. By disabling the field, you ensure none of the other selectable items are updated by the end user while they are waiting on a request from the servlet.

// ----------------------------------------------------------------------------
// Functions to lock/unlock form objects.  Used when processing
// requests so the user doesn't try to update the field when the
// requests are not finished processing.
// ----------------------------------------------------------------------------
function formLockField( id ) {
   document.getElementById( id ).disabled = true;
}
function formUnlockField( id ) {
   document.getElementById( id ).disabled = false;
}

The typical logic flow has the event handler function disable any fields that will be updated during the Ajax call. Once the Ajax responses are received, it is the responsibility of the callback function to unlock the appropriate form fields.

Getting the form data to send as parameters in the Ajax call

The form data is sent as parameters just like a normal request is made for a HTML form. In this code, each DOM object that has information we want to send as part of the Ajax call is encoded and placed in a parameter string to send along with the request. Notice the code also sends a timestamp along with the request. This helps the browser not cache the URL to the servlet, in case there are multiple requests to the same system.

// -------------------------------------------------------------------------------------
// Function to retrieve all of the form data to send to the AJAX servlet as parameters
// -------------------------------------------------------------------------------------
function getFormDataParams() {
  var params = "DS=" + encodeURIComponent( document.getElementById("get_ds").value )
    + "&SCHEMA=" + encodeURIComponent(getSelectedOption("select_schema")) 
    + "&TABLE=" + encodeURIComponent(getSelectedOption("select_table"))
    + "&COLUMNS=" + getOptionList("selected_columns")
    + "&NOCACHE=" + new Date().getTime();
    log("Form params: " + params);
    return params;
}

Logging area

One section of our application provides a window for logging event to better trace the process flow. This is a simple sub-system to implement. Toolkits will typically offer similar functionality, but this serves our purpose.

First, open the css > DEBExplorer.css file and locate the div.logger and stanza that are located near the bottom of the file.

div.logger {
	height: 200px;
	width: 100%;
	font-family: monospace;
	font-size: small;
	background-color: #eaeaea;
	overflow: scroll;
	border: 1px solid #000099;
}

As you can see, we are creating a scrollable DIV area with a fixed height. This causes the content to scroll down as we add new log messages to the top of the logging area. Next, open the DBExplorer.js file and locate the logger function located near the bottom of the file.

// -------------------------------------------------------------------------------------
// update logging window with messages
// -------------------------------------------------------------------------------------
function log(msg, color) {
  var logger = document.getElementById("logger");
  if ( color == null ) {
    color = "black";
  }
  if ( logger ) {
    var d = new Date();
    var time = (d.getHours() + ":" + d.getMinutes() + ":" 
      + d.getSeconds()).replace(/\b(\d)\b/g, '0$1');
    logger.innerHTML = "<span style='color:"+color+"'>"
      + time + ": " + msg + "</span><br />" + logger.innerHTML;
  } else {
    alert("No logging area found! Log message is:\n" + msg);
  }
}

In this function, we obtain a reference to the logger DIV using its ID attribute. We want to add a nicely formatted timestamp to each message, as all logs should do, so we create a Date object, and get the desired fields. The replace regular expression shown just inserts a leading zero to any single digit within a word boundary (for example, "3:47:2" is converted to "03:47:02"). Finally, we insert the log message with the requested color, before the existing content that exists in the logging area. Notice how the innerHTML property is both writable and readable, all within the same context.


Appendix A. Configuring the sample database and data source

The default data source for our application is DB2's ubiquitous "sample" database, defined as a data source named "jdbc/sample". If you know the name of another data source that exists within your application server, you can overwrite the default with your own. Otherwise, let's go ahead and configure the sample.

First, let's create the sample database. DB2 makes this easy. Either run DB2's "First Steps", and select Create sample database, or from the command line, as the DB2 instance user, run db2sampl. Refer to the DB2 getting started documentation if you need more help.

Second, we need to define a data source for the sample database. Start up your primary WebSphere Application Server, typically it is server1.

  1. Log into the administrative console:
    • For WebSphere Application Server v5: http://localhost:9090/admin
    • For WebSphere Application Server v6: http://localhost:9060/ibm/console
  2. Create a new JDBC driver provider:
    1. Select Resources > JDBC Providers.
    2. Click the New button.
    3. For JDBC Providers, select DB2 Universal JDBC Driver Provider.
    4. Click OK.
    5. You can leave everything as is on the New provider definition page.
    6. Click OK.
    7. Click the Save link at the top of the page and save the settings.
  3. Define the location of our DB2 JDBC drivers:
    1. Select Environment > Manage WebSphere Variables.
    2. Set the following environment variables to the location of your db2java.zip file. If you do not have DB2 or DB2client installed, you will need to obtain this file from an installed DB2 deployment and place it somewhere on your system.
      ${DB2UNIVERSAL_JDBC_DRIVER_PATH}
      ${UNIVERSAL_JDBC_DRIVER_PATH}
    3. For each variable:
      1. Click the variable name.
      2. For the Value field, enter the directory where the db2java.zip file is located. For Linux: /opt/IBM/db2/V8.1/java.
      3. Click OK.
      4. Click the Save link at the top of the page and save the settings.
  4. Create a J2C authentication alias:
    1. Select Security > JAAS Configuration > J2C Authentication Data.
    2. Click the New button.
    3. Enter the following fields, adjust as required for your implementation:
      • Alias: sampleDBAuth
      • User ID: Your DB2 instance user ID, such as db2inst1
      • Password: Your DB2 instance's password
    4. Click OK.
    5. Click the Save link at the top of the page and save the settings.
  5. Create a new Data Source for our sample database.
    1. Select Resources > JDBC Providers.
    2. Click on the new JDBC provider we just created, DB2 Universal JDBC Driver Provider.
    3. Click the Data Sources link at the bottom of the page.
    4. Click the New button.
    5. Set the following fields on the Configure Data Source page:
      • Name: sample
      • JNDI name: jdbc/sample
      • Container-Managed Authentication Alias: xxxxx/sampleDBAuth
      Note: The value is prefixed by your application server node name.
    6. Click OK.
    7. Click the link for the newly created sample data source link.
    8. Click the custom properties link at the bottom of the page.
    9. For each of the variable names shown below, set the value to the appropriate value based on your configuration. The values shown are for a default installation.
      • serverName:localhost
      • portNumber:50001
    10. After the fields are updated, click the Save link at the top of the screen and save the settings.
  6. Test out the new data source:
    1. Select Resources > JDBC Providers.
    2. Click on the new JDBC provider we just created, DB2 Universal JDBC Driver Provider.
    3. Click the Data Sources link at the bottom of the page.
    4. Click the link for the newly created sample data source link.
    5. Click the Test Connection button. You should see a success message at the top of the screen. If you see an error, review all of your settings and try again. Also, make sure you actually have your database running!
  7. Congratulations, your database and data source are now ready to use.

Appendix B. Deploying the portlet

This section provides information on how to deploy a portlet WAR file through RAD with just a few simple clicks.

  1. In the Project Explorer, right-click on the Ajax_and Portal project and select Deploy Portlet... from the menu as shown in Figure 12.
    Figure 12. Deploy Portlet menu option
    Deploy Portlet menu option
  2. Configure a portal server to enable deployment. The first time you run this wizard, there will not be a server configured. If there is not a server configured, then follow these steps, otherwise continue to Step number 3.
    1. Click the New... button to configure a new server. Note: If you have a remote portal server you wish to deploy to, then you either FTP access or a shared directory (for example, NFS). Setting up remote servers is out of the scope of this exercise, and you will need to consult the RAD and WebSphere Portal help documentation.
    2. For a normal portal environment, you will want to select the values shown in Figure 13. Adjust these to match your environment and click Next.
      Figure 13. Deploy and define new server
      Deploy and define new server
    3. Enter the following information. Standard values are shown in Figure 14, but you may have to adjust for your particular deployment. When finished, click Next.
      Figure 14. Deploy portal settings
      Deploy portal settings
    4. Configure the publishing settings. The values shown in Figure 15 are for a local installation on a Linux platform. Adjust these to match your configuration, then click Finish.
      Figure 15. Deploy, publishing settings
      Deploy, publishing settings
  3. Select the appropriate server to deploy your portlet to. Typically, this is localhost > WebSphere Portal v5.1 for Import, Export and Deploy @ localhost, and click Finish. See Figure 16.
  4. If the portlet has been previously deployed, you will receive a pop-up asking to overwrite the existing content. Answer Yes to continue to deploy the changes.
    Figure 16. Deploy, server select
    Deploy, server select
  5. Your portlet should now be deployed on the server!

If this is the first time deploying the portlet, you need to add it to a page on your portal:

  1. Using your Web browser, log into your portal using an administrative account.
  2. [Optional] Create a new page for the portlet.
    1. Select the portal Administration link.
    2. Select Portal User Interface > Manage Pages.
    3. Select My Portal link from the list pages.
    4. Click the New page button.
    5. Provide a name of Ajax and Portal,and customize as you see fit. Then click the OK button, and then OK again.
  3. Add the portlet to a page:
    1. Select the portal Administration link.
    2. Select Portal User Interface > Manage Pages.
    3. Select My Portal link from the list pages.
    4. Click the Edit Page Layout button for your page that you will place your portlet.
    5. Click the Add portlets button.
    6. Search for "Ajax", select the Ajax Database Explorer portlet, and click OK.
    7. Click Done to finish the page edit.

Conclusion

In this tutorial, we described how and why you would use Ajax in your portal applications. We also built a fully functional portlet application that uses Ajax in several areas. We also introduced you to the tightly coupled technologies involved with DHTML manipulation. While it is impossible to examine all conceivable uses of Ajax and WebSphere Portal in this tutorial, it was our intention to whet your appetite to learn, and to apply this exciting technology into your future application development toolkit.


Download

DescriptionNameSize
Sample code for this tutorialAJAX_and_Portal.zip38KB

Resources

Learn

Get products and technologies

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, Information Management
ArticleID=153838
ArticleTitle=Building an Ajax portlet for WebSphere Portal
publish-date=08162006