Skip to main content

skip to main content

developerWorks  >  Sample IT projects  >

Create your own portlet and Web service

WebSphere Portal V4 can help

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Introductory

Michael Wanderski (mwanders@us.ibm.com), Advisory Software Engineer, IBM Corp, IBM

01 Dec 2002

In this article, Michael Wanderski introduces you to portlet and Web Services programming. By building a functional example step-by-step, he shows the vast potential and benefit of combining these two exciting new technologies. This article is intended for developers with at least some level of portlet and Web services knowledge, though extensive experience is not required.

Portals and portlets

Portal-based technology lets you surface Web applications through an easy-to-use, consolidated interface. Portals also allow you to personalize user access based on informational needs and visual preferences. Examples of personalized access include company stock quotes, subscription-based news feeds, and application availability tailored to departmental needs.

Portals are traditionally viewed through Web browsers such as Microsoft Internet Explorer (IE) or Netscape Navigator, clients that let users simultaneously view multiple applications and allow for client-side processing using Java applets or rich media, such as Macromedia Flash. Portal Web developers, therefore, have a vast assortment of tools to use to bring content and applications to users. Portal applications, or portlets, integrate seamlessly into the overall portal framework through a variety of application programmatic interfaces and portal-based services. A sample IBM WebSphere Portal Server screen is shown in Figure 1.


Figure 1. Sample WebSphere Portal Server
Sample WebSphere Portal Server

In this article, I introduce portlet and Web service programming. I assume you have at least some level of IBM WebSphere Portal programming experience. Basic knowledge of Web service design is advantageous. I'll give a review of portlet and Web service programming, then show how to use these two technologies in concert with a step-by-step, functional example.



Back to top


Portlet programming example

Let's review the portlet API by looking at a simple portlet example, below, that displays the remaining hours and minutes left in the day. If you are completely unfamiliar with the portlet API, or with portlet deployment within the portal server, see the documentation in the WebSphere Portal InfoCenter (in Resources) for complete descriptions of the APIs, each tag, and related tag values.

package com.ibm.wps.sample.time;
    
import java.io.*;
import java.util.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
    
/** 
 * RemainingTimePortlet.java
 * Simple example to display the remaining hours and minutes in the day.
 */
public class RemainingTimePortlet extends AbstractPortlet {
  
  /**   
   * Main method called by the portal server to render the portlet
   * display.
   * @param  request   The portlet request object.
   * @param  response  The portlet response object.
   * @exception  PortletException  If an unexpected exception occurs.
   * @exception  IOException       If an I/O exception occurs.
   */
  public void service(PortletRequest request, PortletResponse response)
    throws PortletException, IOException {
          
      // Get the remaining hours and minutes.
      Calendar calendar = Calendar.getInstance();
      int hours = 24 - calendar.get(Calendar.HOUR_OF_DAY), minutes = 0;
      if (calendar.get(Calendar.MINUTE) > 0) {
        hours -= 1;
        minutes = 60 - calendar.get(Calendar.MINUTE);
      }
            
      PrintWriter writer = response.getWriter();
      writer.println("<p>The day will be over 
         in " + hours + " hours and " + minutes + " minutes.</p>");
  }
}

This portlet displays the remaining hours and minutes in the day, and is updated every time the portlet is refreshed. It shows the basic class interface, AbstractPortlet, to be extended to create a new portlet application. Like servlets, a portlet has a service method that must be overridden in order to return the content for the portlet window. In this example, I determine the hours and minutes and use them to display the remaining time by obtaining a reference to the response PrintWriter and printing the HTML markup to the output stream, as shown in Figure 2.


Figure 2. Remaining Time Portlet
Remaining Time Portlet

It is good design to frame the application implementation around one of the standard object-oriented design patterns. One such design pattern for application interfaces is the principle of Model-View-Controller (MVC). I won't give a complete description of the MVC design pattern here, but in short, it allows for modularity, scalability, and clarity in interface design. (See Resources for more information on MVC.) The pattern, as shown in Figure 3 below, is broken into the three fundamental components:

Model
The main component that maintains the application state and data model.
View
A graphical representation of the data model for the user.
Controller
A bridge between its model and views that can manipulate the underlying data.


Figure 3. Model-View-Controller (MVC)
Model-View-Controller (MVC)

Let's look at the previous example again, below, now using the MVC design. (Some of the constructs, such as the TimeBean, may be overkill for this example, but they show you the basic design pattern to follow to create more complex and interactive interfaces.)

package com.ibm.wps.sample.time;

import java.io.*;
import java.util.*;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;

/**
 * RemainingTimeController.java
 * Controller class for providing the right view for the portlet.
 */
public class RemainingTimeController extends AbstractPortletController {

  /** The View for HTML devices (e.g. Internet Explorer, Netscape). */
  protected String jspHTML = "/WEB-INF/time/html/";
  
  /**
   * Method for initializing the portlet.
   * @param  config  The portlet configuration object.
   * @exception  UnavailableException  If an unexpected error occurs.
   */
  public void init(PortletConfig config)
    throws UnavailableException {
    
      super.init(config);
      
      // Load the HTML View JSP name from the portlet configuration.
      jspHTML += config.getInitParameter("view.HTML");
  }
  
  /**
   * Method for rendering the portlet view.
   * @param  request   The portlet request object.
   * @param  response  The portlet response object.
   * @exception  PortletException  If an unexpected exception occurs.
   * @exception  IOException       If an I/O exception occurs.
   */
  public void doView(PortletRequest request, PortletResponse response)
    throws PortletException, IOException {
    
    // Get the remaining hours and minutes.
    Calendar calendar = Calendar.getInstance();
    int hours = 24 - calendar.get(Calendar.HOUR_OF_DAY), minutes = 0;
    if (calendar.get(Calendar.MINUTE) > 0) {
      hours -= 1;
      minutes = 60 - calendar.get(Calendar.MINUTE);
    }
    
    // Create a bean to pass the remaining time to the View JSP.
    TimeBean timeBean = new TimeBean();
    timeBean.setHours(hours);
    timeBean.setMinutes(minutes);
    
    // Insert bean into the request for the JSP to use.
    request.setAttribute("timeBean", timeBean);
    
    // Call the View JSP to render the display.
    getPortletConfig().getContext().include(jspHTML, request, response);
  }
}

package com.ibm.wps.sample.time;

import java.io.*;

/**
 * TimeBean.java
 * A bean used to pass data to the JSP View.
 */
public class TimeBean implements Serializable {

  /** The remaining hours. */
  private int hours = 0;
  
  /** The remaining minutes. */
  private int minutes = 0;
  
  /**
   * Set the remaining hours.
   * @param  hours  The remaining hours.
   */
  public void setHours(int hours) {
    this.hours = hours;
  }
  
  /**
   * Get the remaining hours.
   * @return  The remaining hours.
   */
  public int getHours() {
    return hours;
  }
  
  /**
   * Set the remaining minutes.
   * @param  minutes  The remaining minutes.
   */
  public void setMinutes(int minutes) { 
    this.minutes = minutes;
  }
  
  /**
   * Get the remaining minutes.
   * @return  The remaining minutes.
   */
  public int getMinutes() {
    return minutes;
  }
}

<!---------------------------------------------------------------------->
<!- DisplayRemainingTime.jsp                                          -->
<!- A View JSP used to render the data for the remaining time portlet -->
<!---------------------------------------------------------------------->
<%@ page contentType="text/html" errorPage="" %>
<jsp:useBean id="timeBean"
             class="com.ibm.wps.sample.time.TimeBean"
             scope="request"/>
             
<p>The day will be over in <%= timeBean.getHours() %> and
<%= timeBean.getMinutes() %> minutes.</p> 

First I'll describe the important components in this example. Notice that the class no longer extends the AbstractPortlet class, but instead now extends the AbstractPortletController class that provides a portlet controller abstraction. This object defines the same setup methods as on the AbstractPortlet class, as follows: init, login, logout and destroy.

Next, notice that the class no longer directly overrides the service method, but instead overrides a method associated with a particular portlet mode. Portlets have four modes of operation:

  • Portlet.Mode.VIEW
  • Portlet.Mode.EDIT
  • Portlet.Mode.HELP
  • Portlet.Mode.CONFIGURE

The operation of these modes is indicated by their name. For each mode, the AbstractPortletController class provides a method for rendering that can be overridden. These methods are: doView, doEdit, doHelp and doConfigure.

Now look at what occurs when the portlet is called to render its VIEW mode. First, the remaining time is determined for the current time. A TimeBean is then created to hold the hours and minutes that will be necessary to render the display. The TimeBean is then attached to the current request using the setAttribute method of the portlet request object. This will allow the VIEW JavaServer Pages (JSP) component to obtain the bean from the request using the useBean JSP tag, as shown in the above code. Lastly, the JSP component that will produce the HTML markup for the portlet is called to render the display.

For more information on portlet deployment descriptors and portlet packaging, see the WebSphere Portal InfoCenter in Resources.



Back to top


Introduction to Web services

The term Web services describes a framework in which e-business services are described, published, and discovered. These services are deployed in a distributed computing topology and are available for dynamic invocation. There are three fundamental participants in a fully deployed Web Service architecture, as shown in Figure 4.


Figure 4. Web service participants
Web service participants

The service provider implements a service, described as a collection of operations that perform a task, and publishes that service for use. These services are discovered dynamically through a service registry based on specified criteria and ultimately invoked by a service requester. Examples of Web services include services for ordering products, such as a book, or providing methods for computing advanced and intensive mathematical operations. The dynamic nature of Web services lets service consumers and providers work together in a real-time, fluid, and flexible environment.

The implementation of the Web service architecture is based on a stack of XML standards that allow for the loose coupling of the aforementioned service participants. The fundamental XML technologies that form the basis of the Web service architecture are:

SOAP
Simple Object Access Protocol (SOAP) is a lightweight protocol for the exchange of information in a decentralized, distributed environment.
WSDL
Web Services Description Language (WSDL) is a method for describing network-based services as a set of operations for both document-oriented and procedure-oriented messages.
UDDI
Universal Description, Discovery and Integration (UDDI) is a platform-independent registry for describing and discovering services for dynamic service integration.
This is just a small sampling of the growing number of standards that make up the Web service architecture; for more information see Resources.

Luckily, you don't need to fully understand the details of these XML technologies to benefit from the advantages Web services can provide. Bundled within IBM WebSphere Application Server are implementations for many of the XML standards that provide an easy-to-use, class-level programmatic interface. And, IBM provides a Web Services Toolkit (WSTK) for integration into first-class tools, such as IBM WebSphere Studio Application Developer, so that you can easily create your own Web services. See Resources for download information.

In this article I concentrate on the service provider aspect of the Web service architecture. The next sections show you how to create and deploy a service using the SOAP APIs available within Application Server. Once deployed, this service will be available for invocation by service requesters. I'll then demonstrate using a portlet as one such service requester.



Back to top


Web services programming example

There are many reasons why using a Web service is a smart design goal. If an application retrieves a set of stock quotes from a service vendor, it would be advantageous to use the dynamic nature of the Web services framework so new stock quote providers can be found and used. Even for internal projects, Web services can be beneficial. For example, if you are deploying a component that must be available to many other components of an overall system, each with various environmental constraints, the Web services approach provides a platform-independent way to integrate all your design components.

By concentrating only on the service provider role in this example, I can easily demonstrate a method for integrating a portlet and service. (Dealing with all aspects of the Web services stack would be too large a task in a short article. For more information see the links in Resources.) Keeping with my example, I'll create a service that provides the remaining hours and minutes in the day for a given date. Although the example is overkill for such a simple service, it lets me limit the coding complexity and concentrate on the important factors in creating, deploying, and integrating a service.

I want to register a new Enterprise Archive (Application) with the Application Server that will accept incoming requests to the service. I will use the SOAP toolkit packaged in Application Server to accomplish the goal. The first step in designing the service is to determine the public operations that will be made available. They are as follows:

package com.ibm.wps.sample.time;

import java.util.*;

/**
 * RemainingTimeService.java
 * A service that provides the remaining hours and minutes of the date.
 */
public interface RemainingTimeService {

  /**
   * Get the remaining hours and minutes of the date.
   * @param   date    The date to use.
   * @return  The remaining hours and minutes
   */
  public TimeBean getRemainingTime(Date date);
  
  /**
   * Get the remaining hours of the date.
   * @param   date    The date to use.
   * @return  The remaining hours.
   */
  public Integer getRemainingHours(Date date);
  
  /**
   * Get the remaining minutes of the date.
   * @param   date    The date to use.
   * @return  The remaining minutes.
   */
  public Integer getRemainingMinutes(Date date);
}

Next I need to create the object that will provide the service that implements this interface, as shown in the following Java class:

package com.ibm.wps.sample.time;

import java.util.*;

/**
 * RemainingTimeServiceServerImpl.java
 * Provides an implementation for the RemainingTimeService service.
 */
public class RemainingTimeServiceServerImpl implements 
   RemainingTimeService {

  /**
   * Get the remaining hours and minutes of the date.
   * @param   date    The date to use.
   * @return  The remaining hours and minutes.
   */
  public TimeBean getRemainingTime(Date date) {
    
    Calendar calendar = Calendar.getInstance();
    int hours = 24 - calendar.get(Calendar.HOUR_OF_DAY), minutes = 0;
    if (calendar.get(Calendar.MINUTE) > 0) {
      hours -= 1;
      minutes = 60 - calendar.get(Calendar.MINUTE);
    }
    
    TimeBean timeBean = new TimeBean();
    timeBean.setHours(hours);
    timeBean.setMinutes(minutes);
    
    return timeBean;
  }
  
  /**
   * Get the remaining hours of the date.
   * @param   date    The date to use. 
   * @return  The remaining hours.
   */
  public Integer getRemainingHours(Date date) {
    
    return new Integer(getRemainingTime(date).getHours());
  }
  
  /**
   * Get the remaining minutes of the date.
   * @param   date    The date to use.
   * @return  The remaining minutes.
   */
  public Integer getRemainingMinutes(Date date) {
  
    return new Integer(getRemainingTime(date).getMinutes());
  }
}

Now that I have the server implementation, I need to create an Enterprise Archive (EAR) file to deploy this service in Application Server. I need to bundle the SOAP toolkit with the EAR file and create all the necessary SOAP deployment descriptors. Once all these components are in place, the SOAP toolkit running within Application Server takes care of routing all requests to the server-side service implementation. For more details on EAR file deployment, see related articles in Resources.

To create an EAR file and related resource archives, one of several approaches are available. Application Developer provides tools to automatically generate EAR files. You can also simply use an open-source tool such as Ant (from apache.org) to build the EAR file structure. In this example, I'll show the internals of the EAR file and describe each resource; how you package the EAR file is up to you.

My EAR file will contain the following directory and file structure:

		RemainingTimeService.ear
  .\RemainingTimeService.jar 
  .\soap.war 
  .\META-INF\application.xml
  .\META-INF\ibm-application-bnd.xmi
  .\META-INF\ibm-application-ext.xmi

RemainingTimeService.jar contains all the class files we have generated (RemainingTimeService.class, TimeBean.class, RemainingTimeServiceServerImpl.class). soap.war is the Web Archive (WAR) file for all the SOAP related resources and is discussed in more detail below.

Deployment descriptors

There are three deployment descriptors for our ear file in the META-INF directory, as follows:

application.xml
The main deployment descriptor for the application. Most tools will automatically generate this file based on input, but if you're using a build tool such as Ant or JAR, the contents of the file should be similar to:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application
  PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.2//EN"
  "http://java.sun.com/j2ee/dtds/application_1_2.dtd">
<application id="Application_ID">
  <display-name>RemainingTimeService</display-name>
  <module id="WebModule_1">
    <web>
      <web-uri>soap.war</web-uri>
      <context-root>/RemainingTimeService</context-root>
    </web>
  </module>
  <security-role id="SecurityRole_1">
    <role-name>SOAPRouterUsers</role-name>
  </security-role>
</application>

The next two deployment descriptors are generated by the Application Developer tool, but their contents are as follows. (The details are beyond the scope of this article, but see the WebSphere Application Server InfoCenter in Resources for more information.) If you are not automatically generating the files, you can simply tailor these examples for your particular application.

ibm-application-bnd.xmi
<applicationbnd:ApplicationBinding xmi:version="2.0"
  xmlns:xmi="http://www.omg.org/XMI"
  xmlns:applicationbnd="applicationbnd.xmi"
  xmlns:application="application.xmi"
  xmlns:common="common.xmi"
  xmi:id="Application_ID_Bnd"
  appName="RemainingTimeService">
  <application href="META-INF/application.xml#Application_ID"/>
  <authorizationTable xmi:id="AuthorizationTable_1">
    <authorizations xmi:id="RoleAssignment_1">
      <role href="META-INF/application.xml#SecurityRole_1"/>
      <specialSubjects xmi:type="applicationbnd:Everyone"
        xmi:id="Everyone_1"
        name="Everyone"/>
    </authorizations>
  </authorizationTable>
  <runAsMap xmi:id="RunAsMap_1"/>
</applicationbnd:ApplicationBinding>

ibm-application-ext.xmi
<applicationext:ApplicationExtension xmi:version="2.0"
  xmlns:xmi="http://www.omg.org/XMI"
  xmlns:applicationext="applicationext.xmi"
  xmlns:application="application.xmi"
  xmi:id="Application_ID_Ext">
  <application href="META-INF/application.xml#Application_ID"/>
</applicationext:ApplicationExtension>

Next you'll create the soap.war file to add as a resource in the EAR file. soap.war contains the following directory and file structure:

		soap.war
  .\dds.xml
  .\soap.xml
  .\WEB-INF\web.xml
  .\WEB-INF\ibm-web-bnd.xmi
  .\WEB-INF\ibm-web-ext.xmi

The SOAP deployment descriptors are similar to the following samples. The main deployment descriptor exposing a service that is implemented with a standard Java class is:


dds.xml
	 
<root>
 <isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
   id="urn:RemainingTimeService"
   checkMustUnderstands="false">
   <isd:provider type="java" scope="Application"
     methods="getRemainingTime getRemainingHours getRemainingMinutes">
     <isd:java 
       class="com.ibm.wps.sample.time.RemainingTimeServiceServerImpl"
       static="false"/>
   </isd:provider>
   <isd:faultListener>org.apache.soap.server.DOMFaultListener
      </isd:faultListener>
   <isd:mappings>
     <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
       xmlns:x="urn:RemainingTimeService"
       qname="x:TimeBean"
       javaType="com.ibm.wps.sample.time.TimeBean"
       xml2JavaClassName=
          "org.apache.soap.encoding.soapenc.BeanSerializer"
       java2XMLClassName=
          "org.apache.soap.encoding.soapenc.BeanSerializer"/>
   </isd:mappings>
 </isd:service>
</root>

The service-urn, which is the id attribute of the service tag, specifies the URN that the exposed service assumes (for example, urn:TimeRemainingService). I also set the checkMustUnderstands attribute of the service tag to "false" to indicate that the server should not throw a Fault if there are SOAP headers in the request marked as MustUnderstand. I specify the provider type as Application (other possible values are Request and Session), to indicate the application should be created once and reused for the lifetime of the enterprise application. The last attribute set for the provider tag is the exposed methods to surface the service implementation. Since the methods exposed are not static, the java static attribute is set to false. The class attribute is set to the fully-qualified class name of our service object.

The next information to provide in the descriptor is the type mappings for our user-defined class objects. This information is important so the SOAP toolkit can automatically serialize my objects into XML for the SOAP document. Because the example contains the TimeBean class, I register it in the map attribute using the specified URN and QNAME. I also use the standard serializer and deserializer (xml2JavaClassName and java2XMLClassName attributes, respectively) since our TimeBean class is modeled after the JavaBean specification, and this BeanSerializer class is intended for that use.

In the absence of a generation tool, you can individually tailor and include the following additional descriptors directly into the WAR file. Their contents are less interesting and less pertinent to this example than the previous descriptor, so the descriptions have been left out. See Resources for more details.


soap.xml
	
<!-- Apache SOAP Server Configuration File -->
<soapServer>
  <!-- This ConfigManager looks for a dds.xml file in this         -->
  <!-- directory. The dds.xml file should contain a <root> element -->
  <!-- which has deployment descriptor <isd:service> elements as   -->
  <!-- children.                                                   -->
  <configManager value="com.ibm.soap.server.XMLDrivenConfigManager"/>
  <serviceManager>
    <option name="SOAPInterfaceEnabled" value="false"/>
  </serviceManager>
</soapServer>


web.xml
	
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web 
   Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app id="WebApp_1">
  <display-name>Apache-SOAP</display-name>
  <description>no description</description>
  <servlet id="Servlet_1">
    <servlet-name>rpcrouter</servlet-name>
    <display-name>Apache-SOAP RPC Router</display-name>
    <description>no description</description>
    <servlet-class>org.apache.soap.server.http.RPCRouterServlet
       </servlet-class>
    <init-param id="InitParam_1">
      <param-name>faultListener</param-name>
      <param-value>org.apache.soap.server.DOMFaultListener</param-value>
    </init-param>
  </servlet>
  <servlet id="Servlet_2">
    <servlet-name>messagerouter</servlet-name>
    <display-name>Apache-SOAP Message Router</display-name>
    <servlet-class>org.apache.soap.server.http.MessageRouterServlet
       </servlet-class>
    <init-param id="InitParam_2">
      <param-name>faultListener</param-name>
      <param-value>org.apache.soap.server.DOMFaultListener</param-value>
    </init-param>
  </servlet>
  <servlet-mapping id="ServletMapping_1">
    <servlet-name>rpcrouter</servlet-name>
    <url-pattern>/servlet/rpcrouter</url-pattern>
  </servlet-mapping>
  <servlet-mapping id="ServletMapping_2">
    <servlet-name>messagerouter</servlet-name>
    <url-pattern>/servlet/messagerouter</url-pattern>
  </servlet-mapping>
  <security-constraint id="SecurityConstraint_1">
    <web-resource-collection id="WebResourceCollection_1">
      <web-resource-name>/RemainingTimeServiceResource</web-resource-name>
      <url-pattern>/RemainingTimeService/*</url-pattern>
      <url-pattern>/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
      <http-method>DELETE</http-method>
      <http-method>PUT</http-method>
    </web-resource-collection>
    <auth-constraint id="AuthConstraint_1">
      <description>SOAPRouterSecurityConstraints:+:</description>
      <role-name>SOAPRouterUsers</role-name>
    </auth-constraint>
    <user-data-constraint id="UserDataConstraint_1">
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
  <security-role id="SecurityRole_1">
    <role-name>SOAPRouterUsers</role-name>
  </security-role>
</web-app>


ibm-web-bnd.xmi
	
<webappbnd:WebAppBinding xmi:version="2.0"
  xmlns:xmi="http://www.omg.org/XMI"
  xmlns:webappbnd="webappbnd.xmi"
  xmlns:webapplication="webapplication.xmi"
  xmi:id="WebApp_1_Bnd"
  virtualHostName="default_host">
  <webapp href="WEB-INF/web.xml#WebApp_1"/>
</webappbnd:WebAppBinding>


ibm-web-ext.xmi
	
<webappext:WebAppExtension xmi:version="2.0"
  xmlns:xmi="http://www.omg.org/XMI"
  xmlns:webappext="webappext.xmi"
  xmlns:webapplication="webapplication.xmi"
  xmi:id="WebApp_1_Ext">
  <webApp href="WEB-INF/web.xml#WebApp_1"/>
</webappext:WebAppExtension>

So far you've seen how to create a SOAP service that can be deployed into the application server. To interact with this service, the calling application must be able to communicate using SOAP messages. This can be a burden for calling applications, so it's advantageous to provide a programmatic interface to interact with our service that hides the details of SOAP communication and toolkit interaction. Again, I'll use the SOAP toolkit to provide this client-side API.

As shown below, you can create a client object that also implements the RemainingTimeService interface, and that uses the SOAP toolkit to communicate via SOAP messages to the server service.

package com.ibm.wps.sample.time;

import java.net.*;
import java.util.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.encoding.*;
import org.apache.soap.encoding.soapenc.*;

/**
 * RemainingTimeServiceClientImpl.java
 * Provides an implementation for the RemainingTimeService client.
 */
public class RemainingTimeServiceClientImpl implements 
   RemainingTimeService {

  /** The registry to specify user defined objects for serialization. */
  private SOAPMappingRegistry smr = new SOAPMappingRegistry();
  
  /** The URN to address the service. */
  private String serviceURN = "urn:RemainingTimeService";
  
  /** The URL to use to access the service. */
  private URL soapRouter = null;
  
  /**
   * Construct a remaining time service client.
   * @param  host  The host where the service resides.
   * @exception  MalformedURLException  If an error occurs creating 
   *  the URL.
   */
  public RemainingTimeServiceClientImpl(String host)
    throws MalformedURLException {
      super();
      
      soapRouter = new URL("http://" + host + 
         "/RemainingTimeService/servlet/rpcrouter");
      
      // Populate the SOAP Mapping Registry with the user defined type.
      mapSOAPClassTypes();
  }
  
  /**
   * Get the remaining hours and minutes of the date.
   * @param   date    The date to use.
   * @return  The remaining hours and minutes.
   */
  public TimeBean getRemainingTime(Date date) {
    
    Vector params = new Vector();
    params.addElement(new Parameter("date", Date.class, date, null));
    
    return (TimeBean)doCall("getRemainingTime", params);
  }
  
  /**
   * Get the remaining hours of the date.
   * @param   date    The date to use. 
   * @return  The remaining hours.
   */
  public Integer getRemainingHours(Date date) {
  
    Vector params = new Vector();
    params.addElement(new Parameter("date", Date.class, date, null));
    
    return (Integer)doCall("getRemainingHours", params);
  }
  
  /**
   * Get the remaining minutes of the date.
   * @param   date    The date to use.
   * @return  The remaining minutes.
   */
  public Integer getRemainingMinutes(Date date) {
  
    Vector params = new Vector();
    params.addElement(new Parameter("date", Date.class, date, null));
    
    return (Integer)doCall("getRemainingMinutes", params);
  }
  
  /** Make the actual call using the specified method and parameters. */
  /** If any errors occur, then simply return null. */
  private Object doCall(String method, Vector params) {
  
    try {
      // Create SOAP call using the specified parameters.
      Call call = new Call();
      call.setSOAPMappingRegistry(smr);
      call.setTargetObjectURI(serviceURN);
      call.setMethodName(method);
      call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
      call.setParams(params);
      
      // Make the SOAP call and obtain the response.
      Response resp = call.invoke(soapRouter, "");
      // Check if there was an exception in the response.
      if (resp.generatedFault()) {
        return null;
      }
      
      // Return the result.
      Parameter result = resp.getReturnValue();
      return result == null ? null : result.getValue();
    }
    catch (SOAPException e) {
      return null;
    }
  }
    
  /** Map the new SOAP types so that they can be serialized. */
  private void mapSOAPClassTypes() {
    
    BeanSerializer beanSer = new BeanSerializer();
    smr.mapTypes(Constants.NS_URI_SOAP_ENC,
                 new QName(serviceURN, "TimeBean"),
                 TimeBean.class,
                 beanSer,
                 beanSer);
  }
}

The client extends the same interface as the server, so neither the implementation nor the underlying communication details matter to a calling application. In this example, the class type mappings are first added to the serialization registry (SOAPMappingRegistry) in the same manner, and for the same reasons, as with the server-side deployment descriptor. Since this example is not concerned with dynamic discovery of services, I've assumed the host location of the service is known and thus it is passed as part of the constructor parameters and used to create the SOAP URL.

After the client-side object has been constructed, all of the interface methods are available for invocation. They setup the method name and parameters of the service call, and defer to the doCall method for actual communication with the SOAP server. For example, in the getRemainingTime method, a vector is created to contain all the method parameters (in our case, only the Date object). The parameter vector and the string representation of the method name are passed to the doCall method.

Once in the doCall method, a SOAP Call object is constructed and set with all the relevant information needed to communicate with the SOAP server. This includes the serialization registry to use, the target service URN (such as urn:RemainingTimeService), the method with parameters to invoke, and the SOAP encoding style. The call is then initiated with the SOAP router URL that was constructed with the provided host name. Lastly, you should check for any returned or generated exceptions and return the value sent back from the SOAP server.

It's good practice to adhere to the object-oriented Factory Design Pattern when providing a service abstraction where the class-level implementation details will be filled in for you. For example, if we provided the following Factory abstraction, at a later point the Factory could be modified to determine whether to construct the server-side or client-side implementation to pass back to the caller, depending on the current environment. If the server-side service is available locally, there is no reason to incur the cost of all the SOAP communication calls. This is a simple Factory implementation that only returns the client-side service API.

package com.ibm.wps.sample.time;

import java.net.*;

/**
 * RemainingTimeServiceFactory.java
 * Factory for obtaining a reference to a remaining time service.
 */
public class RemainingTimeServiceFactory {
  
  /**
   * Obtain a remaining time service implementation.
   * @param  hostname  The host where the service resides.
   * @exception  MalformedURLException  If an error occurs creating the URL.
   */
  public static RemainingTimeService getInstance(String hostname)
    throws MalformedURLException {
      return new RemainingTimeServiceClientImpl(hostname);
  }
}



Back to top


Combining the portlet and Web service

Now it's time to modify the portlet to make use of this new service. Happily, the majority of the work has already been completed by the creation of the client-side service API that hides the details of the SOAP interactions. Modifying the portlet to use the service is as simple as changing the RemainingTimeController's doView method to use this service API, as shown in the following code fragment:

public void doView(PortletRequest request, PortletResponse response)
  throws PortletException, IOException {

    RemainingTimeService service = null;
    try {
      service = RemainingTimeServiceFactory.getInstance("localhost");
    }
    catch (Exception e) {
      throw new PortletException(e.getMessage());
    }
    
    // Insert bean into the request for the JSP to use.
    request.setAttribute("timeBean", service.getRemainingTime(new Date()));
    
    // Call the View JSP to render the display.
    getPortletConfig().getContext().include(jspHTML, request, response);
  }
}



Back to top


Conclusion

This article walked you through an end-to-end exercise of creating both a portlet and Web service from scratch in a fully functional, albeit limited example. Hopefully, though, I've shown the basic concepts and how to create your own portlet and Web service. The example focused on the service provider role, but I encourage you to investigate the other aspects of the Web Services Toolkit to implement the other Web service participants. I wish you luck with incorporating these ideas into your own development projects.



Resources



About the author

Michael Wanderski is an advisory software engineer at the IBM Raleigh Lab working in the Pervasive Computing Division. He is currently working on the WebSphere Everyplace Access product. You can contact Michael at mwanders@us.ibm.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top