|
Frequently Asked Questions |
Naming.bind
and Naming.lookup
take an extraordinarily long time on Windows systems?connection refused: trying to connect
to host
" error reported when I don't use a host's fully-qualified
domain name. What is happening?System.exit
for graceful
client termination?rmic
command in a DOS batch
file?java.lang.ClassMismatchError
while running my program?ArrayStoreException
. What's going on?ClassNotFoundException
for my
stub class when I try to register a remote object in the registry. What's
happening?java.net.SocketException:
Address already in use
" when I try to run the registry?CLASSPATH
? I thought it could be downloaded.Naming.bind
and Naming.lookup
take an extraordinarily long time on Windows?Most likely, your host's networking setup is incorrect. RMI uses Java's
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
208.2.84.61 homer
This should dramatically cut down the time it takes to make the first lookup. For the answers to other frequently-asked RMI networking questions, please take a look at RMI and Networking Frequently Asked Questions.
connection refused: trying
to connect to host
" error reported when I don't use a host's fully-qualified
domain name. What is happening?
This is due to a bug in Solaris in which the domain name is not always
available to the host machine in a uniform manner. A workaround is to set
the property java.rmi.server.hostname
to specify the domain-qualified
host name. Also, you can put DNS
as the first entry in /etc/nsswitch.conf
or adding the fully-qualified host name to /etc/hosts
file and
puting files
first in the /etc/nsswitch.conf
file.
For the answers to other frequently-asked RMI networking questions, please take a look at RMI and Networking Frequently Asked Questions.
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 ( ie. 192.168.1.1). You should then find that from
a DOS Shell, you can ping yourself (i.e. ping mymachine). You should now
be able to run the stock RMI example. For the answers to other frequently-asked RMI networking questions, please take a look at RMI and Networking Frequently Asked Questions.
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.
Yes, Java replaces all remote objects with their stubs, even deep inside
of graphs of serializable objects.
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.
RMI is a major component of the JDK1.1 core API and is included in Netscape's latest release, Communicator 4.03. 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.
Currently no, but we will be adding object activation to RMI in the
future which will effectively allow spawning a VM.
RMI supports a simple call logging facility for debugging. But there
are no current plans to support a full-featured, interactive, remote debugger.
This is very desirable and is on our RFE list. A notification facility
should be available in a future release.
We have designed the transport interfaces so that different implementations
of these interfaces can be used by RMI. In eariler 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.
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.
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.
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.
Yes. You can use the java.rmi.server.Unreferenced
interface to provide the notification via the unreferenced
method
when all clients disconnect. However, if there is a reference in the registry,
then the Unreferenced.unreferenced
method will never be called.
In RMI, a server VM should exit if there are a) no outstanding client-held
references to the remote objects in VM, and b) 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 Java 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 next release of the JDK will include a new infrastructure that RMI
will use to reduce significantly the number of conditions under which this
problem occurs.
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 them unreachable
from running threads), and then it helps to do something like this before
exiting:
System.gc();
System.runFinalization();
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.
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
The java.rmi.RemoteServer.getClientHost
method returns the
client host for the current invocation on the current thread.
Please read our comprehensive statement
and FAQ regarding
this issue.
RMI does not support out and inout parameters. RMI is just like Java;
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.
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
javaw
command during development.
To watch the server activity, start the server with -Djava.rmi.server.logCalls=true
.
java.lang.ClassMismatchError
while running my program?You probably modified one or more classes 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.
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();
}
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.
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-package-qualified
class name. It uses the current CLASSPATH to find and print the interface
to a class.
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.
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.
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.
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.
To get a trace of the server activity, start the server as follows:
java -Djava.rmi.server.logCalls=true YourServerimpl
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.
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.
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 codebase from where the stub
class can be loaded. You usually need to set the
java.rmi.server.codebase
property on the exporting
server to achieve this.
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 demarshalled, the codebase is used to
load the stub classfile via the RMIClassLoader
, unless
the class can already be found in the CLASSPATH
.
If the _Stub class was loaded by an RMIClassLoader
,
then RMI already knows what codebase to use for 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.
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.
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@javasoft.com. You can subscribe by sending an email message containing the line
subscribe RMI-USERS
unsubscribe RMI-USERS
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 Java classes.
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 runtime 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 environment. There are cases in which the recreation of such an object could compromise not only the privacy guarantees expected by users of the Java environment, but the integrity of the environment 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 environment (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 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 reconstruction. 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 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 Java has attempted to avoid.
Here's an initial list of the classes that are marked serializable. Note that classes that extend these classes are also serializable:
java.io.FileInputStream
)
or are exceedingly hard to do correctly (e.g. java.lang.Thread
).
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 recreated, 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
.
Object serialization does not contain any encryption/decryption in itself. It write to and reads from Java streams, so it can be coupled with any available encryption technology. Object serialization can be used in many different ways from simple persistence, writing and read to/from files, or for RMI to communicate across 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.
Currently there is no direct way to write objects to a random access file.
You can use the ByteArray I/O streams as an intermediate place to write and read bytes to/from the random access file and create Object I/O streams from the byte streams to write/read 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 byte[] of the result.
That in turn can be used with ByteArrayInputStream as input to ObjectInput
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 conherency guarantees for local objects passed to a remote
VM since such objects are passed by copying their contents (a true pass-by-value).
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.
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.
In JDK1.1 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 Java object is allocated 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 Java call stack is insufficient because if there were native methods that had called C procedures that in turn called Java, there would be an incredible mix of Java 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, its hard to tell
what might happen.
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.
ObjectOutputStream produces an OutputStream, If your zip object extends
the OutputStream class there is no problem compressing it.
This is not really viable for arbitrary objects because of the encoding
of objects. For a particular object (such as 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.
AWT does not yet work well with serialization and you will therefore have trouble trying to pass fonts and images. This is because each contains memory pointers that are valid only in the originating VM, which will cause a segmentation violation when passed to a new VM.
These problems should be corrected by the time JDK 1.1 releases. As
a work around for fonts, you will need to pass the information necessary
to recreate a new font object that duplicates the characteristics of the
font object in the originating VM. There is no current work around to allow
images to be passed correctly.
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 l) { id = count++; level = l; if (l > 0) { left = new tree(l-1); right = new tree(l-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(); } } }
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@javasoft.com
To subscribe, send
subscribe rmi-users
to listserv@javasoft.comFor information on technical support, refer
to JavaSoft E-Mail Addresses
Send questions or comments about this site
to webmaster@jse.east.sun.com
|