March 06, 2003

Distributed Notification with Java RMI

Abstract

Java’s implementation of Remote Method Invocation (RMI) is very easy to use and very power. Java makes the task of setting up an RMI server an almost trivial one. Once running, connecting client applications to the RMI server is also a breeze. There are examples and how-tos for client to server communication everywhere. But what about the other way, server to client communication? Is it possible for an RMI server to actively communicate with all the clients that are connected to it without the client initiating the conversation? In other words, is distributed notification possible? The purpose of this post is to demonstrate that yes, it is possible.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Problem

Consider development of a small test application that demonstrates an RMI server notifying its clients of events. A very simple example is an RMI “time server”. Clients register with the server (client to server communication) and then every second all registered clients are notified of an event, the new date and time (server to client communication). The client implements some kind of listener and that listener can be used to handle the event any way the client sees fit.

Proposed Solution

The difficulty that I have with implementing distributed notification is that the server has to have some knowledge of the client application. Just as the client needs an Xxx_Stub.class to talk to the server, the server needs a similar Xxx_Stub.class to talk to the client.

The first, and most obvious (to me anyway) solution was for the client to develop its own remote class, generate an Xxx_Stub.class for it, then give that Xxx_Stub.class file to the server. Not a very attractive solution.

After struggling with the problem for a while I narrowed down the root of the problem. On the server side, I had interfaces and objects like listing 1 and listing 2.

Listing 1 - ClientCallback interface

public interface ClientCallback extends Remote {
    public void informClient(String msg)
    throws RemoteException;
}

Listing 2 - Remote service allowing clients to register

class SomeServiceImpl extends UnicastRemoteObject implements SomeService {
    public void registerWithService(ClientCallback cc)
    throws RemoteException {
        //...
    }
}

Continuing with the root of the problem, on the client side I want to make a remote call that looks something like listing 3.

Listing 3 - What client wants to do

serverObject.registerWithService(this);

The root of the problems caused by this code is that the client would then have to become a ‘server’ itself, generating Xxx_Stub.class files for the classes that implement ClientCallback and somehow getting those Xxx_Stub classes to the server. Not very pretty.

When considering a solution to this problem, realization struck. The server would have no problems if the class that implemented ClientCallback was a class that the server had created! For example, let us say that the server gives to the client the class in listing 4.

Listing 4 - Server gives client this class

class ServerListener implements ClientCallback {
   /**
    * constructor
    */
   public ServerListener(String serverName, int port)
   throws Exception {
      UnicastRemoteObject.exportObject( this );

      SomeService ss = (SomeService)Naming.lookup("rmi://<server>:<port>/SomeService");

      ss.registerWithService(this);
   }


   /**
    * MyCustomListener interface method
    */
   public void informClient(String msg) {
     // do something
   }
}

When the code for the RMI server is compiled, a ServerListener_Stub.class is also created and bundled in the jar file for the client to use. Basically, this is an object that a client can create an instance of just like any other Java object.

ServerListener ls = new ServerListener("localhost", 1099);

This object knows how to contact the RMI server, do the appropriate lookup, and register itself with the server. And because of the line:

UnicastRemoteObject.exportObject(this); 

The server is able to call the informClient() method on the client’s machine! Bingo! that’s exactly what I wanted!

Well not quite. The server can now inform the client of something, but that code is buried inside the ServerListener object that the client really has no access to. Extending the ServerListener object won’t work because that gets you back to the previous problem of the server having to know what the client classes are. Here’s my solution, another listener interface provided by the server!

Let’s say the ClientCallback interface had an analogous, non-remote interface called ClientListener. Also let’s say that the ServerListener class that the server provides as an additional method that looks like listing 5.

Listing 5 - Method to register client-side listener

Vector listeners = new Vector();

public void addClientListener(ClientListener cl) {
    if (!listeners.contains(cl)) {
        listeners.add(cl);
    }
}

Now let ClientListener be just a simple, non-remote interface as in listing 6.

Listing 6 - Non-remote, client-side interface

public interface ClientListener() {
    public void statusChanged(String s);
}

Now the client side code just falls into place as in listing 7.

Listing 7 - Client code

// create an object that can "listen" for events on the server
ServerListener sl = new ServerListener("localhost", 1099);

// add as many classes as you want to handle those events
sl.addClientListener(new ClientListenerA());
sl.addClientListener(new ClientListenerB());
sl.addClientListener(new ClientListenerC());
sl.addClientListener(new ClientListenerD());

The implementation of the ServerListener.informClient() object is nothing more than looping over the list of ClientListener objects and calling the changeStatus() method on each.

March 05, 2003

Drag & Drop With Java GUI Components

Abstract

The Java programming language is very powerful. In my opinion, it has brought the art of programming back to programmers by focusing on fundamentals such as data structures, reusable code, and application design. Though, like all programming languages, Java is not perfect. GUI development with Java can be frustrating at times. This is especially true in Window and Apple environments where Drag & Drop is pervasive. An application that does not properly participate in Drag & Drop can become quite frustrating to work with. Java applications are usually frustrating to use for this reason. The purpose of this paper is to tell my story about Java and Drag & Drop; to relay what I have learned and hopefully give readers of this post a jump start in their own development.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Drag & Drop - My Experience

Where are the listeners?

For a developer, the first instinct for doing Drag & Drop would be to start looking at the listeners for the mouse, in fact looking at anything mouse related. This is futile though because, surprisingly, the implementation of Drag & Drop is not found in anything mouse related!

The place to start looking is the java.awt.dnd package. It is here that can be found all the tools needed to implement Drag & Drop. However the classes and interfaces here are quite confusing. Worse yet, documentation and examples are scarce as well.

The most confusing part of Drag & Drop in Java is that it needs to be developed by a programmer not simply implemented. Here is what that statement means. Programmers typically implement GUI events in Java by implementing a listener and assigning the listener to the object. Take JButton for example. An ActionListener is implemented and then assigned to a JButton instance using the addActionListener() method. When the button is pressed, the method implemented in the listener is run. Because listeners are used everywhere in the Java GUI APIs, it is natural to assume that Drag & Drop is also like this - Find the Drag & Drop listener, plug it in, and the work is done. Sadly this is not the case. A lot more development is needed to implement Drag & Drop.

Step by step

The first step in making any Java component able to do Drag & Drop is to create a new object that extends the existing one. This needs to be done because additions need to be made to the existing component. For example, create a new class, MyJTree that extend JTree.

Next, jump to the java.awt.dnd package. There are the 3 objects to start off with:

  • DragSource
  • DropTarget
  • DragGestureRecognizer

If I want to drag from MyJTree, then I need to add a private DragSource dragsource; to the MyJTree class. That is the simple explanation of what DragSource is for. If I want to drop onto MyJTree, then I need to add a private DropTarget droptarget; to the MyJTree class. That is the simple explanation of what DropTarget is for. Finally, for these two objects to be used, a DragGestureRecognizer needs to be used, so add a private DragGestureRecognizer dgr; to MyJTree.

Adding these three objects to MyJTree creates a new component that has the potential to:

  1. Recognize that a drag event has started by using DragGestureRecognizer
  2. Start dragging something by using DragSource
  3. Accept the drop of something by using DropTarget

But how should MyJTree respond when these events occur? Your developer instincts might be saying that the responses should be an implementation of some listener. This is absolutely correct! In fact, there are 3 listeners, one for each of the three objects just added to MyJTree:

  1. DragGestureListener
  2. DropTargetListener
  3. DragSourceListener

So, putting it all together, this is how drag & drop might be implemented:

Overload the default constructor for the MyJTree class. Create an instance of DragSource by calling its constructor. Using the DragSource object just created, call the createDefaultDragGestureRecognizer() method. This method requires an implementation of the DragGestureListener, which is one of the three listeners that need to be implemented. Also the Component c parameter of that method is the component that the recognizer is for, in other words the keyword this, MyJTree. Finally a DropTarget is constructed, calling its constructor, again using MyJTree (keyword this) for the Component parameter and an implementation of the DropTargetListener. It is a good idea to first make three blank listeners (have all the methods but the methods do not do anything), get everything set in MyJTree, then make sure everything compiles. Once that is done, move onto implementing the methods in the listeners so a Drag & Drop operation can be done.

By this time MyJTree has been developed with three objects from java.awt.dnd and those objects have been created (In MyJTree) utilizing one of their various overloaded constructors and blank implementations of their listeners. At this point implementing those listener methods is all that is left in creating a GUI component capable of performing Drag & Drop operations.

Walkthrough

Everything starts with the DragGestureListener.dragGestureRecognized() method. When a user starts to drag something on MyJTree the DragGestureListener hears it and fires the dragGestureRecognized() method.

NOTE At this time dragging has not begun yet.

The DragGestureListener has simply recognized that the user wants to start dragging. It calls dragGestureRecognized() to figure out what to do.

What the dragGestureRecognized() method needs to do is figure out whether or not a drag should begin, and if so, start it. Any code needed can be added to determine if a drag should start (Perhaps there are some nodes on MyJTree that should never be dragged). If a drag should start, dragGestureRecognized() needs to use its DragGestureEvent parameter and do something like:

Evnt.getDragSource().startDrag(evnt, someCursor, someTransferable, someDragSourceListener

The getDragSource() method will get the DragSource object added to MyJTree, and calling the startDrag() method will finally start a drag.

At this point, while dragging continues, the execution of Java code will wildly flip back and forth between the DragSourceListener and the DropTargetListener. The methods in these listeners are fairly self-explanatory. Here is a typical manner in which the methods are used:

  • DragSourceListener.dragOver() – Used to change the display of the mouse.
  • DropTargetListener.dragEnter() – Figures out if the target will accept a drop or not. If not, the Drag & Drop operation is canceled.
  • DropTargetListener.dragOver() – Changes the display of the object being dropped on (highlighting tree nodes for example).
  • DropTargetListener.drop() – Most important! Run when the mouse button is finally released and a user makes a drop.

DropTargetListener.drop() needs to do two things. One, at the end of the method it needs to use its DropTargetDropEvent parameter and call:

Evnt.getDropTargetContext().dropComplete(true);

This will end the Drag & Drop operation that began with DragGestureListener.dragGestureRecognized(). Two, before the drop method calls dropComplete(true) it needs to get what is being dragged and do something with it! Otherwise what is the point of dragging and dropping?

How does the drop() method know what was is being dropped? Looking at the DragGestureListener.dragGestureRecognized() method. At some point, the call:

Evnt.getDragSource().startDrag(evnt, someCursor, someTransferable, someDragSourceListener)

was made to start the drag. The object someTransferable is what is being dragged and can be any Java object as long as it, and all objects in it, implement the Transferable and Serializable interfaces. Think of the Transferable as an invisible connection between what was dragged from and what was dropped on.

At startDrag() a 100% complete !!copy!! is made of the object passed to the method as the Transferable. The fact that it is a !!copy!! is very important to know because in DropTargetListener.drop(), the Transferable gotten from the DropTargetDropEvent parameter does not reference the original object set as the Transferable. For example, if a TreeNode is dragged and that TreeNode implements Transferable and is set as the Transferable in startDrag() the TreeNode gotten in drop() is not simply a reference to the one on the source tree. It is a completely new object with the same information from the source tree copied exactly!! This is very important, especially when nodes need to be added/deleted from the tree. The original nodes in the tree need to be found from the copy in the Transferable.

Finally, in the drop() method, anything that needs to be done can be done to perform all the operations required for a successful drop, including contacting a database to perform inserts, updates, and deletions to tables.

Tip about Applets

Web browsers have a peculiar feature. A perfectly implemented Drag & Drop component will fail to do any Drag & Drop of any kind if the component is created in the Applet.start() method. When it is created here, for some reason the web browser itself will listen for Drag & Drop operations and all the code that has been developed for the Drag & Drop component will never be run. What is needed to overcome this problem is to create the GUI of the JApplet in a separate thread. This is not difficult to do. Simply move all the code that is in the Applet.start() method to a run() method, have the JApplet class implement Runnable, and in the Applet.start() method do something like:

Thread t = new Thread(this);
t.start();

This sounds very strange but it is the only way to have the code developed for the Drag & Drop component actually run without drag gestures being intercepted by the web browser.

March 04, 2003

Applet to Servlet Communication

Abstract

Java™ Applets are limited by the security restrictions they are run under. This is a good thing, mind you, since Applets are applications that are downloaded and run on a person’s computer, sometimes, without them even realizing it. Consequently, Applets must be executed with very tight restrictions. However, this makes developing Applets difficult because resources like databases, XML files, flat files, etc, cannot be accessed. To make Applets more effective, they need to communicate with a 3rd party, a Servlet for example, which has the power to access those resources. The purpose of this paper is to discuss how an Applet can communicate with a Servlet, and in doing so provide much greater functionality than it can alone.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Hypothetical Project

The goal of this project is to create a Java™ JApplet capable of dragging & dropping database information from one database to another similar database. The JApplet might look something like Figure 1.

Figure 1 - Drag & Drop JApplet

Drag & Drop JApplet
Drag & Drop JApplet

This goal can be accomplished in four broad steps. Each step is a specific area of research and development that must be done in order to be successful in reaching the goal of the project.

  1. Determine what a JApplet is and how to get a simple one running.
  2. Determine how the JApplet will request and receive information from a database.
  3. Determine how drag & drop is provided for in Java™ and how to implement it.
  4. Determine the rules of dragging and dropping so that the application does not try to perform operations that are contrary to the design of the database.

Steps 1 and 2 are going to be covered in this post. Step 3 is covered in a separate post. Step 4 cannot be covered since these are business rules that are specific and unique to each project you work on.

Step 1: What is an Applet?

An Applet is a small Java™ application that extends the java.applet.Applet class or the javax.swing.JApplet class. The compiled *.class files (normally “jarred” up into a *.jar file) sit on the web server along with the rest of the pages of a website. Using the HTML <APPLET> tag on an HTML page will cause the applet’s class files or jar file to be downloaded from the web server onto the client’s machine. After downloading is complete, the Applet is automatically executed within the web browser. Because the Applet is executed within the web browser, it appears to the user as just another element on the web page.

A JApplet is nearly identical to an Applet except for the package that it uses for its GUI components. Applets typically use GUI components from the java.awt package. The GUI components in this package are considered to be a clunky, heavyweight, first-try at implementing GUI components. A much more streamlined, lightweight, and versatile GUI component project was captured in the javax.swing package. All the GUI components of java.awt are re-implemented in javax.swing along with a number of components that do not exist in java.awt at all. The javax.swing package is generally considered to be superior over those in java.awt. Since java.awt does not contain a tree component, javax.swing will have to be used. There is an unwritten rule that GUI components from each of those two packages are not used at the same time in the same application so all GUI components for this project will be from javax.swing.

Web browsers have not caught up with the development of Java™, so special steps must be taken to have a web browser run a JApplet that has swing components. This is the problem: Because the web browser on the client machine executes the JApplet after the JApplet is downloaded, all the classes that the JApplet needs to run have to be found on the client machine. Web browsers typically have the java.awt components build into their installation, but NOT the javax.swing components. The immediate result would be an error from the web browser saying that it could not find a class. To fix the error, a plug-in needs to be downloaded that contains the javax.swing components. To do this the <APPLET> tag in the HTML page needs to be replaced with another tag that will automatically download the plug-in and install it on the client machine (if needed). Replacing the <APPLET> tag is fairly complicated. To simplify the process, a conversion application can be run on the HTML page that will automatically replace the <APPLET> tag. As of this writing the application can be downloaded from http://java.sun.com/products/plugin/index.html, then look for “HTML Converter”. A search of the site for “plugin” or “HTML Converter” should get the right page too if the URL changes in the future. After the converter is downloaded and installed, it can be run on the HTML page that contains the <APPLET> tag(s). The converter will automatically replace them. Opening that HTML page in a web browser again will result in a prompt for downloading and installing the plugin. Once that is done, the web browser can be restarted, the HTML page opened again, and the JApplet will run.

Now, by the end of Step 1, a simple “Hello World” JApplet can be written and executed in a web browser utilizing the plugin that has been downloaded. The first step to completing the goal of the project is complete.

Step 2: Getting data to an Applet

To get data to an Applet, the first instinct is to connect to the database directly using java.sql.Connection. After all, a JApplet is a Java™ application that is running inside a web browser. This is how a connection to a database would be done on any stand-alone application so why not in the JApplet?

Looking into this method, it is quickly discovered that this is not a viable option. There are a number of reasons why.

  1. Following proper Model-View-Controller design, there should already be Data Access Objects (DAOs) that do most of what is already needed. So doing it again in a JApplet is error prone and nothing but reinventing the wheel.
  2. The java.sql package is mostly interfaces and 3rd parties create the working classes. JTurbo (www.jturbo.com) is a popular implementation of these classes for Microsoft SQL Server. Consequently to use this 3rd party code in the JApplet, it would have to be part of the JApplet, and be downloaded with everything else. This would grow the size of the JApplet very quickly since the code implementing the java.sql package would be quite large and complex.
  3. Another problem arising with the code for the java.sql packing being done by a 3rd party is commercial licensing issues. It mostly likely will not be able to be freely distributed and used in this manner.

What will the security manager of a JApplet allow when it comes to making connections (of any kind) outside of the JApplet? Without getting into signed JApplets and certificates, a JApplet is allowed to make connections to the server that it was downloaded from, HTTP request to get a picture or animation to display in the applet for example, and that is it. The security manager blocks all other connections. Knowing this, what method will stay inside the limits of the security manager but ultimately allow access to the database?

To answer this question, leave the world of JApplets for a moment and think about the following scenario. Suppose I have a client (Netscape Navigator), a web server (Apache), a Servlet container (Tomcat), and a database (Microsoft SQL Server). How does information get from the database to the client? Figure 2 shows this network communication.

Figure 2 - Network Communication

Network Communication
Network Communication
  1. A request is made to Apache in a way that Apache will recognize that Tomcat should handle the request. If Tomcat is being used http://jakarta.apache.org/tomcat, the request might be something like http://my.development.server:8080/MyApplication
  2. Apache passes the request to the Tomcat.
  3. Tomcat then contacts Microsoft SQL Server
  4. Microsoft SQL Server returns the desired result sets. Tomcat constructs Java™ objects to hold that information. The information in those objects is then used to create a web page to return in response.
  5. The completed response is then sent to Apache
  6. Apache finally sends the response back to the client machine. The client machine’s web browser recognizes the response as an HTML page and displays it.

The JApplet needs to perform an almost identical process with the difference coming in steps 5 and 6. The JApplet will have GUI components such as trees, scrollbars, and buttons. However, getting a response from the web server in step 6 in the form of an HTML page is useless to the JApplet because the GUI components have no idea what to do with it. A tree is use to work with Java™ objects. If the response in step 6 was in the form of a Collection of Java™ objects then those could be added to a tree to display new nodes very easily. Looking closely at step 4, Java™ objects are constructed and the database information is put in them. Would it not be ideal to return to the JApplet those objects constructed in step 4? But the client machine and the web server are different machines connected only through HTTP requests and responses. Returning a Java™ object does not seem possible.

Java™ API To the Rescue!!

Looking at the java.io package there are two classes that have very suspicious names. ObjectOutputStream and ObjectInputStream. Looking at the descriptions and API of the two it is said that the use of these two is to read and write Java™ objects to and from Streams and to provide methods for doing this simply. What follows in Listing 1–2 is a simple example application that writes a Java™ object to a file and reads it back again.

Listing 1 - A Serializable Object

/** 
 * Cereal.java
 * - A Java object that will be written to a file. Note it implements Serializable
 */
import java.io.*;
public class Cereal implements Serializable 
{
    private String cerealname;    
    
    public Cereal() {}      
    
    public void setCerealName(String name) { cerealname = name; }    
    
    public String toString() {
        StringBuffer sp = new StringBuffer();
        sp.append("[");
        sp.append("cerealname=");
        sp.append(cerealname);
        sp.append("]");
        return sp.toString();
    }
}

Listing 2 - Testing Serialization

/**
 * SerialTest.java
 * - An application that will write a Cereal object to a file and then
 *   read the file back, reconstructing the object that was written
 */
import java.util.*;
import java.io.*;
public class SerialTest {   

    public static void main (String args[]) 
    {        
        SerialTest st = new SerialTest();
        try {
            st.go(args);            
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    private void go(String args[])
    throws Exception
    {
        Cereal frostedFlakes = new Cereal();
        frostedFlakes.setCerealName("Banana Frosted Flakes");              
        
        //
        // out to file
        //
        FileOutputStream fos = new FileOutputStream("cereal.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        oos.writeObject(frostedFlakes);
        String [] strarray = {"hello", "world", "Michael", "J", "Remijan"};
        oos.writeObject(strarray);
        
        String [] emptyarray = {""};
        oos.writeObject(emptyarray);        
             
        String stringObj = "goodbye world";
        oos.writeUTF(stringObj);
        
        int j = 100;
        oos.writeInt(j);
                     
        oos.flush();
        oos.close();
        fos.close();        
        
        //
        // in from file
        //
        Cereal newFrostedFlakes = new Cereal();
        FileInputStream fis = new FileInputStream("cereal.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        newFrostedFlakes = (Cereal)ois.readObject();
        
        Object [] objarray = (Object [])ois.readObject();
        Object [] emptyObjArray = (Object [])ois.readObject();
        String newStringObj = ois.readUTF();
        int newj = ois.readInt();
        
        ois.close();
        fis.close();
                
        //
        // output what i got from file
        //
        System.out.println(newFrostedFlakes);
        for (int i=0; i<objarray.length; i++)
        {
            System.out.println("("+i+") "+objarray[i]);
        }
        System.out.println("");
        for (int i=0; i<emptyObjArray.length; i++)
        {
            System.out.println("("+i+") "+emptyObjArray[i]);
        }        
        System.out.println(newStringObj);
        System.out.println(newj);
    }
}

BINGO!! This is exactly what is needed for the JApplet. After all, HTTP requests and responses are nothing but streams over a network. As long as the objects used implement the Serializable interface, they can be written to a stream, sent as a response over the web to the JApplet and the JApplet can typecast the object right out of the stream. In addition, since a request is nothing but a stream too, the JApplet can send objects to the web application in order to do more complicated tasks. Figure 3 shows this.

Figure 3 - Serialization Communication

Serialization Communication
Serialization Communication

This is a walk through of how to make a tree in the JApplet fill it in those first nodes with information from the database.

  1. Wrap an ObjectOutputStream around a ByteArrayOutputStream, the JApplet would write any objects to the stream it needs to send to the web application. Using URL and URLConnection objects, a connection is opened to the web server, with request properties:
    • Content-type="application/octet-stream"
    • Content-length="array length of the ByteArrayOutputStream."

A BufferedOutputStream wraps the URLConnection’s output stream and the and the array from the ByteArrayOutputStream is written to the stream.

  1. The web server passes the request to the web application.
  2. The web application wraps an ObjectInputStream around the InputStream from the Request object and uses it to get any Java™ objects out of the stream. Then the web application is free to contact the database if it needs too.
  3. The database returns the desired result. The web application constructs Java™ objects to hold that information. Wrapping an ObjectOutputStream around a ByteArrayOutputStream, those Java™ objects are then written to the stream. Then, using Response.getOutputStream(), a connection is opened back to the JApplet, with request properties
    • Content-type="application/octet-stream"
    • Content-length="array length of the ByteArrayOutputStream."
  4. The stream is then sent to the web server
  5. The response, consisting of a stream of Java™ objects, is then sent back to the JApplet. The JApplet then retrieves the objects from the stream by wrapping the InputStream of the URLConnection in an ObjectInputStream. The JApplet can then typecast the objects appropriately and use them however it needs to.

Now, by the end of Step 2, a fairly complex JApplet can start to be written. A request sent when the JApplet first starts can get those first few initial nodes of the tree from information in the database. Listeners put on the tree can respond to clicks on the nodes by sending additional requests to get children nodes. Buttons added to the JApplet can work with the selected nodes, linking information from one database to another. The needed functionality of the JApplet can now be implemented without going any further, but the convenient drag & drop is still missing. But drag & drop is a topic for another post.