JavaSoft, a Sun Microsystems Business

Frequently Asked Questions
RMI and Object Serialization

JDK 1.2 Contents

RMI

Very Frequent Questions

Debugging RMI programs

Networking

Using RMI to achieve X (for some X)

How does RMI work with X (for some X)

Internals, resources, and performance

Miscellaneous

Object Serialization

  1. Why must classes implement Serializable in order to be written to an ObjectOutputStream?
  2. Which JDK 1.1 system classes are marked serializable?
  3. I am having problems deserializing AWT components. How can I make this work?
  4. Does object serialization support encryption?
  5. The object serialization classes are stream oriented. How do I write objects to a random-access file?
  6. When a local object is serialized and passed as a parameter in an RMI call, are the bytecodes for the local object's methods also passed? What about object coherency, if the remote VM application "keeps" the object handle?
  7. How can I create an ObjectInputStream from an ObjectOutputStream without a file in between?
  8. I create an object and then send it across the net using the writeObject method and receive it using the readObject method. If I then change the value of a field in the object and send it as before, the object that the readObject method returns appears to be the same as the first object and does not reflect the new value of the field. Should I be experiencing this behavior?
  9. Are there any plans to support the serialization of thread objects?
  10. Can I compute diff(serial(x),serial(y))?
  11. Can I compress the serial representation of my objects using my own zip and unzip methods?
  12. Can I execute methods on compressed versions of my objects, for example isempty(zip(serial(x)))?
  13. If I try to serialize a font or image object and then try to reconstitute it in a different VM, my application dies. Why?
  14. How do I serialize a tree of objects?
  15. If class A does not implement Serializable but a subclass B implements Serializable, will the fields of class A be serialized when B is serialized?

RMI

A.1 Why do I get an exception for an unexpected hostname and/or port number when I call Naming.lookup?

The hostname and port number you see in the exception trace represent the address on which the looked-up server believes it is listening. While the RMI server can theoretically be on any host, it is usually the same host as that on which the registry is running, and on a different port.

Even if the server is mistaken about its hostname or IP address (or has a hostname that simply isn't resolvable by clients), it will still export all of its objects using that mistaken hostname, but you will see an exception every time you try to receive one of those objects.

The hostname which you specified in Naming.lookup to locate the registry has no effect on the hostname which is already embedded in the remote reference to the server.

Usually, the mysterious hostname is the unqualified hostname of the server, or a private name unknown to the client's nameservice, or (in the case of Windows) the server's Network->Identification->Machine Name.

The appropriate workaround is to set the system property java.rmi.server.hostname when starting the server. The value of the property should be the externally reachable hostname (or IP address) of the server -- whatever works when specified as the host-part in Naming.lookup is good enough.

For more detail, see the questions on callbacks and fully-qualified domain names.

A.2 Do I have to install the _Stub file in the client's CLASSPATH? I thought it could be downloaded.

A stub class can be downloaded, if the server that is exporting the remote object annotates the marshalled stub instance with the java.rmi.server.codebase property, which indicates the location from where the stub class can be loaded. You should set the java.rmi.server.codebase property on the server exporting a remote object. While remote clients could set this property, they would then be limited to only getting remote objects from the specified codebase. You should not assume that any client VM will have specified a codebase that resolves to the location of your object.

When a remote object is marshalled by RMI (whether as an argument to a remote call or as a return value), the codebase for the stub class is retrieved by RMI and used to annotate the serialized stub. When the stub is unmarshalled, the codebase is used to load the stub classfile using the RMIClassLoader, unless the class can already be found in the CLASSPATH or by the context classloader for the receiving object, such as an applet codebase.

If the _Stub class was loaded by an RMIClassLoader, then RMI already knows which codebase to use for its annotation. If the _Stub class was loaded from the CLASSPATH, then there is no obvious codebase, and RMI consults the java.rmi.server.codebase system property to find the codebase. If the system property is not set, then the stub is marshalled with a null codebase, which means that it cannot be used unless the client has a matching copy of the _Stub classfile in the client's CLASSPATH.

It is easy to forget to specify the codebase property. One way to detect this error is to start the rmiregistry separately and without access to the application classes. This will force Naming.rebind to fail if the codebase is omitted.

B.1 Will there be debugging mechanisms built into RMI?

RMI supports a simple call-logging facility for debugging. But there are no current plans to support a full-featured, interactive, remote debugger.

B.2 I am having a problem debugging my program on Windows95. Any suggestions?

The javaw command throws away output to stdout and stderr, so for debugging purposes it is better to run the java command in a separate window so that you can see reported errors. To do this, execute a command like the following:
      start java EchoImpl

It is advised not to use the javaw command during development. To watch the server activity, start the server with -Djava.rmi.server.logCalls=true.

B.3 Why do I get a java.lang.ClassMismatchError while running my program?

You probably modified one or more classes that were being used by RMI programs while your program was running. Try restarting all RMI applications (including java.rmi.registry.RegistryImpl). This should clear things up.

B.4 I am sending an array of remote objects and receive an ArrayStoreException. What's going on?

RMI replaces the remote objects with the stub and therefore the type of the array must be that of the interface. The code would look like:
   FooRemote[] f = new FooRemote[10];
   for (int i = 0; i < f.length; i++) {
      f[i] = new FooRemoteImpl();
   }

Now RMI can put the stub into each cell of the array without an exception on the remote call.

B.5 I have local objects that are synchronized. When I make them remote, my application hangs. What's the problem?

What you encountered was distributed deadlock. In the local VM case, the VM can tell that the calling object "A" owns the lock and will allow the call back to "A" to proceed. In the distributed case, no such determination can be made, so the result is deadlock.

Distributed objects behave differently than local objects. If you simply reuse a local implementation without handling locking and failure, you will probably get unpredictable results.

B.6 I am getting a ClassNotFoundException for my stub class when I try to register a remote object in the registry. What's happening?

When you make a call to the registry to bind an object, the registry actually binds a reference to the stub for the remote object. So, the classpath on the VM running the registry either needs to include the directory containing the stub file, or your server needs to specify the java.rmi.server.codebase property to indicate the location of the stub. An easy way to check CLASSPATH is to use the javap command, supplying a fully-qualified class name (for example, com.bar.pro.Useful). It uses the current CLASSPATH to find and print the interface to a class.

B.7 My server died. Can I get a trace of the server activity?

To get a trace of the server activity, start the server as follows:
    java -Djava.rmi.server.logCalls=true YourServerimpl
where YourServerImpl is the name of your server. If your server has hung, you can get a monitor dump and thread dump by doing a ctrl-\ on solaris and a ctrl-break on Windows.

C.1 How do RMI clients contact remote RMI servers?

For an RMI client to contact a remote RMI server, the client must first hold a reference to the server. The Naming.lookup method call is the most common mechanism by which clients initially obtain references to remote servers. Remote references may be obtained by other means, for example: all remote method calls can return remote references. This is what Naming.lookup does; it uses a well-known stub to make a remote method call to the rmiregistry, which sends back the remote reference to the object requested by the lookup method.

Every remote reference contains a server hostname and port number that allow clients to locate the virtual machine that is serving a particular remote object. Once an RMI client has a remote reference, the client will use the hostname and port provided in the reference to open a socket connection to the remote server.

Please note that with RMI the terms client and server can refer to the same Java program. A Java program that acts as an RMI server contains an exported remote object. An RMI client is a program that invokes one or more methods on a remote object in another Java Virtual Machine (JVM). If a JVM performs both of these functions, it may be referred to as an RMI client and an RMI server.

C.2 Why does my remote method or "callback" routine fail with a nested java.net.UnknownHostException?

In many versions of the JDK (all versions of the JDK except in 1.1, and the latest releases), RMI may default to using an unresolvable server hostname (for example: unqualified names, WINS names, or unqualified DHCP names). When an RMI client invokes a remote method using a reference that contains an unresolvable server hostname, the client will throw an UnknownHostException.

In order to generate functional remote references, RMI servers must be able to supply a fully-qualified hostname or IP address that is resolvable from all RMI clients (an example of a fully-qualified hostname is foo.bar.com). If an RMI program provides a remote callback operation then that program serves an RMI object and consequently, must be able to determine a resolvable hostname to use as its server hostname in the remote references it passes to RMI clients. JVM's that make calls to applets that serve remote objects may throw UnknownHostExceptions because the applet has failed to provide a useable server hostname.

If your RMI application throws an UnknownHostException, you can look at the resulting stack trace to see if the hostname that the client is using to contact its remote server is incorrect or not fully-qualified. If necessary, you can set the java.rmi.server.hostname property on the server to the correct IP address or hostname of the server machine and RMI will use this property's value to generate remote references to the server.

C.3 My server is using a fully-qualified domain name or IP address, so why do I still get an UnknownHostException?

Depending upon the configuration of your network's name service, a fully-qualified hostname that is recognized on one RMI host may not be resolvable from another RMI host. Some examples of where this situation may arise are:

C.4 I am using the latest release of the JDK and I have a host that has multiple IP addresses. RMI is choosing the wrong IP address for its server hostname, how do I work around this problem?

Set the java.rmi.server.hostname property to the correct IP address of the RMI server machine. You can also specify that your server use a fully-qualified hostname obtained from a name service by setting the property:
    java.rmi.server.useLocalHostname=true

C.5 How does RMI obtain a server hostname in each of the versions of the JDK?

Methods RMI uses to obtain a server hostname in each of the versions of the JDK:

JDK1.1

RMI relied upon java.net.InetAddress.getLocalHost() to return a fully-qualified domain name. InetAddress objects initialized local hostnames in a static block of code, performing a reverse lookup on the local IP address to retrieve a local hostname. However, on machines that were not connected to the network, this behavior caused the program to hang while InetAddress looked for a hostname that could not be found.

JDK1.1.1-1.1.6 & JDK1.2beta2-3

To work around the JDK1.1 problem on stand-alone systems, InetAddress was modified in JDK1.1.1 to only retrieve the [potentially unqualified] hostname returned from a native system call, which did not attempt to consult a name service. RMI was not modified to compensate for this change since the property java.rmi.server.hostname allowed users to override incorrect hostnames provided by InetAddress. RMI made no attempt to consult a name service and could default to using unqualified hostnames.

Later versions

To compensate for the many problems that were generated by the 1.1.1 change in functionality of InetAddress, the following behavior has been integrated into the most recent versions of the JDK:

RMI will use an IP address or a fully-qualified domain name to identify a machine that serves a remote object. Server hostnames are initialized to the value obtained by performing the following actions:

  1. By default, RMI uses the IP address of the server host as the server name for remote references.
  2. If the property, java.rmi.server.hostname is set, RMI will use its value as the server hostname, and will not attempt to find a fully-qualified domain name through any other method. This property takes precedence over all other means of finding an RMI server name.
  3. If the property, java.rmi.server.useLocalHostname is set to true (by default, the value of this property is false), RMI applies the following routine to obtain a hostname for the RMI server:
    1. If the value returned by the InetAddress.getLocalHost().getHostName() method contains a "." character, then RMI will assume that this value is the server's fully-qualified domain name and will use it as the server hostname.
    2. Otherwise, RMI will spawn a thread to query the local name service for the fully-qualified domain name of the RMI server. If the name service takes too long to return, or the name service returns but its response does not contain a "." then RMI will use the server's IP address obtained from InetAddress.getLocalHost().getHostAddress().
    Users can override the default time (10 seconds or 10000 milliseconds) that RMI will look for a fully-qualified domain name by setting the following property:
    sun.rmi.transport.tcp.localHostnameTimeOut=timeOutMillis
    where timeOutMillis is the time that RMI will wait in milliseconds. For example:
                java -Dsun.rmi.transport.tcp.localHostnameTimeOut=2000 MyServerApp
            
When using activatable remote objects, it is recommended that RMI servers set the value of the java.rmi.server.useLocalHostname property to true. In general, hostnames are more stable than IP addresses. Activatable remote objects tend to last longer than transient remote objects (for example,  survive a reboot). An RMI client will be more likely to locate a remote object over a long period of time if it uses a qualified hostname rather than an explicit IP address.

C.6 Why do Naming.bind and Naming.lookup take an extraordinarily long time on Windows?

Most likely, your host's networking setup is incorrect. RMI uses the Java API networking classes, in particular java.net.InetAddress, which will cause TCP/IP host name lookups - both host to address mapping and address to hostname mapping (the InetAddress class does this for security reasons). On Windows, the lookup functions are performed by the native Windows socket libraray, so the delays are happening not in RMI, but in the Windows libraries. If your host is set up to use DNS, then it is usually a problem with the DNS server not knowing about the hosts involved in communication, and what you are experiencing are DNS lookup timeouts. Try specifying all the involved hostnames/addresses in the local file \winnt\system32\drivers\etc\hosts or \windows\hosts. The format of a typical host file is:
    IPAddress     Machine Name
e.g.:
    208.2.84.61   homer
This should dramatically cut down the time it takes to make the first lookup.

C.7 How do I use RMI on a Windows95 standalone machine, not connected to the network?

To get RMI working on a Win95 machine that is not on a network, TCP/IP must be configured. One way to accomplish this, is to configure an unused COM port as a dedicated PPP or SLIP connection. Then disable DHCP and manually configure an IP address (e.g. 192.168.1.1). You should then find that from a DOS Shell, you can ping yourself (for example, ping mymachine). You should now be able to run the stock RMI example.

C.8 Why do I get the exception "java.net.SocketException: Address already in use" when I try to run the registry?

This exception means that the port that the RegistryImpl uses (by default 1099) is already in use. You may have another registry already running on your machine and will need to stop it.

C.9 How can I use RMI through a firewall?

It depends on whether you need to traverse a firewall for outgoing calls or for incoming calls.

C.10 How can I make outgoing RMI calls through a local firewall?

There are three main methods: HTTP-tunneling, SOCKS, and downloaded socket factories.

HTTP-tunneling

This well-worn method is popular since it requires almost no setup, and works quite well in firewalled environments which permit handle HTTP through a proxy, but disallow regular outbound TCP connections.

If RMI fails to make a normal (or SOCKS) connection to the intended server, and it notices that a HTTP proxy server is configured, it will attempt to tunnel RMI requests through that proxy server, one at a time.

There are two forms of HTTP-tunneling, tried in order. The first is http-to-port; the second is http-to-cgi.

In http-to-port tunneling, RMI attempts a HTTP POST request to a http: URL directed at the exact hostname and port number of the target server. The HTTP request contains a single RMI request. If the HTTP proxy accepts this URL, it will forward the POST request to the listening RMI server, which will recognise the request and unwrap it. The result of the call is wrapped in a HTTP reply, which is returned through the same proxy.

Often, HTTP proxies will refuse to proxy requests to unusual port numbers. In this case, RMI will fall back to http-to-cgi tunneling. The RMI request is encapsulated in a HTTP POST request as before, but the request URL is of the form http://hostname:80/cgi-bin/java-rmi.cgi?port=n (where hostname and n are the hostname and port number of the intended server). There must be a HTTP server listening on port 80 on the server host, which will run the java-rmi.cgi script (supplied with the JDK), which will in turn forward the request to an RMI server listening on port n. RMI can unwrap a HTTP-tunneled request without help from a http server, CGI script, or any other external entity. So, if the client's HTTP proxy can connect directly to the server's port, then you don't need a java-rmi.cgi script at all.

To trigger the use of HTTP tunneling, the standard system property http.proxyHost must be set to the hostname of the local HTTP proxy. (There are reports that some Navigator versions do not set this property.)

The major disadvantage of HTTP tunneling is that it does not permit inward calls or multiplexed connections. A secondary disadvantage is that the http-to-cgi method opens a dramatic security hole on the server side, since without modification it will redirect any incoming request to any port.

SOCKS

The default implementation of sockets in the JDK will use a SOCKS server if available and configured. The system property socksProxyHost must have been set to the hostname of the SOCKS server; if the port number of the SOCKS server is not 1080 it must be specified in the socksProxyPort property.

This approach would appear to be the most generally-useful solution. As yet, ServerSockets do not use SOCKS, so incoming calls must use another mechanism.

Downloaded socket factories

This is an innovation in JDK1.2beta4, allowing the server to specify the socket factory that the clients must use. The clients must be running JDK1.2beta4 or later. Detailed instructions on creating custom RMI socket factories are available.

The disadvantage of this approach is that the traversal of the firewall must be done by code provided by the RMI server side, which does not necessarily know how that traversal must be done, nor does it automatically have sufficient privilege to traverse the firewall.

C.11 How can I receive incoming RMI calls through a local firewall?

There are four main methods: known ports, transport-level bridge, application-level proxy, and connection multiplexing.

Known Ports

If the exported objects are all exported on a known port on a known host, then that host and port can be explicitly permitted at the firewall. Normally, RMI asks for port 0 (which is code for "any port"). In JDK1.2, there is an extra argument to the exportObject method to specify the exact port number. In JDK1.1, the server must subclass the RMISocketFactory and intercept requests to createServerSocket(0), replacing it with a request to bind to a specific port number.

This approach has the disadvantage that it requires the assistance of the network administrator responsible for the local firewall. If the exported object is being run in a different location (because code was downloaded to that site), then the local firewall may be run by network administrators who don't know who you are.

Transport-level bridge

A transport-level bridge is a program that reads bytes from one TCP connection and writes them to another (and vice versa) without knowing or caring what the bytes represent.

The idea here is to export objects in such a way that anyone outside the firewall who wants to call remote methods on that object instead contacts a different port (perhaps on a different machine). That different port has a running program which makes a second connection to the real server and then pumps bytes each way.

The tricky part is convincing the client to connect to the bridge. A downloadable socket factory (JDK1.2) can do this efficiently; otherwise, it is possible to set the java.rmi.server.hostname property to name the bridge host and arrange for port numbers to be the same.

Application-level proxy

This approach is quite a bit of work, but leads to a very secure arrangement. A proxy program runs on a firewall host (one which can be accessed from outside as well as from inside). When an internal server intends to make an exported object available to the world, it contacts the proxy server and gives it a remote reference. The proxy server creates a proxy object (a new remote object residing in the proxy server) which implements the same remote interfaces as the original. The proxy server returns a remote reference for the new proxy object to the internal server, which communicates it to the outside world (somehow).

When an outsider makes a call on the proxy, the proxy immediately forwards the call to its original object on the internal server. The use of the proxy is transparent to the outsider (but not to the internal server, who has to decide whether to pass the original reference or the proxy reference when talking to anyone).

Needless to say, this requires considerable setup and the co-operation of the local netadmins.

Connection multiplexing

Since JDK1.0.2 did not permit unprivileged code to listen on TCP ports (even those > 1023, the so-called anonymous ports), JDK1.0.2 code could not export objects to JDK1.1 code, even though JDK1.1 code didn't have that restriction. Consequently, the transport layer was modified to permit multiplexed connections: that is, connections whose function is to contain more connections, each of which could do the actual work. Multiplexed connections do not time out, however, so they are not scalable.

Nevertheless, it is possible to abuse multiplexed connections to bypass a particular firewall rule: firewalls which allow connections in one direction but not in another.

The technique is to prevent RMI from listening on a TCP port, thereby triggering the multiplex mechanism. The only way to do this is to install a security manager which throws a SecurityException inside checkListen.

If you could do that, you might as well install a socket factory instead.

C.12 So what do I have to do to get RMI to operate through two firewalls?

First of all, what co-operation can you expect from the client-side firewall?

In the most pessimistic case, the client-side firewall allows no direct TCP connections and has only a HTTP proxy server so that firewalled clients can "surf the web". In this case, your server host will receive connections at port 80 containing RMI requests embedded in HTTP requests. You can use a HTTP server with the java-rmi.cgi program, or you can run the RMI server directly on port 80. Either way, the server cannot use callback objects exported by the clients..

A more optimistic case is that the client can make direct connections to the server but cannot receive incoming connections from the server. In this case, callback objects are not normally possible either. (There is a specific case where applets in some environments may trigger a multiplexed connection mode intended for JDK1.0.2 compatibility -- this would allow callbacks but is not scalable or supported.)

The most conservative approach, assuming no help from the client firewall administrators, is:

D.1 Is there a way to get automatic notification as soon as a remote Java VM fails?

This is very desirable and is on our RFE list. A notification facility should be available in a future release.

D.2 From within a Java Virtual Machine, can a new Java VM be spawned on a remote machine?

JDK1.2 includes object activation, and there are several tutorials explaining how to use it.

D.3 Is it possible for a remote object to be notified when all clients disconnect?

Yes. Your remote object will need to implement the java.rmi.server.Unreferenced interface (in addition to any other necessary interfaces). RMI will provide the notification by calling the unreferenced method when all clients disconnect. Your implementation of the unreferenced method will determine what action your remote object should take upon receiving such a notification. However, if there is a reference in the registry, then the Unreferenced.unreferenced method will never be called.

D.4 Why doesn't my server program exit when all clients disconnect?

In RMI, a server VM should exit if there are
  1. No outstanding client-held references to the remote objects in VM, and
  2. No non-daemon threads executing in the VM.
However, just because there are no local or remote references to a remote object does not mean the object will be garbage collected in a timely fashion. It does mean that the remote object's memory can be collected to satisfy a memory allocation that would otherwise fail (with an OutOfMemoryError).

Although the Java API does not specify the timeliness of collection anyway, there is a particular reason for the what can seem like indefinitely-delayed collection of remote objects in the JDK 1.1 implementation. Under the covers, the RMI runtime holds a weak reference to exported remote objects in a table (to keep track of local as well as remote references to the object). The only weak reference mechanism available in the JDK 1.1 VM uses a non-aggressive, caching collection policy (well-suited for a browser), so objects that are only "weakly reachable" will not get collected until the local GC decides that it really needs that memory to satisfy another allocation. For an idle server, this could never happen. But if memory is needed, an unreferenced server object will be collected.

The JDK1.2 release will include a new infrastructure that RMI will use to reduce significantly the number of conditions under which this problem occurs (currently available in JDK1.2Beta4).

D.5 How does the distributed garbage collector detect a client that disconnects? Is it advisable to use System.exit for graceful client termination?

When the RMI runtime in a client VM detects that a remote object is no longer referenced locally, it asynchronously notifies the server relatively quickly so that the server can update the object's referenced set accordingly. The distributed garbage collector uses a lease associated with each client-held remote object reference, and renews leases to remote objects while the client still holds such references. The purpose of the lease renewal mechanism is to allow the server to detect the abnormal termination of clients, so that a server does not hold on to a remote object forever because of a client that was not able to send the appropriate "unreferenced" message before it stopped running. In this context, a client invoking System.exit() is considered abnormal termination, because it does not allow the RMI runtime to send the appropriate "unreferenced" messages to the server. Executing System.runFinalizersOnExit in the client before termination is not sufficient, because not all of the necessary processing is handled in a finalizer; i.e. the "unreferenced" message will not get sent to the server. (Using "runFinalizersOnExit" is generally ill-advised and deadlock-prone anyway.)

If you need to use System.exit() to terminate a client VM, to ensure that remote references held in that VM are cleaned up in a more timely fashion, you should make sure that there are no remote references still reachable (explicitly null any local references to make them unreachable from running threads), and then it helps to do something like this before exiting:

    System.gc();
    System.runFinalization();

D.6 How can my server tell when a client crashes?

Wait for the client's lease to run out, and then the unreferenced() method will be called by RMI (remember that the registry is also a client for this purpose, since it holds references for all its bindings).

If a client is holding a remote reference, it also holds a lease for that reference, which must be renewed (by contacting the server and making a dirty() call). When the final lease for an exported object has expired or closed, the object is considered unreferenced, and (if it implements java.rmi.Unreferenced) its unreferenced() method will be invoked.

If two or more clients have references to the same remote object, the unreferenced() method will not be called until all of them have expired their leases on it. Consequently, if you are using this technique to track individual clients, each client must have a reference to its own Unreferenced object.

D.7 The unreferenced() method doesn't get called until ten minutes after I have stopped using the remote object! How can I shorten this delay?

The lease expiration time is specified by the server, where it can be set using a system property, java.rmi.dgc.leaseValue, whose value is in milliseconds. To set this to a shorter time (for example: 30 seconds), start the server like this:
    java -Djava.rmi.dgc.leaseValue=30000 ServerMain

The default value is 600000 milliseconds (or 10 minutes).

The client will renew each lease when it is halfway expired. If the lease interval is too short, the client will waste a lot of network bandwidth needlessly renewing a lease. If the lease interval is much too short, the client will be unable to renew the lease in time, and the exported object may be deleted as a result.

Future releases of RMI may invalidate remote references if they fail to renew their leases (in order to preserve referential integrity); you should not rely on being able to use stale references to remote objects.

Note that you'll only have to wait for the timeout if the client machine crashes. If the client has some control when the disconnect occurs, it can send out the DGC clean call quickly, making the use of Unreferenced quite timely. You can help this process along by nulling out any references the client may have to the remote object and then calling System.gc(). (In 1.1.x, you may have to run finalizers synchronously and then run GC again.)

D.8 Why can't I get an immediate notification when a client crashes?

Because nothing the server does can distinguish a crashed host from a network delay.

If or when the crashed client later restarts and contacts the server, the server can infer that the client has lost its state. If a TCP connection is held open between the client and the server throughout their interaction, then the server can detect the client reboot when a later attempt to write to the connection fails (including the hourly TCP keepalive packet, if enabled). However, RMI is designed not to require such permanent connections, as it impairs scalability and doesn't help very much.

Given that it is absolutely impossible to instantly determine when a network peer crashes or becomes otherwise unavailable, you must decide how your application should behave when a peer stops responding.

The major tools you have for this task are timeouts and resets. After a timeout, you may conclude that a peer is unreachable, but the peer must be aware of the timeout so that it gives up trying to reach you. The leasing mechanism is designed to do this semi-automatically.

A reset is a purge of existing state held for a peer. For example, a client may cause a reset when it first registers with its server, causing the server to discard any previous state held for that client (having deduced that the client has restarted without memory of the previous, dead, session).

Frequently, the aim is to have and maintain a definitive list of clients at the server, and to keep it up-to-date without error or failure. Since failure and delay can happen at any time in a networked system, some degree of error in the list must be anticipated. If a lease or other mechanism is used to enforce a timeout, then the problem of resource leakage is solved. If the problem of stale data is more serious -- that is, if it would interfere with correct operation -- then it must be explicitly purged in cases where it would otherwise have an effect.

For example, if a business object is locked for editing by a human, and the session dies, then the lock must be broken somehow. In this case, the lock would need a timeout, but if the same human logs in immediately and expects not to have to wait for the timeout to expire, the new session must either take over the lock or assert that the user holds no locks (allowing the server to safely kill the lock).

D.9 How do I run the rmic command in a DOS batch file?

In a DOS batch file, you have to insert the command call before the executable in order for control to return to the batch file. For example:
    call rmic ClientHandler
    call rmic Server
    call rmic ServerHandler
    call rmic Client

D.10 In a remote object implementation, how can I find the host name of the caller of a remote method?

The java.rmi.RemoteServer.getClientHost method returns the client host for the current invocation on the current thread.

D.11 Does RMI handle "out" and "inout" parameters (like CORBA)?

RMI does not support "out" or "inout" parameters, just like the rest of the core Java programming language. All remote calls are methods of a remote object. Local objects are passed by copy and remote objects are passed by reference to a stub. For more details, see Parameter Passing in Remote Method Invocation in the RMI Specification.

D.12 Normally in the Java programming language, it is possible to cast an interface instance to an instance of the class from which it was created and use the result. Why doesn't this work in RMI?

In RMI the client sees only a stub for the original object. The stub implements only the remote interfaces and their remote methods and cannot be cast back to the original implementation class because it's just a stub.

So, you cannot pass a remote object reference from a server to a client, and then send it back to the server and be able to cast it back to the original implementation class. You can, though, use the remote object reference on the server to make a remote call to the object.

If you need to find the implementation class again, you'll need to keep a table that maps the remote reference to the implementation class.

E.1 When will Netscape support RMI?

RMI is a major component of the JDK1.1 core API and is included in Netscape's latest release, Communicator 4.06. You may also wish to consider using the JavaTM Plug-in for browsers that do not support the 1.1.x version of the JDK that you are using.

The HotJava Browser is fully JDK1.1 compliant, and as such, supports RMI.

E.2 Can I implement remote observer/observable objects in RMI?

What you can do is "wrap" java.util.Observable and java.util.Observer with new interfaces (you could call them RemoteObservable and RemoteObserver). In these new interfaces, make each of the methods throw java.rmi.RemoteException. Then, your remote objects can implement these interfaces.

Note that since the "wrapped" non-remote object does not extend java.rmi.server.UnicastRemoteObject, you will need to explicitly export the object using the exportObject method of UnicastRemoteObject. In doing this though, you lose the java.rmi.server.RemoteObject implementations of the equals, hashCode, and toString methods.

E.3 How is RMI related to CORBA?

Please read our comprehensive statement and FAQ regarding this issue.

F.1 At what point is there a "live" connection between the client and the server and how are connections managed?

When a client does a "lookup" operation, a connection is made to the rmiregistry on the specified host. In general, a new connection may or may not be created for a remote call. Connections are cached by the RMI-transport for future use, so if a connection is free to the right destination for a remote call, then it is used. A client cannot explicitly close a connection to a server, since connections are managed at the RMI-transport level. Connections will time out if they are unused for a period of time.

F.2 Does the Java platform replace all remote objects with their stubs during a remote method invocation?

Yes, the Java platform replaces all remote objects with their stubs, even deep inside of graphs of serializable objects.

F.3 Is it possible to write a new transport layer for RMI which does not use sockets? As a follow-up question, how about a transport layer that uses non-TCP based sockets?

We have designed the transport interfaces so that different implementations of these interfaces can be used by RMI. In earlier releases, this abstraction was used for our purposes and was not exposed for general use. Now, in the 1.2 JDK release, RMI supports "client and server socket factories" that can be used to make RMI calls over non-TCP based sockets.

F.4 I notice the registry continues to use CPU resources, as if it were polling rather than blocking on a select() call. Is the registry implemented by polling?

RMI does not poll on select calls. There is a thread that wakes up every so often and polls the table of remote objects (that reside in the address space). This "reap" thread is used for the purposes of the distributed garbage collector.

F.5 Is there only one socket connection between a client process and the server, no matter how many stubs exist in that client process?

RMI reuses the socket connections between client and server whenever possible. The current implementation creates additional sockets on demand when they are needed. For example, if the existing socket is in use by a call then a new socket is created for the new call. Typically, there are at least two sockets open since the distributed garbage collector needs to make remote calls when remote objects are returned from the server. If a cached connection remains unused for a period of time, the connection is closed.

G.1 What are the licensing issues surrounding the use of RMI?

Client- and server-side support are a part of the JDK base platform and can be downloaded free of charge. There is no cost for tools like the rmic compiler.

G.2 I have a single-threaded program that waits on standard input for a user command which will initiate an RMI call. However, my remote object cannot service this incoming remote call as the program appears to be blocked on standard input. What's the problem?

This is a known problem, not with RMI, but with the thread that reads standard input. The thread does not yield on the blocking read, but instead stays running, hardly letting the listener get any cycles. We have tried two workarounds that seem successful: set the main thread (the one reading standard input) to a lower priority, or yield while bytes are not available in the stream before actually reading it.

G.3 I am copying array elements to my remote server and changing the values, but the incremented values are not copied back to the client. Why?

Non-remote objects are passed by copy, so if you want to have the new values of the array reflected in the client, you will have to send them back as a return argument.

G.4 Am I allowed to have static fields in a remote interface?

Yes. An initializer is run in each VM that loads the remote interface, creating a new static variable with the specified values. So, you have a separate copy of this static variable in each Java VM that loads the remote interface.

G.5 I locate the registry, but then it seems that it's not there, what's happening?

The method LocateRegistry.getRegistry(String host) does not contact the registry on the host, but rather just looks up the host to make sure it exists. So, even though this method succeeded, this does not necessarily mean that a registry is running on the specified host. It just returns a stub that can then access the registry.

G.6 I can't find the answer to my question here, are there any other resources?

A vast amount of information can be found from the hypermail archive of the RMI-USERS mailing list.

Users of both RMI and object serialization can discuss issues and tips with other users via the mailing list rmi-users@java.sun.com. You can subscribe by sending an email message containing the line

    subscribe RMI-USERS
to listserv@java.sun.com, and unsubscribe by sending a message containing the line
    unsubscribe RMI-USERS

Object Serialization

1. Why must classes implement Serializable in order to be written to an ObjectOutputStream?

The decision to require that classes implement the java.io.Serializable interface was not made lightly. The design called for a balance between the needs of developers and the needs of the system to be able to provide a predictable and safe mechanism. The most difficult design constraint to satisify was the safety and security of classes for the Java programming language.

If classes were to be marked as being serializable the design team worried that a developer, either out of forgetfulness, laziness, or ignorance might not declare a class as being Serializable and then make that class useless for RMI or for purposes of persistence. We worried that the requirement would place on a developer the burden of knowing how a class was to be used by others in the future, an essentially unknowable condition. Indeed, our preliminary design, as reflected in the alpha API, concluded that the default case for a class ought to be that the objects in the class be serializable. We changed our design only after considerations of security and correctness convinced us that the default had to be that an object not be serialized.

Security restrictions

The first consideration that caused us to change the default behavior of objects had to do with security, and in particular in the privacy of fields declared to be private, package protected, or protected. The Java platform restricts access to such fields for either read or write to a subset of the objects within the runtime.

No such restriction can be made on an object once it has been serialized; the stream of bytes that are the result of object serialization can be read and altered by any object that has access to that stream. This allows any object access to the state of a serialized object, which can violate the privacy guarantees users of the language expect. Further, the bytes in the stream can be altered in arbitrary ways, allows the reconstruction of an object that was never created within the protections of a Java platform. There are cases in which the re-creation of such an object could compromise not only the privacy guarantees expected by users of the Java platform, but the integrity of the platform itself.

These violations cannot be guarded against, since the whole idea of serialization is to allow an object to be converted into a form that can be moved outside of the Java platform (and therefore outside of the privacy and integrity guarantees of that environment) and then be brought back into the environment. Requiring objects to be declared serializable does mean that the class designer must make an active decision to allow the possibility of such a breach in privacy or integrity. A developer who does not know about serialization should not be open to compromise because of this lack of knowledge. In addition, we would hope that the developer who declares a class to be serializable does so after some thought about the possible consequences of that declaration.

Note that this sort of security problem is not one that can be dealt with by the mechanism of a security manager. Since serialization is intended to allow the transport of an object from one virtual machine to some other (either over space, as it is used in RMI, or over time, as when the stream is saved to a file), the mechanisms used for security need to be independent of the runtime environment of any particular Java virtual machine. We wanted to avoid as much as possible the problem of being able to serialize an object in one virtual machine and not being able to deserialize that object in some other virtual machine. Since the security manager is part of the runtime environment, using the security manager for serialization would have violated this requirement.

Forcing a conscious decision

While security concerns were the first reason for considering the design change, a reason that we feel is at least as convincing is that serialization should only be added to a class after some design consideration. It is far too easy to design a class that falls apart under serialization and re-construction. By requiring a class designer to declare support for the serialization interface, we hoped that the designer would also give some thought to the process of serializing that class.

Examples are easy to cite. Many classes deal with information that only makes sense in the context of the runtime in which the particular object exists; examples of such information include file handles, open socket connections, security information, etc. Such data can be dealt with easily by simply declaring the fields as transient, but such a declaration is only necessary if the object is going to be serialized. A novice (or forgetful, or hurried) programmer might neglect to mark fields as transient in much the same way he or she might neglect to mark the class as implementing the Serializable interface. Such a case should not lead to incorrect behavior; the way to avoid this is to not serialize objects not marked as implementing Serializable.

Another example of this sort is the "simple" object that is the root of a graph that spans a large number of objects. Serializing such an object could result in serializing lots of others, since serialization works over an entire graph. Doing something like this should be a conscious decision, not one that happens by default.

The need for this sort of thought was brought home to us in the group when we were going through the base Java API class libraries marking the system classes as serializable (where appropriate). We had originally thought that this would be a fairly simple process, and that most of the system classes could just be marked as implementing Serializable and then use the default implementation with no other changes. What we found was that this was far less often the case than we had suspected. In a large number of the classes, careful thought had to be given to whether or not a field should be marked as transient or whether it made sense to serialize the class at all.

Of course, there is no way to guarantee that a programmer or class designer is actually going to think about these issues when marking a class as serializable. However, by requiring the class to declare itself as implementing the Serializable interface we do require that some thought be given by the programmer. Having serialization be the default state of an object would mean that lack of thought could cause bad effects in a program, something that the overall design of the Java platform has attempted to avoid.

2. Which JDK 1.1 system classes are marked serializable.?

Removed. This information is readily available from the Javadoc API material.

3. I am having problems deserializing JDK 1.0.2 AWT components. How can I make this work?

When you serialize AWT widgets, also serialized are the Peer objects that map the AWT functions to the local window system. When you deserialize (reconsitute) the AWT widgets, the old Peers are re-created, but they are out of date. Peers are native to the local window system and contain pointers to data structures in the local address space, and therefore cannnot be moved.

As a work-around, you should first remove the top-level widget from its container (so the widgets are no longer "live"). The peers are discarded at this point and you will save only the AWT widget state. When you later deserialize and read the widgets back in, add the top level widget to the frame to make the AWT widgets appear. You may need to add a show call.

In JDK 1.1 and later, AWT widgets are serializable. The java.awt.Component class implements Serializable.

4. Does object serialization support encryption?

Object serialization does not contain any encryption/decryption in itself. It writes to and reads from standard streams in the Java API, so it can be coupled with any available encryption technology. Object serialization can be used in many different ways. Other than just writing and reading to and from files, it can also be used for RMI to communicate between hosts.

RMI's use of serialization leaves encryption and decryption to the lower network transport. We expect that when a secure channel is needed the network connections will be made using SSL or the like (see The Scoop On RMI and SSL).

5. The object serialization classes are stream oriented. How do I write objects to a random-access file?

Currently there is no direct way to write objects to a random-access file.

You can use ByteArrayInputStream and ByteArrayOutputStream objects as intermediate places to write and read bytes to and from the random access file and create ObjectInputStreams and ObjectOutputStreams from the byte streams to transport the objects. You just have to make sure that you have the entire object in the byte stream or reading/writing the object will fail.

For example, java.io.ByteArrayOutputStream can be used to receive the bytes of ObjectOutputStream. From it you can get a result in the form of a byte array. That in turn can be used with ByteArrayInputStream as input to an ObjectInput stream.

6. When a local object is serialized and passed as a parameter in an RMI call, are the bytecodes for the local object's methods also passed? What about object coherency, if the remote VM application "keeps" the object handle?

The bytecodes for a local object's methods are not passed directly in the ObjectOutputStream, but the object's class may need to be loaded by the receiver if the class is not already available locally. The class files themselves are not serialized, just the names of the classes. All classes must be able to be loaded during deserialization using the normal class loading mechanisms. For applets this means they are loaded by the AppletClassLoader.

There are no coherency guarantees for local objects passed to a remote VM since such objects are passed by copying their contents (a true pass-by-value).

7. How can I create an ObjectInputStream from an ObjectOutputStream without a file in between?

ObjectOutputStream and ObjectInputStream work to/from any stream object. You could use a ByteArrayOutputStream and then get the array and insert it into a ByteArrayInputStream. You could also use the piped stream classes as well. Any java.io class that extends the OutputStream and InputStream classes can be used.

8. I create an object and then send it across the net using the writeObject method and receive it using the readObject method. If I then change the value of a field in the object and send it as before, the object that the readObject method returns appears to be the same as the first object and does not reflect the new value of the field. Should I be experiencing this behavior?

The ObjectOutputStream class keeps track of each object it serializes and sends only the handle if that object is seen again. This is the way it deals with graphs of objects. The corresponding ObjectInputStream keeps track of all of the objects it has created and their handles so when the handle is seen again it can return the same object. Both output and input streams keep this state until they are freed.

Alternatively, the ObjectOutputStream class implements a reset method that discards the memory of having sent an object, so sending an object again will make a copy.

9. Are there any plans to support the serialization of thread objects?

Threads will NOT be serializable. In the present implementation, if you attempt to serialize and then deserialize a thread, there is NO explicit allocation of a new native thread or stack; all that happens is that the object is allocated system resources with none of the native implementation. In short, it just won't work and will fail in unpredictable ways.

The difficulty with threads is that they have so much state which is intricately tied into the virtual machine that it is difficult or impossible to re-establish the context somewhere else. For example, saving the JVM call stack is insufficient because if there were native methods that had called C procedures that in turn called code for the Java platform, there would be an incredible mix of Java programming language constructs and C pointers to deal with. Also, serializing the stack would imply serializing any object reachable from any stack variable.

If a thread were resumed in the same VM, it would be sharing a lot of state with the original thread, and would therefore fail in unpredictable ways if both threads were running at once, just like two C threads trying to share a stack. When deserialized in a separate VM, it's hard to tell what might happen.

10. Can I compute diff(serial(x),serial(y))?

The diff will produce the same stream each time the same object is serialized. You will need to create a new ObjectOutputStream to serialize each object.

11. Can I compress the serial representation of my objects using my own zip and unzip methods?

ObjectOutputStream produces an OutputStream; if your zip object extends the OutputStream class there is no problem compressing it.

12. Can I execute methods on compressed versions of my objects, for example isempty(zip(serial(x)))?

This is not really viable for arbitrary objects because of the encoding of objects. For a particular object (such as a String) you can compare the resulting bit streams. The encoding is stable, in that every time the same object is encoded it is encoded to the same set of bits.

13. If I try to serialize a font or image object and then try to reconstitute it in a different VM, my application dies. Why?

Removed. Fonts are now serializable, but images are not.

14. How do I serialize a tree of objects?

Here's a brief example that shows how to serialize a tree of objects.

import java.io.*;

class tree implements java.io.Serializable {
    public tree left;
    public tree right;
    public int id;
    public int level;

    private static int count = 0;

    public tree(int depth) {
        id = count++;
        level = depth;
        if (depth > 0) {
            left = new tree(depth-1);
            right = new tree(depth-1);
        }
    }

    public void print(int levels) {
        for (int i = 0; i < level; i++)
            System.out.print("  ");
        System.out.println("node " + id);

        if (level <= levels && left != null)
            left.print(levels);

        if (level <= levels && right != null)
            right.print(levels);
    }


    public static void main (String argv[]) {

        try {
            /* Create a file to write the serialized tree to. */
            FileOutputStream ostream = new FileOutputStream("tree.tmp");
            /* Create the output stream */
            ObjectOutputStream p = new ObjectOutputStream(ostream);

            /* Create a tree with three levels. */
            tree base = new tree(3);

            p.writeObject(base); // Write the tree to the stream.
            p.flush();
            ostream.close();    // close the file.
            
            /* Open the file and set to read objects from it. */
            FileInputStream istream = new FileInputStream("tree.tmp");
            ObjectInputStream q = new ObjectInputStream(istream);
            
            /* Read a tree object, and all the subtrees */
            tree new_tree = (tree)q.readObject();

            new_tree.print(3);  // Print out the top 3 levels of the tree
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

15. If class A does not implement Serializable but a subclass B implements Serializable, will the fields of class A be serialized when B is serialized?

Only the fields of Serializable objects are written out and restored. The object may be restored only if it has a no-arg constructor that will initialize the fields of non-serializable supertypes. If the subclass has access to the state of the superclass it can implement writeObject and readObject to save and restore that state.
Talk with RMI developers via the mailing list RMI-USERS
To subscribe, send subscribe rmi-users to listserv@javasoft.com
For information on technical support, refer to JavaSoft E-Mail Addresses
Send questions or comments about this site to webmaster@jse.east.sun.com