CORBA vs RMI: Choosing Your Distributed Object Protocol in 1999
CORBA vs RMI: Choosing Your Distributed Object Protocol in 1999
In 1999 the question "how do services communicate?" had a more complicated answer than it does today. REST did not exist as a named pattern. HTTP APIs for machine-to-machine communication were not standard practice. The serious options for distributed object communication were CORBA and Java RMI.
At Motorola we used RMI for the NMS and evaluated CORBA for a planned integration with a third-party system. Here is what the comparison looked like.
RMI: Remote Method Invocation
RMI was Java's built-in distributed objects protocol. The idea: call a method on an object running in a different JVM as if it were local.
Define a remote interface:
import java.rmi.*;
public interface DeviceService extends Remote {
DeviceStatus getStatus(String ip) throws RemoteException;
void updateStatus(String ip, DeviceStatus s) throws RemoteException;
List getAllDevices() throws RemoteException;
}
Every method declares throws RemoteException — network calls can fail in ways local calls cannot.
Implement it:
import java.rmi.server.*;
public class DeviceServiceImpl extends UnicastRemoteObject
implements DeviceService {
private final DeviceRegistry registry;
public DeviceServiceImpl(DeviceRegistry registry) throws RemoteException {
this.registry = registry;
}
public DeviceStatus getStatus(String ip) throws RemoteException {
return registry.get(ip);
}
public void updateStatus(String ip, DeviceStatus s) throws RemoteException {
registry.update(ip, s);
}
public List getAllDevices() throws RemoteException {
return new ArrayList(registry.getAll());
}
}
Register it with the RMI registry:
// Server setup
DeviceRegistry registry = new DeviceRegistry();
DeviceServiceImpl service = new DeviceServiceImpl(registry);
Naming.rebind("rmi://localhost:1099/DeviceService", service);
System.out.println("DeviceService registered");
Look it up from a client:
// Client — could be on a different host
DeviceService service =
(DeviceService) Naming.lookup("rmi://nms-primary:1099/DeviceService");
DeviceStatus status = service.getStatus("192.168.1.10");
System.out.println("Status: " + status);
The RMI stub generated by rmic handled serialisation and network communication. The client code looked identical to a local method call.
RMI limitations:
- Java-only. Both client and server must be Java. Integrating with a C++ or Python system was not possible.
- Serialisation coupling. Both sides must have the same version of the serialised class. Schema evolution was painful.
- Firewall unfriendly. RMI used dynamic port assignment by default, which did not traverse firewalls.
CORBA: Common Object Request Broker Architecture
CORBA was the language-neutral alternative. Instead of Java interfaces, you defined services in IDL (Interface Definition Language):
// device.idl
module motorola {
module nms {
struct DeviceStatus {
string status;
long uptime;
long last_polled;
};
exception DeviceNotFound {
string ip;
};
interface DeviceService {
DeviceStatus getStatus(in string ip) raises(DeviceNotFound);
void updateStatus(in string ip, in DeviceStatus s);
};
};
};
The IDL compiler (idlj for Java, idl2cpp for C++) generated stubs and skeletons for each target language. A Java client could call a C++ server transparently.
// CORBA Java client
ORB orb = ORB.init(args, null);
org.omg.CORBA.Object obj =
orb.string_to_object("IOR:000000000000002849444c...");
DeviceService service = DeviceServiceHelper.narrow(obj);
DeviceStatus status = service.getStatus("192.168.1.10");
The IOR (Interoperable Object Reference) was a stringified object reference — long, opaque, and not human-readable.
CORBA strengths:
- Language neutral. Java, C++, Python, Ada — anything with an IDL compiler could participate.
- Mature feature set. Transactions, security, naming, event channels — CORBA had standards for all of these.
- Vendor support. Every major middleware vendor shipped a CORBA ORB.
CORBA weaknesses:
- Complexity. A simple CORBA deployment required an ORB, a naming service, possibly an event service, and generated code from the IDL compiler. The learning curve was steep.
- Interoperability in theory, not in practice. IIOP was the standard wire protocol, but ORB implementations from different vendors had interoperability problems that required testing to find.
- Configuration. CORBA systems required significant infrastructure configuration that was fragile and difficult to automate.
The Decision
We chose RMI for the NMS because:
- All components were Java
- Simplicity mattered more than language neutrality
- The team understood Java well; CORBA's concepts were an additional learning burden
- We did not need the distributed transaction or event channel features
CORBA would have been the right choice if we needed to integrate with C++ systems or with third-party products that exposed CORBA interfaces. For a self-contained Java system, it was unnecessary complexity.
What Replaced Both
REST with JSON, over HTTP. Simple to implement in any language. Human-readable. Firewall-friendly. No code generation required. No framework dependency.
The distributed objects model — making remote calls look like local calls — turned out to be a leaky abstraction. Remote calls fail in ways local calls do not: network partitions, timeouts, partial failures. The RMI and CORBA style of hiding these differences produced code that was easy to write and difficult to make reliable.
REST's HTTP semantics (GET is safe and idempotent, POST is not) made failure modes more explicit. The explicit acknowledgement that remote calls are different from local calls, rather than an attempt to hide the difference, produced more robust systems.