★ wanayoo — archive 1999 http://developer.iplanet.com/viewsource/marchal_xml.htmNouvelle recherche | Portail wanayoo
iPlanet

You are here:  Home > Developers > View Source Articles > Java View Source Article
Java View Source Article
 iPlanet Developers


Developer Program
  Membership
  One-to-One Support
  Newsgroups
  Member Services

Developer Publications
  View Source
  Developer News

Documentation
  Technical Manuals
  White Papers
  TechNotes
  Sample Code
  FAQs
  Books

Technologies
  Application Server   CSS
  DOM
  CORBA
  Directory & LDAP
  Dynamic HTML
  Java
  JavaScript
  Linux
  RDF
  Security
  SSJS
  XML
  XUL

Developer Downloads
  Tools & SDKs
  Patches

iPlanet Products
  Technical Resources










spacer
Servlet Programming for Teams :
How Java Programmers and HTML Designers Can Collaborate Using XML

By Benoît Marchal


Send comments and questions about this article to View Source.
Click here for printer-friendly version

[Editor's note: Since this article was published, the W3C has updated XSL. For further information, see More Servlet Programming for Teams: Style Sheet Changes and Advanced Techniques.]

Servlets - Java's replacement for CGI scripts - are increasingly popular because they're more efficient than CGI scripts and are available on all major platforms. But there's one problem with servlets: they encourage you to mix the code for the presentation (HTML) with the code for the application logic (computing values, querying databases, and so on).

This article presents a technique I developed for cleanly separating the presentation aspect from the application logic in Java servlets, to match the organization of most web site development teams into HTML designers and Java developers. The technique uses XML (Extensible Markup Language) and XSL (XML Stylesheet Language). You can download the source code for the example I'll use to illustrate the solution.

ALL MIXED UP

Listing 1 shows a servlet that mixes presentation code and application logic. (The View Source article Adding Java Servlet API Capability to Netscape Servers: Introducing Live Software's JRun Servlet Engine describes how to use servlets with Netscape servers.) The servlet replies with an HTML file that's hard-coded in the servlet.


Listing 1
protected void doGet(HttpServletRequest request,
                     HttpServletResponse resp)
   throws IOException {
   Writer w = response.getWriter();
   w.write("<HTML>\n<HEAD>\n<TITLE>Pineapplesoft</TITLE>\n</HEAD>\n");
   w.write("<BODY>\n");
   w.write("<P>Pineapplesoft welcomes you to its servlet.\n");
   w.write("<BR>You are visitor " + visitorCount++);
   w.write("</BODY>\n</HTML>");
   w.flush();
}

Sun has introduced Java Server Pages (JSP) as an alternative to servlets. (See the article Introduction to JavaServer Pages: Server-Side Scripting the Java Way.) JSP works the other way around: instead of embedding HTML in Java code, it embeds Java code in HTML. Although this makes servlet programming more accessible, it's hardly an improvement from a maintenance point of view.

The major problem with mixing HTML and Java code is that it doesn't reflect how web site development teams are typically organized, into Java developers in charge of Java programming and HTML designers responsible for the presentation. Obviously the two sides need to work together, but they don't have the same priorities. Java developers are usually more concerned with the robustness and efficiency of their code than with the details of the presentation, while HTML designers need rapid turnover of presentation changes.

My goals for a solution to this problem were:

  • to separate the HTML design from the Java coding
  • to give the HTML designer tools to modify the presentation without requiring assistance from the Java developer
  • to support multilingual web sites (which are very common where I live, in Belgium)

XML TO THE RESCUE

XML is a relatively new markup language that's been developed by the World Wide Web Consortium (W3C), the same organization that's in charge of HTML development. XML syntax is similar to HTML but, as its name implies, XML is extensible. In practice, this means that XML lets you create your own tags.

HTML has a fixed set of tags (<BODY>, <TITLE>, <P>, <IMG>, and so on) that were created by the W3C. XML has no built-in tags; it's up to you to create the tags you need. Whereas HTML has many tags that carry presentation instructions (including <FONT>, <CENTER>, and <PRE>), the tags you create in XML instead focus on the logical structure of the information. To render the information on the screen, you apply a style sheet - an approach that cleanly isolates the presentation.

Listing 2 is an XML document for a shopping cart. The syntax will look familiar: as in HTML, tags are the names of elements enclosed in angle brackets, with a slash (/) added in the end tag. However, you'll recognize none of the tags you're used to from HTML; the tags used here have been created specifically for this application.


Listing 2
<?xml version="1.0" encoding="ISO-8859-1"?>
<shopping-cart>
   <product id="0">
      <name>WhizBang Ultra Word Processor</name>
      <description xml:lang="EN">More words per minute than the competition.</description>
      <description xml:lang="FR">Plus de mots à la minute que la concurrence.</description>
      <image>wordprocessor.jpg</image>
      <price>$799.99</price>
   </product>
   <product id="1">
      <name>Super WhizBang Calculator</name>
      <description xml:lang="EN">Cheap and reliable with power saving.</description>
      <description xml:lang="FR">Economique et fiable avec économie d'énergie.</description>
      <image>calculator.jpg</image>
      <price>$5.99</price>
   </product>
   <total>$805.98</total>
</shopping-cart>

In general, XML is stricter than HTML:

  • Every element must have both a start tag and an end tag. For example, it would be illegal to write <price>$799.99; you have to write <price>$799.99</price> (with two tags). Fortunately, empty elements (with nothing between the start and end tags) have a special syntax that saves typing: the ending slash can be placed in the start tag rather than in a separate end tag, as in <img src=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Blogo.gif%26quot%3B%2F%26gt%3B%3C%2FTT&y=1999>.
  • Tags are case-sensitive: <price> is different from <PRICE>.
  • Attribute values must be enclosed in quotation marks. For example, <description xml:lang=EN> ... </description> is incorrect; you must write <description xml:lang="EN"> ... </description> or <description xml:lang='EN'> ... </description>.

The first line of Listing 2 is the XML declaration. It identifies the XML version and the character set being used. XML is based on the Unicode character set, which means it supports all alphabets, including Japanese and Chinese characters. Yet most documents need only a subset of the characters in the Unicode set. The document in Listing 2 is based on the ISO-8859-1 character set, also known as Latin-1; this is the default character set for Windows.

VIEWING XML IN A BROWSER

In the future, browsers will be able to render XML directly. The Mozilla browser from mozilla.org is a good example of how next-generation browsers will handle XML. In the meantime, it's safer to convert XML to HTML on the server. This gives you the best of both worlds: the site is viewable with the current generation of browsers, but internally it benefits from a clean separation between the processing and the presentation.

Because so many people need to convert XML documents to HTML, the W3C has developed XSL. XSL is a standard style sheet language that, among other things, supports conversion from XML to HTML.

Listing 3 is an XSL style sheet that converts the shopping cart in Listing 2 to HTML. As you can see, the style sheet is itself an XML document. I won't go into all the glorious details of XSL in this article; a future article will cover more advanced XSL techniques.


Listing 3
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
                xmlns="http://www.w3.org/TR/REC-html40" result-ns="">

<xsl:output method="html"/>

<xsl:template match="/">
   <HTML>
   <HEAD>
      <TITLE>WhizBang Shopping Cart</TITLE>
   </HEAD>
   <BODY>
      <P>Your shopping cart contains the following items:</P>
      <TABLE BORDER="0">
         <TR><TD><B>Nom</B></TD><TD><B>Prix</B></TD></TR>
         <xsl:for-each select="shopping-cart/product">
            <TR>
               <TD><xsl:value-of select="name"/></TD>
               <TD><xsl:value-of select="price"/></TD>
               <TD>
                  <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B&y=1999 METHOD="POST">
                     <INPUT TYPE="HIDDEN" NAME="remove">
                        <xsl:attribute name="VALUE"><xsl:value-of select="@id"/></xsl:attribute>
                     </INPUT>
                     <INPUT TYPE="SUBMIT" VALUE="Remove"/>
                  </FORM>
               </TD>
            </TR>
         </xsl:for-each>
         <TR>
            <TD>Total</TD>
            <TD><xsl:value-of select="shopping-cart/total"/></TD>
         </TR>
      </TABLE>
      <TABLE BORDER="0">
      <TR>
         <TD><FORM><INPUT TYPE="BUTTON" VALUE="Checkout"/></FORM></TD>
         <TD><FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bproductlist%26quot%3B%26gt%3B%26lt%3BINPUT&y=1999 TYPE="SUBMIT" VALUE="Shop more"/></FORM></TD>
      </TR>
      </TABLE>
      EN | <A HREF=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%3Fxsl%3D%2Fshoppingcart_fr.xsl%26quot%3B%26gt%3BFR%26lt%3B%2FA%26gt%3B&y=1999
   </BODY>
   </HTML>
</xsl:template>

</xsl:stylesheet>

The elements to notice here are as follows:

  • xsl:stylesheet - This is the first element in an XSL style sheet. To convert from XML to HTML, you must specify three attributes in this element: xmlns:xsl, xmlns, and result-ns. The attributes declare what's known as an XML namespace, but don't worry about that for now. Suffice it to say that these attributes must be present with the values shown in Listing 3.

  • xsl:output - This empty element states that the result of the conversion will be an HTML document. It's specific to XT, the XSL processor I use (see below).

  • xsl:template - This element makes up the bulk of the style sheet. You'll recognize familiar HTML tags in the xsl:template element because, as the name implies, it's a template for the resulting HTML document. You can paste any HTML code into the template; however, you must respect the XML syntax - that is, every start tag must have a corresponding end tag.

  • xsl:value-of - This special element selects values from the XML document as specified by its select attribute. The select attribute specifies a path into the XML document, similar to a file path; it lists the elements separated by the / character. So, for example, shopping-cart/total retrieves the content of the element named total in shopping-cart - that is, $805.98 in Listing 2.

  • xsl:for-each - For elements that repeat, such as shopping-cart/product, the xsl:for-each element loops over the various elements. It's great for lists and tables.

  • xsl:attribute - This element makes it possible to add an attribute to an element. However, because XSL is written in XML, it's not possible to have tags in attributes. Therefore, you can't write

    <INPUT TYPE="HIDDEN" NAME="remove" VALUE="<xsl:value-of select="@id"/>">

    Instead, you have to write

    <INPUT TYPE="HIDDEN" NAME="remove">
       <xsl:attribute name="VALUE"><xsl:value-of select="@id"/></xsl:attribute>
    </INPUT>

    Here the xsl:attribute element creates the attribute "VALUE" in the INPUT element.

PUTTING IT ALL TOGETHER

To achieve the separation we want, servlets need to produce XML documents instead of HTML documents, and they need to apply a style sheet to convert the XML documents to HTML. The style sheet must be easy to replace, so that the responsibilities can be divided as follows:

  • The Java programmer works on the servlet, which generates XML that's independent of presentation.
  • The HTML designer is in charge of the style sheet, which takes the XML document and formats it nicely.

Obviously, we need to integrate an XSL processor into the servlet. There are several XSL processors that are available free of charge. The main ones are XT by James Clark, LotusXSL from IBM's alphaWorks, and Data Channel's XJParse. I decided to use XT, but you could of course use a different XSL processor. In fact, switching to another processor isn't a lot of work.

I also wanted to make the XSL postprocessing as invisible as possible. There was no point in manually invoking the XSL processor in every servlet I wrote. So I created a special servlet, XSServlet, that servlets need to inherit from (instead of from HttpServlet) in order to benefit from XSL postprocessing.

XSServlet is shown is Listing 4. It accepts GET and POST requests and forwards them to doGetPost(), a method that the descendants of XSServlet must overwrite. It then applies a style sheet to convert the document to HTML.


Listing 4
package com.psol.xsservlet;

import java.io.*;
import java.net.*;
import java.util.*;
import org.xml.sax.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.jclark.xsl.sax.*;

public abstract class XSServlet extends HttpServlet {
   private final static String PARSER_CLASS =
      "com.jclark.xml.sax.Driver";

   protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
      throws ServletException, IOException {
      doGetPost(request,response);
   }

   protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
      throws ServletException, IOException {
      doGetPost(request,response);
   }
   
   protected void doGetPost(HttpServletRequest request,
                            HttpServletResponse response)
      throws ServletException, IOException {
      StringWriter writer = new StringWriter();
      String stylesheet = request.getParameter("xsl");
      if (null == stylesheet)
         stylesheet = doGetPost(request,response,writer);
      else
         doGetPost(request,response,writer); // For debugging, we return the raw XML.
      if (stylesheet.equalsIgnoreCase("none")) {
         response.setContentType("text/xsl");
         Writer w = response.getWriter();
         w.write(writer.toString());
         w.flush();
      }
      else {
         stylesheet = getServletContext().getRealPath(stylesheet);
         XSLProcessor xslProcessor = createXSLProcessor(stylesheet);
         StringReader reader = new StringReader(writer.toString());
         transformAndWrite(xslProcessor,reader,response);
      }
   }

   protected abstract String doGetPost(HttpServletRequest request,
                                       HttpServletResponse response,
                                       Writer writer)
      throws ServletException, IOException;

   protected XSLProcessor createXSLProcessor(String url)
      throws ServletException, IOException {
      // Note: We might cache processors for common style
      // sheets; however, we need to deal with multithreading.
      Parser parser = null;
      try {
         Class clasz = Class.forName(PARSER_CLASS);
         parser = (Parser)clasz.newInstance();
      }
      catch(ClassNotFoundException e) {
         throw new ServletException(e);
      }
      catch(InstantiationException e) {
         throw new ServletException(e);
      }
      catch(IllegalAccessException e) {
         throw new ServletException(e);
      }

      XSLProcessor xslProcessor = new XSLProcessorImpl();
      xslProcessor.setParser(parser);

      Reader reader = new FileReader(url);
      InputSource inputSource = new InputSource(reader);
      try {
         xslProcessor.loadStylesheet(inputSource);
      }
      catch (SAXException e) {
         throw new ServletException(e);
      }

      return xslProcessor;
   }

   protected void transformAndWrite(XSLProcessor xslProcessor,
                                    Reader reader,
                                    HttpServletResponse response)
      throws ServletException, IOException {
      OutputMethodHandlerImpl outputMethodHandler = new OutputMethodHandlerImpl(xslProcessor);
      xslProcessor.setOutputMethodHandler(outputMethodHandler);
      outputMethodHandler.setDestination(new ServletDestination(response));
      try {
         xslProcessor.parse(new InputSource(reader));
      }
      catch (SAXException e) {
         throw new ServletException(e);
      }
   }
}

As a convenience, XSServlet also takes care of selecting the XSL style sheet. The browser can request a specific style sheet through the xsl parameter. If no xsl value is given, XSServlet uses the style sheet selected by the servlet. The fact that the browser can select the style sheet gives the HTML designer more control over presentation. It also makes it easier to translate the web site to another language without having to modify the application.

A SHOPPING CART SERVLET

To illustrate the use of XSServlet, I've written a simple shopping cart servlet. For the sake of simplicity, the shopping cart doesn't connect to a database or a payment system; instead, it collects product information in a cookie.

The product object is defined in Listing 5. A product consists of a name, a description (in English and in French), an image, and a price. The product object's main() method creates a vector of products from a text file and serializes the vector. You'll need to call Product at least once to prepare the vector for the servlet; this effectively replaces a database. The product also has a method to write itself in XML.


Listing 5
package com.psol.xsservlet;

import java.io.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;

public class Product implements Serializable {
   protected String name,
                    englishDescription,
                    frenchDescription,
                    image;
   protected double price;

   public void setName(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }

   public void setEnglishDescription(String englishDescription) {
      this.englishDescription = englishDescription;
   }

   public String getEnglishDescription() {
      return englishDescription;
   }

   public void setFrenchDescription(String frenchDescription) {
      this.frenchDescription = frenchDescription;
   }

   public String getFrenchDescription() {
      return frenchDescription;
   }

   public void setImage(String image) {
      this.image = image;
   }

   public String getImage() {
      return image;
   }

   public void setPrice(double price) {
      this.price = price;
   }

   public double getPrice() {
      return price;
   }

   protected void toXML(int id,Writer w)
	     throws IOException {
      w.write("<product id=\"");
      w.write(String.valueOf(id));
      w.write("\">\n<name>");
      w.write(getName());
      w.write("</name>\n<description xml:lang=\"EN\">");
      w.write(getEnglishDescription());
      w.write("</description>\n<description xml:lang=\"FR\">");
      w.write(getFrenchDescription());
      w.write("</description>\n<image>");
      w.write(getImage());
      w.write("</image>\n<price>");
      w.write(NumberFormat.getCurrencyInstance().format(getPrice()));
      w.write("</price>\n</product>\n");
   }

   public static Dictionary readSerializedVector(String filename)
      throws ServletException {
      try {
         ObjectInputStream in =
            new ObjectInputStream(new FileInputStream(filename));
         Date date = (Date)in.readObject();
         Vector vector = (Vector)in.readObject();
         Dictionary dictionary = new Hashtable();
         dictionary.put("date",date);
         dictionary.put("vector",vector); 
         return dictionary;
      }
      catch(ClassNotFoundException e) {
         throw new ServletException(e);
      }
      catch(IOException e) {
         throw new ServletException(e);
      }
   }

   public static void main(String[] params)
      throws IOException {
      if (params.length < 2) {
         System.out.println("java com.psol.xss.MakeProductList input output");
         return;
      }

      Vector vector = new Vector();
      BufferedReader reader = new BufferedReader(new FileReader(params[0]));
      String line = reader.readLine();
      while (line != null) {
         StringTokenizer tokenizer = new StringTokenizer(line,",");
         Product product = new Product();
         product.setName(tokenizer.nextToken());
         product.setEnglishDescription(tokenizer.nextToken());
         product.setFrenchDescription(tokenizer.nextToken());
         product.setImage(tokenizer.nextToken());
         product.setPrice(Double.valueOf(tokenizer.nextToken()).doubleValue());
         vector.addElement(product);
         line = reader.readLine();
      }

      ObjectOutputStream out =
         new ObjectOutputStream(new FileOutputStream(params[1]));

      // Today's date, for GET lastModified
      Date today = new Date();
      out.writeObject(today);

      out.writeObject(vector);
   }
}

The shopping cart itself is actually implemented in two servlets: ProductList (Listing 6), which displays a list of products, and ShoppingCart (Listing 7), which manages the shopping cart.


Listing 6
package com.psol.xsservlet;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ProductList extends XSServlet {
   protected Vector products;
   protected long lastModified;

   public void init()
      throws ServletException {
      Dictionary dictionary =
         Product.readSerializedVector(getInitParameter("products.filename"));
      products = (Vector)dictionary.get("vector");
      Date serializedDate = (Date)dictionary.get("date");
      lastModified = serializedDate.getTime();
   }

   protected String doGetPost(HttpServletRequest request,
                              HttpServletResponse response,
                              Writer w)
      throws ServletException, IOException {     
      w.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
      w.write("<products>\n");

      int size = products.size();
      for (int i = 0; i < size; i++) {
         Product product = (Product)products.elementAt(i);
         product.toXML(i,w);
      }

      w.write("</products>");
      w.flush();
      return getInitParameter("productlist.stylesheet");
   }

   protected long getLastModified(HttpServletRequest req) {
      return lastModified;
   }
}

Listing 7
package com.psol.xsservlet;

import java.io.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ShoppingCart extends XSServlet {
   protected Vector products;

   public void init()
      throws ServletException {
      super.init(config);
      Dictionary dictionary =
         Product.readSerializedVector(getInitParameter("products.filename"));
      products = (Vector)dictionary.get("vector");
   }

   protected String doGetPost(HttpServletRequest request,
                              HttpServletResponse response,
                              Writer w)
      throws ServletException, IOException {
      w.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
      w.write("<shopping-cart>\n");

      Cookie cookie = getCartCookie(request);
      
      String add = request.getParameter("add");
      StringTokenizer tokenizer;
      if (null != add)
         tokenizer = new StringTokenizer(add + " " + cookie.getValue());
      else
         tokenizer = new StringTokenizer(cookie.getValue());

      String remove = request.getParameter("remove");
      int toRemove = -1;
      if (null != remove)
         toRemove = Integer.parseInt(remove);
      boolean removed = false;
      double total = 0.0;
      StringBuffer newValue = new StringBuffer();

      while (tokenizer.hasMoreTokens()) {
         String current = tokenizer.nextToken();
         int i = Integer.parseInt(current);
         if (i == toRemove && !removed)
            removed = true;
         else if (i >= 0 && i < products.size()) {
            Product product = (Product)products.elementAt(i);
            total += product.getPrice();
            product.toXML(i,w);
            newValue.append(" " + current);
         }
      }

      w.write("<total>");
      w.write(NumberFormat.getCurrencyInstance().format(total));
      w.write("</total>\n</shopping-cart>");
      w.flush();

      cookie.setValue(newValue.toString());
      response.addCookie(cookie);

      return getInitParameter("shoppingcart.stylesheet");
   }

   protected Cookie getCartCookie(HttpServletRequest request) {
      Cookie[] cookies = request.getCookies();
      Cookie cookie = null;
      if (null != cookies)
         for (int i = 0; i < cookies.length; i++)
            if (cookies[i].getName().equals("cart")){
               cookie = cookies[i];
               break;
            }
      if (null == cookie)
         cookie = new Cookie("cart","");
      return cookie;
   }
}

To add a product to the shopping cart, it suffices to call ShoppingCart and pass the product number in the add parameter. Removing a product requires passing the product number in the remove parameter. (These servlets have been simplified such that the shopping cart doesn't explicitly handle the ordering of several copies of a product; for a more realistic shopping cart, see my earlier article, E-Shop in JavaScript.)

These servlets are completely free of formatting aspects. They generate XML documents with the appropriate data, while the formatting is delegated to the style sheets.

PRESENTATION: THE STYLE SHEET

Listing 8 is the style sheet for the list of products in the shop. It doesn't display all the information passed to it by the servlet: it uses only the English description.


Listing 8
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
                xmlns="http://www.w3.org/TR/REC-html40" result-ns="">

<xsl:output method="html"/>

<xsl:template match="/">
   <HTML>
   <HEAD>
      <TITLE>Shop</TITLE>
   </HEAD>
   <BODY>
      <P>Select a product:</P>
      <TABLE BORDER="0">
         <xsl:for-each select="products/product">
         <TR>
            <TD>
               <B><xsl:value-of select="name"/></B><BR/>
               <xsl:value-of select="description[@xml:lang='EN']"/><BR/>
               <xsl:value-of select="price"/>
            </TD>
            <TD><IMG>
               <xsl:attribute name="SRC"><xsl:value-of select="image"/></xsl:attribute>
            </IMG></TD>
            <TD><FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B&y=1999 METHOD="POST">
               <INPUT TYPE="HIDDEN" NAME="add">
                  <xsl:attribute name="value"><xsl:value-of select="@id"/></xsl:attribute>
               </INPUT>
               <INPUT TYPE="SUBMIT" VALUE="Buy"/>
            </FORM></TD>
         </TR>
         </xsl:for-each>
      </TABLE>
      <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B%26gt%3B&y=1999
         <INPUT TYPE="SUBMIT" VALUE="Shopping cart"/>
      </FORM>
      EN | <A HREF=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bproductlist%3Fxsl%3D%2Fproductlist_fr.xsl%26quot%3B%26gt%3BFR%26lt%3B%2FA%26gt%3B&y=1999
   </BODY>
   </HTML>
</xsl:template>

</xsl:stylesheet>

The HTML designer can modify the style sheet with no intervention from the programmer. For example, Listing 9 is a style sheet that does a completely different presentation and also uses the French description. Note the use of the xsl parameter in URLs to switch between the French shop and the English shop; this requires no modification of the servlet.


Listing 9
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
                xmlns="http://www.w3.org/TR/REC-html40" result-ns="">

<xsl:output method="html"/>

<xsl:template match="/">
   <HTML>
   <HEAD>
      <TITLE>La Boutique WhizBang</TITLE>
   </HEAD>
   <BODY BGCOLOR="orange">
      <CENTER><TABLE BGCOLOR="white"><TR><TD><CENTER>
         <TABLE BORDER="0">
            <xsl:for-each select="products/product">
            <TR>
               <TD>
                  <B><xsl:value-of select="name"/></B><BR/>
                  <xsl:value-of select="description[@xml:lang='FR']"/><BR/>
                  <xsl:value-of select="price"/>
               </TD>
               <TD><IMG>
                  <xsl:attribute name="SRC"><xsl:value-of select="image"/></xsl:attribute>
               </IMG></TD>
               <TD><FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B&y=1999 METHOD="POST">
                  <INPUT TYPE="HIDDEN" NAME="xsl" VALUE="/shoppingcart_fr.xsl"/>
                  <INPUT TYPE="HIDDEN" NAME="add">
                     <xsl:attribute name="value"><xsl:value-of select="@id"/></xsl:attribute>
                  </INPUT>
                  <INPUT TYPE="SUBMIT" VALUE="Ajouter au panier"/>
               </FORM></TD>
            </TR>
            </xsl:for-each>
         </TABLE>
         <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B%26gt%3B&y=1999
            <INPUT TYPE="SUBMIT" VALUE="Votre panier"/>
            <INPUT TYPE="HIDDEN" NAME="xsl" VALUE="/shoppingcart_fr.xsl"/>
         </FORM>
         <A HREF=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bproductlist%26quot%3B%26gt%3BEN%26lt%3B%2FA%26gt&y=1999 | FR
      </CENTER></TD></TR></TABLE></CENTER>
   </BODY>
   </HTML>
</xsl:template>

</xsl:stylesheet>

Listing 9 demonstrates how style sheets make it easy to support multiple languages and multiple presentations. Similarly, Listing 10 translates the shopping cart; it's the French equivalent of the shopping cart style sheet introduced in Listing 3.


Listing 10
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
                xmlns="http://www.w3.org/TR/REC-html40" result-ns="">

<xsl:output method="html"/>

<xsl:template match="/">
   <HTML>
   <HEAD>
      <TITLE>Votre panier chez WhizBang</TITLE>
   </HEAD>
   <BODY BGCOLOR="orange">
      <CENTER><TABLE BGCOLOR="white"><TR><TD><CENTER>
         <TABLE BORDER="0">
            <TR><TD><B>Nom</B></TD><TD><B>Prix</B></TD></TR>
            <xsl:for-each select="shopping-cart/product">
               <TR>
                  <TD><xsl:value-of select="name"/></TD>
                  <TD><xsl:value-of select="price"/></TD>
                  <TD>
                     <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B&y=1999 METHOD="POST">
                        <INPUT TYPE="HIDDEN" NAME="xsl" VALUE="/shoppingcart_fr.xsl"/>
                        <INPUT TYPE="HIDDEN" NAME="remove">
                           <xsl:attribute name="VALUE"><xsl:value-of select="@id"/></xsl:attribute>
                        </INPUT>
                        <INPUT TYPE="SUBMIT" VALUE="Supprimer"/>
                     </FORM>
                  </TD>
               </TR>
            </xsl:for-each>
            <TR>
               <TD>Total</TD>
               <TD><xsl:value-of select="shopping-cart/total"/></TD>
            </TR>
         </TABLE>
         <CENTER><TABLE BORDER="0"><TR>
            <TD><FORM><INPUT TYPE="BUTTON" VALUE="Passer commande"/></FORM></TD>
            <TD><FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bproductlist%26quot%3B%26gt%3B&y=1999
               <INPUT TYPE="SUBMIT" VALUE="Retourner à la boutique"/>
               <INPUT TYPE="HIDDEN" NAME="xsl" VALUE="/productlist_fr.xsl"/>
            </FORM></TD>
         </TR></TABLE></CENTER>
         <A HREF=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2F%26quot%3Bshoppingcart%26quot%3B%26gt%3BEN%26lt%3B%2FA%26gt&y=1999 | FR
      </CENTER></TD></TR></TABLE></CENTER>
   </BODY>
   </HTML>
</xsl:template>

</xsl:stylesheet>

RUNNING THE EXAMPLE

You'll of course need to compile and install the servlets. You'll also have to execute the product application once, to serialize a list of products. The following is a short text file that you can use to create your first shop. (The lines are folded here for readability; you'd need to use the text file downloaded with the other example files.)

WhizBang Ultra Word Processor,More words per minute than the competition.,
   Plus de mots à la minute que la concurrence.,wordprocessor.jpg,799.99
Super WhizBang Calculator,Cheap and reliable with power saving.,
   Economique et fiable avec économie d'énergie.,calculator.jpg,5.99
WhizBang Safest Safe,Choose the authentic WhizBang Safest Safe.,
   Exigez l'original!,safe.jpg,1999.00

Finally, you'll have to define these properties for the servlet:

products.filename=properties/products.ser
shoppingcart.stylesheet=/shoppingcart_en.xsl
productlist.stylesheet=/productlist_en.xsl

A CLEAN BREAK

Our XML-based solution cleanly separates the presentation from the application logic in servlets, corresponding to the separation of roles between HTML designers and Java developers in real-life projects. Using this technique, both sides can work independently.


FURTHER RESOURCES


View Source wants your feedback!
Write to us and let us know
what you think of this article.

Many thanks to the participants in Edifrance's XML training, who triggered the thought process that led to the development of XSServlet. Special thanks to Caroline Rose for her superb editorial work.

Benoît Marchal is a software engineer and consultant based in Namur, Belgium, who has been working extensively in Java and XML. He runs his own software company, Pineapplesoft. He also likes teaching and writing; his first book, XML by Example, will be published by Que in late 1999.

(9.99)


Related Readings:


Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use

spacer spacer


                                                       
iPlanet International | Year 2000 | Site Map | Feedback
Products | Solutions | Support | Services | Download | About Us | Developer
© 2000 Sun-Netscape Alliance. All Rights Reserved  Privacy Policy