|
WELCOME to the Java Developer Connection sm(JDC) Tech Tips, February 27, 2001. This issue covers: This tip was developed using Java 2 SDK, Standard Edition, v 1.3. This issue of the JDC Tech Tips is written by Stuart Halloway, a Java specialist at DevelopMentor (http://www.develop.com/java). The Lifecycle of an RMI ServerRMI allows you to invoke methods on objects in other Java virtual machines*, often across the network. Applications that use RMI to invoke methods in these remote objects are typically composed of two separate programs: an RMI client that makes requests, and an RMI server that executes the requests and returns results to the client. This tip examines the steps in the operation of an RMI server, that is, it's lifecycle.
All RMI servers implement a remote interface, that is an interface that extends the interface
Because An object that implements a remote interface becomes a remote object, and its methods can be invoked remotely (that is, in other VMs) through RMI. Let's write a simple RMI server that implements the remote interface:
The
A seventh step in an RMI server's lifecycle (though not demonstrated in the Step 1 is common to any Java object. Step 4 is common to any Java object that needs to serve a client request. Also garbage collection is common to Java objects that become unreferenced. But the other steps are specific to remote objects.
In Step 2, you explicitly export an object. This tells the RMI runtime that you want this object to be available to remote VMs. Exporting an object also returns the stub for the object. The stub is a class that does the work of formatting and transmitting method arguments to the RMI server and returning results to the RMI client. Later in this tip, you'll see how to create the stub. Note that many RMI classes skip this step of explicitly exporting an object. Instead these classes subclass In Step 3, you make an object findable by code running in other VMs. The simplest way to do this is to register the stub with the RMI registry. The registry a simple name-object lookup service that listens on port 1099, by default. Other ways to make an object findable are to simply pass the object to a remote method, or return it in a remote method implementation. In these situations, the RMI runtime automatically replaces the object with its stub. Of course, from the RMI client's perspective, this leads to an interesting question: Where did the remote object come from? Which leads to the interesting answer: It came from another remote object! Sooner or later, there must be one or more "original" objects that are obtained without the help of other RMI objects. The RMI registry provides this bootstrap service.
In Step 4, you decide how long the object will be available to service clients. In the simple example above, the server process presents a console prompt that tells the user how to shut down the server ( Steps 5 and 6 simply reverse steps 3 and 2. Now let's test this simple RMI server. To do that, you need to performs the following actions:
Create the stub class with the rmic command-line tool: rmic EchoServer
If you look in your class file directory, you should see the
Use the following code for the RMI client that connects to and uses
You will need three console windows to start the RMI registry, run the RMI server, and run the RMI client. In all of these windows, make sure your class path is set to the directory where the various rmiregistry java EchoServer java EchoClient The client should successfully connect to the server and return an echo. You should see the following displayed: Connecting to echo server... Echo returned Hello If it does not, read on. The tip "Dynamic Class Loading in RMI" shows some techniques for debugging RMI problems. For more advanced ideas on the RMI server lifecycle, read the Remote Object Activation tutorial at http://java.sun.com/j2se/1.3/docs/guide/rmi/activation.html Dynamic Class Loading in RMI
The example in the tip "The Lifecycle of an RMI Server" is a bit unrealistic because the client and server code reside in the same directory or folder. In a typical deployment, the RMI client, The stub presents more of a deployment problem. In JDK 1.3, RMI requires that clients use a stub class to connect to a remote VM. The stub class hides the details of network communication, sending method arguments and waiting for a return value. Today, you generate stub classes with the rmic command line tool. However future versions of RMI might allow clients to build stubs dynamically at runtime using dynamic proxies. Until that feature is added, clients need a way to guarantee that stubs are available. Installing the stubs on the client is not always viable, because stubs are an implementation detail that might change over time. Dynamic class loading allows client virtual machines to find stubs at runtime, without any special coding in the client.
To see the problem, test the
First, start the RMI registry from a console that does not have any of the rmiregistry -J-Dsun.rmi.loader.logLevel=VERBOSE
Now move all the server code ( java -cp server EchoServer You should see an exception:
This exception, received by the server, reports an error that actually fist occurred in the rmiregistry process. When the server attempts to bind the name into the registry, the registry must be able to load the stub class. The debug flag on the command line causes the registry to log this process. So in the rmiregistry console, you see something like this: ... loading class "EchoSever_Stub" from []
The
java -cp server \
-Djava.rmi.server.codebase=file:{serverloc}/ EchoServer
Replace the codebase value (that is file: java -cp server \ -Djava.rmi.server.codebase=file:/myhome/server/ EchoServer When you specify a codebase, the server annotates all outbound objects with URL location information. Clients can use this location information to download classes when necessary. If you try this command, the server should function normally, that is, you should see the following prompt on the console line:
Type
In the rmiregistry output you should also see a line proving that the registry loaded your code from the codebase: ... loading ... from [file:/yourURL/]
Now copy the java -cp client -Dsun.rmi.loader.logLevel=VERBOSE EchoClient You will get an exception telling you that the RMI class loader is disabled.
To fix this problem, you need to use dynamic class loading. However, to use dynamic class loading, you must install a security manager. Without a security manager installed, servers could easily attack you by sending malicious code that masquerades as a remote stub. When you turn on security, you also need a policy file that gives you the permissions necessary to do RMI work. So save the following policy file as
The
Notice the use of Now try the client with security installed, and with class load tracing enabled:
java -cp client -Dsun.rmi.loader.logLevel=VERBOSE \
-Djava.security.policy=client/SimpleRMI.policy \
-Djava.security.manager EchoClient
This time the client works as expected. The log output shows that the client successfully downloaded the stub from the server folder. ... loading class "EchoServer_Stub" from [file:/yourpath/] Echo returned Hello
This is very powerful. After the class is annotated with the codebase, the codebase travels with the class without any extra effort. You could pass But there's something to watch out for, something that you can observe by doing the following: Making sure that all console processes are shut down, restart the rmiregistry. But this time restart it with the server classes on its class path. (The easiest way to do this is to make sure that the class path is not set and then run rmiregistry from the server directory or folder.) Start the server as before, passing in the codebase annotation. Now if you try to run the client from the client folder, class loading fails:
This can be the most baffling problem that a novice RMI programmer faces. Everything looks normal. The codebase is set correctly in the server, and it appears that rmiregistry loaded the stub from the correct codebase. But somehow the codebase annotation gets lost, and the client cannot find the stub. In order for the stub's annotation to flow from one process to another, intermediate processes must either (1) implicitly load the annotated class using an RMI-created class loader or (2) explicitly reset the codebase. If any intermediate VM finds the stub on its class path, the normal system class loader is used, and the annotation passed from the server is lost. The series of events leading to this failure is:
You could work around this problem by resetting the codebase on every intermediate virtual machine, or even on the end client. This is rarely appropriate. The server is the logical owner of the stub, and should tell clients where to find it. In order to leave the server in charge, set the codebase on the server, and make sure that client processes never place dynamically-loaded classes on their class paths.
This tip demonstrates that RMI problems are much easier to troubleshoot if you use the correct debugging flags. In this example, the Note Sun respects your online time and privacy. The Java Developer Connection mailing lists are used for internal Sun MicrosystemsTM purposes only. You have received this email because you elected to subscribe. To unsubscribe, go to the Subscriptions page (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click the Update button. Subscribe To subscribe to a JDC newsletter mailing list, go to the Subscriptions page (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to, and click Update. Feedback Comments? Send your feedback on the JDC Tech Tips to: jdc-webmaster@sun.com Archives You'll find the JDC Tech Tips archives at: http://java.sun.com/jdc/TechTips/index.html Copyright
Copyright 2001 Sun Microsystems, Inc. All rights reserved. This document is protected by copyright. For more information, see: http://java.sun.com/jdc/copyright.html LINKS TO NON-SUN SITES The JDC Tech Tips may provide, or third parties may provide, links to other Internet sites or resources. Because Sun has no control over such sites and resources, You acknowledge and agree that Sun is not responsible for the availability of such external sites or resources, and does not endorse and is not responsible or liable for any Content, advertising, products, or other materials on or available from such sites or resources. Sun will not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such Content, goods or services available on or through any such site or resource. JDC Tech Tips February 27, 2001 * As used in this document, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform. Sun, Sun Microsystems, Java, Java Developer Connection, and Java Remote Method Invocation are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. | |||||||||||||
|
| ||||||||||||