This article is sponsored by the Sun Microsystems, Inc..
The story takes us back to Germany in the late 1990s. German community banks have for long relied on centralized data centers for their computing needs: Instead of each bank running its independent IT department, as is common with U.S. banks, German banks have formed alliances, and alliance members receive their computing services from an uber-IT department operated on behalf of member banks. Such shared IT departments serve dozens of member banks and possibly tens of thousands of users. In essence, shared IT departments deliver computing resources as a service to member banks' branch offices.
A branch bank office, however, maintains a eclectic computing environment on its own. In addition to employees' desktop PCs, a branch office contains a myriad of special-purpose banking devices: ATM machines, cash dispensing machines used by tellers, and check printing machines, to mention just a few.
While banking devices offer special capabilities, they have surprisingly limited connectivity options. Connectivity is typically restricted to the RS-232 serial communication protocol, connecting to the COM1, COM2 or parallel ports of a PC via device-specific communication protocols. In spite of the limited connectivity, such devices must be shared between bank employees. For instance, a cash dispenser connected to one employee's PC must be accessible from another user's computer.
Banking devices are connected to PCs, and are commanded by driver software installed on those PCs. Maintaining driver installations for thousands of device types scattered around hundreds of bank branch offices meant a gargantuan effort from bank alliance IT departments: Device drivers that worked on an Ethernet-based LAN did not work on a TokenRing LAN, or on different OSs, such as Unix, OS/2, or Windows.
Changing a branch PC's operating system, network configuration, or attached devices demanded a new driver distribution, as well as configuration and testing efforts. Configuration databases containing device details, such as baud rate, x/on-x/off settings, parity, COM port configuration, had to be administered for thousands of different devices and configuration options. This proved very costly since update and configuration procedures required the effort of many staff members. And administrators often felt the angst instability and poor software quality brought about.
But the legacy driver software at least provided a communication link between the banking software and the banking devices. When a large IT operator decided in 1998 to move from a home-brewed legacy system to J2EE, it asked Rubean to help migrate device access to the Java environment. Rubean quickly realized that nothing comparable to device drivers existed in Java technology. Instead, communication with banking devices was limited to the Java Communication API (Java COMM) that allowed low-level commands to be passed between a Java program and a device's serial or parallel ports.
In addition, while the J2EE software resided in the IT operator's remote data centers, the banking devices had to be located inside the branches, and the J2EE software had not only to know about these devices, but also had to be able to invoke their various capabilities. For instance, a user interacting with the J2EE software via a Web browser might wish to dispense cash to a customer. The J2EE backend in the remote data center would then have to instruct a cash dispenser in the branch office to emit the cash. And users interacted with the banking software not only through a Web browser, but sometimes also via rich-client UIs installed on employees desktops.
Rubean wanted to take advantage of two Java features from the outset: JavaBeans and RMI. The benefit of JavaBeans arises from the realization that a banking device often combines a variety of capabilities. For instance, an ATM machine is a magnetic card reader (it reads the ATM card), a cash dispenser, a printer (it prints receipts), and can also accept and hold deposit envelops. Another device might combine these functional areas differently - for instance, a cash drawer may offer the cash dispenser and printer functions, but not a magnetic card reader function.
Representing device capabilities as JavaBeans abstracts out device capabilities into the bean interfaces, and then composes those beans into the specific set of functions offered by a device. For instance, an ATM machine may be composed of a
DepositHolder beans. An implementation of such interfaces for each kind of device can supply the tedious mechanics of shuffling device commands back and forth on the serial interface to affect the desired operation, such as changing device settings or printing a receipt, hiding device-specific complexity from clients.
JavaBeans, however, defines no means to invoke the functionality of a banking device over the network. Rubean considered RMI (Java Remote Method Invocation, or Java RMI) for that purpose. RMI communication is based on Java interfaces: An object declares a remote interface -- a Java interface that extends
java.rmi.Remote -- and then an administrator publishes an implementation of that interface into a registry on the network. Clients can locate the remote object in the registry, retrieve from the registry the remote implementation, and invoke methods on that remote object to facilitate procedure calls across the network. Because RMI relies on Java interfaces, it dovetails with the approach of composing complex devices from beans centered around functionality, since a bean interface can also extend the
A key aspect of RMI is that the code for the implementation of the remote object entered in the RMI registry downloads dynamically to the client. That allows the client to rely on the Java implementation classes to dispatch the remote method call, preserving the full Java semantics of the interface. Code downloading in RMI occurs automatically, and is arranged by the RMI runtime system, a part of every J2SE and J2EE system. An RMI client can count on an interface's full Java semantics, since no translation to an intermediate language such as XML takes place.
In Rubean's architecture, dynamic code downloading allows a client to know only that an ATM machine implements the
Printer interface, for instance, to print a receipt on that ATM. The intricacies of communicating with the remote ATM machine remain hidden from clients: Device-specific communication code can simply download under the covers by the RMI runtime when the client invokes the
print() method on the remote object.
RMI and JavaBeans, therefore, allowed Rubean to separate the concerns not only of what a device does from how a specific device performs that functionality, but also from communicating with that device.
However, RMI still requires remote objects to be registered into a centralized object registry. And because clients discover RMI objects based on a registry key, the management of such keys would still have presented a formidable configuration challenge. Instead of providing a solution to the pre-Java configuration nightmare, RMI might have just moved the problem to managing remote object registrations instead.
In addition, RMI defines a point-to-point communication pattern, where one object directly invokes another object across a network. In order for that object invocation to work, the remote object implementation (the service) must arrange to listen on a TCP socket for incoming method invocations. The client, in turn, must know the IP address of the remote service's host.
In RMI, the service exports itself on its host via RMI helper classes. In some cases, a remote object service may register with the host operating system as an operating system service, such as a Unix daemon or a Windows service, ensuring that it becomes available to service remote calls as soon as the host boots up.
Once such a remote service is exported, the service would register a proxy object in an RMI registry. That proxy object represents the remote service to RMI clients on the network. Among other data, the RMI proxy - or stub -- embeds the remote service host's IP address as well as a URL a client can download the proxy's classes from. Most of the exporting and the creation of a proxy object is automated by the RMI runtime.
That convenience, however, presents a problem in an environment where hosts receive dynamic IP addresses from a DHCP server. As a host with an exported service restarts, it may receive a new IP address, one different from the IP embedded in that service's proxy inside the RMI registry. A client that retrieved the proxy with the wrong IP address would receive network communication failures when attempting to invoke methods on the remote service's proxy. Equally important, a client has no way of learning when a remote service's host powers down, and thus has no chance to retrieve an alternate device's proxy.
Rubean concluded that they needed a service registration paradigm that would enable them to employ the power of interface-based design in their very dynamic deployment environment. For this, they turned to Jini technology.
In the late 1990s, RMI's designers at Sun were working on making RMI more dynamic, and defined a more comprehensive Java network communication infrastructure, Jini technology. Although first presented to developers as a way to enable devices to communicate, Rubean realized that Jini technology defined a general object-oriented network infrastructure and, more important, that it provided a framework for their branch banking architecture.
The Jini architecture extends RMI in two ways that were important to Rubean: First, it no longer mandates registry keys for service registration and lookup.
Instead, it defines a discovery protocol based on IP multicast that enables clients to discover service registries. Once a client discovers a registry,
that client can search for services based on a set of Java interfaces a service must implement, as well as
any number of attribute values associated with a service's registration in Jini lookup services. For instance, a client looking to dispense cash would
search for a service registration implementing the
CashDispenser interface. It might, in addition, search for a cash dispenser with appropriate
currency. To do this, the client would add the
CurrencyCode class to the list of service attributes, with the
code property set
USD. The client would then receive a list of zero of more service proxies matching the search criteria.
Another way the Jini architecture extends RMI is that Jini lookup service registrations are never permanent. When a service registers its proxy, a lookup service returns to that service an object representing the proxy's lease inside the lookup service. The service must periodically renew that lease. If the lease expires, the lookup service purges the proxy object, making the service unavailable for discovery.
The Jini dynamic service discovery and lease-based registration abilities provided a solution to Rubean's problem: When a PC with a connected device boots up, that PC starts and exports and remote object representing the device as a Jini service, and advertises that service's interfaces on the network. Since Jini service selection is based on Java interfaces, registry keys need not be managed. And lease-based registrations in Jini lookup services ensure that clients always receive service proxies of available services, since proxies of unavailable hosts are purged by the lookup service.
Rubean's architecture, therefore, presents banking devices not as RMI objects, but as Jini services. An implementation of such services is installed on bank branch PCs with the appropriate connected devices, and users can discover any device in the bank office via Jini discovery.
Rubean's remaining challenges were the installation and configuration of Jini services on PCs offering banking devices; and making the J2EE application running in a remote data center aware of the available devices in a bank branch.
This article is sponsored by the Sun Microsystems, Inc..