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 JavaTM Remote Method Invocation (Java 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 platforms) 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.
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 Java RMI (whether as an argument
to a remote call or as a return value), the codebase for the stub
class is retrieved by Java 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 Java 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 Java 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.
No. You can set your java.rmi.server.codebase property
to use any valid URL protocol, such as file or
ftp. Using an HTTP server just makes your life
simpler by providing an automated mechanism for class file
downloading. If you don't have access to an HTTP server nor the
inclination to set one up, you can use our small class file server
found at
http://java.sun.com/javase/technologies/core/basic/rmi/class-server.zip.
The Java RMI implementation attempts to reuse open sockets
where possible for remote invocations. When a remote method
is invoked on a stub that uses a
custom socket factory, the Java RMI implementation will reuse an
open connection (if any) as long as that socket was
created by an equivalent socket factory. Since client socket
factories are serialized to clients, a single client may have
several distinct copies of the same logical socket factory.
To ensure that the Java RMI implementation will reuse
sockets created by custom socket factories, make sure your
custom client socket factory classes implement the
hashCode and equals methods
appropriately. If the client socket factory does not implement
these methods correctly, another ramification is that stubs
(using the client socket factory) that refer to the same remote
object will not be equal.
The Java RMI implementation attempts to reuse server-side ports
as well. It will only do so if there is an existing server socket
for the port created by an equivalent socket factory. Make sure
the server socket factory class implements the hashCode and
equals methods too.
If your socket factory has no instance state, a trivial
implementation of the hashCode and
equals methods are the following:
public int hashCode() { return 57; }
public boolean equals(Object o) { return this.getClass() == o.getClass() }
Java RMI supports a simple call-logging facility for debugging.
But there are no current plans to support a full-featured,
interactive, remote debugger.
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.
You probably modified one or more classes that were being used by
Java RMI programs while your program was running.
Try restarting all Java RMI applications (including
java.rmi.registry.RegistryImpl).
This should clear things up.
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.
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. In order to instantiate a stub object, the registry VM
needs to be able to load its class definition. The VM (in this case
the server VM) that sends the serialized forms of a stub in a
remote method call to the registry is responsible for annotating
the stub with the location from which its classes can be
downloaded. If stubs are not annotated properly, Java RMI will throw a
ClassNotFoundException when it tries to instantiate
the stub.
To annotate classes properly, the server needs to set the
value of the java.rmi.server.codebase property value
to the location(s) of the stub classes. Java RMI will automatically
annotate the serialized form of outgoing object instances with the
value of the java.rmi.server.codebase property.
NOTE: It is possible (and in a small number of environments
appropriate) to enable the rmiregistry to unmarshal stub
objects by placing all relevant stub class files in the CLASSPATH
of the rmiregistry. However, the rmiregistry
does not have to download stub classes. If stub classes are
available locally, it will use those classes. Using the
rmiregistry's CLASSPATH for stub deployment requires that
all VMs that reference a stub instance obtained from that registry
have the stub's class file installed locally (in the VM's
CLASSPATH).
For example, if the registry loads stub classes from its CLASSPATH,
when the registry sends serialized stub objects to other VMs, those
serialized objects will be annotated with the value of the
registry's java.rmi.server.codebase property (which
will almost always be null). If the VMs receiving serialized stub
objects from the registry do not have the class files for those
stubs installed locally then those VMs are likely to throw a
ClassNotFoundException.
Instead, if classes are downloaded dynamically from a server VM's
java.rmi.server.codebase annotation, only the
server VM needs to have the stub classes in its CLASSPATH.
With this approach, application deployment is simpler and it is
possible to introduce new stub versions into a running distributed
system.
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 the SolarisTM
Operating System (Solaris OS) and a ctrl-break on
Windows platforms.
Properties that begin with
"sun.rmi." are only supported by certain
versions of the JavaTM SE Development Kit (JDK) software from
Sun Microsystems. While these
"sun.rmi.*" properties can be quite useful for
debugging and tuning at runtime, please note that they are not
considered part of the public API, and their use is
subject to change (or may be removed completely) in future versions
of the implementation.
For an Java RMI client to contact a remote Java 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 VM that is serving a
particular remote object.
Once a Java 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 Java RMI the terms client and server can
refer to the same program.
A Java program that acts as a Java RMI server contains an exported
remote object.
A Java RMI client is a program that invokes one or more methods on a
remote object in another virtual machine.
If a VM performs both of these functions, it may be referred to
as an RMI client and a Java RMI server.
In many versions of the JDK (all
versions of the JDK except in v1.1
and the latest
releases), Java RMI may default to using an unresolvable server
hostname (for example: unqualified names, Windows Internet Naming
Service (WINS) names, or unqualified DHCP names).
When a Java 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, Java RMI servers
must be able to supply a fully qualified hostname or IP address
that is resolvable from all Java RMI clients (an example of a
fully qualified hostname is foo.bar.com).
If a Java RMI program provides a remote callback operation, then that
program serves a Java 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 Java RMI clients.
VMs that make calls to applets that serve remote objects may
throw UnknownHostExceptions because the applet has
failed to provide a usable server hostname.
If your Java 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 Java RMI
will use this property's value to generate remote references to
the server.
Depending on the configuration of your network's name service, a
fully qualified hostname that is recognized on one Java RMI host may
not be resolvable from another Java RMI host.
Some examples where this situation may arise are:
Misconfigured DHCP servers may set the fully qualified domain
name of Java RMI server machines to be the domain name of the
resolver domain instead of the domain in which the Java RMI server
actually resides.
In this case, Java RMI clients outside the server's DHCP domain
will be unable to contact the server, because of its
incorrect domain name.
The server machine is on a network that is configured to use
WINS.
Hosts that are only registered under WINS may not be reachable
by hosts that rely solely upon DNS.
The Java RMI client and server reside on opposite sides of a
firewall.
If your Java RMI client lies outside a firewall and the server
resides inside it, the client will not be able to make any
remote calls to the server.
If the Java RMI client lies inside the firewall, you will need to
configure the Java RMI client to contact the server using HTTP.
Set the java.rmi.server.hostname property to the
correct IP address of the Java 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 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.
To work around the JDK v1.1 problem on stand-alone systems,
InetAddress was modified in JDK v1.1.1 to only
retrieve the [potentially unqualified] hostname returned from
a native system call, which did not attempt to consult a name
service.
Java 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.
Java RMI made no attempt to consult a name service and could
default to using unqualified hostnames.
To compensate for the many problems that were generated by the
v1.1.1 change in functionality of InetAddress, the
following behavior has been integrated into the most recent
versions of the JDK:
Java 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:
By default, Java RMI uses the IP address of the server host as
the server name for remote references.
If the property java.rmi.server.hostname is
set, Java 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 a Java RMI server name.
If the property
java.rmi.server.useLocalHostname is set to
true (by default, the value of this property
is false), Java RMI applies the following routine to
obtain a hostname for the Java RMI server:
If the value returned by the
InetAddress.getLocalHost().getHostName()
method contains a "." character, then Java RMI will assume
that this value is the server's fully qualified domain
name and will use it as the server hostname.
Otherwise, Java RMI will spawn a thread to query the local
name service for the fully qualified domain name of
the Java RMI server.
If the name service takes too long to return, or the
name service returns but its response does not contain
a "." then Java 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 Java 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 Java RMI will wait
in milliseconds.
For example:
When using activatable remote objects, it is recommended that
Java 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, surviving a reboot).
A Java 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.
Most likely, your host's networking setup is incorrect.
Java 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 platforms, the lookup functions are performed by the native
socket library, so the delays are happening not in Java RMI,
but in the 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.
To get Java RMI working on a Windows 95 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, pingmymachine).
You should now be able to use Java RMI on the machine.
This exception means that the port that the RegistryImpl
uses (by default 1099) is already in use.
You may have another registry running on your machine and
will need to stop it.
There are three main methods: HTTP tunnelling, SOCKS, and
downloaded socket factories.
HTTP tunnelling
This well-worn method is popular since it requires almost no
setup, and works quite well in firewalled environments which
permit you to handle HTTP through a proxy, but disallow regular
outbound TCP connections.
If Java 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 Java RMI requests through
that proxy server, one at a time.
There are two forms of HTTP tunnelling, tried in order.
The first is http-to-port; the second is
http-to-cgi.
In http-to-port tunneling, Java 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 Java RMI request.
If the HTTP proxy accepts this URL, it will forward the POST
request to the listening Java RMI server, which will recognize 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, Java RMI will fall back to http-to-cgi tunneling.
The Java 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 a Java RMI server listening on port n.
Java 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 the JDK, allowing the server to
specify the socket factory that the clients must use.
The clients must be running J2SE v1.2 or later.
See the tutorial Using Custom Socket Factories with Java RMI for details.
The disadvantage of this approach is that the traversal of the
firewall must be done by code provided by the Java 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.
There are three main methods: known ports, transport-level bridges,
and application-level proxies.
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, Java RMI asks for port 0 (which is code for "any port").
In the JDK, there is an extra argument to the
exportObject method to specify the exact port
number.
In JDK v1.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 bridges
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 (JDK, v1.2 or later) 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 proxies
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 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
cooperation of the local network administrators.
First of all, what cooperation 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 Java RMI requests embedded in HTTP requests.
You can use a HTTP server with the java-rmi.cgi
program, or you can run the Java 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.
The most conservative approach, assuming no help from the client
firewall administrators, is:
Avoid using callback objects
Run your servers on `public' ports such as 80, 81, 8001, or 443
If the servers are not running on port 80, either:
Put a CGI-capable HTTP server on port 80 using the
java-rmi.cgi script; or
Run a port redirector (such as DeleGate) on
port 80, which will accept connections and immediately
connect to the real server port to pass bytes back and
forth. This will cause getClientHost() to
return misleading information, so don't make the Registry
available through this method unless it's on a different
host.
We've provided an example
that demonstrates how to implement the
java-rmi.cgi script using a servlet. The example also
explains how to run a remote object inside a servlet VM.
Note: If you do not understand the role that
java-rmi.cgi plays in tunnelling remote method calls
over HTTP, please see the FAQ question regarding HTTP tunnelling in Java RMI.
Yes.
Your remote object will need to implement the java.rmi.server.Unreferenced interface (in
addition to any other necessary interfaces).
Java 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.
No
outstanding client-held references to the remote objects in VM,
and
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 v1.1 implementation.
Under the covers, the Java 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 v1.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 Java SE platform includes a new infrastructure that Java RMI will
use to reduce significantly the number of conditions under which
this problem occurs.
When the Java 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. It also may help to run a full garbage collection and
to run finalizers before exiting:
If you wait for the client's lease to run out, and the
unreferenced() method will then be called by the Java RMI
implementation (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.
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 its 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 Java 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 v1.1.x, you may have to run finalizers synchronously and then
run GC again.)
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, Java 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).
Java 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 Java RMI
Specification.
In Java 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.
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.
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 Java 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 Java RMI transport level.
Connections will time out if they are unused for a period of time.
The JRMP and Java RMI-IIOP implementations will replace each remote
object with a corresponding stub (of the same protocol), even
deep inside graphs of serializable objects.
We have designed the transport interfaces so that different
implementations of these interfaces can be used by Java RMI.
In earlier releases, this abstraction was used for our purposes
and was not exposed for general use.
Now, in the JDK, Java RMI supports client and server socket
factories that can be used to make Java RMI calls over
non-TCP-based sockets.
Java RMI does not poll on select calls.
There is a thread that wakes up every so often and polls the table
of exported remote objects.
This "reap" thread is used for the purposes of the distributed
garbage collector.
Java 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.
This is a known problem, not with Java 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.
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.
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 VM that loads the remote interface.
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.
A vast amount of information can be found from the hypermail
archive of the RMI-USERS mailing list.
Users of both Java 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 satisfy 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 is 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, allowing 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 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 v1.1 system classes are marked
serializable?
Removed. This information is readily available from the
API documentation generated by the javadoc tool.
3.
I am having problems deserializing JDK v1.0.2AWT
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 (reconstitute) 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 cannot 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 v1.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 Java 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
Using RMI with 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 a Java 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 VM 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 class A 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 Java RMI developers via the mailing list RMI-USERS
To subscribe, send subscribe rmi-users to listserv@javasoft.com