HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Fitting The Pieces Into The Enterprise Java Jigsaw, by Tony Loton

In Part 1 of this series (JDJ Vol. 6, issue 4) I developed a simple access control mechanism for my application using HTTP authentication and servlets. In my view, servlets have always been underrated as a technology.

Their use has sometimes been limited to replacing traditional CGI scripts for the processing of HTML form submissions. However, the fact that you can send and receive serialized Java objects to and from servlets means they can be combined with applets as part of simple distributed object architecture, competing with RMI, CORBA, and EJB. No tunneling is required, and it works in browsers that don't support RMI.

Despite my enthusiasm for servlets, for some applications the interactivity and graphical capabilities of applets make them invaluable pieces in the jigsaw. I once wrote an applet that displayed aviation flight paths on a globe projection that could be zoomed and rotated about each axis. Try doing that with a servlet!

Although applets have fallen somewhat out of favor, it's possible to write one without regard to whether it will be downloaded to Internet Explorer or Netscape, on Windows or UNIX. I was doing that four years ago, and now with the Java plug-in down to a relatively trim 5MB in version 1.3 and Java WebStart on the way, the promise of true "write once, run anywhere" applets may yet become a reality.

Applet-Servlet Communication with Applet Parameters
To accommodate the first applet within my architecture, I'll first make a change to the LoginServlet. The code for writing out the user's options as HTML links (see April JDJ) will be replaced by code to download an applet with a set of parameters representing the user's options (see Listing 1).

The resulting HTML, containing an applet tag and a set of parameters, looks like this:

<applet code=com.lotontech.applets.
OptionsApplet
height=30, width=600>
<param name=user value=bill></param>
<param name=option1 value=getTasks></param>
<param name=option2 value=transferTasks></param>
</applet>
This passing of parameters to the OptionsApplet represents the simplest possible servlet-to-applet communication mechanism, although I'll cover a more useful alternative later in this article.

The OptionsApplet will now be responsible for displaying the user options menu, with each option presented as an AWT button (see Figure 1) rather than as an HTML link. I know it's not very fashionable to use AWT, but for this series I've decided to concentrate on the Enterprise aspects and leave the GUI advice to someone else.

figure 1
Figure 1: OptionsApplet presentation

There are two important points to look for in the OptionsApplet code included in Listing 2. The first is the retrieval of the applet parameters representing the current user's menu options. The second is the technique I have used for launching a new servlet into a browser frame - via the AppletContext - when an option button is clicked.

The AppletContext is the applet's handle on the browser it's running in. By calling the showDocument() method it's possible to fetch and display any HTML document the server can provide. You could replace the current document - which isn't such a good idea if it contains your applet - or display a new document in a separate frame or a new browser window. I'm doing the latter, and when I say displaying a document in this context, I actually mean running a servlet and displaying its response, where the response might contain yet another embedded applet.

This applet is only slightly more impressive than the original HTML links because I've kept it simple to demonstrate the technique. In applying this to any real scenario, I've often used an applet to display a multilevel menu via a Swing JTree that allows users to expand and contract the various levels in a way that can't be achieved easily with HTML alone.

This kind of multilevel menu wouldn't be as easy to specify as a flat series of applet parameters, so the applet-servlet communication channel would need to be enhanced to handle the transmission of more descriptive data. Two methods for transmitting descriptive data spring to mind: XML and Java serialized objects. I'll cover the latter and, rather than tire you with yet another menu variation, I'll find a different use for serialization within my application.

Applet-Servlet Communication with Serialized Objects
When user "bill" clicks on his getTasks button (see Figure 1), he sees a list of the tasks that are assigned to him. When a different user, "ben", clicks that button, he'll see a different list. A sample Task Page for "bill" can be seen in Figure 2.

figure 2
Figure 2:  Task Page for "bill"

Once again I'll use an applet and servlet combination, but this time in a slightly different way. When the TaskServlet is invoked via the getTasks button, with no parameters, it serves up an HTML tag to download the TaskApplet to the browser. As soon as the applet is instantiated in the browser, it makes a second call directly to the same servlet. This time the applet specifies a getdata parameter as part of the URL connection string, and expects the servlet to return a vector of serialized objects representing the user's tasks. In Listing 3 take a look at the first highlighted code, which tests the presence of the getdata parameter. You can then follow the alternate behavior that depends on the presence, or absence, of the parameter.

After constructing the two AWT Lists - one for high-priority tasks and one for low - during initialization, the TaskApplet (see Listing 4) reinvokes the TaskServlet by opening an InputStream on its URL, this time with the getdata parameter included in the URL connection string.

Note that when the applet requests the task data from the servlet, there's no need to specify which user's tasks should be returned because the HttpSession object - accessible from any servlet - already holds the user name for the current session, as set by the LoginServlet at the time of authentication. (For more about the LoginServlet, please refer to my earlier article.)

The tasks for the current user are returned in the form of a vector of serialized Java objects - in this case simple Strings - that are immediately ready for use as objects in the receiving applet. The contents of the vector don't need to be Strings, and the container doesn't need to be a vector. The only limitation is that any Java objects transmitted in this way must be serializable, which for your own objects means that their class(es) must implement java.io.Serializable. For Java runtime classes it means that you can't transmit things like JDBC connection objects.

Thus an applet/servlet combination allows a style of distributed object programming that in my opinion is much underused.

Conclusion
This check-in/checkout style of object programming using servlets and serialized objects isn't suitable for every situation. Many applications are more suited to the RMI/CORBA model, in which objects are passed between distributed clients and servers by reference rather than by value (serialized), and remain at all times at their point of origination. The issues to consider include performance (references are smaller than objects in transmission) and consistency (a remotely referenced server-side object appears in the same state to all its clients).

However, my preferred technique is easy to understand, doesn't suffer from tunneling issues (since you're using HTTP anyway), and may just be worth a try before you consider something more complex.

On the subject of complexity, no Enterprise Java series would be complete without the appearance of Enterprise JavaBeans. My next article will introduce the simplest possible EJB architecture - consisting of a lone session bean - and will suggest (but not recommend) how this bean's functionality could instead be provided by a more complex combination of session beans, entity beans, and distributed transactions.

Author Bio
After graduating with a degree in computer science and management, Tony Loton worked for almost 10 years as a consultant, course instructor, and technical author. He uses his company - LOTONtech Limited (www.lotontech.com) - as a vehicle for researching, developing, and commercializing innovative software solutions. Tony is currently writing a book, Web Mining with Java, to be published by Wiley later this year. He can be contacted at [email protected].

	


Listing 1: Writing User Options as Applet Parameters
 
//-- write the options out as an applet -- 

out.println( 
 "<applet code=com.lotontech.applets.OptionsApplet 
  height=30, width=600>"); 

out.println("<param name=user value="+user+"></param>"); 

for (int opNum=0; opNum<options.size(); opNum++) 
{ 
  String thisOption=(String) options.elementAt(opNum); 

  out.println("<param name=option"+(opNum+1) 
   +" value="+thisOption+"></param>"); 
} 

out.println("</applet>"); 
  

Listing 2: Options Applet Java Code
 
public class OptionsApplet extends Applet 
 implements ActionListener 
{ 
 public void init() 
 { 
  // -- get the user from parameter -- 
  String user=getParameter("user"); 

  // -- GUI would be initialized here -- 

  int opNum=0; 
  boolean finished=false; 
  while (!finished) 
  { 
   opNum++; 

   String thisOption=getParameter 
    ("option"+opNum); 

   if (thisOption==null) finished=true; 
   else 
   { 
    Button newButton=new Button(thisOption); 
    this.add(newButton); 
    newButton.addActionListener(this); 
   } 

  } 

 } 

 // -- ActionListener Method -- 
 public void actionPerformed(ActionEvent event) 
 { 

  if (event.getSource() instanceof Button) 
  { 
   Button sourceButton=(Button)event.getSource(); 

   // -- tell browser to launch selected app. -- 

   AppletContext theBrowser 
    =this.getAppletContext(); 

   try 
   { 
     URL documentBase=getCodeBase(); 

     URL newURL=new 
      URL(documentBase,sourceButton.getLabel()); 

     theBrowser.showDocument(newURL,"main"); 
   } 
   catch (Exception ex) {ex.printStackTrace();} 
  } 

 } 

} 

Listing 3: TaskServlet
 
public class TaskServlet extends HttpServlet 
{ 
 public void doGet(HttpServletRequest req 
  , HttpServletResponse res) throws IOException 
 { 

  // -- get the current http session -- 
  HttpSession session=req.getSession(true); 

  String user=(String) 
   session.getAttribute("user"); 

  // -- check for null user omitted - 

  String getdata=req.getParameter("getdata"); 
  if (getdata==null) getdata="false"; 

  if (!getdata.equals("true")) 
  { 
   // -- download HTML with task applet tag 
  } 
  else 
  { 
   // --task applet calling me again for data -- 

   Vector tasks=new Vector(); 

   // -- get connection from datasource -- 
   InitialContext ic = new InitialContext(); 

   DataSource ds = (DataSource) 
    ic.lookup("java:comp/env/jdbc/LOTONtech"); 

   Connection con = ds.getConnection(); 

   // -- select user tasks from the database -- 
   Statement st=con.createStatement(); 

   ResultSet results=st.executeQuery( 
    "SELECT username, task , priority 
    FROM usertasks WHERE username='"+user+"'"); 

   while (results.next()) 
   { 
    Task thisTask=new Task(results.getString(2) 
     , results.getString(3)); 

    tasks.addElement(thisTask); 
   } 

   // -- return serialized vector to the applet -- 
   ObjectOutputStream taskStream = new 
    ObjectOutputStream(res.getOutputStream()); 

   taskStream.writeObject(tasks); 
  } 
 } 
} 
  

Listing 4: TaskApplet
 
public class TaskApplet extends Applet 
{ 
 public void init() 
 { 
  // -- get the user from parameter -- 
  String user=getParameter("user"); 

  // -- set up the GUI Lists -- 

  this.setBackground(new Color(0,0,128)); 
  setLayout(new GridLayout(1,2)); 

  java.awt.List highList=new java.awt.List(); 
  highList.add("HIGH PRIORITY"); 
  highList.add(""); 
  add(highList); 

  java.awt.List lowList=new java.awt.List(); 
  lowList.add("LOW PRIORITY"); 
  lowList.add(""); 
  add(lowList); 

  try 
  { 
   // -- get user tasks from task servlet -- 

   URL servletURL=new URL(this.getCodeBase() 
    ,"getTasks?getdata=true"); 

   ObjectInputStream taskStream= 
    new ObjectInputStream(servletURL.openStream()); 

   Vector tasks=(Vector) taskStream.readObject(); 

   // -- and display them in 2 lists -- 

   for (int taskNum=0; taskNum<tasks.size(); 
   taskNum++) 
   { 
    Task thisTask=(Task) tasks.elementAt(taskNum); 

    if (thisTask.priority.equals("high")) 
     highList.add(thisTask.taskName); 
    else if (thisTask.priority.equals("low")) 
     lowList.add(thisTask.taskName); 
   } 

  } 
  catch (Exception ex) 
  { 
   // -- send error to Java Console -- 
   ex.printStackTrace(); 
  } 

 } 
} 

  
 
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.