BizTalk Utilities CV ,   Jobs ,   Code library  
 
 
Page 6 of 9

 

Previous Page Table Of Contents Next Page

WML Applications using JavaServer Pages (JSP)

An Overview of JSP

JSP syntax is quite straightforward and compact: it all fits on a double-page syntax card available from Sun (java.sun.com/products/jsp/syntax.pdf). A JSP consists of template data and JSP elements, which fall into the following groups: directives, scripting elements, comments, and actions. Scripting elements are further subdivided into declarations, expressions, and code fragments (or scriptlets). We'll see their syntax and describe their meaning shortly.

 

The first three groups have always been part of JSP, and they have non-XML syntax as well as alternative XML+Namespace syntax. The actions group is more recent, and uses only the XML+Namespace syntax.


Non-XML Elements

Non-XML syntax of directives, scripting elements and comments is summarized in the table below. You have seen all of them used in our simple example:

 

Type of element

Syntax description

Example

Directives

<%@ directive %>

<%@ page language="java" %>

Declarations

<%! declarations %>

<%! int i=0, j=5; %>

Expressions

<%= expression %>

<%= i+7 %>

Code fragments

<% code fragment %>

<% if(i < j-4 { %>

Comments

<%-- comment --%>

<%-- not for the client --%>

What they all Mean

Ø       Directives are addressed to the JSP engine. They do not produce any output. You will see two directives in our output pages: page and include. include does exactly what its name suggests: it is used to include files into a page. The page directive is for setting the global properties of the page, such as language and content type.

Ø       Within scripting elements, declarations are exactly that: Java declarations and (perhaps) initializations. Declarations do not produce any output.

Ø       Expressions are Java expressions; they are evaluated and their values are inserted into the output stream.

Ø       Code fragments are stretches of Java code. They don't have to be complete statements or valid expressions.

Ø       JSP comments are not sent to the client; they are strictly for documenting code.

Ø       You can also use standard XML comments in a JSP, and they will be treated like regular XML comments. You can even include non-XML JSP content in XML-style comments, and it will be treated as part of the comment - that is, it will be ignored.

Action Elements

Action elements fall into two groups:

 

Ø       Actions having to do with JavaBeans: useBean, getProperty, setProperty. Beans are Java classes that conform to some simple patterns: using public get/set methods to access properties, providing a public, no-arguments constructor, and being serializable.

Ø       include and forward actions

 

All action tags appear with the jsp: namespace prefix; here's an example of using them:

 

<jsp:useBean id="mbean" scope="session" class="weather.MainBean" />

 

Beginning with JSP 1.1, it is possible to define custom actions (using the jsp:taglib element). This has the potential greatly to extend the application of JSP and the reuse of common functionality, allowing non-programmers to create JSPs by simplifying the architecture of JSP-based applications.


JSPs in the Application

Our application uses the main JSP page for overall control, configure.jsp to configure the system, and several JSP pages for output. I'll show them in that order.

The Main Page, weather.jsp

This page never sees the light of day (in the browser), so it has only JSP elements: two directives, a useBean action, and a piece of Java code. The code carries out the dialog between the main page and the main bean described in the diagram: the main page sends the request object to the main bean for analysis; the main bean tells it to which output page to forward the request. Here's the code:

 

<%@ page errorPage="errorpage.jsp" %>

<%@ include file="configure.jsp" %>

 

<jsp:useBean id="qBean" class="MyNa.jspUtil.QBean" scope="session"/>

 

<%

   qBean.doCommand(request);

   if(true) {

      out.clear();

      pageContext.forward(qBean.whereTo());

      return;

   }

%>

configure.jsp

Here is configure.jsp. Like weather.jsp, it contains nothing but Java code:

 

<%

   // Information for connecting to database

   String[] dbParams = new String[] {

      "sun.jdbc.odbc.JdbcOdbcDriver",

      "jdbc:odbc:WEATHER",

      "userName",

      "defaultPassword"

   };

 

   // Queries available to the client

   String[][] dbQueries = new String[][] {

      {"AllTable", // All fields formatted as a table

       "SELECT * FROM FORECAST WHERE Zip=?"},

 

      {"AllText",  // All fields formatted as a paragraph of text

       "SELECT * FROM FORECAST WHERE Zip=?"},

 

      {"TimeTemp", // Date and temperature only

       "SELECT RecTime,Temp,DayLo,DayHi FROM FORECAST WHERE Zip=?"}

   };

 

   // Associate each query with an output JSP file

   String[][] responseTargets = new String[][]{

      {"AllTable ", "/jsp/weather/xhtml/AllTable.jsp"},

      {"AllText ", "/jsp/weather/xhtml/AllText.jsp"},

      {"TimeTemp", "/jsp/weather/xhtml/TimeTemp.jsp"}

   };

 

   // This code is actually run once per session

   qBean.doConfigure(dbParams, dbQueries, responseTargets);

%>

 

As you can see, there are three declaration sections in the file, followed by a single function call. The first section has to do with information that is needed to connect to a database using a JDBC driver - that is, information needed to create a Connection object.

 

The second section has to do with running queries. It consists of name-value pairs, where each value is a string you would use to create a PreparedStatement object. These values are stored in a dictionary-like object (a Hashtable), indexed by the corresponding names. In order to run a query, the user has only to specify its name (in a select element of the form) and provide the values for the parameters of the PreparedStatement. Since these parameters are ordered, the form elements for entering query parameters must have such names as Parameter1, Parameter2, and so on (or, to shorten them for the wireless Web where every byte counts, QP1, QP2). This is a convention that is needed for our DBHandler.

 

The third section associates output templates with query names. It also consists of name-value pairs, where the names are the names of queries, and values are the names of JSP files to forward the request to.

 

The configuration page includes a call to qBean.doConfigure(). The task of doConfigure() is to set up an object of our DBHandler class, presented in the next section.

 

Although doConfigure() is called once per request, it will have no effect after the session is first set up and a DBHandler object is stored in it. (The method starts with an if statement that checks to see whether the DBHandler is null.)

 

We could also instantiate and configure the main bean, including a DBHandler, from an XML configuration file. This would require more background machinery to explain. The method shown here allows a system administrator to configure the database access and alter the target files without introducing too many new concepts.

JSPs for Output

There are three output pages corresponding to three queries: TimeTemp, AllTable, and AllText. Here is AllTable.jsp:

 

<?xml version="1.0"?>

<!DOCTYPE wml PUBLIC "-//PHONE.COM//DTD WML 1.1//EN"

                     "http://www.phone.com/dtd/wml11.dtd">

<wml>

 

   <%@ page errorPage="../wmlerrorpage.jsp" %>

   <jsp:useBean id="qBean" scope="session" class="MyNa.jspUtil.QBean" />

 

   <head>

      <meta http-equiv="Cache-Control" content="no-cache" forua="true"/>

   </head>

 

   <card id="output" title="AllTable">

      <do type="accept" label="again" > <go href="#askCard" /> </do>

 

      <%

         MyNa.jspUtil.QueryResult qR = qBean.queryResult();

         String[][] rows = (null == qR) ? null : qR.getRows();

         if(null == rows || rows.length < 1) {

      %>

      <p>Sorry, no weather in zip-code.</p>

      <%

         } else { String[] headers = qR.getColumnHeaders();

      %>
      <p>Weather:</p>

      <p><table columns="2">

         <%  // We use only rows[0] to generate a 2-column table of fields

            for(int j = 0; j < headers.length; j++) {

         %>

         <tr>

            <td><%= headers[j] %></td><td><%= rows[0][j] %></td>

         </tr>

         <%   }   %>

         </table>

      </p>

      <% } %>

   </card>

 

   <card id="askCard" title="Weather">

      <do type="accept" label="Go!" >

         <go href="weather.jsp">

            <postfield name="query" value="$query" />

            <postfield name="QP1" value="$QP1" />

            <postfield name="target" value="wml-$(query)" />

         </go>

      </do>

      <p align="center">Weather Page</p>

      <p>

         Zip?

         <input name="QP1" format="*N" maxlength="10" value="" /><br />

         Query?

         <select name="query">

            <option value="TimeTemp" > time &amp; temp </option>

            <option value="AllTable" > all fields </option>

            <option value="AllText" > all fields-format </option>

         </select>

      </p>

   </card>

</wml>

 

Notice how this page gets the result set from the DBHandler as a string matrix and dumps it directly to an output table, without any regard for its size. This is OK for a web application, but a risky thing to do in a WAP application. It is for this reason that we also provide an AllText query, in which the output is crafted by hand and so can be controlled more precisely. Here are the cards for AllText.jsp:

 

<card id="output" title="TimeTemp">

   <do type="accept" label="again">

      <go href="#ask" />

   </do>

   <do type="accept" label="details" >

      <go href="#details" />

   </do>

   <% // Zip,Day,RecTime,Temp,DayLo,DayHi,Precip%,Warn,Tomorrow,NextDay

 

      MyNa.jspUtil.QueryResult qR = qBean.queryResult();

      String[][]rows = (null == qR) ? null : qR.getRows();

      if(rows == null || rows.length < 1){

      // title row should be always there

   %>

   <p>Sorry, no weather in your zip </p>

</card>

 

<% } else {

   Dict D = new Dict();

   D.setDef(qR.getColumnHeaders(), rows[0]);

   D.setOutLimit(300);

%>

 

   <p>

      zip: <%= D.getDef("zip") %> <br/>

      temp: <%= D.getDef("temp") %><br/>

      at <%= D.getDef("RECTIME") %> <br/>

      day's lo: <%= D.getDef("daylo") %> <br/>

      day's hi: <%= D.getDef("dayhi") %>

   </p>

</card>
<card id="details" title="TimeTemp">

   <do type="accept" label="again" >

      <go href="#ask" />

   </do>

   <do type="accept" label="more" >

     <go href="#moreDetails" />

   </do>

   <p>

      day: <%=D.getDef("day") %><br/>

      precip: <%=D.getDef("precip") %>

   </p>

</card>

 

<card id="moreDetails" title="TimeTemp">

   <do type="accept" label="again" > <go href="#ask" /> </do>

   <do type="accept" label="basics" > <go href="#output" /> </do>

   <p>

      <%  String W = D.getDef("warn");

         if (W.length() > 0) { %>

      Warning: <%= W %> <br/>

      <%  } %>

     Tomorrow: <%= D.getDef("tomorrow") %> <br/>

     NextDay: <%= D.getDef("nextday") %>

   </p>

</card>

 

<% } %>

<!-- the remaining ask card is the same -->

 

This time, we control the amount of output by means of the setOutLimit() method, and we provide three cards of increasing level of detail. Something to be aware of is that the amount of output is controlled on the server, in characters. Testing is needed to see how this limit translates into limits on the amount of output going from the gateway to the browser.

JDBC basics

We have now seen the configuration page and the output pages. It's time to look at the machinery in the middle that gets the data. In outline, JDBC code usually goes through the following steps:

 

Ø       Load the database driver

Ø       Open a connection to the database

Ø       Create a Statement object

Ø       Use the Statement object to send SQL statements to the database

Ø       Process the results

 

I will now go through the steps in more detail, with simple code to illustrate them.

Load the driver

The JDBC API is based on the notion of database-specific drivers that are manipulated by a DriverManager object.

 

Drivers come in several shapes and forms. Some drivers are freeware or open source; others are commercial products. The JDK comes with a generic JDBC-ODBC bridge that passes SQL statements on to an appropriate ODBC driver. This way, a Java application can work with any database for which an ODBC driver is available. JDBC drivers that talk to the database directly will generally provide better performance, though, so the bridge shouldn't be used in production environments.

 

A JDBC driver is loaded by calling the forName() method of the class called Class:

 

   String driverName = "sun.jdbc.odbc.JdbcOdbcDriver";

   ...

   Class.forName(driverName);


Objects of the Class class contain information about Java classes, such as the names of their methods and the arguments of their constructors.

 

For each Java class that has been loaded into the Java Virtual Machine there is a Class object. You can retrieve that object by calling the getClass() method on any instance of your class, or you can simply say myObj.class. Once you have obtained the Class object for your class, you have access to a lot of information about it.

 

The forName() method dynamically loads a class that has not yet been loaded. Since a JDBC driver is a Java class, it can be loaded using the forName() method. All you need to know is the fully qualified name of the driver, which in our case is sun.jdbc.odbc.JdbcOdbcDriver.

Open a connection

 

   String dbURL = "jdbc:odbc:PhoneBook";

   ...

<