Chapter 30

Performing Secure Transactions

by Mark Wutka


CONTENTS

When you do business on the Web, you need to assure your customers that their personal information is safe. Obviously, you need to keep the credit card numbers secure, but sometimes you need to protect more than that. Sometimes the contents of an order need to be kept quiet. You may be selling items or services of a personal nature, for instance. Sometimes competitors can learn about a company's plans just by analyzing their recent orders.

Obviously, you need to provide Web services that support encryption to keep the contents of the services private. For you to really do this securely, you need a signed digital certificate, registered with some trusted certificate authority. To protect your customers, you should also allow customers to verify themselves with signed certificates, as well.

As digital commerce becomes more mainstream, digital signatures will continue to grow in importance. Your digital signature may one day be as important, or even more important, than your handwritten signature. Unfortunately, digital signatures are still rather expensive to maintain. The average user isn't going to pay a fee to a certificate authority just to keep their signature on file.

At some point, however, there should be a cheaper way to keep digital signatures on file. It may be a service offered by credit card companies, who have a vested interest in preventing fraud. It is also possible that other signature mechanisms will be available soon.

Letting Customers Digitally Sign Orders

Security is making its way onto the Web slowly, but its pace is increasing. With the increasing need to secure browsers and servers, more companies are able to offer secure services. There are enough companies doing business without security, however, so that the ability to do a secure transaction is a competitive advantage.

One of the next new features will be personal digital signatures. Each customer will have their own signature, or set of signatures. By letting a customer digitally sign an order, you protect yourself and your customer.

From the customer's standpoint, being able to digitally sign orders means that other people can't place phony orders using the customer's name and credit card number. This gives the customer extra security, knowing that even if someone had their credit card number, they couldn't place an order on your system.

This mechanism also protects you from the same kind of fraud. You don't want someone else placing phony orders using the names of your good customers. When you receive a digitally signed order, you know that it came from the person who signed the order.

Your customer places an order, digitally signing it to verify that it is their order. The customer then sends both the order and the digital signature to you, as shown in Figure 30.1.

Figure 30.1 : A customer sends you a digitally signed order.

At this point, you can confirm the customer's identity by verifying it with a certificate authority. If someone were trying to create a fake order, they would not know the customer's digital signature key, so they would not be able to sign the order.

In the future, credit card companies might require digital signatures on all electronic transactions. This could cut down on fraud, as long as people keep their private signature keys away from prying eyes. There are a number of signature exchange protocols that may be required for credit card transactions. The credit card company would need your signature on a receipt, as well as the customer's.

After a customer sends you a digitally signed order, you create an electronic credit card receipt and digitally sign it, then pass it to the customer, as shown in Figure 30.2.

Figure 30.2 : You send the customer an electronic, digitally signed credit card receipt.

Figure 30.3 illustrates the next step in the sequence. The customer verifies your signature on the receipt, then digitally signs the receipt and sends it back.

Figure 30.3 : The customer digitally signs the receipt and sends it back.

Now, you have a signed receipt to send to the credit card company which shows that the customer agrees to the transaction. The credit card company can verify the customer's signature.

You can also use digital signatures to prevent the transmission of credit card numbers over the Internet. In this case, you need some way to get the customer's credit card number up front, as well as the public key for their digital signature. You keep their credit card number in a secure database, along with their signature key and the customer account number.

Now, when a customer places an order, they give you only their customer number, which you use to look up their credit card number. Since you also require that the order is digitally signed, someone else couldn't use that customer's account number.

It is conceivable that you could perform unencrypted transactions this way, however, you still need to use a secure download method to download the Java applets that will perform the transaction. Otherwise, you have the potential for someone to create a phony applet, as discussed in Chapter 27, "Encrypting Data."

Using Encryption in All Network Communications

At the moment, the only encryption mechanism that is readily accessible to Java applets is through the SSL protocol built into the Web browsers. Unfortunately, not all Java-enabled browsers support SSL.

Future releases of Java will include a security library with many encryption routines. This will eventually allow remote-object systems, like RMI and CORBA, to support encrypted sessions. This is extremely important. You don't want to be tied to the restrictions of http communications, as you are when using https URLs (SSL-enabled URLs).

Creating Java Services for Netscape Servers

While it would be nice to write secure Web services using a Java Web server like Jeeves or Jigsaw, these servers do not yet support secure protocols such as SSL. If you were really desperate to use one of these, you could write a CGI script on a secure server to forward requests to the non-secure Java Web server and then relay the results back. This is not a very pretty solution, however.

Netscape's Web servers support server-side Java programs, which Netscape calls "server-side applets." In addition, Netscape's Web servers support the SSL protocol, allowing you to create secure transactions. You used the SSL protocol in Chapter 28 to download an applet securely. For secure transactions, you also use it to transmit data.

Netscape took a more traditional CGI-like approach to server-side Java. Every time the server gets a request that is serviced by a server applet, the server creates a new instance of the server applet and invokes that applet's run command. When the applet has serviced the request and sent a result, the applet terminates.

If you look at the HttpApplet class, and its parent class, ServerApplet, you'll notice that they are similar to CGI. Instead of environment variables for the various HTTP header values, there are methods to retrieve the interesting header values. You can retrieve any value from the header using the getHeader method, like:

String contentType = getHeader("Content-type");

The ServerApplet class also provides methods to get an input stream and an output stream for the client connection. In CGI, these are mapped to the standard input and standard output streams. The getInputStream and getOutputStream methods are defined as follows:

public InputStream getInputStream() throws IOException
public OutputStream getOutputStream() throws IOException

When your HttpApplet is ready to return a response to the client, it calls the returnNormalResponse method:

public boolean returnNormalResponse(String contentType) throws IOException

The returnNormalResponse method returns true if you need to send a response. The reason for this is that there is an HTTP request type called "HEAD" that doesn't expect a response. The returnNormalResponse method returns false if the incoming request was a HEAD request. If you are returning an HTML form, you should use "text/html" for the contentType parameter. If you are returning non-HTML text, you should use a contentType of "text/plain."

Creating a Server-Side "Hello World"

You now have enough information to write the ubiquitous "Hello World" program for a Netscape Web server. Listing 30.1 shows the ServerHello class, which generates a "Hello World" page in HTML.


Listing 30.1  Source Code for ServerHello.java
import netscape.server.applet.*;
import java.io.PrintStream;

public class ServerHello extends HttpApplet {

        ServerHello() {}

        public void run() throws Exception {

            if (returnNormalResponse("text/html")) {
                  PrintStream out = getOutputStream();

               out.println("<HTML><HEAD>");
               out.println("<TITLE>Hello World!</TITLE>");
               out.println("</HEAD>");

               out.println("<BODY>");
               out.println("<H1>Hello World!</H1>");
               out.println("</BODY></HTML>");
                }
        }
}

Figure 30.4 shows the very simple output from this applet on a Web browser.

Figure 30.4 : Server-side Java applets may generate HTML output.

Installing a New Server-Side Java Applet

The Netscape Web servers store all the Java applets in a single directory. From the root directory of the server, the applets are installed in plugins/java/applets. When you install a new applet, you just copy the .class file into that directory, and restart the server.

Tip
While it seems to go against the "on-demand" loading concept taken by many Java environments, Netscape Web servers load all the Java applets at startup, taking the speed hit up front, instead of when the applet is requested.

Once you install a server-side Java applet, you can run it using an URL of this form:

http://host_name/server-java/applet_name

For example, if you were running the ServerHello applet on a host called pandora.contessa.com, you would use the following URL:

http://pandora.contessa.com/server-java/ServerHello

Handling Forms from Server-Side Applets

It is very simple to handle forms in a server-side applet. The HttpApplet class takes the same approach as the servlet API by returning the form parameters in a hash table. You simply call the getFormData method, like this:

Hashtable formItems = getFormData();

Listing 30.2 shows a server-side applet that displays a form, and then receives the submitted form. It is able to do both of these things because the form results are sent using an HTTP POST, while the initial input form is retrieved via GET. The applet simply checks to see what HTTP method was used to invoke it.


Listing 30.2  Source Code for FormDemo.java
import netscape.server.applet.*;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Enumeration;

// This is a Netscape server-side applet that generates a
// form which posts information back to this same applet.

public class FormDemo extends HttpApplet {

        FormDemo() {}

        public void run() throws Exception {

          if (returnNormalResponse("text/html")) {

// If this applet was retrieved with a GET, send the input form
               if (getMethod().equals("GET")) {
                    sendInputForm();
               } else {
// Otherwise, this must have been a post, so retrieve the posted data
                    processForm();
               }
          }
     }

     protected void sendInputForm()
     throws Exception
     {
                PrintStream out = getOutputStream();

// Send the header
          out.println("<HTML><HEAD>");
          out.println("<TITLE>Java-Five Needs Input!</TITLE>");
          out.println("</HEAD>");

          out.println("<BODY>");
          out.println("<H1>Give Me Some Input!</H1>");

// Send the input form
          out.println("<FORM action=\"/server-java/FormDemo\" "+
               "method=POST>");

// Input field titled "First Name"
          out.println("First Name: ");
          out.println("<INPUT type=\"text\" name=\"First Name\">");
          out.println("<P>");

// Input field titled "Last Name"
          out.println("Last Name: ");
          out.println("<INPUT type=\"text\" name=\"Last Name\">");
          out.println("<P>");

// Button to submit the form
          out.println("<INPUT type=submit><P>");
          out.println("</FORM>");
          out.println("</BODY></HTML>");
        }

     protected void processForm()
     throws Exception
     {
                PrintStream out = getOutputStream();

// Send the initial part of the response

          out.println("<HTML><HEAD>");
          out.println("<TITLE>OOOOH!!  Input!! MMM!!</TITLE>");
          out.println("</HEAD>");

          out.println("<BODY>");
          out.println("<H1>Thanks for the input!</H1>");
          out.println("Just for the record, here's what you sent:<P>");

// Get the fields from the form
          Hashtable formData = getFormData();
     
// For each field on the form, print the value
          Enumeration keys = formData.keys();
          while (keys.hasMoreElements()) {
               String key = (String) keys.nextElement();

               out.println(key+": "+formData.get(key)+"<P>");
          }
          out.println("</BODY></HTML>");
     }
}

Figure 30.5 shows the initial input form from this applet, while Figure 30.6 shows the results from the submission of the form.

Figure 30.5 : The FormDemo applet displays a simple input form.

Figure 30.6 : The FormDemo applet processes its own input form.

Sending Files as a Response

Instead of generating HTML or other content straight from Java, you can create server-side applets that return other files. For example, you might be creating an on-demand audio or video library controlled by a server-side applet. The applet verifies the client's access, handles any billing information, and then tells the server to return the requested file. The returnFile method lets you tell the server which file to return. The method is defined like this:

public void returnFile(String contentType, File file)
throws IOException

The file is an absolute pathname; it is not relative to the root directory of the Web server. This means that a server-side applet can return any file on the system, as long as it has sufficient permissions.

If the server can figure out the type of the file based on its suffix (like .gif or .mov), you don't have to specify the content type when you return a file. In these cases, you can use the alternate version of returnFile:

public void returnFile(File file) throws IOException

Listing 30.3 shows a minimal server-side applet that returns an image file. Since the filename ends with ".jpg," the applet doesn't have to specify the content type.


Listing 30.3  Source Code for ShowPicture.java
import netscape.server.applet.*;
import java.io.File;

public class ShowPicture extends HttpApplet {

        ShowPicture() {}

        public void run() throws Exception {
          returnFile(new File("\\pictures\\kaitlynn.jpg"));
        }
}

Figure 30.7 shows the picture returned by this applet. Notice that there is no surrounding text, only an image.

Figure 30.7 : A server-side applet can return images, movies, and audio files.

Returning Multi-Part Responses

Multi-part responses are a neat little hack to HTTP that allow you to perform primitive kinds of animation. The idea is that, instead of sending a single response to the client, you send multiple responses. This allows you to send a series of images as an animation, or display a page of information that occasionally updates itself. The returnMultipartResponse method in the HttpApplet class allows your server-side Java programs to return multi-part responses:

public boolean returnMultipartResponse(String subtype)
     throws IOException

The subtype value should either be "mixed" or "x-mixed-replace." Once you send the multi-part response header, you send responses normally, using the returnNormalResponse method.

When you generate a multi-part response, you keep the connection to the client open until you tell the client that you are through sending responses. In a server-side applet, you tell the client you are through sending responses by calling the endMultipartResponse method:

public void endMultipartResponse() throws IOException

Listing 30.4 shows a server-side applet that displays the current time using a multi-part response. It updates the time every five seconds.


Listing 30.4  Source Code for Multipart.java
import netscape.server.applet.*;
import java.io.PrintStream;
import java.util.Date;

// This applet sends a multi-part response that displays the
// current time every 5 seconds.

public class Multipart extends HttpApplet {

        Multipart() {}

        public void run() throws Exception {

// Tell the server we are sending a multi-part response

          if (returnMultipartResponse("x-mixed-replace")) {

               PrintStream out = getOutputStream();

               while (true) {

// Send the next part of the response
                    returnNormalResponse("text/html");

                    out.println("<HTML><HEAD>");
                    out.println("<TITLE>Web Clock</TITLE>");
                    out.println("</HEAD>");

                    out.println("<BODY>");
                    out.println("<H1>Current Time</H1>");
                    out.println(new Date());
                    out.println("<P>");
                    out.println("</BODY></HTML>");

// Wait a little while before sending another
                    Thread.sleep(5000);
               }
                }
        }
}

Maintaining Information Between Applet Invocations

One of the advantages to the Servlet API is that there is only one instance of a particular servlet and it stays around after a request completes. The Netscape server-side applets don't work that way. There are multiple instances of a particular applet, and the instances go away after completing their assigned task.

This is not a difficult problem to get around, however. All you need to do is set up another class that is implemented as a singleton. In other words, there is only one instance of the class. Listing 30.5 shows an example singleton class.


Listing 30.5  Source Code for PersistentInfo.java
import java.util.Vector;

// This is a singleton class that maintains a vector of
// strings. Since the constructor is protected, the only
// way you can get an instance of this class is by calling
// the instance method, which returns the single shared
// copy of the class.

public class PersistentInfo
{
// singleInstance is the lone instance of this class
     protected static PersistentInfo singleInstance;

// info is the vector of strings
     protected Vector info;

// instance returns the lone instance of this class, and creates one
// if there isn't one already.

     public synchronized static PersistentInfo instance()
     {
          if (singleInstance == null) {
               singleInstance = new PersistentInfo();
          }
          return singleInstance;
     }

     protected PersistentInfo()
     {
          info = new Vector();
     }

// getInfo returns an array of the strings stored in the info vector

     public synchronized String[] getInfo()
     {
          String[] strings = new String[info.size()];

          info.copyInto(strings);

          return strings;
     }

// addInfo adds another string to the info list

     public synchronized void addInfo(String newInfo)
     {
          info.addElement(newInfo);
     }
}

The PersistentInfo class stays active while the Java VM is running. This means that any instances of HttpApplet can access the info in PersistentInfo at any time. You can use this method to keep database connections open, or keep open a session with a host that may take a long time to set up.

Tip
If you use singleton classes with your applets, be especially careful about using synchronization. While you may not be explicitly creating threads, each HttpApplet object runs in its own thread.

Listing 30.6 shows a server-side applet that uses the PersistentInfo object. The applet generates a form with a single text field. Any information in the text field is added to the PersistentInfo object.


Listing 30.6  Source Code for PersistDemo.java
import netscape.server.applet.*;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Enumeration;

// This is a Netscape server-side applet that generates a
// form which posts information back to this same applet.
// It uses a second class called PersistentInfo.
// The PersistentInfo class sticks around, so information
// is preserved for future instances of this class.

public class PersistDemo extends HttpApplet {

        PersistDemo() {}

        public void run() throws Exception {

          if (returnNormalResponse("text/html")) {

// If this applet was retrieved with a GET, send the input form
               if (getMethod().equals("GET")) {
                    sendInputForm();
               } else {
// Otherwise, this must have been a post, so retrieve the posted data
                    processForm();
               }
          }
     }

     protected void sendInputForm()
     throws Exception
     {
                PrintStream out = getOutputStream();

// Send the header
          out.println("<HTML><HEAD>");
          out.println("<TITLE>Persistent Information Demo</TITLE>");
          out.println("</HEAD>");

          out.println("<BODY>");

          out.println("<H1>Current information:</H1>");

// Print out the strings currently stored in the PersistenceInfo class

          String strings[] = PersistentInfo.instance().getInfo();

          for (int i=0; i < strings.length; i++) {
               out.println(strings[i]+"<P>");
          }

          out.println("<H1>Please enter some new information</H1>");

// Send the input form
          out.println("<FORM action=\"/server-java/PersistDemo\" "+
               "method=POST>");

// Input field titled "First Name"
          out.println("Information: ");
          out.println("<INPUT type=\"text\" name=\"Information\">");
          out.println("<P>");

// Button to submit the form
          out.println("<INPUT type=submit><P>");
          out.println("</FORM>");
          out.println("</BODY></HTML>");
        }

     protected void processForm()
     throws Exception
     {
// Get the fields from the form
          Hashtable formData = getFormData();
     
// If there's any information, add it to the PersistentInfo class
          String newInfo = (String) formData.get("Information");
          if (newInfo != null) {
               PersistentInfo.instance().addInfo(newInfo);
          }

// Put up another form so we can get more input
          sendInputForm();
     }
}

Figure 30.8 shows the output from this server-side applet.

Figure 30.8 : A singleton object can preserve information for multiple HttpApplet instances.

Making Server-Side Applets Work on Different Web Servers

If you need to create secure server-side Java objects, whether they are applets or servlets, you want to do the tough work only once. In fact, you want to write the application only one time.

After that, you may have to do a little work hooking your application into the local Web server. This goes back to one of the basic design principles, where you design the application without considering the user interface. When you run an application on a Java Web server, the applet or servlet represents the user interface.

Try to create the application without regard to the user interface. Don't return HTML forms from the application itself, and don't let it parse HTML. Instead, dedicate your servlet, or server-side applet, to handling HTML and invoking methods on a separate application object.

This method will allow you to provide multiple forms of access to your application. You might have a Web interface and a CORBA interface to the same application simultaneously.

Performing Secure Transactions

The only mechanism currently available for performing a secure transaction with Java is through https URLs. Furthermore, the only Web servers that currently support Java on the server side, and https, are the Netscape Web servers.

Caution
If the URL for your service starts with http and not https, your transactions are not encrypted.

This will certainly not be the case forever, though. As security becomes integrated into the Java environment, more Web servers, and other systems, will support encryption and digital signatures. The trick, as always, is to design and build your applications so you can run them now, but keep them flexible enough to embrace other technologies as they become available.

Listing 30.7 shows a framework for a simple ordering system. Notice that the system doesn't have any knowledge of applets or servlets-just ordering.


Listing 30.7  Source Code for TheStore.java
// A dummy storefront that has some items for sale.
// The store doesn't record its transactions, however.

public class TheStore
{
// Make sure there's only one instance of the store

     protected static TheStore singleInstance;

     protected StoreItem items[];

     public static TheStore instance()
     {
          if (singleInstance == null) {
               singleInstance = new TheStore();
          }
          return singleInstance;
     }

// Create a store with some items
     protected TheStore()
     {
          items = new StoreItem[3];

          items[0] = new StoreItem("Dongle", 199);
          items[1] = new StoreItem("Widget", 599);
          items[2] = new StoreItem("Tweaker", 799);
     }

// Return a list of available items

     public StoreItem[] getItems()
     throws StoreException
     {
          try {
               return (StoreItem[]) items.clone();
          } catch (Exception e) {
               return null;
          }
     }

// A dummy routine for purchasing products

     public void purchase(String customerId, String itemList[])
     throws StoreException
     {
// Normally you would do something here to record the purchase
     }
}

This class also uses some auxiliary classes, which are shown in Listings 30.8 and 30.9.


Listing 30.8  Source Code for StoreException.java
public class StoreException extends Exception
{
     public StoreException(String why)
     {
          super(why);
     }
}


Listing 30.9  Source Code for StoreItem.java
public class StoreItem
{
     public String itemName;
     public int price;

     public StoreItem(String itemName, int price)
     {
          this.itemName = itemName;
          this.price = price;
     }
}

Now that the first step has been taken, and there is an application defined, you can start creating different interfaces into the application. Since you already have some examples of server-side applets that present HTML forms and parse them, it is fairly simple to create a similar applet that places orders with this application. Listing 30.10 shows an HTML front end for this application.


Listing 30.10  Source Code for HTMLStoreFront.java
import netscape.server.applet.*;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;

// This class implements an HTML interface for the TheStore class.

public class HTMLStoreFront extends HttpApplet {

        HTMLStoreFront() {}

        public void run() throws Exception {

          if (returnNormalResponse("text/html")) {

// If this applet was retrieved with a GET, send the input form
               if (getMethod().equals("GET")) {
                    sendInputForm();
               } else {
// Otherwise, this must have been a post, so retrieve the posted data
                    processForm();
               }
          }
     }

     protected void sendInputForm()
     throws Exception
     {
                PrintStream out = getOutputStream();

// Send the header
          out.println("<HTML><HEAD>");
          out.println("<TITLE>The Store</TITLE>");
          out.println("</HEAD>");

          out.println("<BODY>");
          out.println("<H1>What would you like to order?</H1>");

// Send the input form
          out.println("<FORM action=\"/server-java/HTMLStoreFront\" "+
               "method=POST>");

          out.println("<P>Customer ID #: ");
          out.println("<INPUT type=text name=\"customerID\">");
          out.println("<P>");
// List the items on sale from the store, and their prices
          StoreItem[] items = TheStore.instance().getItems();

          for (int i=0; i < items.length; i++) {
               out.println("<INPUT type=checkbox name=\""+
                    items[i].itemName+"\" value=off>");
               out.println(items[i].itemName+"     "+
                    items[i].price+"<P>");
          }

// Button to submit the form
          out.println("<INPUT type=submit><P>");
          out.println("</FORM>");
          out.println("</BODY></HTML>");
        }

     protected void processForm()
     throws Exception
     {
                PrintStream out = getOutputStream();

// Get the fields from the form
          Hashtable formData = getFormData();

          Vector partOrder = new Vector();
          String customerID = null;

// For each field on the form, see if the checkbox is on

          Enumeration keys = formData.keys();
          while (keys.hasMoreElements()) {
               String key = (String) keys.nextElement();

// If we got the customerID field, save it and go on

               if (key.equals("customerID")) {
                    customerID = (String) formData.get(key);
                    continue;
               }

// If the checkbox was on for this part, add it to the parts vector

               partOrder.addElement(key);
          }

          String[] orderItems = new String[partOrder.size()];

          partOrder.copyInto(orderItems);

          try {
               TheStore.instance().purchase(customerID, orderItems);

// Send a success response
               out.println("<HTML><HEAD>");
               out.println("<TITLE>The Store - Order Completed");
               out.println("</TITLE>");
               out.println("</HEAD>");

               out.println("<BODY>");
               out.println("<H1>Order Complete. ");
               out.println("Thanks for the business!</H1>");

               out.println("<P>Items on your order:<P>");
               for (int i=0; i < orderItems.length; i++) {
                    out.println(orderItems[i]+"<P>");
               }

          } catch (Exception e) {

// If we got an error ordering, print the reason
               out.println("<HTML><HEAD>");
               out.println("<TITLE>The Store - Order Aborted!");
               out.println("</TITLE>");
               out.println("</HEAD>");

               out.println("<BODY>");
               out.println("<H1>Order Aborted!</H1>");
               out.println("Here's why:<P>");
               out.println(e+"<P>");
          }
          out.println("</BODY></HTML>");
     }
}

Plain old HTML forms are pretty boring. You really want to liven up your ordering system by running a Java applet on the client side, right? You can do this, and still keep everything secure!

You can use the technique outlined in Chapter 6 "Communicating with a Web Server," for posting to a URL. This will allow you to send secure data to a server-side applet, which can then interpret the posted data and send a response.

Rather than making your applet parse the form returned by the HTMLStoreFront class, you can create a similar class that is friendly to applets. Instead of sending HTML data, it sends plain text in a format that the applet can easily read. Listing 30.11 shows such a class.


Listing 30.11  Source Code for AppletStoreFront.java
import netscape.server.applet.*;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;

// This class implements a secure interface for the TheStore class.
// This interface is used by client-side applets to store and
// retrieve data securely.

public class AppletStoreFront extends HttpApplet {

        AppletStoreFront() {}

        public void run() throws Exception {

          if (returnNormalResponse("text/plain")) {

// If this applet was retrieved with a GET, send the input form
               if (getMethod().equals("GET")) {
                    sendItemList();
               } else {
// Otherwise, this must have been a post, so retrieve the posted data
                    processOrder();
               }
          }
     }

     protected void sendItemList()
     throws Exception
     {
                PrintStream out = getOutputStream();

          StoreItem[] items = TheStore.instance().getItems();

          for (int i=0; i < items.length; i++) {
               out.println(items[i].itemName);
               out.println(items[i].price);
          }
        }

     protected void processOrder()
     throws Exception
     {
                PrintStream out = getOutputStream();

// Get the fields from the form
          Hashtable formData = getFormData();

          Vector partOrder = new Vector();
          String customerID = null;

// For each field on the form, see if the checkbox is on

          Enumeration keys = formData.keys();
          while (keys.hasMoreElements()) {
               String key = (String) keys.nextElement();

// If we got the customerID field, save it and go on

               if (key.equals("customerID")) {
                    customerID = (String) formData.get(key);
                    continue;
               }

// add the part to the parts vector

               partOrder.addElement(key);
          }

          String[] orderItems = new String[partOrder.size()];

          partOrder.copyInto(orderItems);

          try {
               out.println("OK");
          } catch (Exception e) {
               out.println("ERROR: "+e);
          }
     }
}

Once again, you haven't had to change a single line of code in the original application class. All you do is add new user interfaces for it. You could follow this same track and create an RMI interface and a CORBA interface to this application. Of course, if you can't get a secure version of RMI or CORBA yet, you can't do secure transactions.