Memra

Exporting and the registry

◈ 5 cards

exportObject 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.

exportreturnsthenpublishnew PrimeServiceImpl()hold it in a fieldexportObject(impl, 0)listen on any free portthe stubcarries host + port + object idcreateRegistry(1099)a name service in this JVMrebind(NAME, stub)replaces any old entryFour statements and the service islive.
exportObject returns the stub — it does not store it anywhere. Publishing is the separate rebind step, which is why an exported-but-unbound object is reachable in principle and findable by nobody.
a local handlethe stub comes backgetRegistry(host, 1099)no network yetlookup(NAME)first real round trip(PrimeService) stubcast to the interfaceA wrong host fails at lookup,not at getRegistry.
getRegistry performs no I/O, so a typo in the host survives until lookup. That is the single most confusing thing about the client: the line that fails is never the line that is wrong.
NORMAL ~/memra/learn/comp-348/rmi-export-and-registry utf-8 LF