Setup for Remote Method Invocation


Running even the simplest remote object example requires quite a bit more setup than does running a standalone program or applet. You must run programs on both the server and client computers. The necessary object information must be separated into client-side interfaces and server-side implementations. Also, a special lookup mechanism allows the client to locate objects on the server.

To get started with the actual coding, we walk through each of these requirements, using a simple example. In our first example, we generate a couple of objects of a type Product on the server computer. We then run a program on a client computer that locates and queries these objects.

Interfaces and Implementations

Your client program needs to manipulate server objects, but it doesn't actually have copies of them. The objects themselves reside on the server. The client code must still know what it can do with those objects. Their capabilities are expressed in an interface that is shared between the client and server and so resides simultaneously on both machines.

 interface Product // shared by client and server    extends Remote {    String getDescription() throws RemoteException; } 

Just as in this example, all interfaces for remote objects must extend the Remote interface defined in the java.rmi package. All the methods in those interfaces must also declare that they will throw a RemoteException. The reason for the declaration is that remote method calls are inherently less reliable than local callsit is always possible that a remote call will fail. For example, the server or the network connection may be temporarily unavailable, or there may be a network problem. Your client code must be prepared to deal with these possibilities. For these reasons, the Java programming language forces you to catch the RemoteException with every remote method call and to specify the appropriate action to take when the call does not succeed.

The client accesses the server object through a stub that implements this interface.

 Product p = . . .; // see below how the client gets a stub reference String d = p.getDescription(); System.out.println(d); 

In the next section, you will see how the client can obtain a reference to this kind of remote object.

Next, on the server side, you must implement the class that actually carries out the methods advertised in the remote interface.

 public class ProductImpl // server    extends UnicastRemoteObject    implements Product {    public ProductImpl(String d)       throws RemoteException    {       descr = d;    }    public String getDescription()       throws RemoteException    {       return "I am a " + descr + ". Buy me!";    }    private String descr; } 

NOTE

The ProductImpl constructor is declared to throw a RemoteException because the UnicastRemoteObject might throw that exception if it can't connect to the network service that keeps track of server objects.


This class has a single method, geTDescription, that can be called from the remote client.

You can tell that the class is a server for remote methods because it extends UnicastRemoteObject, which is a concrete Java platform class that makes objects remotely accessible.

NOTE

The ProductImpl class is not a typical server class because it does so little work. Normally, you only want to have server classes that do some heavy-duty work that a client could not carry out locally. We just use the Product example to walk you through the mechanics of calling remote methods.


Server classes generally extend the class RemoteServer from the java.rmi.server package, but RemoteServer is an abstract class that defines only the basic mechanisms for the communication between server objects and their remote stubs. The UnicastRemoteObject class that comes with RMI extends the RemoteServer abstract class and is concreteso you can use it without writing any code. The "path of least resistance" for a server class is to derive from UnicastRemoteObject, and all server classes in this chapter do so. Figure 5-5 shows the inheritance relationship between these classes.

Figure 5-5. Fundamental RMI classes


A UnicastRemoteObject object resides on a server. It must be alive when a service is requested and must be reachable through the TCP/IP protocol. This is the class that we extend for all the server classes in this book and is the only server class available in the current version of the RMI package. Sun or third-party vendors may, in the future, design other classes for use by servers for RMI.

NOTE

Occasionally, you may not want a server class that extends the UnicastRemoteObject class, perhaps because it already extends another class. In that situation, you need to manually instantiate the server objects and pass them to the static exportObject method. Instead of extending UnicastRemoteObject, call

 UnicastRemoteObject.exportObject(this, 0); 

in the constructor of the server object. The second parameter is 0 to indicate that any suitable port can be used to listen to client connections.


When you use RMI (or any distributed object mechanism, for that matter), you will have to master a somewhat bewildering set of classes. In this chapter, we use a uniform naming convention for all of our examples that we hope makes it easier to recognize the purpose of each class (see Table 5-1).

Table 5-1. Naming Conventions for RMI Classes

No suffix (e.g., Product)

A remote interface

Impl suffix (e.g., ProductImpl)

A server class implementing that interface

Server suffix (e.g., ProductServer)

A server program that creates server objects

Client suffix (e.g., ProductClient)

A client program that calls remote methods

_Stub suffix (e.g., ProductImpl_Stub)

A stub class that is located on the client (needed prior to JDK 5.0)

_Skel suffix (e.g., ProductImpl_Skel)

A skeleton class that is located on the server (needed prior to JDK 1.2)


Stub Class Generation

As of JDK 5.0, all stub classes are generated automatically, using the proxy mechanism discussed in Volume 1, Chapter 6. However, before JDK 5.0, you had to manually generate stubs with the rmic tool, as in the following example.

 rmic -v1.2 ProductImpl 

This call to the rmic tool generates a class file ProductImpl_Stub.class.

If you still use JDK 1.1, call

 rmic -v1.1 ProductImpl 

Two files are generated: the stub file and a second class file named ProductImpl_Skel.class.

NOTE

Remember to first compile the source file with javac before running rmic. If you are generating stubs for a class in a package, you must give rmic the full package name.


Locating Server Objects

To access a remote object that exists on the server, the client needs a local stub object. How can the client request such a stub? The most common method is to call a remote method of another server object and get a stub object as a return value. There is, however, a chicken-and-egg problem here. The first server object has to be located some other way. The Sun RMI library provides a bootstrap registry service to locate the first server object.

A server program registers objects with the bootstrap registry service, and the client retrieves stubs to those objects. You register a server object by giving the bootstrap registry service a reference to the object and a name. The name is a string that is (hopefully) unique.

 // server ProductImpl p1 = new ProductImpl("Blackwell Toaster"); Context namingContext = new InitialContext(); namingContext.bind("rmi:toaster", p1); 

The client code gets a stub to access that server object by specifying the server name and the object name in the following way:

 // client Product p = (Product) namingContext.lookup("rmi://yourserver.com/toaster"); 

RMI URLs start with rmi:// and are followed by a server, an optional port number, another slash, and the name of the remote object. Another example is:

 rmi://localhost:99/central_warehouse 

By default, the server is localhost and the port number is 1099.

NOTE

Because it is notoriously difficult to keep names unique in a global registry, you should not use this technique as the general method for locating objects on the server. Instead, there should be relatively few named server objects registered with the bootstrap service. These should be objects that can locate other objects for you. In our example, we temporarily violate this rule and register relatively trivial objects to show you the mechanics for registering and locating objects.


For security reasons, an application can bind, unbind, or rebind registry object references only if it runs on the same host as the registry. This prevents hostile clients from changing the registry information. However, any client can look up objects.

The RMI naming service is integrated into the Java Naming and Directory Information (JNDI) service. In JDK 1.3 and below, you use a standalone RMI naming service, like this:

 Naming.bind("toaster", p1); // on the server Product p = (Product) Naming.lookup("rmi://yourserver.com/toaster"); 

The code in Example 5-1 through Example 5-3 shows a complete server program that registers two Product objects under the names toaster and microwave.

TIP

If you compare our server with the server examples in the Sun tutorial documentation, you will note that we do not install a security manager in the server. Contrary to the statements in the tutorial, a security manager is not necessary for RMI servers. You do need a security manager if the client sends to the server objects that belong to classes that the server doesn't know. However, in our service, the client only sends String parameters. In general, it seems wise to limit dynamic class loading on servers.


Example 5-1. ProductServer.java
  1. import java.rmi.*;  2. import java.rmi.server.*;  3. import javax.naming.*;  4.  5. /**  6.    This server program instantiates two remote objects,  7.    registers them with the naming service, and waits for  8.    clients to invoke methods on the remote objects.  9. */ 10. public class ProductServer 11. { 12.    public static void main(String args[]) 13.    { 14.       try 15.       { 16.          System.out.println("Constructing server implementations..."); 17. 18.          ProductImpl p1 = new ProductImpl("Blackwell Toaster"); 19.          ProductImpl p2 = new ProductImpl("ZapXpress Microwave Oven"); 20. 21.          System.out.println("Binding server implementations to registry..."); 22.          Context namingContext = new InitialContext(); 23.          namingContext.bind("rmi:toaster", p1); 24.          namingContext.bind("rmi:microwave", p2); 25.          System.out.println("Waiting for invocations from clients..."); 26.       } 27.       catch (Exception e) 28.       { 29.          e.printStackTrace(); 30.       } 31.    } 32. } 

Example 5-2. ProductImpl.java
  1. import java.rmi.*;  2. import java.rmi.server.*;  3.  4. /**  5.    This is the implementation class for the remote product  6.    objects.  7. */  8. public class ProductImpl  9.    extends UnicastRemoteObject 10.    implements Product 11. { 12.    /** 13.       Constructs a product implementation 14.       @param n the product name 15.    */ 16.    public ProductImpl(String n) throws RemoteException 17.    { 18.       name = n; 19.    } 20. 21.    public String getDescription() throws RemoteException 22.    { 23.       return "I am a " + name + ". Buy me!"; 24.    } 25. 26.    private String name; 27. } 

Example 5-3. Product.java
  1. import java.rmi.*;  2.  3. /**  4.    The interface for remote product objects.  5. */  6. public interface Product extends Remote  7. {  8.    /**  9.       Gets the description of this product. 10.       @return the product description 11.    */ 12.    String getDescription() throws RemoteException; 13. } 

Starting the Server

Our server program isn't quite ready to run yet. Because it uses the bootstrap RMI registry, that service must be available. To start the RMI registry under UNIX, execute the statement

 rmiregistry & 

Under Windows, call

 start rmiregistry 

at a DOS prompt or from the Run dialog box. (The start command is a Windows command that starts a program in a new window.)

Now you are ready to start the server. Under UNIX, use the command:

 java ProductServer & 

Under Windows, use the command:

 start java ProductServer 

If you run the server program as

 java ProductServer 

then the program will never exit normally. This seems strangeafter all, the program just creates two objects and registers them. Actually, the main function does exit immediately after registration, as you would expect. However, when you create an object of a class that extends UnicastRemoteObject, a separate thread that keeps the program alive indefinitely is started. Thus, the program stays around to allow clients to connect to it.

TIP

The Windows version of the JDK contains a command, javaw, that starts the bytecode interpreter as a separate Windows process and keeps it running. Some sources recommend that you use javaw, not start java, to run a Java session in the background in Windows for RMI. Doing so is not a good idea, for two reasons. Windows has no tool to kill a javaw background processit does not show up in the task list. It turns out that you need to kill and restart the bootstrap registry service when you change the stub of a registered class. To kill a process that you started with the start command, all you have to do is click on the window and press CTRL+C.

There is another important reason to use the start command. When you run a server process by using javaw, messages sent to the output or error streams are discarded. In particular, they are not displayed anywhere. If you want to see output or error messages, use start instead. Then, error messages at least show up on the console. And trust us, you will want to see these messages. Lots of things can go wrong when you experiment with RMI. The most common error probably is that you forget to run rmic. Then, the server complains about missing stubs. If you use javaw, you won't see that error message, and you'll scratch your head wondering why the client can't find the server objects.


Listing Remote Objects

Before writing the client program, let's verify that we have succeeded in registering the remote objects. Call

 NamingEnumeration<NameClassPair> e = namingContext.list("rmi:"); 

to get an enumeration of all server objects with an rmi: URL. The enumeration yields objects of type NameClassPair, a helper class that contains both the name of the bound object and the name of its class. We only care about the names:

 while (e.hasMore()) System.out.println(e.next().getName()); 

Example 5-4 contains the complete program. The output should be

 toaster microwave 

Example 5-4. ShowBindings.java
  1. import java.rmi.*;  2. import java.rmi.server.*;  3. import javax.naming.*;  4.  5. /**  6.    This programs shows all RMI bindings.  7. */  8. public class ShowBindings  9. { 10.    public static void main(String[] args) 11.    { 12.       try 13.       { 14.          Context namingContext = new InitialContext(); 15.          NamingEnumeration<NameClassPair> e = namingContext.list("rmi:"); 16.          while (e.hasMore()) 17.             System.out.println(e.next().getName()); 18.       } 19.       catch (Exception e) 20.       { 21.          e.printStackTrace(); 22.       } 23.    } 24. } 

The Client Side

Now, we can write the client program that asks each newly registered product object to print its description.

Client programs that use RMI should install a security manager to control the activities of the dynamically loaded stubs. The RMISecurityManager is such a security manager. You install it with the instruction

 System.setSecurityManager(new RMISecurityManager()); 

NOTE

If all classes (including stubs) are available locally, then you do not actually need a security manager. If you know all class files of your program at deployment time, you can deploy them all locally. However, it often happens that the server program evolves and new classes are added over time. Then you benefit from dynamic class loading. Any time you load code from another source, you need a security manager.


NOTE

Applets already have a security manager that can control the loading of the stub classes. When using RMI from an applet, you do not install another security manager.


Example 5-5 shows the complete client program. The client simply obtains references to two Product objects in the RMI registry and invokes the getdescription method on both objects.

Example 5-5. ProductClient.java

[View full width]

  1. import java.rmi.*;  2. import java.rmi.server.*;  3. import javax.naming.*;  4.  5. /**  6.    This program demonstrates how to call a remote method  7.    on two objects that are located through the naming service.  8. */  9. public class ProductClient 10. { 11.    public static void main(String[] args) 12.    { 13.       System.setProperty("java.security.policy", "client.policy"); 14.       System.setSecurityManager(new RMISecurityManager()); 15.       String url = "rmi://localhost/"; 16.          // change to "rmi://yourserver.com/" when server runs on remote machine  yourserver.com 17.       try 18.       { 19.          Context namingContext = new InitialContext(); 20.          Product c1 = (Product) namingContext.lookup(url + "toaster"); 21.          Product c2 = (Product) namingContext.lookup(url + "microwave"); 22. 23.          System.out.println(c1.getDescription()); 24.          System.out.println(c2.getDescription()); 25.       } 26.       catch (Exception e) 27.       { 28.          e.printStackTrace(); 29.       } 30.    } 31. } 

Running the Client

By default, the RMISecurityManager restricts all code in the program from establishing network connections. However, the program needs to make network connections

  • To reach the RMI registry; and

  • To contact the server objects.

NOTE

Once the client program is deployed, it also needs permission to load its stub classes. We address this issue later when we discuss deployment.


To allow the client to connect to the RMI registry and the server object, you supply a policy file. We discuss policy files in greater detail in Chapter 9. For now, just use and modify the samples that we supply. Here is a policy file that allows an application to make any network connection to a port with port number of at least 1024. (The RMI port is 1099 by default, and the server objects also use ports 1024.)

 grant {    permission java.net.SocketPermission       "*:1024-65535", "connect"; }; 

NOTE

Multiple server objects on the same server can share a port. However, if a remote call is made and the port is busy, another port is opened automatically. Thus, you should expect somewhat fewer ports to be used than there are remote objects on the server.


In the client program, we instruct the security manager to read the policy file by setting the java.security.policy property to the file name. (We use the file client.policy in our example programs.)

 System.setProperty("java.security.policy", "client.policy"); 

Alternatively, you can specify the system property setting on the command line:

 java -Djava.security.policy=client.policy ProductClient 

NOTE

In JDK 1.1, a policy file was not required for RMI clients. You don't need a policy file for applets either, provided the RMI server and the RMI registry are both located on the host that serves the applet code.


If the RMI registry and server are still running, you can proceed to run the client. Or, if you want to start from scratch, kill the RMI registry and the server. Then follow these steps:

1.

Compile the source files for the interface, implementation, client, and server classes.

 javac Product*.java 

2.

If you use JDK 1.4 or below, run rmic on the implementation class.

 rmic -v1.2 ProductImpl 

3.

Start the RMI registry.

 rmiregistry & 

or

 start rmiregistry 

4.

Start the server.

 java ProductServer & 

or

 start java ProductServer 

5.

Run the client.

 java ProductClient 

(Make sure the client.policy file is in the current directory.)

The program simply prints

 I am a Blackwell Toaster. Buy me! I am a ZapXpress Microwave Oven. Buy me! 

This output doesn't seem all that impressive, but consider what goes on behind the scenes when the client program executes the call to the getdescription method. The client program has a reference to a stub object that it obtained from the lookup method. It calls the geTDescription method, which sends a network message to a receiver object on the server side. The receiver object invokes the getdescription method on the ProductImpl object located on the server. That method computes a string. The receiver sends that string across the network. The stub receives it and returns it as the result (see Figure 5-6).


 javax.naming.InitialContext 1.3 

Figure 5-6. Calling the remote getDescription method


  • InitialContext()

    constructs a naming context that can be used for accessing the RMI registry.


 javax.naming.Context 1.3 

  • static Object lookup(String name)

    returns the object for the given name. Throws a NamingException if the name is not currently bound.

  • static void bind(String name, Object obj)

    binds name to the object obj. Throws a NameAlreadyBoundException if the object is already bound.

  • static void unbind(String name)

    unbinds the name. It is legal to unbind a name that doesn't exist.

  • static void rebind(String name, Object obj)

    binds name to the object obj. Replaces any existing binding.

  • NamingEnumeration<NameClassPair> list(String name)

    returns an enumeration listing all matching bound objects. To list all RMI objects, call with "rmi:".


 javax.naming.NamingEnumeration<T> 1.3 

  • boolean hasMore()

    returns true if this enumeration has more elements.

  • T next()

    returns the next element of this enumeration.


 javax.naming.NameClassPair 1.3 

  • String getName()

    gets the name of the named object.

  • String getClassName()

    gets the name of the class to which the named object belongs.


 java.rmi.Naming 1.1 

  • static Remote lookup(String url)

    returns the remote object for the URL. Throws a NotBoundException if the name is not currently bound.

  • static void bind(String name, Remote obj)

    binds name to the remote object obj. Throws an AlreadyBoundException if the object is already bound.

  • static void unbind(String name)

    unbinds the name. Throws the NotBound exception if the name is not currently bound.

  • static void rebind(String name, Remote obj)

    binds name to the remote object obj. Replaces any existing binding.

  • static String[] list(String url)

    returns an array of strings of the URLs in the registry located at the given URL. The array contains a snapshot of the names present in the registry.

Preparing for Deployment

Deploying an application that uses RMI can be tricky because so many things can go wrong and the error messages that you get when something does go wrong are so poor. We have found that it really pays off to stage the deployment locally. In this preparatory step, separate the class files into three subdirectories:

 server download client 

The server directory contains all files that are needed to run the server. You will later move these files to the machine running the server process. In our example, the server directory contains the following files:

 server/    ProductServer.class    ProductImpl.class    Product.class 

CAUTION

If you use JDK 1.4 or below, add the stub classes (such as ProductImpl_Stub.class) to the server directory. They are needed when the server registers the implementation object. Contrary to popular belief, the server will not locate them in the download directory, even if you set the codebase.


The client directory contains the files that are needed to start the client. These are

 client/    ProductClient.class    Product.class    client.policy 

You will deploy these files on the client computer.

Finally, the download directory contains those class files needed by the RMI registry, the client, and the server, as well as the classes they depend on. In our example, the download directory looks like this:

 download/    Product.class 

If your clients run JDK 1.4 or lower, also supply the stub classes (such as ProductImpl_Stub.class). If the server runs JDK 1.1, then you need to supply the skeleton classes (such as ProductImpl_Skel.class). You will later place these files on a web server.

CAUTION

Keep in mind that three virtual machines use the download directory: the client, the server, and the RMI registry. The RMI registry needs the class files for the remote interfaces of the objects that it binds. The client and server need the class files for parameters and return values.


Now you have all class files partitioned correctly, and you can test that they can all be loaded.

You need to run a web server to serve the class files on your computer. If you don't have a web server installed, download Tomcat from http://jakarta.apache.org/tomcat and install it. Make a directory tomcat/webapps/download, where tomcat is the base directory of your Tomcat installation. Make a subdirectory tomcat/webapps/download/WEB-INF, and place the following minimal web.xml file inside the WEB-INF subdirectory:

 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"    "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> </web-app> 

Then copy the class files from the download staging directory into the directory tomcat/webapps/download.

Next, edit the client.policy file. It must give the client these permissions:

  • To connect to ports 1024 and above to reach the RMI registry and the server implementations;

  • To connect to port 80 (the standard HTTP port) to load the stub class files. You can omit this permission if you use Tomcat as a web serverit uses port 8080 by default.

Change the file to look like this:

 grant {    permission java.net.SocketPermission       "*:1024-65535", "connect";    permission java.net.SocketPermission       "*:80", "connect"; }; 

Finally, you are ready to test your setup.

1.

Start the web server.

2.

Point a web browser to the download URL (http://localhost:8080/download/Product.class for Tomcat) to verify that the web server is running.

3.

Start a new shell. Make sure that the class path is not set to anything. Change to a directory that contains no class files. Then start the RMI registry.

CAUTION

If you just want to test out your program and have client, server, and stub class files in a single directory, then you can start the RMI registry in that directory. However, for deployment, make sure to start the registry in a shell with no class path and in a directory with no class files. Otherwise, the RMI registry may find spurious class files that will confuse it when it should download additional classes from a different source. There is a reason for this behavior; see http://java.sun.com/j2se/5.0/docs/guide/rmi/codebase.html.

RMI registry confusion is a major source of grief for RMI deployment. The easiest way of protecting yourself is to make sure that the RMI registry cannot find any classes.

4.

Start a new shell. Change to the server directory. Start the server, giving a URL to the download directory as the value of the java.rmi.server.codebase property:

 java -Djava.rmi.server.codebase=http://localhost:8080/download/ ProductServer & 

CAUTION

It is very important that you make sure that the URL ends with a slash (/).

5.

Change to the client directory. Make sure the client.policy file is in that directory. Start the client.

 java -Djava.security.policy=client.policy ProductClient 

If both the server and the client started up without a hitch, then you are ready to go to the next step and deploy the classes on a separate client and server. If not, you need to do some more tweaking.

TIP

If you do not want to install a web server locally, you can use a file URL to test class loading. However, the setup is a bit trickier. Add the line


permission java.io.FilePermission
   "downloadDirectory", "read";

to your client policy file. Here, the download directory is the full path name to the download directory, enclosed in quotes, and ending in a minus sign (to denote all files in that directory and its subdirectories). For example,

 permission java.io.FilePermission "/home/test/download/-", "read"; 

In Windows file names, you have to double each backslash. For example,

 permission java.io. FilePermission "c:\\home\\test\\download\\-", "read"; 

Start the RMI registry, then the server with

 java -Djava.rmi.server.codebase=file://home/test/download/ ProductServer & 

or

 start java -Djava.rmi.server.codebase=file:/c:\home\test\download/ ProductServer 

(Remember to add a slash at the end of the URL.) Then start the client.


Deploying the Program

Now that you have tested the deployment of your program, you are ready to distribute it onto the actual clients and servers.

Move the classes in the download directory to the web server. Make sure to use that URL when starting the server. Move the classes in the server directory onto your server and start the RMI registry and the server.

Your server setup is now finalized, but you need to make two changes in the client. First, edit the policy file and replace * with your server name:

 grant {    permission java.net.SocketPermission       "yourserver.com:1024-65535", "connect";    permission java.net.SocketPermission       "yourserver.com:80", "connect"; }; 

Finally, replace localhost in the RMI URL of the client program with the actual server.

 String url = "rmi://yourserver.com/"; Product c1 = (Product) namingContext.lookup(url + "toaster"); . . . 

Then, recompile the client and try it. If everything works, then congratulations are in order. If not, you may find the sidebar checklist helpful. It lists a number of problems that can commonly arise when you are trying to get RMI to work.

TIP

In practice, you would not want to hardcode the RMI URLs into your program. Instead, you can store them in a property file. We use that technique in Example 5-13.


RMI Deployment Checklist

Deploying RMI is tough because it either works or it fails with a cryptic error message. Judging from the traffic on the RMI discussion list at http://archives.java.sun.com/archives/rmi-users.html, many programmers initially run into grief. If you do too, you may find it helpful to check out the following issues. We managed to get every one of them wrong at least once during testing.

  • Did you put the interface class files into the download and client directory?

  • Did you put the stub class files into the server directory (for JDK 1.4 or below)?

  • Did you include the dependent classes for each class? For example, in the next section, you will see an interface Warehouse that has a method with a parameter of type Customer. Be sure to include Customer.class whenever you deploy Warehouse.class.

  • When you started rmiregistry, was the CLASSPATH unset? Was the current directory free from class files?

  • Did you use a policy file when starting the client? Does the policy file contain the correct server names (or * to connect to any host)?

  • If you used a file: URL for testing, did you specify the correct file name in the policy file? Does it end in a /- or \\- ? Did you remember to use \\ for Windows file names?

  • Does your codebase URL end in a slash?

Finally, note that the RMI registry remembers the class files that it found as well as those that it failed to find. If you keep adding or removing various class files to test which files are really necessary, make sure to restart rmiregistry each time.


Logging RMI Activity

The Sun RMI implementation is instrumented to produce logging messages, using the standard Java logging API. (See Volume 1, Chapter 11 for more information on logging.)

To see the logging in action, make a file logging.properties with the following content:

 handlers=java.util.logging.ConsoleHandler .level=FINEST java.util.logging.ConsoleHandler.level=FINEST java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 

You can fine-tune the settings by setting individual levels for each logger rather than setting the global level to FINEST. Table 5-2 lists the RMI loggers.

Table 5-2. RMI Loggers

Logger Name

Logged Activity

sun.rmi.server.call

Server-side remote calls

sun.rmi.server.ref

Server-side remote references

sun.rmi.client.call

Client-side remote calls

sun.rmi.client.ref

Client-side remote references

sun.rmi.dgc

Distributed garbage collection

sun.rmi.loader

RMIClassLoader

sun.rmi.transport.misc

Transport layer

sun.rmi.transport.tcp

TCP binding and connection

sun.rmi.transport.proxy

HTTP tunneling


Start the RMI registry with the option


-J-Djava.util.logging.config.file=directory/logging.properties

Start the client and server with


-Djava.util.logging.config.file=directory/logging.properties

It is best to start the RMI registry, client, and server in different windows. Alternatively, you can log the messages to a filesee Volume 1, Chapter 11 for instructions.

Here is an example of a logging message that shows a class loading problem: The RMI registry cannot find the Product class. It needs it to build a dynamic proxy.

[View full width]

Aug 15, 2004 10:44:07 AM sun.rmi.server.LoaderHandler loadProxyClass FINE: RMI TCP Connection(1)-127.0.0.1: interfaces = [java.rmi.Remote, Product], codebase = "http://localhost:8080/download/" Aug 15, 2004 10:44:07 AM sun.rmi.server.LoaderHandler loadProxyClass FINER: RMI TCP Connection(1)-127.0.0.1: (thread context class loader: java.net .URLClassLoader@6ca1c) Aug 15, 2004 10:44:07 AM sun.rmi.server.LoaderHandler loadProxyClass FINE: RMI TCP Connection(1)-127.0.0.1: proxy class resolution failed java.lang.ClassNotFoundException: Product



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net