Exporting and the registry
◈ 5 cardsexportObject turns an object into a listener and hands back its stub; LocateRegistry publishes that stub under a name so a client with nothing but a host, a port and a string can find it.
Exporting: from an object to a listener
UnicastRemoteObject.exportObject(impl, 0) does two things and returns one. It makes impl able to receive calls — the RMI runtime opens a server socket, starts listener threads, and records that incoming calls naming this object should be dispatched to it. And it returns the stub you will hand out.
The 0 is the port to listen on, and 0 means "any free port the OS gives you". That is what you want, because the stub records whichever port was chosen and carries it to the client; nothing has to agree on the number in advance. Unicast in the class name is a promise about the object: a single, non-replicated instance living in one JVM, reachable over TCP, valid only while that JVM is running. (There is a one-argument exportObject(Remote) — it is deprecated, because it belongs to the pre-Java-5 world of pre-generated stub classes. Always pass a port.)
The registry breaks the bootstrap circle
The client now needs the stub. But a stub is an object, and the only way to receive an object from another JVM is to call a remote method — for which you need a stub. The registry breaks that circle by being a remote object whose address is known in advance: a small name service that maps String names to stubs, listening by convention on port 1099 (Registry.REGISTRY_PORT).
LocateRegistry.createRegistry(1099) starts one inside your own server JVM. That is the modern form the assignment asks for: no separate rmiregistry process to launch, no second window to forget about, and the registry dies with the server it belongs to. On the client, LocateRegistry.getRegistry(host, port) does not connect — it just fabricates a local stub pointing at that address, so it succeeds even when nothing is running there. The first real network contact is the lookup, which is where a wrong host or a dead server actually shows up.
rebind, not bind
bind(name, stub) throws AlreadyBoundException if the name is taken. During development you will restart the server dozens of times, so use rebind, which replaces any existing entry silently. One more rule to know before it bites you: a registry accepts bind, rebind and unbind only from the same host, and answers a remote attempt with AccessException. That is deliberate — otherwise anyone on the network could substitute their own object for your service. lookup has no such restriction.
Keep a strong reference to the implementation
The RMI runtime holds your exported object only weakly. Distributed garbage collection keeps it alive while some client holds a live remote reference (a lease, ten minutes by default), but once the last lease lapses, only your own references are left. If there are none — because you exported a temporary in a method that has returned — the implementation can be collected and unexported, and the next call fails with NoSuchObjectException. Store it in a field that outlives the method, and the problem disappears.
The converse is a pleasant surprise: while an object is exported, RMI's listener threads are non-daemon, so main can return and the JVM stays up serving calls. You do not need a while (true) { } to keep the server alive.
Worked example — the four lines that start the prime server
Create the implementation and hold it in a field. Export it on port 0 and keep the returned stub. Create the registry on 1099 in this same JVM. rebind the stub under a name both programs share as a constant. Four statements, and the service is live; the client then needs a host, a port and that same string, and nothing else.