Implementing the System

After doing all the detailed design and the UML diagrams, the system was sent to the development team for implementation. In this section we will get our hands dirty covering all the coding details. Here we will list the source files used in the application and design the internal working details of all the classes and JSPs used in the system.

Implementing and Configuring JMS

This section explains the configuration and implementation details of the helper classes used for JMS messaging.

Common Utilities

The first task in modeling the system was to identify the common entities that would be used by both the systems. Since both the systems were going to use JMS for messaging it was decided to create a service for handling the JMS internals and expose a simple yet efficient API to the application developers for producing and consuming JMS messages. For this the expected functionality for the JMS adapter service was specified. The service should:

  • Encapsulate the naming and lookup complexities of administered objects

  • Encapsulate the complexities in creating JMS connections and sessions

  • Have an adaptable interface that would work with both PTP and Pub/Sub models

  • Have an interface to produce and consume JMS messages, start and stop JMS connections, and create different JMS messages

  • Support asynchronous message delivery

  • Support client IDs and durable subscriptions

  • Be extendable to support XA connections and sessions

It was also decided to enumerate all the constants in an interface (recall that if a class implements an interface, then the constant declarations are available directly within the class). The following were identified:

  • Constants to indicate the message model, PTP or Pub/Sub

  • Constants to indicate the role of the JMS adapter: message producer, message consumer, or both message producer and consumer

The following class diagram depicts the entities used for performing common messaging utilities:

click to expand

The IJMSConstants Interface

This is an interface used for defining the different constants used in implementing the messaging facade. This interface defines constants to indicate the following:

  • PTP messaging model

  • Pub/Sub messaging model

  • Message producer role

  • Message consumer role

  • Both message producer and consumer role

     package com.acme.common;     public interface IJMSConstants {         public static final int PRODUCER = 1;         public static final int CONSUMER = 2;         public static final int PRODUCER_CONSUMER = 3;         public static final int PTP_MODEL = 1;         public static final int PUB_SUB_MODEL= 2;     } 

The JMSManager Class

As mentioned earlier this is a facade helper class for handling all the messaging functionality. This class exposes a simple API for initializing the messaging components, creating different JMS messages, and sending and receiving messages. This class can be configured to work with either the PTP model or the Pub/Sub model. It can also be configured to act as a message producer, message consumer, or both message producer and consumer. All the JNDI lookup functionality for administered objects is encapsulated within the class:

     package com.acme.common;     import javax.naming.*;     import javax.jms.*;     import java.util.Hashtable;     public class JMSManager implements IJMSConstants {        private ConnectionFactory connFx;        private Connection conn;        private Destination dest;        private Session session;        private MessageConsumer consumer;        private MessageProducer producer;        private int model;        private String jndiFactory;        private String url;        private String jmsFactory;        private String destName;        private Context ctx; 

This class is constructed with the following values:

  • JNDI initial context factory

  • JNDI service provider URL

  • The JNDI name of the JMS destination to be used

  • The JNDI name of the JMS connection factory to be used

  • A constant indicating the message model as specified in the IJMSConstants interface

     public JMSManager(String ctxFact, String url, String dest,         String jmsFact, int model) {       this.jndiFactory = ctxFact;       this.url = url;       this.destName = dest;       this.jmsFactory = jmsFact;       this.model= model;     } 

The getInitialContext() method is used for getting the JNDI initial context, using the initial context factory name and the service provider URL passed in the constructor:

     public Context getInitialContext() throws NamingException {       Hashtable env = new Hashtable();       if(jndiFactory != null) {         env. put (Context. INITIAL_CONTEXT_FACTORY, jndiFactory)       }       if(url != null) {         env.put (Context.PROVIDER_URL, url) ;       }       Context context = new InitialContext (env) ;       return context;     } 

The class then provides the method initializeJMS() to initialize and start the JMS services for sending and receiving messages. This method expects the following parameters:

  • A constant defined in the IJMSConstants interface to identify the messaging role: message producer, consumer, or both producer and consumer

  • A message listener implementation for handling asynchronous message delivery

  • A Boolean value specifying whether to start the JMS connection implicitly or not

     public void initializeJMS(int type, MessageListener listener,         boolean start) throws NamingException, JMSSecurityException,           JMSException { 

This method first initializes the JNDI initial context and looks up the connection factory using the name specified in the constructor:

       if(ctx == null) {          ctx = getInitialContext();        }        ConnectionFactory connFx = (ConnectionFactory) ctx.lookup (jmsFactory); 

Then if the message model is Pub/Sub it creates a topic connection and topic session, and looks up the topic:

       if(model == PUB_SUB_MODEL) {         conn = ((TopicConnectionFactory)connFx).createTopicConnection();         session = ((TopicConnection)conn).createTopicSession(           false, Session.AUTO_ACKNOWLEDGE);         dest = (Topic)ctx.lookup(destName); 

If the messaging role is consumer or producer/consumer and the passed message listener is not null a topic subscriber is created and the passed message listener is registered with the topic subscriber:

     if ((type == CONSUMER || type == PRODUCER_CONSUMER) &&         listener != null) {       consumer = ((TopicSession)session).createSubscriber((Topic)dest));       consumer.setMessageListener(listener);     } 

If the messaging role is producer or producer/consumer, a topic publisher is created:

         if (type == PRODUCER || type == PRODUCER_CONSUMER) {           producer = ((TopicSession)session).createPublisher(((Topic)dest)) ;         } 

If the message model is PTP it creates a queue connection and queue session, and looks up the queue:

       } else if(model == PTP_MODEL) {         conn = ((QueueConnectionFactory)connFx).createQueueConnection();         session = ((QueueConnection)conn).createQueueSession(           false, Session.AUTO_ACKNOWLEDGE);         dest = (Queue) ctx.lookup(destName); 

If the messaging role is consumer or producer/consumer and the message listener is not null, a queue receiver is created and the message listener parameter is registered with the queue receiver:

         if((type == CONSUMER || type == PRODUCER_CONSUMER) &&             listener != null) {           consumer = ((QueueSession)session) .createReceiver(((Queue)dest)) ;           consumer.setMessageListener(listener);         } 

If the messaging role is producer, or producer/consumer, a queue sender is created:

         if(type == PRODUCER || type == PRODUCER_CONSUMER) {           producer = ((QueueSession)             session).createSender(((Queue)dest));         }       } 

Finally the JMS connection is started depending on the boolean value passed to the method:

       if(start)         conn.start();     } 

Various utility and cleanup methods follow. This method starts the JMS connection:

     public void jmsStart() throws JMSException {       if(conn != null) {         conn.start();       }     } 

This method temporarily stops the JMS connection:

     public void jmsStop() throws JMSException {       if(conn != null) {         conn.stop();       }     } 

This method releases all the JMS resources by closing and stopping the JMS connection:

     public void closeJMS() {       if(conn != null) {         try {             conn.stop();             conn.close();         } catch (JMSException e) {             System.out.println("JMSManager.closeJMS:");             e.printStackTrace();         } catch(Exception e) {             System.out.println("JMSManager.closeJMS: ") ;             e.printStackTrace();         }       }     }     public boolean isTopic() {       return (model == PUB_SUB_MODEL) ;     } 

The class provides a method for sending messages that will send or publish the passed messages depending on the configured message model. This method also provides an argument to specify whether the message needs to be persistent or not:

     public void sendMessage(Message msg, boolean persistent)         throws JMSException {       if(persistent) {         msg.setJMSDeliveryMode(DeliveryMode.PERSISTENT) ;       } else {         msg.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT) ;       } 

If the configured messaging model is Pub/Sub then publish the message using the Pub/Sub publisher or else send the message using the PTP queue sender:

       if(model == PUB_SUB_MODEL) {         ((TopicPublisher)producer).publish(msg);       } else if (model == PTP_MODEL) {         ((QueueSender)producer).send(msg);       }     } 

Finally, methods are also provided for creating different types of JMS messages:

       public MapMessage createMapMessage() throws JMSException {         return session.createMapMessage();       }       public TextMessage createTextMessage() throws JMSException {         return session.createTextMessage();       }       public ObjectMessage createObjectMessage() throws JMSException {         return session.createObjectMessage() ;       }       public BytesMessage createBytesMessage() throws JMSException {         return session.createBytesMessage();       }       public StreamMessage createStreamMessage() throws JMSException {         return session.createStreamMessage();       }     } 

Configuring Messaging in the Server

We will now configure the required messaging functionality within the server. Of the following four steps, only the creation of a persistent store for the queue is not covered in Appendix A:

  • Create a new connection factory qcFactory

  • Create a new JMS server MyJMSServer

  • Create a file system destination store in C:\temp

  • Create a new queue destination requestQueue using the above stor

Before we begin the configuration, start an instance of WebLogic 6.0 server running and access the administration console through the browser. Assuming you are running the server locally on port 7001, the URL will be as follows, http://localhost:7001/console. This will prompt you for the system user ID and password that you configured on installation.

The Connection Factory

First, create the connection factory by expanding the JMS node and clicking the Connection Factories link under JMS in the left-hand pane and then clicking on the Create a new JMS Connection Factory link in the right-hand pane.

Enter qcFactory for the Name and JNDI Name, leave the rest as default, and click on Create:

click to expand

Click on the Targets tab, select myserver in the Available list, move it to the Chosen list, and click Apply.

Destinations

For configuring destinations, first create a store for persistent messages by clicking on the Stores link under the JMS node and click the link Create a new JMSFile Store in the right-hand pane. Give the name as MyJMSFileStore and the directory as C:\temp. Click on Create:

click to expand

Now click on the Servers link under the JMS node and click on the Create a new JMSServer link in the right-hand pane. Give the name as MyJMSServer and select the store created in the previous step. Leave everything else as default and click Create.

click to expand

Select the Targets tab and move myserver from the Available list to the Chosen list and click Apply.

Now click on the Destinations node under MyJMSServer and click on the link Create a new JMSQueue in the right-hand pane. Specify the name and JNDI name as requestQueue, leave the rest as default, and click Create:

click to expand

Repeat the above step for a new queue named responseQueue as well.

The Case Logging System

This section explains the implementation details of the classes and JSPs used in the case logging system, the customer's view of the application.

The Object Model

The basic entities identified in the case logging system were:

  • An encapsulation of the details of case responses

  • A store for the incoming case responses from the message broker

  • A helper class for generating sequential case IDs

The first entity was called CaseResponse with accessor and mutator methods for the attributes. The following attributes were defined for the CaseResponse entity:

  • The case ID

  • The name of the person raising the case

  • The location of the person

  • The description of the case

  • The case status

  • A comment for the case response

  • The time at which the response was made

The entity for storing the case requests was called ResponsePool and has the following functionality:

  • Asynchronously listens to the messages in the response queue

  • Exposes a method to get the list of case responses for the passed case ID

  • Exposes a method to get a case request for the specified case ID

  • Exposes a method to remove a case from the pool for the specified case ID

The helper class for generating case IDs is called IDGenerator with a method for returning the next ID.

The class diagram shown below depicts the entities used in the case logging system:

click to expand

The CaseResponse class follows the basic bean pattern with accessor and mutator methods for the attributes.

The ResponsePool class implements the javax.jms.MessageListener interface to handle asynchronous message delivery, and the java.io.Serializable interface to store the instance of the class in web application session contexts (for replication or persistence). It also defines methods for returning a list of all the case responses for the passed case ID.

Before continuing with the analysis of the web.xml file, which underpins the entire configuration including the security aspects, we'll show how to set up the security details within WebLogic server.

Setting up Security

We need to define the users and groups within the realm of the web container. For our application, we need to create two groups called user and system and users belonging to the groups. For the time being we will create a single user to be added to both the groups. To create a user, click on the Users node under Security in the left-hand pane of the browser-based administration tool, enter the username as tom with an appropriate password, and click on Create.

Important 

Please note that for WebLogic's default security realm the usernames and passwords are case sensitive.

click to expand

To create a new group click on the Groups node under Security, enter the username as user, and click on Create:

click to expand

Now to add a user to a group, enter the user tom in the following screen:

click to expand

Repeat the previous steps to create another group called system and add the user tom to the group system as well.

Setting up the Web Application

Now we have configured the necessary security credentials, we get on with implementing the web application itself starting with the all-important web.xml file:

web.xml

This is the standard J2EE web application configuration file for declaring servlets/JSPs, URL mapping, security constraints, and initialization arguments:

     <?xml version="1.0" encoding="Cp1252"?>     <!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>       <display-name>ACME</display-name>       <description>ACME Helpdesk System</description> 

First, we declare the application initialization parameters. These values are available to the application using the getInitParameter() method of the servlet context. The initialization parameters declared in the file are the following:

  • JNDI name of the queue to which case requests are sent

  • JNDI name of the queue to which case responses are sent

  • JNDI name of the queue connection factory

  • JNDI initial context factory name

  • JNDI service provider URL

     <context-param>       <param-name>responseQueue</param-name>       <param-value>responseQueue</param-value>     </context-param>     <context-param>       <param-name>requestQueue</param-name>       <param-value>requestQueue</param-value>     </context-param>     <context-param>       <param-name>qcFactory</param-name>       <param-value>qcFactory</param-value>     </context-param>     <context-param>       <param-name>ctxFactory</param-name>       <param-value>weblogic.jndi.T3InitialContextFactory</param-value>     </context-param>     <context-param>       <param-name>provURL</param-name>       <param-value>t3://localhost:7001</param-value>     </context-param> 

We then declare the followingJSPs:

  • newcase.jsp
    This JSP contains the form for submitting a new case

  • createcase.jsp
    This JSP does the processing for creating a case and sending JMS messages to the case management system using custom tags

  • casehistory.jsp
    This JSP lists the responses received for the case ID entered by the user

     <servlet>       <servlet-name>NewCase</servlet-name>       <jsp-file>newcase.jsp</jsp-file>     </servlet>     <servlet>       <servlet-name>CreateCase</servlet-name>       <jsp-file>createcase.jsp</jsp-file>     </servlet>     <servlet>       <servlet-name>CaseHistory</servlet-name>       <jsp-file>casehistory.jsp</jsp-file>     </servlet> 

The following servlet mappings are declared next:

  • The URL NewCase is mapped to newcase.jsp

  • The URL CreateCase is mapped to createcase.jsp

  • The URL CaseHistory is mapped to casehistory.jsp

     <servlet-mapping>       <servlet-name)NewCase</servlet-name>       <url-pattern>NewCase</url-pattern>     </servlet-mapping>     <servlet-mapping>       <servlet-name>CreateCase</servlet-name>       <url-pattern>CreateCase</url-pattern>     </servlet-mapping>     <servlet-mapping>       <servlet-name>CaseHistory</servlet-name>       <url-pattern>CaseHistory</url-pattern>     </servlet-mapping> 

The list of welcome files is declared next. The first file displayed after user authentication is index.jsp:

     <welcome-file-list>       <welcome-file>/index.jsp</welcome-file>     </welcome-file-list> 

Then the tag library URI is declared. This helps the developers to refer to tag libraries using symbolic URIs rather than specifying relative paths:

     <taglib>       <taglib-uri>/user</taglib-uri>       <taglib-location>/WEB-INF/user.tld</taglib-location>     </taglib> 

The web application uses standard J2EE web container form-based authentication declared in the web.xml file. Access to all the resources in the web application is restricted to the users belonging to the group user. The users and groups are to be defined within the realm of the web container in which the web application will be deployed. The configuration file also specifies the page to be presented to the user for authentication as well as the page to be presented when authentication fails. In our case both pages are defined by logon.jsp:

         <security-constraint>           <web-resource-collection>             <web-resource-name>user</web-resource-name>             <url-pattern>/*</url-pattern>           </web-resource-collection>           <auth-constraints>             <description>Helpdesk Users</description>             <role-name>user</role-name>           </auth-constraint>           <user-data-constraint>             <transport-guarantee>NONE</transport-guarantee>           </user-data-constraint>         </security-constraint>         <login-config>           <auth-method>FORM</auth-method>           <form-login-config>             <form-login-page>/logon.jsp</form-login-page>             <form-error-page>/logon.jsp</form-error-page>           </form-login-config>         </login-config>         <security-role>           <description>Helpdesk Users</description>           <role-name>user</role-name>         </security-role>     </web-app> 

The Logon Screen

This is how our final application will look. For accessing the case logging system, the user would enter the following URL to the browser http://localhost:7001/user. This will prompt them with the screen for entering username and password:

click to expand

Upon entering valid information and hitting Logon, they will be presented with the index page:

click to expand

The logon.jsp Page

This JSP is used for authenticating the users. Both case logging and case management systems share the code in this file. This file is also redisplayed when the user authentication fails because of invalid security credentials:

     <%@ page language="Java" %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY BGCOLOR="#CCCCCC" >         <BR><BR><BR><BR><BR><BR><BR><BR><BR> 

For standard form-based authentication the login page should have a form with action name j_security_check and two input fields, one named j_username and the other named j_password.

In HTTP form-based authentication, the names of the pages to be presented to the user for security authentication, and on authentication failure, are specified in the deployment descriptor for the web applications. The deployment descriptor also specifies the security group to which the users should belong to be able to access specific resources within the web application.

The real definition of the users and groups, however, is done within the web container. When a user first accesses a resource within the web application the container presents the logon page specified in the deployment descriptor to the user. Once the user fills in the credentials and submits the form, the container uses a resource mapped to the HTTP action j_security_check that does the authentication in a container-specific manner. This resource expects the username and password as HTTP request parameters named j_username and j_password respectively.

If the authentication fails due to invalid security credentials the container presents the user with the error page or else the user is given access to the resources within the web application depending on the access control specified in the deployment descriptor:

           <FORM ACTION="j_security_check" METHOD="post">           <TABLE ALIGN="center">             <TR>               <TD COLSPAN="2" ALIGN="center" BGCOLOR="#000000">                 <FONT SIZE="3" COLOR="#0055FF">                   <B>Please Logon</B>                 </FONT>             </TD>           </TR>           <TR><TD ALIGN="right"><B>User Name:</B> </TD>                 <TD><INPUT TYPE="text" NAME="j_username"></TD>           </TR>           <TR><TD ALIGN="right"><B>Password:</B></TD>                 <TD><INPUT TYPE="password" NAME="j_password"></TD>           </TR>           <TR><TD COLSPAN=2 ALIGN="center">                   <INPUT TYPE="submit" value="Logon">                 </TD>           </TR>         </TABLE>         </FORM>       </BODY>     </HTML> 

The banner.jsp Page

This JSP is included in all the JSP files in the case logging system and it contains the common menu items and search field to enter case IDs:

     <%@ page language="Java" %>     <TD VALIGN="center" BGCOLOR="#000000">       <FONT SIZE="6" COLOR="#0055FF">         <B><I>ACME Systems Helpdesk</I></B>       </FONT>     </TD>     <TD VALIGN="bottom" ALIGN="right" BGCOLOR="#000000"> 

The form contains three actions. The first link points to the URL NewCase, which is mapped to newcase.jsp through the web.xml. The second link points to the file index.jsp. The third submits the form named history, which defines an input field for specifying the caseID. The form's action is CaseHistory, which is mapped to casehistory.jsp:

       <FORM NAME="history" ACTION="CaseHistory" METHOD="post">         <A HREF="NewCase">           <FONT SIZE="3" COLOR="#0055FF">             <B>New Case</B>           </FONT>         </A>           <FONT SIZE="3" COLOR="#0055FF">             <B>&nbsp;|&nbsp;</B>           </FONT>           <A HREF="index.jsp">             <FONT SIZE="3" COLOR="#0055FF">               <B>Home</B>             </FONT>           <"/A>         <FONT SIZE="3" COLOR="#0055FF">           <B>&nbsp;|&nbsp;</B>         </FONT>         <INPUT TYPE="TEXT" name="caseId" SIZE="10">         <A HREF="javascript:history.submit()">           <FONT SIZE="3" COLOR="#0055FF">             <B>Search&nbsp;&nbsp;</B>           </FONT>         </A>       </FORM>       <BR>     </TD> 

The index.jsp Page

This is the first file served to the user after authentication:

     <%@ page language="Java" %> 

This JSP initializes an instance of the class ResponsePool by passing in the ServletContext extracted from pageContext. This instance will asynchronously listen to the case responses coming from the case management system through the message broker and acts as a data repository for the case responses:

     <jsp:useBean  scope="application"        />     <%       if (!responsePool.isInitialized()) {         responsePool.init(pageContext.getServletContext());       }     %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>       <TABLE WIDTH="100%" HEIGHT="100%" CELLPADDING="0" CELLSPACING="0">         <TR HEIGHT="20%">           <%@ include file="banner.jsp" %>         </TR>         <TR>           <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center">           <B><I><FONT SIZE="4" COLOR="#000000">             Welcome to ACME Systems Helpdesk<BR>             This system helps ACME users to raise cases online             <BR>           </FONT></I></B>           </TD>         </TR>       </TABLE>       </BODY>     </HTML> 

The Business Logic

We now examine the Java code that implements the logic of the customer logging application.

The newcase.jsp Page

This JSP presents the user with a form to enter the initial case details. In the application, the user would first click on New Case link on the banner in the welcome page:

click to expand

This JSP file contains the form for entering the details for a new case:

     <%@ page language="Java"%>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE WIDTH="100%" HEIGHT="100%" CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center"> 

The JSP listed above provides a form for entering the details for a new case. The form's action is CreateCase, which is in turn mapped to createcase.jsp:

           <FORM METHOD="post" ACTION="CreateCase">             <TABLE CELLPADDING="3" CELLSPACING="3">               <TR>                 <TD COLSPAN="2" ALIGN="center" BGCOLOR="#000000">                   <FONT SIZE="3" COLOR="#0055FF">                     <B>New Case</B>                   </FONT>                 </TD>               </TR>               <TR>                 <TD ALIGN="right">                   <B>Name:</B>                 </TD> 

The form defines the following controls for entering the case details:

  • A text control to enter the name of the person

  • A select control to specify the location

  • A text area to specify the case description

                   <TD><INPUT SIZE="30" NAME="name"></TD>                 </TR>                 <TR><TD ALIGN="right">                       <B>Location:</B>                     </TD>                     <TD>                       <SELECT name="location">                         <OPTION>Berlin</OPTION>                         <OPTION>London</OPTION>                         <OPTION>Paris</OPTION>                         <OPTION>Zurich</OPTION>                       </SELECT>                     </TD>                 </TR>                 <TR><TD VALIGN="top" ALIGN="right">                       <B>Description:</B>                     </TD>                     <TD><TEXTAREA NAME="description" COLS="40"                                 ROWS="10"></TEXTAREA>                     </TD>                 </TR>                 <TR>                   <TD COLSPAN="2" ALIGN="right">                     <INPUT TYPE="submit" value="Submit Case">                   </TD>                 </TR>               </TABLE>               </FORM>             </TD>           </TR>         </TABLE>       </BODY>     </HTML> 

The createcase.jsp Page

This JSP is called when the user submits a new case. In the New Case page the user would click on Submit Case:

click to expand

The JSP file declares a tag library with prefix as user and the URI as /user. This URI will be resolved to a tag library descriptor by interrogating the web.xml file. The actual mapping of the file user. tld to the resource /user was done in the deployment descriptor for the web application:

     <%@ page language="Java"%>     <%@ taglib uri="/user" prefix="user" %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE WIDTH="100%" HEIGHT="100%" CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center"> 

The JSP file also uses the custom tag createCase, which will invoke the relevant methods in the CreateCaseTag class. The tag handler class will retrieve the case details entered by the user and send a JMS message as explained earlier. It also stores the generated case ID as an HTTP request attribute, which is later retrieved and rendered by the JSP:

               <user:createCase/>               <B>                 Your request has been successfully processed.<br>                 Your case ID for reference is                 <%= caseId %>               </B>             </TD>           </TR>         </TABLE>       </BODY>     </HTML> 

The casehistory.jsp Page

This JSP is invoked when the user searches the case status by specifying the case ID. Note that the full listing shown here will not occur until the helpdesk has responded to the initial enquiry. So if, for example, the user entered the case ID 10001 and clicked on the link Search:

click to expand

     <%@ page language="Java"%>     <%@ taglib uri="/user" prefix="user" %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE WIDTH="100%" HEIGHT="100%" CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center">               <TABLE CELLPADDING="3" CELLSPACING="3" WIDTH="90%">                 <TR>                   <TD COLSPAN="7" ALIGN="center" BGCOLOR="#000000">                     <FONT SIZE="3" COLOR="#0055FF">                       <B>Case History</B>                     </FONT>                   </TD>                 </TR>                 <TR>                   <TD><B>Case Id</B></TD>                   <TD><B>Name</B></TD>                   <TD><B>Location</B></TD>                   <TD><B>Description</B></TD>                   <TD><B>Status</B></TD>                   <TD><B>Comment</B></TD>                   <TD><B>Date</B></TD>                 </TR> 

The JSP declares the tag library user. It uses the custom tag caseHistory whose tag handler class is CaseHistoryTag. This tag will be executed multiple times depending on the number of items in the case response list for the specified case ID in the application scope bean responsePool.

The request scope bean caseResponse of type CaseResponse is declared with in the aforementioned custom tag. Each time the tag is iterated the tag handler class retrieves the next available item in the response list for the specified case ID and stores it in the HTTP request object. The JSP renders the attributes of the case response object using the standard getProperty tag of useBean:

                 <user:caseHistory>                   <jsp:useBean  scope="request"                      />                   <TR>                   <TD>                   <jsp:getProperty name="caseResponse" property="caseId" />                   </TD>                   <TD>                   <jsp:getProperty name="caseResponse" property="name" />                   </TD>                   <TD>                   <jsp:getProperty name="caseResponse" property="location" />                   </TD>                   <TD>                   <jsp:getProperty name="caseResponse" property="description" />                   </TD>                   <TD>                   <jsp:getProperty name="caseResponse" property="status" />                   </TD>                   <TD>                   <jsp:getProperty name="caseResponse" property="comment" />                   </TD>                   <TD>                   <jsp:getProperty name="caseResponse" property="date" />                   </TD>                   </TR>                 </user:caseHistory>               </TABLE>             </TD>           </TR>         </TABLE>       </BODY>     </HTML> 

The CaseResponse Class

This is the class that models a response received from the case management system. This class has been explained in detail in the section on the object model. This class is used as the type for the request scope bean explained in casehistory.jsp:

     package com.acme.user;     import java.util.Date;     public class CaseResponse {       private String caseId;       private String name;       private String location;       private String description;       private String status;       private String comment;       private Date date;       public CaseResponse() {       }       public String getCaseId() {         return caseId;       }       public void setCaseId (String caseId) {         this.caseId = caseId;       }       public String getName() {         return name;       }       public void setName(String name) {         this.name = name;       }       public String getLocation() {         return location;       }       public void setLocation (String location) {         this.location = location;       }       public String getDescription() {         return description;       }       public void setDescription (String description) {         this.description = description;       }       public String getStatus() {         return status;       }       public void setStatus (String status) {         this.status = status;       }       public String getComment() {         return comment;       }       public void setComment (String comment) {         this.comment = comment;       }       public Date getDate() {         return date;       }       public void setDate(Date date) {         this.date = date;       }     } 

The ResponsePool Class

An instance of this class works as a data repository for the case responses coming from the case management system. One thing to note here is in real world implementation you may be backing up the data modeled by this class into persistent data storage. The same instance is used as an application scope bean that listens for JMS messages sent to the response pool by the case management system:

     package com.acme.user;     import java.util.HashMap;     import java.util.ArrayList;     import java.util.Date;     import javax.naming.*;     import javax.jms.*;     import java.io.Serializable;     import javax.servlet.ServletContext;     import com.acme.common.JMSManager;     import com.acme.common.IJMSConstants; 

This class implements javax.jms.MessageListener interface for asynchronously receiving the response messages sent by the case management system to the response queue:

     public class ResponsePool implements MessageListener, Serializable { 

The class uses three instance variables:

  • A Boolean to indicate whether the instance of the class is initialized or not. The instances are initialized by calling the init() method.

  • An instance of JMSManager that acts a message consumer to the response queue.

  • An instance of HashMap that stores a map of case IDs to list of responses for that case. This data structure is interrogated when the users query case statuses by specifying case IDs.

     private boolean initialized;     private JMSManager responseManager;     private HashMap responseMap = new HashMap(); 

The init() method takes an instance of ServletContext as the argument. It then queries the web.xml file through the instance of servlet context for the JMS configuration information. This information is then used to create and initialize responseManager and the instance of the class registers itself as a message listener with the responseManager. Finally it sets the value of initialized to true:

     public void init (ServletContext ctx) throws NamingException,         JMSException, Exception {       String responseQueue = ctx.getInitParameter("responseQueue");       System.out.printIn("responseQueue: " + responseQueue);       String qcFactory = ctx.getInitParameter ("qcFactory");       System.out.printIn("qcFactory: " + qcFactory);       String ctxFactory = ctx.getInitParameter("ctxFactory");       System.out.printIn("ctxFactory: " + ctxFactory);       String provURL = ctx.getInitParameter("provURL");       System.out.printIn("provURL: " + provURL);       responseManager = new JMSManager (ctxFactory, provURL,         responseQueue, qcFactory, IJMSConstants.PTP_MODEL);       responseManager.initializeJMS(IJMSConstants.CONSUMER,         this, true);       initialized = true;       }       public boolean isInitialized() {         return initialized;       } 

The callback method for asynchronous messages retrieves the case response details from the message and creates an instance of CaseResponse class. If the case ID is already present in the responseMap, the list stored against the case ID is retrieved and the response is added to the list. Otherwise a new list is created, and the response is added to the list and stored in the responseMap against the case ID:

     public synchronized void onMessage(Message msg) {       try {         MapMessage mapMsg = (MapMessage)msg;         CaseResponse response = new CaseResponse();         response.setCaseId(mapMsg.getString ("caseId"));         response.setName(mapMsg.getString("name"));         response.setLocation(mapMsg.getString("location"));         response.setDescription(mapMsg.getString("description"));         response.setStatus(mapMsg.getString("status"));         response.setComment(mapMsg.getString("comment"));         response.setDate(new Date());         addResponse(response);         } catch(JMSException e) {           e.printStackTrace();         }       } 

The class also provides a method for retrieving the list of responses for a given case ID:

       public synchronized ArrayList getResponseByCaseId(String caseId) {         ArrayList Is = (ArrayList)responseMap.get(caseId);         return (Is !=null)?((ArrayList)Is.clone()):(new ArrayList());       }       private void addResponse(CaseResponse response) {         ArrayList list = (ArrayList)responseMap.get(response.getCaseId());         if(list == null) {           list = new ArrayList();           responseMap.put(response.getCaseId(), list);         }         list.add(response);       }     } 

The JSP Tag Library

This is the tag library descriptor file user.tld that stores the information about the custom tags used in the system:

     <?xml version="1.0" ?>     <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"     "web-jsptaglib_1_1.dtd">     <taglib>       <tlibversion>1.0</tlibversion>       <jspversion>1.1</jspversion>       <shortname>User</shortname>       <info>         This tag library contains tag extensions       </info> 

This file defines two custom tags explained in the previous sections. The custom tag createCase is associated with the tag handler class com.acme.user.CreateCaseTag and the body content is defined as empty and defines com.acme.user.CreateCaseTagVariableInfo as the tag extra info class:

     <tag>       <name>createCase</name>       <tagclass>com.acme.user.CreateCaseTag</tagclass>       <teiclass>com.acme.user.CreateCaseTagVariableInfo</teiclass>       <bodycontent>JSP</bodycontent>     </tag> 

The custom tag caseHistory is associated with the tag handler class com.acme.user.CaseHistoryTag and the body content is defined as parsable JSP content:

       <tag>         <name>caseHistory</name>         <tagclass>com.acme.user.CaseHistoryTag</tagclass>         <bodycontent>JSP</bodycontent>       </tag>     </taglib> 

The CreateCaseTag Class

This is the tag handler class for the custom tag createCase:

     package com.acme.user;     import javax.servlet.jsp.*;     import javax.servlet.jsp.tagext.*;     import javax.servlet.*;     import javax.servlet.http.*;     import javax.jms.*;     import com.acme.common.*;     public class CreateCaseTag extends TagSupport { 

In the doStartTag() method of the tag handler instances of ServletContext and HttpServletRequest are retrieved from the implicit variable pageContext. The servlet context is then used to retrieve the JMS configuration information. This information is used to create and initialize an instance of JMSManager that will act as a message producer to the request queue.

The case details are retrieved from the HTTP request object and the next available case ID is retrieved from IDGenerator. These details are stored in a JMS map message and the message is sent to the request queue using the instance of JMSManager. The retrieved case ID is then stored in the HTTP request object to be retrieved by the enclosing JSP:

       public int doStartTag() throws JspTagException {         try {           ServletContext ctx = pageContext.getServletContext();           HttpServletRequest req =             (HttpServletRequest)pageContext.getRequest();           String requestQueue = ctx.getInitParameter("requestQueue");           System.out.printIn("requestQueue: " + requestQueue);           String qcFactory = ctx.getInitParameter("qcFactory");           System.out.printIn("qcFactory: " + qcFactory);           String ctxFactory = ctx.getInitParameter("ctxFactory");           System.out.printIn("ctxFactory: " + ctxFactory);           String provURL = ctx.getInitParameter("provURL");           System.out.printIn("provURL: " + provURL);           JMSManager requestManager =             new JMSManager (ctxFactory, provURL, requestQueue, qcFactory,               IJMSConstants.PTP_MODEL);           requestManager.initializeJMS(IJMSConstants.PRODUCER, null, true);           MapMessage msg = requestManager.createMapMessage();           String caseId = IDGenerator.getNextId();           msg.setString("caseId", caseId);           msg.setString("name", req.getParameter("name"));           msg.setString("location", req.getParameter("location"));           msg.setString("description", req.getParameter("description"));           requestManager.sendMessage(msg, true);           requestManager.closeJMS();           pageContext.setAttribute("caseId", caseId);           return EVAL_BODY_INCLUDE;         } catch (Exception e) {           System.out.printIn ("Exception in doStartTag: CreateCaseTag");           System.out.printIn(e.getClass());           System.out.printIn(e.getMessage());           e.printStackTrace();           throw new JspTagException(e.getMessage());         }       }       public int doEndTag() throws JspTagException {         return EVAL_PAGE;       }     } 

The CreateCaseTagVariableInfo Class

This class is used to notify the JSP engine about the scripting variable introduced by the case create tag. Tag extra info classes are used in custom tags for introducing scripting variables in the JSP enclosing the custom tag:

     package com.acme.user;     import javax.servlet.jsp.tagext.TagExtraInfo;     import javax.servlet.jsp.tagext.VariableInfo;     import javax.servlet.jsp.tagext.TagData;     public class CreateCaseTagVariableInfo extends TagExtraInfo {       public VariableInfo[] getVariableInfo(TagData data) {         VariableInfo info[] = new VariableInfo[1]; 

The tag extra info class introduces a variable named caseId of type java.lang.String that is available from the beginning of the tag to the end of the JSP and is newly declared by the JSP engine. This variable is used in the JSP for displaying the case ID:

         info[0] = new VariableInfo("caseId", "java.lang.String",                    true, VariableInfo.AT_BEGIN);         return info;       }     } 

The CaseHistoryTag Class

This is the tag handler class for the custom tag caseHistory:

     package com.acme.user;     import javax.servlet.jsp.*;     import javax.servlet.jsp.tagext.*;     import javax.servlet.*;     import javax.servlet.http.*;     import javax.jms.*;     import com.acme.common.*;     import java.util.*;     public class CaseHistoryTag extends BodyTagSupport {       private Iterator it; 

In the doStartTag() method of the tag handler, instances of ServletContext and HttpServletRequest are retrieved from the implicit variable pageContext. The servlet context is then used to retrieve an instance of the application scope bean responsePool. The requested case ID is retrieved from the HTTP Request object. The responsePool object is then queried to get a list of responses for the specified case ID. If the returned list is empty the method returns SKIP_BODY, otherwise an iterator is set to the list, the first object in the list is stored in the HTTP Request object, and EVAL_BODY_TAG is returned, which will cause the body of the custom tag to be evaluated:

     public int doStartTag() throws JspTagException {       ServletContext ctx = pageContext.getServletContext();       HttpServletRequest req =         (HttpServletRequest)pageContext.getRequest();       ResponsePool pool = (ResponsePool)ctx.getAttribute ("responsePool");       String caseId = req.getParameter ("caseId");       System.out.printIn(caseId);       it = pool.getResponseByCaseId(caseId).iterator();       if (it.hasNext()) {         req.setAttribute("caseResponse", it.next());         return EVAL_BODY_TAG;       }       System.out.printIn("Iterator is empty.");       return SKIP_BODY;     } 

In the doAfterBody() method, the current body content is flushed into the enclosing writer. If the iterator retrieved in the doStartTag() method contains more elements, the next available in the list stored in the HTTP Request object by the name caseResponse and EVAL_BODY is returned. Otherwise SKIP_BODY is retuned, which will break out of the tag loop:

     public int doAfterBody(){       try {         BodyContent body = getBodyContent();         if (body != null) {           getBodyContent().getEnclosingWriter().write(body.getString());           body.clear();         }       } catch (Exception e) {         new JspTagException(e.getMessage());       }       if (it.hasNext()) {         pageContext.getRequest().setAttribute("caseResponse", it.next());         return EVAL_BODY_TAG;       }       return SKIP_BODY;       }       public int doEndTag() throws JspTagException {         return EVAL_PAGE;       }     } 

The IDGenerator Class

This is a helper class for generating sequential case IDs:

     package com.acme.user;     public class IDGenerator {       public static long nextId = 10000;       public static synchronized String getNextId() {         return String.valueOf(++nextId);       }     } 

The Case Management System

This section explains the implementation details of the classes and JSPs used in the case management system.

The Object Model

The two basic entities identified in the case management system were:

  • An encapsulation of the details of case requests

  • A store for the incoming case requests from the message broker

The first entity was called CaseRequest with accessor and mutator methods for the attributes. The following attributes were defined for the CaseRequest entity:

  • The case ID

  • The name of the person raising the case

  • The location of the person

  • The description of the case

The entity for storing the case requests was called RequestPool and was expected to have the following functionality:

  • Asynchronously listen to the messages in the request queue

  • Expose a method to get the case requests currently in the pool

  • Expose a method to get a case request for the specified case ID

  • Expose a method to remove a case from the pool for the specified case ID

The class diagram shown below depicts the entities used in the case management system:

click to expand

The CaseRequest class follows the basic bean pattern with accessor and mutator methods for the attributes.

The RequestPool class implements the javax.jms.MessageListener interface to handle asynchronous message delivery, and the java.io.Serializable interface for cloning and persistence by the web container. It also defines methods to: return a map of all the case IDs to case requests available in the pool; return a case request for the passed case ID; and remove a case request identified by the passed case ID.

Setting up the Security

If you haven't already done so, repeat the previous steps with the WebLogic console to create a group called system and add the user tom to the group.

Setting up the Web Application

This is the standard J2EE web application configuration file for declaring servlets/JSPs, URL mapping, security constraints, and initialization arguments. It follows the same pattern as for the web.xml file of the case logging system, so we will not examine it in detail:

     <?xml version="1.0" encoding="Cp1252"?>     <!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>       <display-name>ACME</display-name>       <description>ACME Helpdesk System</description>       <context-param>         <param-name>responseQueue</param-name>         <param-value)responseQueue</param-value>       </context-param>       <context-param>         <param-name>requestQueue</param-name>         <param-value>requestQueue</param-value>       </context-param>       <context-param>         <param-name>qcFactory</param-name>         <param-value>qcFactory</param-value>       </context-param>       <context-param>         <param-name>ctxFactory</param-name>         <param-value>weblogic.jndi.T3InitialContextFactory</param-value>       </context-param>       <context-param>         <param-name>provURL</param-name>         <param-value>t3://localhost</param-value>       </context-param>       <servlet>         <servlet-name>ListCase</servlet-name>         <jsp-file>listcase.jsp</jsp-file>       </servlet>       <servlet>         <servlet-name>ViewCase</servlet-name>         <jsp-file>viewcase.jsp</jsp-file>       </servlet>       <servlet>         <servlet-name>UpdateCase</servlet-name>         <jsp-file>updatecase.jsp</jsp-file>       </servlet>       <servlet-mapping>         <servlet-name>ListCase</servlet-name>         <url-pattern>ListCase</url-pattern>       </servlet-mapping>       <servlet-mapping>         <servlet-name>ViewCase</servlet-name>         <url-pattern>ViewCase</url-pattern>       </servlet-mapping>       <servlet-mapping>         <servlet-name>UpdateCase</servlet-name>         <url-pattern>UpdateCase</url-pattern>       </servlet-mapping>       <welcome-file-list>         <welcome-file>/index.jsp</welcome-file>       </welcome-file-list>       <taglib>         <taglib-uri>/system</taglib-uri>         <taglib-location>/WEB-INF/system.tld</taglib-location>       </taglib>       <security-constraint>         <web-resource-collection>         <web-resource-name>system</web-resource-name>         <url-pattern>/*</url-pattern>       </web-resource-collection>       <auth-constraint>         <description>Helpdesk Assistants</description>         <role-name>system</role-name>       </auth-constraint>       <user-data-constraint>         <transport-guarantee>NONE</transport-guarantee>       </user-data-constraint>       </security-constraint>       <login-config>         <auth-method>FORM</auth-method>         <form-login-config>           <form-login-page>/logon.jsp</form-login-page>           <form-error-page>/logon.jsp</form-error-page>         </form-login-config>       </login-config>       <security-role>         <description>Helpdesk Assistants</description>         <role-name>system</role-name>       </security-role>     </web-app> 

The Welcome Screen

In the application we log on to the case management system by entering the URL http://localhost:7001/system in the browser and entering the system ID and password:

click to expand

The logon.jsp Page

This is exactly the same as before.

The banner.jsp Page

This JSP is included in all the JSP files in the case management system, and it contains the common menu items. The JSP basically displays two links. The first link points to the URL ListCase, which is mapped to listcase.jsp. The second link points to the file index.jsp:

     <%@ page language="Java" %>     <TD VALIGN="center" ALIGN="left" BGCOLOR="#000000">       <FONT SIZE="6" COLOR="#0055FF">         <B><I>ACME Systems Helpdesk</I></B>       </FONT>     </TD>     <TD ALIGN="right" BGCOLOR="#000000">       <A HREF="ListCase">         <FONT SIZE="3" COLOR="#0055FF">           <B>List Cases</B>         </FONT>       </A>       <FONT SIZE="3" COLOR="#0055FF">       <B>&nbsp;</B>       <A HREF="index.jsp">         <FONT SIZE="3" COLOR="#0055FF">           <B>Home&nbsp;</B>         </FONT>       </A>     </TD> 

The index.jsp Page

This is the first file served to the user after authentication. This JSP initializes an instance of the class RequestPool by passing the implicit variable pageContext. This instance will asynchronously listen to the case requests coming from the case logging system through the message broker and acts as a data repository for the case requests:

     <%@ page language="Java" %>     <jsp:useBean  scope="application"        />     <%       if(!requestPool.isInitialized()) {         requestPool.init(pageContext.getServletContext());       }     %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE BORDER="0" WIDTH="100%" HEIGHT="100%"          CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center">               <B><I>                 <FONT SIZE="4" COLOR="#000000">                   Welcome to ACME Systems Helpdesk<BR>                   This system helps ACME helpdesk assistants<BR>                   view cases raised by the users                 </FONT>               </I></B>             </TD>           </TR>         </TABLE>       </BODY>     </HTML> 

The Business Logic

We now examine the Java code that implements the logic of the helpdesk application.

The listcase.jsp Page

This JSP is used for listing all the pending and open cases in the request pool. In the application we would start by clicking on the List Cases link on the banner:

click to expand

The JSP first declares the tag library. Then it uses the custom tag listCase whose tag handler class is ListCaseTag:

     <%@ page language="Java"%>     <%@ taglib uri="/system" prefix="system" %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE WIDTH="100%" HEIGHT="100%" CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center">               <TABLE CELLPADDING="3" CELLSPACING="3" WIDTH="60%">                 <TR>                   <TD COLSPAN="4" ALIGN="center" BGCOLOR="#000000">                     <FONT SIZE="3" COLOR="#0055FF">                       <B>Case List</B>                     </FONT>                   </TD>                 </TR>               <TR>                 <TD><B>Case Id</B></TD>                 <TD><B>Name</B></TD>                 <TD><B>Location</B></TD>                 <TD><B>Description</B></TD>               </TR> 

This tag will be executed multiple times depending on the number of items in the case request list in the application scope bean requestPool. The request scope bean caseRequest of type CaseRequest is declared in the aforementioned custom tag. Each time the tag is iterated the tag handler class retrieves the next available item in the request list and stores it in the HTTP request object. The JSP renders the attributes of the case request object using the standard getProperty tag of useBean to access the object's attributes:

                 <system: listCase>                   <jsp: useBean  scope="request"                      />                   <TR>                   <TD>                   <A href="ViewCase?caseId=<jsp:getProperty name="caseRequest" property="caseId" />">                   <jsp:getProperty name="caseRequest" property="caseId" />                   </A>                   </TD>                   <TD>                   <jsp:getProperty name="caseRequest" property="name" />                   </TD>                   <TD>                   <jsp:getProperty name="caseRequest" property="location" />                   </TD>                   <TD>                   <jsp:getProperty name="caseRequest" property="description" />                   </TD>                   </TR>                 </system: listCase>               </TABLE>             </TD>           </TR>         </TABLE>       </BODY>     </HTML> 

The viewcase.jsp Page

This JSP is called when the helpdesk assistant selects a case from the list and requests to view the details. We would view the case details by clicking on the link 10001 in the last screenshot:

click to expand

The JSP first declares the tag library. Then it uses the custom tag viewCase whose tag handler class is ViewCaseTag:

     <%@ page language="Java"%>     <%@ taglib uri="/system" prefix="system" %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE WIDTH="100%" HEIGHT="100%" CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center"> 

This tag retrieves the selected case request from the requestPool. The request scope bean caseRequest of type CaseRequest is declared with in the above tag. The JSP renders the attributes of the case request object using the standard getProperty tag of useBean to access the object's attributes:

                 <system: viewCase>                   <jsp: useBean  scope="request"                      />                 <FORM METHOD="post" ACTION="UpdateCase">                 <INPUT NAME="caseId" TYPE="hidden" value=                 "<jsp: getProperty name="caseRequest"                   property="caseId" />">                 <INPUT NAME="name" TYPE="hidden" value=                 "<jsp: getProperty name="caseRequest"                     property="name" />">                 <INPUT NAME="location" TYPE="hidden" value=                 "<jsp: getProperty name="caseRequest"                     property="location" />">                 <INPUT NAME="description" TYPE="hidden" value=                 "<jsp: getProperty name="caseRequest"                     property="description" />">                 <TABLE CELLPADDING="3" CELLSPACING="3">                   <TR>                   <TD COLSPAN="2" ALIGN="center" BGCOLOR="#000000">                         <FONT SIZE="3" COLOR="#0055FF">                           <B>Case Details</B>                         </FONT>                       </TD>                     </TR>                     <TR>                     <TD ALIGN="right"><B>Case Id:</B></TD>                     <TD>                     <jsp:getProperty name="caseRequest" property="caseId" />                     </TD>                   </TR>                   <TR>                     <TD ALIGN="right"><B>Name:</B></TD>                     <TD>                     <jsp:getProperty name="caseRequest" property="name" />                     </TD>                   </TR>                   <TR>                     <TD ALIGN="right"><B>Location:</B></TD>                     <TD>                     <jsp:getProperty name="caseRequest" property="location" />                     </TD>                   </TR>                   <TR>                     <TD ALIGN="right"><B>Description:</B></TD>                     <TD>                     <jsp:getProperty name="caseRequest" property="description" />                     </TD>                   </TR>                   <TR>                     <TD ALIGN="right"><B>Status:</B></TD>                     <TD>                       <SELECT NAME="status">                         <OPTION>pending</OPTION>                         <OPTION>closed</OPTION>                       </SELECT>                     </TD>                   </TR>                   <TR>                     <TD VALIGN="top" ALIGN="right"><B>Comment:</B></TD>                     <TD><TEXTAREA NAME="comment" COLS="40"                                 ROWS="10"></TEXTAREA>                     </TD>                   </TR>                   <TR><TD COLSPAN="2" ALIGN="right">                           <INPUT TYPE="submit" value="Update Case">                       </TD>                   </TR>                 </TABLE>               </FORM>             </system:viewCase>           </TD>         </TR>       </TABLE>     </BODY>   </HTML> 

The updatecase.jsp Page

This JSP is called when the helpdesk assistant updates the case status. After adding a comment or solution, click Update Case:

click to expand

     <%@ page language="Java"%>     <%@ taglib uri="/system" prefix="system" %>     <HTML>       <HEAD><TITLE>ACME Systems</TITLE></HEAD>       <BODY>         <TABLE BORDER="0" WIDTH="100%" HEIGHT="100%"                CELLPADDING="0" CELLSPACING="0">           <TR HEIGHT="20%">             <%@ include file="banner.jsp" %>           </TR>           <TR>             <TD BGCOLOR="#CCCCCC" COLSPAN="2" ALIGN="center"> 

The JSP file also uses the custom tag updateCase, which will invoke the relevant methods in the UpdateCaseTag class. The tag handler class will retrieve the case update details entered by the helpdesk assistant and send a JMS message as explained earlier:

               <system:updateCase/>               <B>                 Your request has been successfully processed.<BR>                 The case update has been made available in the<BR>                 user's case history               </B>             </TD>           </TR>         </TABLE>       </BODY>     </HTML> 

The CaseRequest Class

This is the class that models a case request received from the case logging system. This class has been explained in detail in the section on the object model:

     package com.acme.system;     public class CaseRequest {       private String caseId;       private String name;       private String location;       private String description;       public CaseRequest() {}       public String getCaseId() {         return caseId;       }       public void setCaseId(String caseId) {         this.caseId = caseId;       }       public String getName() {         return name;       }       public void setName (String name) {         this.name = name;       }       public String getLocation() {         return location;       }       public void setLocation (String location) {         this.location = location;       }       public String getDescription() {         return description;       }       public void setDescription (String description) {         this.description = description;       }     } 

The RequestPool Class

An instance of this class works as a data repository for the case requests coming from the case logging system. The same instance is used as an application scope bean that listens for asynchronous JMS messages sent to the request pool by the case logging system:

     package com.acme.system;     import java.util.HashMap;     import javax.naming.*;     import javax.jms.*;     import javax.servlet.ServletContext;     import java.io.Serializable;     import com.acme.common.JMSManager;     import com.acme.common.IJMSConstants; 

This class implements the javax.jms.MessageListener interface for asynchronously receiving the request messages sent by the case logging system to the request queue:

     public class RequestPool implements MessageListener, Serializable { 

The class uses four instance variables:

  • A boolean to indicate whether the instance of the class is initialized or not. The instances are initialized by calling the init() method.

  • An instance of JMSManager called requestManager, that acts as a message consumer to the request queue.

  • An instance of JMSManager, called responseManager that acts as a message producer to the response queue. This object is used for sending the initial response when a case request first arrives.

  • An instance of HashMap that stores a map of case IDs to case requests. This data structure is interrogated when the helpdesk assistants query case request lists and case request details:

     private boolean initialized;     private JMSManager requestManager;     private JMSManager responseManager;     private HashMap requestMap = new HashMap(); 

The init() method takes an instance of ServletContext as the argument. It then queries the web.xml file through the instance of the servlet context for the JMS configuration information. This information is then used to create and initialize responseManager and the instance of the class registers itself as a message listener with the responseManager. It also initializes the instance requestManager. Finally it sets the value of initialized to true:

     public void init (ServletContext ctx) throws NamingException,         JMSException, Exception {       String responseQueue = ctx.getInitParameter ("responseQueue");       System.out.println ("responseQueue: " + responseQueue);       String requestQueue = ctx.getInitParameter ("requestQueue");       System.out.println ("requestQueue: " + requestQueue);       String qcFactory = ctx.getInitParameter ("qcFactory");       System.out.println ("qcFactory: " + qcFactory);       String ctxFactory = ctx.getInitParameter ("ctxFactory");       System.out.println ("ctxFactory: " + ctxFactory);       String provURL = ctx.getInitParameter ("provURL");       System.out.println ("provURL: " + provURL);       responseManager = new JMSManager (ctxFactory, provURL,         responseQueue, qcFactory, IJMSConstants.PTP_MODEL);       responseManager.initializeJMS (IJMSConstants.PRODUCER,         null, true);       requestManager = new JMSManager (ctxFactory, provURL,         requestQueue, qcFactory, IJMSConstants.PTP_MODEL);       requestManager.initializeJMS (IJMSConstants.CONSUMER,         this, true);       initialized = true;     }     public boolean isInitialized() {       return initialized;     } 

The callback method for asynchronous messages retrieves the case request details from the message and creates an instance of CaseRequest class. This object is stored against the case ID in the responseMap. A response message is then sent to the response pool using responseManager with the case status set to "open":

     public synchronized void onMessage (Message msg) {       try {         MapMessage mapMsg = (MapMessage) msg;         String caseId = mapMsg.getString ("caseId");         String name = mapMsg.getString ("name");         String location = mapMsg.getString ("location");         String description = mapMsg.getString ("description");         CaseRequest request = new CaseRequest();         request.setCaseId (caseId);         request.setName (name);         request.setLocation (location);         request.setDescription (description);         requestMap.put (caseId, request);         acknowledge (request);       } catch(JMSException e) {         e.printStackTrace();       }     } 

Methods are also defined for getting the case request details and removing the case request for the specified case ID:

       public synchronized CaseRequest getRequestByCaseId(String caseId) {         return (CaseRequest) requestMap.get (caseId);       }       public synchronized void removeCaseRequest(String caseId) {         requestMap.remove(caseId);       }       public synchronized HashMap getCaseRequestMap() {         return (HashMap) requestMap.clone();       }       private void acknowledge (CaseRequest request) throws JMSException {         MapMessage mapMsg = responseManager.createMapMessage();         mapMsg.setString("caseId", request.getCaseId());         mapMsg.setString("name", request.getName());         mapMsg.setString("location", request.getLocation());         mapMsg.setString("description", request.getDescription());         mapMsg.setString("status", "open ");         mapMsg.setString("comment", "");         responseManager.sendMessage(mapMsg, true);       }     } 

The JSP Tag Library

This is the tag library descriptor file system.tld that stores the information about the custom tags used in the helpdesk system:

     <?xml version="1.0" ?>     <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"     "web-jsptaglib_1_1.dtd">     <taglib>       <tlibversion>1.0</tlibversion>       <jspversion>1.1</jspversion>       <shortname>System</shortname>       <info>         This tag library contains tag extensions       </info> 

This file defines three custom tags used in the previous sections. The custom tag listCase is associated with the tag handler class com.acme.system.ListCaseTag and the body content is defined as parsable JSP content:

       <tag>         <name>listCase</name>         <tagclass>com.acme.system.ListCaseTag</tagclass>         <bodycontent>JSP</bodycontent>       </tag> 

The custom tag viewCase is associated with the tag handler class com.acme.user.ViewCaseTag and the body content is defined as parseable JSP content:

       <tag>         <name>viewCase</name>         <tagclass>com.acme.system.ViewCaseTag</tagclass>         <bodycontent>JSP</bodycontent>       </tag> 

The custom tag updateCase is associated with the tag handler classcom.acme.user.UpdateCaseTag and the body content is defined as parseable JSP content:

       <tag>         <name>updateCase</name>         <tagclass>com.acme.system.UpdateCaseTag</tagclass>         <bodycontent>empty</bodycontent>       </tag>     </taglib> 

The UpdateCaseTag Class

This is the tag handler class for the custom tag updateCase:

     package com.acme.system;     import javax.servlet.jsp.*;     import javax.servlet.jsp.tagext.*;     import javax.servlet.*;     import javax.servlet.http.*;     import javax.jms.*;     import com.acme.common.*;     public class UpdateCaseTag extends TagSupport { 

In the doStartTag() method of the tag handler instances of ServletContext and HttpServletRequest are retrieved from the implicit variable pageContext. The servlet context is then used to retrieve the JMS configuration information. This information is used to create and initialize an instance of JMSManager that will act as a message producer to the response queue. The case update details are retrieved from the HTTP request object. These details are stored in a JMS map message and the message is sent to the response queue using the instance of JMSManager. If the case status is "closed" the case request is removed from the case request pool:

       public int doStartTag() throws JspTagException {         try {           ServletContext ctx = pageContext.getServletContext();           HttpServletRequest req =             (HttpServletRequest)pageContext.getRequest();           RequestPool pool =             (RequestPool) ctx.getAttribute("requestPool");           String responseQueue = ctx.getInitParameter("responseQueue");           System.out.println("requestQueue: " + responseQueue);           String qcFactory = ctx.getInitParameter("qcFactory");           System.out.println("qcFactory: " + qcFactory);           String ctxFactory = ctx.getInitParameter("ctxFactory");           System.out.println("ctxFactory: " + ctxFactory);           String provURL = ctx.getInitParameter("provURL");           System.out.println("provURL: " + provURL);           JMSManager responseManager =             new JMSManager(ctxFactory, provURL, responseQueue, qcFactory,               IJMSConstants.PTP_MODEL);           responseManager.initializeJMS(IJMSConstants.PRODUCER, null, true);           MapMessage msg = responseManager.createMapMessage();           msg.setString("caseId", req.getParameter("caseId"));           msg.setString("name", req.getParameter("name"));           msg.setString("location", req.getParameter("location"));           msg.setString("description", req.getParameter("description"));           msg.setString("status", req.getParameter("status"));           msg.setString("comment", req.getParameter("comment"));           responseManager.sendMessage(msg, true);           responseManager.closeJMS();           if(req.getParameter("status").equals("closed")) {             pool.removeCaseRequest(req.getParameter("caseId"));           }           return EVAL_BODY_INCLUDE;         } catch(Exception e) {           System.out.println("Exception in doStartTag: CreateCaseTag");           System.out.println(e.getClass());           System.out.println(e.getMessage());           e.printStackTrace();           throw new JspTagException(e.getMessage());         }       }       public int doEndTag() throws JspTagException {         return EVAL_PAGE;       }     } 

The ListCaseTag Class

This is the tag handler class for the custom tag listCase. This class implements JSP body tag:

     package com.acme.system;     import javax.servlet.jsp.*;     import javax.servlet.jsp.tagext.*;     import javax.servlet.*;     import javax.servlet.http.*;     import javax.jms.*;     import com.acme.common.*;     import java.util.*;     public class ListCaseTag extends BodyTagSupport {       private Iterator it; 

In the doStartTag() method of the tag handler, instances of ServletContext and HttpServletRequest are retrieved from the implicit variable pageContext. The servlet context is then used to retrieve an instance of the application scope bean requestPool. The requestPool object is then queried to get the list of case requests. If the returned list is empty the method returns SKIP_BODY, otherwise an Iterator is created for the list, the first object in the list is stored in the HTTP request object, and EVAL_BODY_TAG is returned, which will cause the body of the custom tag to be evaluated:

       public int doStartTag() throws JspTagException {         ServletContext ctx = pageContext.getServletContext();         RequestPool pool = (RequestPool) ctx.getAttribute("requestPool");         it = pool.getCaseRequestMap().values().iterator();         if(it.hasNext()) {           pageContext.getRequest() .setAttribute("caseRequest", it.next());           return EVAL_BODY_TAG;         }         return SKIP_BODY;       }       public int doAfterBody(){         try {           BodyContent body = getBodyContent();           if(body != null) {             getBodyContent().getEnclosingWriter() .write(body.getString());             body.clear();           }         } catch(Exception e) {           new JspTagException(e.getMessage());         }         if(it.hasNext()) {           pageContext.getRequest() .setAttribute("caseRequest", it.next());           return EVAL_BODY_TAG;         }         return SKIP_BODY;       }       public int doEndTag() throws JspTagException {         return EVAL_PAGE;       }     } 

The ViewCaseTag Class

This is the tag handler class for the custom tag viewCase:

     package com.acme.system;     import javax.servlet.jsp.*;     import javax.servlet.jsp.tagext.*;     import javax.servlet.*;     import javax.servlet.http.*;     import javax.jms.*;     import com.acme.common.*;     import java.util.*;     public class ViewCaseTag extends TagSupport {       private CaseRequest caseReq;       public int doStartTag() throws JspTagException { 

In the doStartTag() method the case ID is retrieved from the HTTP request and the application scope bean requestPool is retrieved from the servlet context. The case details are then retrieved from the bean for the specified case is and the case request object is stored in the Request object:

         ServletContext ctx = pageContext.getServletContext();         HttpServletRequest req =           (HttpServletRequest)pageContext.getRequest();         RequestPool pool = (RequestPool)ctx.getAttribute("requestPool");         String caseId = req.getParameter("caseId");         caseReq = pool.getRequestByCaseId(caseId);         if(caseId != null) {           pageContext.getRequest().setAttribute("caseRequest", caseReq);         }         return EVAL_BODY_INCLUDE;       }       public int doEndTag() throws JspTagException {         return EVAL_PAGE;       }     } 



Professional JMS
Professional JMS
ISBN: 1861004931
EAN: 2147483647
Year: 2000
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net