Revolutionary RMI: Dynamic class loading and behavior objects

Find out how RMI can help you define extensible, distributed object-oriented frameworks

Welcome to Enterprise Java, a new column dedicated to the Java Enterprise APIs: RMI, Java IDL, JDBC, Servlets, and EJB. I originally intended to call this column "Java Enterprise Jumpgate," in reference to the jumpgate technology in the hit TV series Babylon 5. (A jumpgate allows you to move between known points in the universe for trade, exploration, and laying waste -- which is similar to the purpose of the Internet.) I feel the analogy to distributed programming fits: the goal of this column is to demonstrate the capabilities, strengths, and weaknesses of each distributed programming API, helping you jump to new programming destinations. But you know editors, they just won't let you have any fun at their magazine's expense!

A word to the wise: distributed programming is inherently more difficult than standalone programming. However, the Java Enterprise APIs give Java programmers tools to ease the burden of producing robust, efficient applications. This column will help you get production-level, industrial-strength applications out the door, on time and on target.

When it comes to distributed computing, no area is getting more interest today from programmers than the Remote Method Invocation (RMI) API. Programmers love it, Microsoft hates it, and vendors want to make money off of it. EJB, Jini, and JavaSpaces all have their roots in RMI. So what better subject to kick off this column.

Getting excited about RMI

As an instructor, no Java Enterprise API is more rewarding to teach than RMI. It always works the same way:

  • Introduction
  • RMI hello world
  • Callbacks
  • Object serialization and RMI

The student reactions follow the modules:

  • Come on, caffeine, work!
  • Not "hello world" again...
  • Finally, something I can use
  • Wow, cool! Can we do that again?

Object serialization and RMI puts a smile on programmers' faces because they witness the subtle strength of RMI: the ability to transport true objects between processes.

What excites students is an example that demonstrates behavior objects. A behavior object is an instance that implements an interface, where the interface declares a remote method parameter type. For example, consider the following interfaces:

public interface ScribbleShape {
    public void draw(java.awt.Graphics g);
}

import java.rmi.*;
public interface ScribblePad extends Remote {
    public void addShape(ScribbleShape s) throws RemoteException;
}

The ScribblePad.addShape() method parameter represents a behavior object because the parameter instance must support the ScribbleShape interface, the ScribbleShape interface doesn't extend Remote, and the parameter will be passed between processes using serialization. From the ScribblePad implementation's perspective, the addShape() method is invoked by a Java object running in a process across the network and delivers a serialized version of the ScribbleShape instance that existed in the client process. The ScribblePad implementation can store the parameter value in a Vector, invoke methods on the object, serialize it to a file, or pass to yet another RMI object on a different machine.

All the RMI object knows about that parameter value is that it implements the ScribbleShape interface, exists in its address space (not remote), and supports the draw() method. When the ScribblePad implementation is done with the object, it will be garbage collected; of course, the client's version maintains its own separate state and identity.

In a nutshell, behavior objects are quite useful: they provide a mechanism for defining extensible, distributed object-oriented frameworks. Such frameworks prove useful when implementing the following:

  • Distributed algorithms. Some parallelized versions of sorting and searching algorithms are well suited to distributed processing. The premise behind implementing these is the presence of heterogeneous systems residing on the network. That is, after all, Java's claim to fame -- placing a homogeneous wrapper around heterogeneous networked systems.

  • User interfaces to networked services. Ever think about how a smart home would actually be configured? How do you go about controlling all the devices in a manufacturing plant (vision systems, robots, automated testing equipment, and environmental systems)? One method for solving this problem is to remotely load objects from the device that subclasses Panel, which presents controls to configure the device. The remotely loaded panels allow the user to fully customize a networked device without requiring installation of device drivers or special libraries.

  • Performing tasks within remote servers without being connected or polling. Just think if you could upload your stock object into an online brokerage server and it could monitor continuously, changing stock prices, news, indices, and whatever other parameters you would like, and the stock purchase object would autonomously buy and sell stocks without you having to do a thing. Send in your object and forget about it, no more staying up late to see how the Asian markets did!

  • Games. Some online games enable players to add weapons that have new powers and behaviors, like autonomously guarding areas or performing sophisticated search patterns. Behavior objects provide the capability to pass these objects into an opponent's space and wreak havoc until the garbage is collected.

RMI class loading

After contemplating the idea of behavior objects, you may start mumbling to yourself: What if the remote object can't locally load the necessary classes to deserialize the behavior object? Well now, that is the question when it comes to RMI. But the answer requires a little under-the-covers RMI, concerning how RMI loads classes when dealing with RMI methods.

From RMI's perspective, class files come in two flavors: code directly used by a program, and everything else. All classes a program compiles to are considered code directly used; this includes all core Java classes along with classes and interfaces used to declare parameters, return types, and variable declarations. Code considered not directly used includes stub and skeleton classes and extended classes of parameters.

When servicing a remote method call, RMI attempts to load marshalled values using one of three class loaders:

  • AppletClassLoader, if code is running within an applet

  • The system default class loader, if all classes are directly used by an application

  • RMIClassLoader to load classes not directly used by the application

So, here's the test:

  1. What class loader is used to load core Java classes (String, Thread, Component)? Answer: the system default class loader.

  2. What class loader is used to load the ScribbleShape interface when the ScribblePad.addShape() method is invoked by a remote process? Answer: the system default class loader.

  3. What class loader is used to load an implementation of ScribbleShape passed as a parameter to ScribblePad.addShape()? Answer: the RMIClassLoader, because the parameter instance's class is not directly used by the application.

Congratulations, you all get an A for the day. So where does the RMIClassLoader find the missing class files? Why, from the annotated codebase URL, of course.

The annotated what?

Dynamic class loading

RMI uses serialization to marshall remote method parameters and return values. Specifically, it serializes two values for each parameter: a String representation of a URL from which to remotely load the method value's class, and the serialized value. The act of writing the URL onto the stream is known as annotating the codebase URL.

The codebase URL is determined in one of the following ways:

  • If the class was loaded by a class loader other than the system's class loader, the class loader codebase is annotated

  • The value of java.rmi.server.codebase system property

  • If the codebase system property is not defined, then the codebase URL is null

Thus,

  • All objects created within an applet are annotated with the applet's codebase

  • Objects created within a typical application are annotated with the java.rmi.server.codebase system property

  • Behavior objects created on another machine and passed via an RMI call are loaded from the codebase host from which the object was originally instantiated

So, here's an interesting question: What if the remote object doesn't possess the necessary classes to deserialize the behavior object? RMI will load the classes from the codebase of the invoking process.

But, wait! That's not all. The Java virtual machine requires a security manager be installed to enable remote class loading. For applets, this is a nonissue because a security manager is ever present within Web browsers. Applications require the manual installation of a security manager. The most commonly used security manager to enable dynamic class loading is the RMISecurityManager. Applications usually install a security manager before performing any other functions:

public static void main(String args[]) {
   System.setSecurityManager(new RMISecurityManager());
   //other application tasks
} 

With the introduction of JDK 1.2, you must provide a security policy file when using the RMISecurityManager. Assuming you are on a Windows machine, you can modify the system policy file at java_home\jre\lib\security\java.policy. Alternatively, you can specify a policy file on the command line by setting the java.security.policy system property. That's my approach for this article. The policy file contains:

grant {
    // Allow everything for now
    permission java.security.AllPermission;
};

For more information on configuring policy files, see the JDK 1.2 security link in the Resources.

An example program

A distributed scribble application helps to demonstrate dynamic class loading and behavior objects. I chose this program because it's simple without being trivial, and it provides a visual means to demonstrate this nonvisual technology. The scribble application enables multiple clients to simultaneously present and manipulate a drawing. This is accomplished by maintaining a list of shapes in a central RMI object.

The scribble application is composed of three separate programs:

  • Scribble Pad -- A scribble editor program that enables the user to add new shapes to the drawing.

  • Scribble Viewer -- A client scribble program that provides viewing capabilities but no editing. capabilities.

  • Scribble Server -- The central RMI server application that maintains the list of shapes and registered clients. A client program adds a shape to the drawing by sending a ScribbleShape object to the server; in turn, the server broadcasts the new shape to all registered clients through a callback.

When the server is up and running, it presents an activity log using a JTextArea, and the scribble pad clients present a JFrame window that displays the drawing. The following figures show the appearance of each program.

Figure 1: Scribble Server Log
Figure 2: Scribble Pad editor
Figure 3: Scribble Viewer

The drawing consists of a list of objects that implement the ScribbleShape interface. Thus, all shapes passed to the server for inclusion to the drawing must implement the simple interface:

interface ScribbleShape {
   public void draw(Graphics g);
}

The figures above illustrate the three implementations of the ScribbleShape interface: ScribbleLine, ScribblePoint, and ScribbleX. Each class implements the draw() method to render their characteristic appearance on a Graphics object. The simplest shape implementation is the ScribbleX:

class ScribbleX implements ScribbleShape, Serializable {
   private Point fPt;
   private Color fColor = Color.red;
   public ScribbleX(Point p1) {
      this(p1, Color.red);
   }
   public ScribbleX(Point p1, Color c) {
      fPt = p1;
      fColor = c;
   }
   public void draw(Graphics g) {
      g.setColor(fColor);
      g.drawLine(fPt.x-5, fPt.y-5, fPt.x+5, fPt.y+5);
      g.drawLine(fPt.x+5, fPt.y-5, fPt.x-5, fPt.y+5);
   }
}

Where does dynamic loading come into play? Like many applications today, the viewer is free, but there's a catch. The viewer doesn't ship with any shapes. In fact, the server costs money, but doesn't ship with any shapes, either. Users who wish to create and add shapes to the drawing must purchase a Scribble Pad, which ships with three shapes: a line, a point, and an X. The distributed scribble program relies on RMI's dynamic class loading capability to fetch the appropriate ScribbleShape implementation classes from the Scribble Pad codebase that created and added the shape(s).

The following diagram illustrates what happens when a Scribble Pad adds an X shape to the drawing.

Figure 4: Adding an X shape to the drawing with two clients

This illustration assumes all programs are currently running and connected.

Here's what happens:

  1. The user of Client 1 draws an X using the mouse. The application creates a new ScribbleX object to encapsulate the required data and drawing behavior. This object is added to the drawing by calling the addShape(ScribbleShape s) method in the server.

  2. The server receives the parameter but can't locate the ScribbleX class locally, so it uses codebase information marshalled with the parameter to load the class from the Client 1 host HTTP server. (RMI performs this dynamic class loading automatically.)

  3. The server notifies each client about the new ScribbleShape by invoking the addShape(ScribbleShape s) callback. One of the clients happens to be Client 2.

  4. Client 2 cannot locally locate the ScribbleX class. RMI is smart enough to remember the codebase URL from which the class was originally loaded, so it marshalls the codebase URL with the parameter. So, the RMI system on Client 2 remotely loads the class from the HTTP server on Client 1. Magic!

Programming with behavior objects

Enough high-level discussion, let's look at some code. Here's a class diagram of the entire application.

Figure 5: Click here for a closer look at the UML class diagram of the Scribble application classes

The basis for constructing the application is two remote interfaces: ScribblePad and ScribblePadServer. A ScribblePad represents a client that is typically presenting the drawing and possibly allowing the user to place new shapes on the drawing. The ScribblePad interface is defined as:

public interface ScribblePad extends Remote {
   public void addShape(ScribbleShape s) throws RemoteException;
   public void clear() throws RemoteException;
}

Notice how the ScribblePad.addShape() method takes a ScribbleShape instance, which is a nonremote interface. This indicates the parameter is a behavior object and will be passed from the client to the server through serialization. What types of shapes can be added to the drawing? A square, circle, picture, animation, or any other shape as long it supports the ScribbleShape interface.

The ScribblePad interface is implemented by the ScribbleViewer class. The ScribbleViewer instance is displayed by a ScribbleViewerFrame object, which serves as the basis for the Scribble Viewer applications. The ScribblePadImpl class extends the ScribbleViewer class and adds editing capabilities. The Scribble Pad application is formed by placing a ScribblePadImpl object inside of a ScribblePadFrame instance.

The ScribblePadServer represents the server that maintains a list of ScribblePad clients and a list of ScribbleShape instances added to the drawing:

public interface ScribblePadServer extends ScribblePad {
   public Vector getShapes() throws RemoteException;
   public void addScribblePad(ScribblePad p) 
      throws RemoteException;
   public void removeScribblePad(ScribblePad p) 
      throws RemoteException;
}

Take a look at the following figure, which illustrates the calls involved with adding a shape to the drawing.

Figure 6: Adding a shape to the drawing

Things should be starting to come into focus; look in the mirror, if you're working along with me your face still has a strange look -- albeit not as bad as "the look."

Before getting into methods of running the different pieces of this application, let me walk you through a few of the coding high points. I am not going to list and discuss all the code here; you can download and view the source files from Resources. Let's look at the code that executes Steps 4, 5, and 6 from Figure 6.

When the user presses a mouse button, it executes a mouse event handler in the ScribblePadImpl class (Step 4 in the above diagram):

public class ScribblePadImpl extends ScribbleViewer {
   int fShapeType = LINE;
   Point fP1;   
   //inherited from ScribbleViewer
   //ScribblePadServer fServer;
   public static final int LINE = 1;
   public static final int POINT = 2;
   public static final int X = 3;
   public ScribblePadImpl(ScribblePadServer s) 
   throws RemoteException         
   {
       super(s);
       addMouseHandlers();  //not shown in listing
   }
   public void setShapeType(int t) {
      fShapeType = t;
   }
   //other support methods here      
   void addShapeToServer(ScribbleShape s) {
      try {
         fServer.addShape(s);
      } catch(RemoteException ex) {
        ex.printStackTrace();
      }
   }
   //user pressed the mouse indicating the point to create an
   //instance of the currently selected shape
   public void mousePressed(MouseEvent e) {
      Point p;
      switch (fShapeType) {
         case X:
            p = new Point(e.getX(), e.getY());
            ScribbleX x = new ScribbleX(p, Color.blue);
            addShapeToServer(x);
            break;
         case LINE:
            //start of a new line
            fP1 = new Point(e.getX(), e.getY());
            break;
         case POINT:
            p = new Point(e.getX(), e.getY());
            ScribblePoint pt = new ScribblePoint(p, Color.blue, 
                                                 true);
            addShapeToServer(pt);
            break;
      }
   }
   //other mouse handler code here . . .
}

The mousePressed() method is called whenever the user presses a mouse button. The method determines the type of shape to create, creates the shape, and then adds it to the server. Assuming the fShapeType instance variable was previously set to ScribblePadImpl.X by the containing ScribblePadFrame instance menu handlers (see the class diagram), the mousePresssed() method would generate and add a ScribbleX instance to the drawing. Of course, ScribbleX implements the ScribbleShape interface as shown previously.

The fServer.addShape() in addShapeToServer() will transfer control across the network to the ScribblePadServerImpl.addShape() method of the Scribble Server application:

public class ScribblePadServerImpl extends UnicastRemoteObject 
implements ScribblePadServer 
{
   private Vector fShapes;
   private Vector fClients;
   protected JTextArea fLog;
  public void addShape(ScribbleShape s) {
      Class cl = s.getClass();
      String annotation = RMIClassLoader.getClassAnnotation(cl);
      fLog.append("Adding shape: " + s.getClass().getName() +  
                  " loaded from " + annotation + "\n");
      fShapes.addElement(s);
      sendToClients(s);
   }

   protected void sendToClients(ScribbleShape s) {
      Vector v = (Vector)fClients.clone();
      ScribblePad p; 
      //iterate clients and pass shape to each one
      //assume com failure means client no longer valid
      for (int i=v.size()-1; i>=0; i--) {
         p = (ScribblePad)v.elementAt(i); 
         try {
            p.addShape(s);
         } catch(Exception ex) {
            fClients.removeElement(p);
            fLog.append("Error adding shape to client: " + 
                        ex + "\n");
         }
      }
   }    
   //other methods here       
}

Note how the server is written without requiring the actual types that'll be operated on in addShape(). The method declares the parameter using the nonremote ScribbleShape interface, so the parameter may be a ScribbleLine, ScribblePoint, ScribbleX, or ScribbleVirus. More on this last parameter later, but just to reiterate, the parameter is a behavior object because the server will be passed a copy of the object and all methods will run within the server's address space.

The addShape() method performs three basic operations:

  • Gets the annotated URL from which the class was remotely loaded. Because the server doesn't have local access to any shape classes, an annotation will always contain a URL. Refer to the server screenshot above to see the annotated URL for the received shapes.

  • Adds the shape to the Vector of shapes that make up the drawing.

  • Broadcasts the new shape to all registered ScribblePad instances. As a side note, the RMI stubs of the clients will be loaded using the same means as the shapes.

The ScribblePad.addShape() method invocation in sendToClients() transfers control to the remote ScribblePad implementations. For our client applications, the addShape() method is implemented in the ScribbleViewer class:

public class ScribbleViewer extends JPanel implements ScribblePad {
    ScribblePadServer fServer;
    Image fImage;
    Graphics fG;
    Dimension fSize = new Dimension(-1, -1);
   Vector fShapes = new Vector();
    public void addShape(ScribbleShape s) {
      fShapes.addElement(s);
       s.draw(fG);
       repaint();
    }    
    //other methods here - including paint()
 }

The ScribbleViewer.addShape() method is written using only the ScribbleShape interface. The ScribbleViewer class maintains an offscreen image of the current drawing, fImage, and a graphics object that draws on the offscreen image, fG. The method performs three basic operations:

  • Adds the shape to the local copy of the drawing shapes.

  • Draws the new shape onto the drawing using the ScribbleShape.draw() method, passing in the offscreen graphics context. In this case, a ScribbleX is passed into the method from the server and the Java VM will invoke the ScribbleX.draw() method.

  • Requests the viewer repaint the screen using the updated offscreen image.

Like ScribblePadServer, the viewer will remotely load the ScribbleX class from the annotated codebase URL during the argument unmarshalling process.

Experiencing the power of RMI

Now for the ultimate test: using the application. If you wish to follow along, you can download and extract application files from Resources. I will run three processes on three machines, but you can run the application on a single machine (the distribution separates the files into three directories, dynamic\server\, dynamic\pad\, and dynamic\viewer\), as shown in Figure 4. Running the application on a single machine decreases the setup complexity, but it sure creates a lot of processes.

I have three machines:

  • STEELRAIN, which runs the Scribble Server application
  • BUBBA, which runs the Scribble Pad editor application
  • FRED, which runs the Scribble Viewer application

As with any distributed application, the server must be started before the client processes. The following steps set up the server host STEELRAIN:

Step 1. Start the HTTP process that will serve the class files to remote RMI objects. I will use a simple Java class that understands HTTP well enough to deliver Java class files to requesting processes, called httpd. In a production-level environment, you'll want to use a commercial-quality Web server. Simply start in the Java interpreter in the directory root of your class files and specify the port on which it should run.

Note: All of my machines are Win32 machines. The start command you see in the following code runs processes in a new, independent command shell. start is the Windows equivalent of ending a command line with & in Unix.

> start java httpd 8000

Step 2.

Start the

rmiregistry

process to enable clients to locate the

ScribblePadServer

object.

> cd ..
> start rmiregistry
> cd server

The registry must be run such that it cannot locate any stub files using the CLASSPATH environment variable. If it can load classes from the filesystem, the marshalled stub will not be annotated with the codebase URL.

Step 3. Start the server process.

> java -Djava.rmi.server.codebase=http://steelrain:8000/    >>>
       -Djava.security.policy=dynamic.policy ScribblePadServerImpl

Note: It's imperative the codebase URL end with a slash (/).

The -D property specifies a system property on the command line. In this case, we're specifying the RMI codebase and the name of the security policy file.

Step 4. Interestingly, the registry requested some Scribble Server class files from our HTTP server. The registry process is just another RMI object that exists on a well known port. Processes wishing to expose RMI objects pass a remote reference, a stub, to the registry as a method argument that requires it to unmarshall the parameter value. The registry locates and loads the required class files from the filesystem using CLASSPATH, or the invoking process's codebase URL if it cannot be loaded locally. Consequently, the HTTP server console output on STEELRAIN reads:

> java httpd 8000
httpd running on port: 8000
document root is: C:\dynamic\server\.
Request from STEELRAIN: ScribblePadServerImpl_Stub.class
Request from STEELRAIN: ScribblePadServer.class
Request from STEELRAIN: ScribblePad.class
Request from STEELRAIN: ScribbleShape.class

Time to roll my chair over to BUBBA and run the Scribble Pad editor. The client machines don't require a registry process because the Scribble Server serves as the sole coordination point. Step 1. Start the HTTP process that will serve the class files to remote RMI objects in the dynamic/client directory. This server will listen on port 8001. I'm running this on a separate port so you can follow these instructions and run this example on a single machine.

> start java httpd 8001

Step 2. Start the Scribble Pad process.

> java -Djava.rmi.server.codebase=http://bubba:8001/    >>>
  -Djava.security.policy=dynamic.policy ScribblePadFrame STEELRAIN

Note: The optional command-line parameter is the Scribble Server host.

Remember, the codebase URL must end with a slash (/).

This command will create a Scribble Pad frame window if it is successful. The Scribble Server will dynamically load the Scribble Pad's stub class file from BUBBA's HTTP server:

> java httpd 8001
httpd running on port: 8001
document root is: E:\dynamic\client\.
Request from STEELRAIN: ScribbleViewer_Stub.class

Notice the request for the stub came from STEELRAIN, and we're running on BUBBA! The stub file is based on the ScribbleViewer class because it implements the remote interface ScribblePad and ScribblePadImpl extends ScribbleViewer (see the class diagram). Now draw a couple of shapes and inspect the output of httpd.

> httpd running on port: 8001
document root is: E:\dynamic\client\.
Request from STEELRAIN: ScribbleViewer_Stub.class
Request from STEELRAIN: ScribbleX.class
Request from STEELRAIN: ScribblePoint.class
Request from STEELRAIN: ScribbleLine.class

The Scribble Server dynamically loaded the class files from BUBBA because it couldn't find them on STEELRAIN using its CLASSPATH.

I hate to be rude and leave the room, but it's time to run the Scribble Viewer on FRED. Remember, this viewer doesn't have local access to the shapes, and it'll connect to the Scribble Server RMI object not directly to the Scribble Pad RMI object.

Step 1. Start the HTTP process that will serve the class files to remote RMI objects in the dynamic/viewer directory. This server will listen on port 8002.

> start java httpd 8002

Step 2. Start the Scribble Viewer process.

> java -Djava.rmi.server.codebase=http://fred:8002/    >>>
       -Djava.security.policy=dynamic.policy    >>>
       ScribbleViewerFrame STEELRAIN

Again, don't forget to end the codebase URL with a slash (/).

Step 3. FRED's display now has a Scribble Viewer displaying the drawing and BUBBA's HTTP server console reads:

> java httpd 8001
httpd running on port: 8001
document root is: E:\dynamic\client\.
Request from STEELRAIN: ScribbleViewer_Stub.class
Request from STEELRAIN: ScribbleX.class
Request from STEELRAIN: ScribbleLine.class
Request from STEELRAIN: ScribblePoint.class
Request from FRED: ScribbleX.class
Request from FRED: ScribbleLine.class
Request from FRED: ScribblePoint.class

Security precautions

That was a long answer to a short question. And usually the same student who asked the original question asks the follow-up question: Aren't there security issues with dynamically loading code from a remote, untrusted host? As a matter of fact, many potential problems can arise. Consider this shape class:

public class ScribbleVirus implements ScribbleShape {
  public void draw(Graphics g) {
      System.exit(0);
   }
}

Now we can inject the virus into the system with this simple class:

public class PoisonPill {
   public static void main(String args[]) throws Exception {
      System.setSecurityManager(new RMISecurityManager());
      String host = "localhost";
      if (args.length > 0) {
         host = args[0];
      }
      //locate server
      String url = "rmi://"+ host + "/Scribble Server";
      ScribblePadServer server = 
                (ScribblePadServer)Naming.lookup(url);
      //create a ScribbleVirus and add to the drawing     ScribbleVirus virus = new ScribbleVirus();
      server.addShape(virus);
   }
}

This is a nasty virus because the ScribbleVirus will be delivered to the clients and promptly terminate the Java runtime all the while leaving the virus active in the Scribble Server's list of shapes. The Scribble Server will not be affected because it stores the shapes but never calls ScribbleShape.draw(). You can take several steps to protect your distributed applications from evil or clumsy classes.

The most severe form of protection offered by RMI is the java.rmi.useCodebaseOnly system property. Defining this system property tells the RMI system to load classes only from the local host's codebase. This setting will prevent the RMI system from remotely loading class files across the network. A severe but effective precaution to protect your server.

Specifying security policy files provides a more flexible mechanism for specifying dynamic class loading policies. You must be using JDK 1.2 to take advantage of this mechanism, but it provides the capability to selectively enable VM capabilities on a per-host or per-class basis, such as invoking System.exit(), listening on ports, creating threads, and accessing the filesystem. The new security model is quite extensive and is way beyond the scope of this article. For more information on this very important subject see the security links in the Resources.

Conclusion

After playing around with the code for a while, I'm sure you'll be quite excited and start running around the room looking at the havoc you can create. Serious learning at the rim of Java's capabilities and exercise too!

RMI is all about passing objects between address spaces; behavior objects and dynamic class loading represent two very important RMI capabilities that make this possible. Future articles in this column will discuss custom RMI sockets and RMI activation, two technologies that rely heavily on both capabilities. In the meantime, you can take the "jump" and add increased runtime extensibility to your distributed applications.

Note: The next Enterprise Java column will appear in the February 1999 issue of JavaWorld.

Andy Krumel graduated from the US Naval Academy and started his career as a naval aviator; however, after a series of engine fires, radio failures, and assorted other mishaps, a career in computer programming looked more long-lived. Today, Andy is the founder of K&A Software, a rapidly growing, three-year-old Silicon Valley training and consulting firm specializing in Java and distributed applications. Andy and his cohorts will do just about anything to spread the word about Java's fantastic capabilities, from speaking at conferences, to teaching corporate training classes, to even writing an article. When not pontificating, Andy is squirreled away creating solutions for one of K&A's corporate clients.

Learn more about this topic

  • Download the complete source in zip format; you will need to have JDK 1.1.x and Swing 1.1 Beta 3 release, or the JDK 1.2 Release Candidate 1 (which includes Swing) to run the code http://www.javaworld.com/jw-12-1998/enterprise/jw-12-enterprise.zip
  • Download the Java Swing Toolkit from JavaSoft http://developer.java.sun.com/developer/earlyAccess/jfc/index.html
  • Bryan Morgan's "Java 1.2 extends Java's distributed object capabilities" (JavaWorld, April 1998) provides an excellent step-by-step RMI example http://www.javaworld.com/javaworld/jw-04-1998/jw-04-distributed.html
  • Sun's online RMI tutorial offers an excellent starting point for those needing a little RMI brush up http://java.sun.com/docs/books/tutorial/rmi/index.html
  • Sun's Java security home page, with specifications whitepapers, articles, and more http://java.sun.com/security/index.html
  • Sun's RMI Web page http://www.javasoft.com/products/jdk/rmi/
  • Always go to the specification if you want to know all the details http://java.sun.com/products/jdk/1.2/docs/guide/rmi/spec/rmiTOC.doc.html