You are on page 1of 43
Java remote method invocation mA ow & In computing, the Java Remote Method Invocation (Java RMI) is a Java AP! that performs remote method invocation, the object-oriented equivalent of remote procedure calls (RPC), with support for direct transfer of serialized Java classes and distributed garbage-collection. an) A typical implementation model of Java-RMI using stub and skeleton objects. Java 2 SDK, Standard Edition, v1.2 removed the need for a skeleton. The original implementation depends on Java Virtual Machine (JVM) class- representation mechanisms and it thus only supports making calls from one JVM to another. The protocol underlying this Java-only implementation is known as Java Remote Method Protocol (JRMP). In order to support code running in a non-JVM context, programmers later developed a CORBA version. Usage of the term RMI may denote solely the programming interface or may signify both the API and JRMP IIOP, or another implementation, whereas the term RMI-IIOP (read: RMI over IIOP) specifically denotes the RMI interface delegating most of the functionality to the supporting CORBA implementation. The basic idea of Java RMI, the distributed garbage-collection (DGC) protocol, and much of the architecture underlying the original Sun implementation, come from the "network objects" feature of Modula-3. RMI (Remote Method Invocation) The RMI (Remote Method Invocation) is an API that provides a mechanism to create distributed application in java. The RMI allows an object to invoke methods on an object running in another JVM. The RMI provides remote communication between the applications using two objects stub and skeleton. Understanding stub and skeleton RMI uses stub and skeleton object for communication with the remote object. A remote object is an object whose method can be invoked from another JVM. Let's understand the stub and skeleton objects: stub The stub is an object, acts as a gateway for the client side. All the outgoing requests are routed through it. It resides at the client side and represents the remote object. When the caller invokes method on the stub object, it does the following tasks: 1. It initiates a connection with remote Virtual Machine (JVM), 2. It writes and transmits (marshals) the parameters to the remote Virtual Machine (JVM), 3. It waits for the result 4. It reads (unmarshals) the return value or exception, and 5. It finally, returns the value to the caller. skeleton The skeleton is an object, acts as a gateway for the server side object. All the incoming requests are routed through it. When the skeleton receives the incoming request, it does the following tasks: 1. It reads the parameter for the remote method 2. It invokes the method on the actual remote object, and 3. It writes and transmits (marshals) the result to the caller. In the Java 2 SDK, an stub protocol was introduced that eliminates the need for skeletons. Understanding requirements for the distributed applications If any application performs these tasks, it can be distributed application. 1. The application need to locate the remote method 2. It need to provide the communication with the remote objects, and 3. The application need to load the class definitions for the objects. The RMI application have all these features, so it is called the distributed application. Java RMI Example The is given the 6 steps to write the RMI program. 1. Create the remote interface 2. Provide the implementation of the remote interface 3. Compile the implementation class and create the stub and skeleton objects using the rmic tool 4. Start the registry service by rmiregistry tool 5. Create and start the remote application 6. Create and start the client application RMI Example In this example, we have followed all the 6 steps to create and run the rmi application. The client application need only two files, remote interface and client application. In the rmi application, both client and server interacts with the remote interface. The client application invokes methods on the proxy object, RMI sends the request to the remote JVM. The return value is sent back to the proxy object and then to the client application. { Client Li Adder Implementation 1) create the remote interface For creating the remote interface, extend the Remote interface and declare the RemoteException with all the methods of the remote interface. Here, we are creating a remote interface that extends the Remote interface. There is only one method named add() and it declares RemoteException. import java.rmi.*; public interface Adder extends Remote{ public int add(int x,int y)throws RemoteException; } 2) Provide the implementation of the remote interface Now provide the implementation of the remote interface. For providing the implementation of the Remote interface, we need to Either extend the UnicastRemoteObject class, © or use the exportObject() method of the UnicastRemoteObject class In case, you extend the UnicastRemoteObject class, you must define a constructor that declares RemoteException. import java.rmi.*; import java.rmi.server.*; public class AdderRemote extends UnicastRemoteObject i AdderRemote()throws RemoteException{ super(); } public int add(int x,int y){return x+y;} } 3) create the stub and skeleton objects using the rmic tool. Next step is to create stub and skeleton objects using the rmi compiler. The tmic tool invokes the RMI compiler and creates stub and skeleton objects. rmic AdderRemote 4) Start the registry service by the rmiregistry tool Now start the registry service by using the rmiregistry tool. If you don't specify the port number, it uses a default port number. In this example, we are using the port number 5000. rmiregistry 5000 import java.rmi.*; import java.rmi.registry.*; public class MyServer{ public static void main(String args[)){ try Adder stub=new AdderRemote(); Naming.rebind("rmi://localhost:5000/sonoo",stub); }eatch(Exception e){System.out.printin(e);} } } 6) Create and run the client application At the client we are getting the stub object by the lookup() method of the Naming class and invoking the method on this object. In this example, we are running the server and client applications, in the same machine so we are using localhost. If you want to access the remote object from another machine, change the localhost to the host name (or IP address) where the remote object is located. import java.rmi.*; public class MyClient{ public static void main(String argsf]){ try Adder stub= (Adder)Naming.lookup("rmi://localhost:5000/sonoo’); System.out.printin(stub.add(34,4)); }eatch(Exception e){} } z =. a import.” ; % import java.sql.*; import java.util.*; class BankImpl extends UnicastRemoteObject implements Bank{ Bankimpl()throws RemoteException{} public List getCustomers(){ List list=new ArrayList(); try{ Class.forName("oracle.jdbc.driver.OracleDriver’); Connection con=DriverManager.getConnection("jdbc:oracl PreparedStatement ps=con.prepareStatement(select * from customer400"); ResultSet rs=ps.executeQuery(); while(rs.next()){ Customer c=new Customer(); c.setAcc_no(rs.getint(1)); c.setFirstname(rs.getString(2)); c,setLastname(rs.getString(3)); c.setEmail(rs.getString(4)); c.setAmount(rs.getFloat(5)); list.add(c); } con.close(); }eatch(Exception e){System.out.printin(e);} return list; V/end of getCustomers() } In distributed architecture, components are presented on different platforms and several components can cooperate with one another over a communication network in order to achieve a specific objective or goal = In this architecture, information processing is not confined to a single machine rather it is distributed over several independent computers. A distributed system can be demonstrated by the client-server architecture which forms the base for multi-tier architectures; alternatives are the broker architecture such as CORBA, and the Service-Oriented Architecture (SOA). » There are several technology frameworks to support distributed architectures, including .NET, J2EE, CORBA, .NET Web services, AXIS Java Web services, and Globus Grid services. Middleware is an infrastructure that appropriately supports the development and execution of distributed applications. It provides a buffer between the applications and the network. It sits in the middle of system and manages or supports the different components of a distributed system. Examples are transaction processing monitors, data convertors and communication controllers etc. Middleware as an infrastructure for distributed system Application 1 Application 2 Application 3 Middleware platform Personal digital assistance Personal Computer Mainframe Middleware as an infrastructure for distributed system The basis of a distributed architecture is its transparency, reliability, and availability. lil Oo < Advantages Resource sharing - Sharing of hardware and software resources. Openness - Flexibility of using hardware and software of different vendors. Concurrency - Concurrent processing to enhance performance. Scalability - Increased throughput by adding new resources. Fault tolerance - The ability to continue in operation after a fault has occurred. Disadvantages Complexity - They are more complex than centralized systems. Security - More susceptible to external attack. Manageability - More effort required for system management. Unpredictability - Unpredictable responses depending on the system organization and network load. Centralized System vs. Distributed System Criteria Centralized system Distributed System Economics Low High Availability Low High Complexity Low High Consistency Simple High Scalability Poor Good Technology Homogeneous Heterogeneous Security High Low Client-Server Architecture The client-server architecture is the most common distributed system architecture which decomposes the system into two major subsystems or logical processes - = Client - This is the first process that issues a request to the second process i.e. the server. ® Server - This is the second process that receives the request, carries it out, and sends a reply to the client. In this architecture, the application is modelled as a set of services that are provided by servers and a set of clients that use these services. The servers need not know about clients, but the clients must know the identity of servers, and the mapping of processors to processes is not necessarily 1 : 1 4 eae ne kel cE lao oo) eC kee ty 2-Tier Client Server Architecture Client-server Architecture can be classified into two models based on the functionality of the client - Thin-client model In thin-client model, all the application processing and data management is carried by the server. The client is simply responsible for running the presentation software. e Used when legacy systems are migrated to client server architectures in which legacy system acts as a server in its own right with a graphical interface implemented on a client = A major disadvantage is that it places a heavy processing load on both the server and the network. Thick/Fat-client model In thick-client model, the server is only in charge for data management. The software on the client implements the application logic and the interactions with the system user. = Most appropriate for new C/S systems where the capabilities of the client system are known in advance More complex than a thin client model especially for management. New versions of the application have to be installed on all clients. Presentation a Thin Client Model Client }- | Data Management Application Processing J Presentation & Application Processing [ Server ] Thick Client Model Client - | Data Management Application Processing Advantages Separation of responsibilities such as user interface presentation and business logic processing. Reusability of server components and potential for concurrency Simplifies the design and the development of distributed applications It makes it easy to migrate or integrate existing applications into a distributed environment. It also makes effective use of resources when a large number of clients are accessing a high-performance server. Disadvantages a Lack of heterogeneous infrastructure to deal with the requirement changes. Security complications. a” Limited server availability and reliability. Limited testability and scalability. Fat clients with presentation and business logic together. Multi-Tier Architecture (n-tier Architecture) Multi-tier architecture is a client-server architecture in which the functions such as presentation, application processing, and data management are physically separated. By separating an application into tiers, developers obtain the option of changing or adding a specific layer, instead of reworking the entire application. It provides a model by which developers can create flexible and reusable applications. => Server Process The most general use of multi-tier architecture is the three-tier architecture. A three-tier architecture is typically composed of a presentation tier, an application tier, and a data storage tier and may execute on a separate processor. Data Tier In this layer, information is stored and retrieved from the database or file system. The information is then passed back for processing and then back to the user. It includes the data persistence mechanisms (database servers, file shares, etc.) and provides API (Application Programming Interface) to the application tier which provides methods of managing the stored data. Level 1 Level 2 Level 3 | Sending | {i} sQu HTTP Request query Request, Files, sal, meen | Sending Replies | oB t Application pe Client Server Server 3 Tier Architecture Advantages ® Better performance than a thin-client approach and is simpler to manage than a thick-client approach. = Enhances the reusability and scalability - as demands increase, extra servers can be added. = Provides multi-threading support and also reduces network traffic. = Provides maintainability and flexibility Disadvantages ® Unsatisfactory Testability due to lack of testing tools. = More critical server reliability and availability. sending it back to the client. Bridge A bridge can connect two different networks based on different communication protocols. It mediates different brokers including DCOM, NET remote, and Java CORBA brokers. Bridges are optional component, which hides the implementation details when two brokers interoperate and take requests and parameters in one format and translate them to another format. 7 z Service 1 * 3 Broker 1 + ; Request Service 2 + J 4 5 . Response £ Service 3 Broker 2 Broker Model Broker implementation in CORBA CORBA is an international standard for an Object Request Broker - a middleware to manage communications among distributed objects defined by OMG (object management group). Operation Arguments Return Value , Out arguments Stub CORBA Architecture lil Oo < Service-Oriented Architecture (SOA) A service is a component of business functionality that is well-defined, self- contained, independent, published, and available to be used via a standard programming interface. The connections between services are conducted by common and universal message-oriented protocols such as the SOAP Web service protocol, which can deliver requests and responses between services loosely. Service-oriented architecture is a client/server design which support business-driven IT approach in which an application consists of software services and software service consumers (also known as clients or service requesters). Service Directory Finds & Retriéves oN Register Invokes . eee — | Service Consumer - SOA SOA Operation The following figure illustrates how does SOA operate - arches in Service Repository | Search Develop } - Creates Certain 1 Describes Invokes Client Service Contract || Service (Application front end l ; cr Services) Fulfills Uses Service Stub Based On ay, SOA Operations f “ Advantages Loose coupling of service-orientation provides great flexibility for enterprises to make use of all available service recourses irrespective of platform and technology restrictions. = Each service component is independent from other services due to the stateless service feature. The implementation of a service will not affect the application of the service as long as the exposed interface is not changed A client or any service can access other services regardless of their platform, technology, vendors, or language implementations. » Reusability of assets and services since clients of a service only need to know its public interfaces, service composition. SOA based business application development are much more efficient in terms of time and cost. » Enhances the scalability and provide standard connection between systems. 1 Efficient and effective usage of ‘Business Services’, Integration becomes much easier and improved intrinsic interoperability. Ahetraet eamniavity far davalanare and anarniza hricinace lil Oo < Creating the Stubs and Skeletons After you define the remote Java interface and implementa- tion class, compile them into Java bytecodes using a standard Java compiler. Then you use the RMI stub/skeleton compiler, rmic , to generate the stub and skeleton interfaces that are used at either end of the RMI communication link, as was shown in Figure 3-1. In its simplest form, you can run rmic with the fully qualified classname of your implementation class as the only argument. For example, once we've com- piled the Account and Account Imp] classes, we can gener- ate the stubs and skeletons for the remote Account object with the following command (Unix version): O % rmic AccountImp1 ay £¢ If the RMI compiler is successful, this command generates the stub and skeleton classes, AccountImp1_Stub and AccountImp1_Skel, in the current directory. The rmic com- piler has additional arguments that let you specify where the generated classes should be stored, whether to print warn- ings, etc. For example, if you want the stub and skeleton classes to reside in the directory /usr/local/classes, you can run the command using the -d option: @ rmic -d /usr/local/classes AccountImpl 11.5. Creating Stub and Skeleton Classes The stub and skeleton classes are responsible for dispatch- ing and processing RMI requests. Developers should not write these classes, however. Once a service implementation ex- ists, the rmic tool, which ships with the JDK, should be used to create them. The implementation and interface should be compiled, and then the following typed at the command line: rmic implementation ZV N y / where implementation is the name of the service imple- mentation class. For example, if the class files for the RMILightBulb system are in the current directory, the following would be typed to produce stub and skeleton classes: rmic RMILightBulbImp1 eating Stubs and Skeletons to | CB dowlcadoracecom ] © Q@ LQ Creating Stubs and Skeletons to .NET Web Services This topic contains information to help you when you create a stub or skeleton to a .NET web service using the Web Service Stub/Skeleton Wizard. Creating a Stub which Uses Array Types When JDeveloper creates a stub to a .NET web service which uses the array typeArrayList, the associated JavaBean generated by the wizard contains an unknown type. For example vate UnKnown[] m_Produc You should edit the generated code in the JavaBean to change type UnKnown|[ ] to the type that is expected at runtime, for example, String[]. Creating a Stub for .NET Web Service where .NET types inherit other .NET types When JDeveloper creates a stub to a .NET web service that contains types that inherit from other .NET types, the JavaBean created for the inherited type does not correctly reflect the inheritence. Follow the steps below to resolve this issue. This describes how to create stub to a web service that uses the.NET complex type Bean1 and type Bean2, which extends Beani. 1. After running the Web Service Stub/Skeleton wizard, create an inherited Be an2 JavaBean by hand. 2. Copy the stub for a web service that returns the Be an1 complex type. 3. Modify the copied stub to use 8 2 everywhere that Beani was used in the original stub, and if you are passing in a value make sure that the name of the parameter matches. Creating a Skeleton from a .NET Web Service lll @ < P ate UnKnown[] m_Producers You should edit the generated code in the JavaBean to change the type UnKnown | ] to the type that is expected at runtime, for example, String[]. Creating a Stub for .NET Web Service where .NET types inherit other .NET types When JDeveloper creates a stub to a .NET web service that contains types that inherit from other .NET types, the JavaBean created for the inherited type does not correctly reflect the inheritence. Follow the steps below to resolve this issue. This describes how to create stub to a web service that uses the.NET complex type Bean1 and type Bean2, which extends Bean1. 1. After running the Web Service Stub/Skeleton wizard, create an inherited Be an2 JavaBean by hand. 2. Copy the stub for a web service that returns the Be ani complex type. 3. Modify the copied stub to use Bean2 everywhere that Be ani was used in the original stub, and if you are passing in a value make sure that the name of the parameter matches. a Creating a Skeleton from a .NET Web Service , J2EE web services use WSDL documents which describe web a services as RPC services, where a set of parameters to a method are encoded in a special way and the sent in an XML document. In contrast, a.NET WSDL describes web services as document services, i.e. services that accept and return some XML documents, and the signature to a NET web service must be Element doSth(Element ement doSth() Of vo oSth(Element sth). When JDeveloper creates a skeleton to a .NET web service, it generates signatures with the type E1cment instead of the type that would be generated for a stub to the same service. To use the skeleton, change Element to the correct type, and continue creating your new web service. lil O < Defining Remote Objects Now that you have a basic idea of how Java RMI works, we can explore the details of creating and using distributed ob- jects with RMI in more detail. As mentioned earlier, defining a remote RMI object involves specifying a remote interface for the object, then providing a class that implements this inter- face. The remote interface and implementation class are then used by RMI to generate a client stub and server skeleton for your remote object. The communication between local ob- jects and remote objects is handled using these client stubs and server skeletons. The relationships among stubs, skelet- ons, and the objects that use them are shown in Figure 3-2. — smitten by you ‘generated from rmic by you 4 inesitnce 4 ener by mic ito ‘stiri Figure 3-2. Relationships among remote object, stub, and skeleton classes W Defining Remote Objects (Java En’ x | CO A docstoremikua/orely) 1 © Q@ QD communication link between the client and your exported remote object, making it seem to the client that the object actually exists within its Java VM. The RMI compiler (rmic) automatically generates these stub and skeleton classes for you. Based on the remote interface and implementation class you provide, rmic generates stub and skeleton classes that implement the remote interface and act as go-betweens for the client application and the actual server object. For the client stub class, the compiler generates an implementation of each remote method that simply packages up (marshals) the method arguments and transmits them to the server. For the server skeleton class, the RMI compiler generates another set of implementations of the remote methods, but these are designed to receive the method arguments from the remote method call, unpackage them, and make the corresponding method call on the object implementation. Whatever the method call generates (return data or an exception), the results are packaged and transmitted back to the remote client. The client stub method (which is still executing at this point) unpackages the results and delivers them to the client as the result of its remote method call So, the first step in creating your remote objects is to define the remote interfaces for the types of objects you need to use in a distributed object context. This isn't much different from defining the public interfaces in a nondistributed application, with the following exceptions: « Every object you want to distribute using RMI has to directly or indirectly extend an interface that extends the java.rmi.Remote interface. + Every method in the remote interface has to declare that it throws a java.rmi.RemoteException or one of the parent classes of RemoteException.[1] [1]Note that prior to Java 1.2, the RMI specification required that t “ every method on a remote interface had to throw RemoteException specifically. In Java 1.2, this has been loosened to allow any superclass of RemoteException. The reason for this change is to make it easier to define generic interfaces that support both local and remote objects. RMI imposes the first requirement to allow it to differentiate quickly between objects that are enabled for remote distribution and those that are not. As we've already seen, during a remote method invocation, the RMI runtime system needs to be able to determine whether each argument to the remote method is a Remote object or not The Remote interface, which is simply a tag interface that marks remote objects, makes it easy to perform this check. lil O < The second requirement is needed to deal with errors that can happen during a remote session. When a client makes a method call on a remote object, any number of errors can occur, preventing the remote method call from completing. These include client-side errors (e.g., an argument can't be marshaled), errors during the transport of data between client and server (e.g., the network connection is dropped), and errors on the server side (e.g., the method throws a local exception that needs to be sent back to the remote caller). The RemoteException class is used by RMI as a base exception class for any of the different types of problems that might occur during a remote method call. Any method you declare in a Remote interface is assumed to be remotely callable, so every method has to declare that it might throw a RemoteException, or one of its parent interfaces. Example 3-3 shows a simple remote interface that declares two methods: doThis() and doThat(). These methods could do anything that we want; in our Account example, we had remote methods to deposit, withdraw, and transfer funds. Each method takes a single String argument and returns a String result. Since we want t use this interface in an RMI setting, we've declared that the interface extends the Remote interface. In addition, each method is declared as throwing a RemoteException. Example 3-3. The ThisOrThatServer Interface ) import java.rmi.Remote; import java.rmi.RemoteException; public interface ThisOrThatServer extends Remote { public String doThis(String todo) throws RemoteException; public String doThat(String todo) throws RemoteException; With the remote interface defined, the next thing we need to do is write a class that implements the interface. Example 3-4 shows the ThisOrThatServer Impl Class, which implements the ThisOrThatServer interface. Example 3-4. Implementation of the ThisOrThatServer import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; public class ThisOrThatServer Impl extends UnicastRemoteObject implements ThisOrThatServer { public ThisOrThatServerImpl() throws RemoteException {} // Remotely accessible methods public String doThis(String todo) throws RemoteException { return doSomething("this", todo); public String doThat(String todo) throws RemoteException { return doSomething("that", todo); // Non-remote methods private String doSomething(String what, String todo) { String result = "Did " + what + " to " + todo + "."; return result; } ~~ } / This class has implementations of the doThis() and doThat() methods declared in the ThisOrThatServer interface; it also has a nonremote method, doSomething( ), that is used to implement the two remote methods. Notice that the doSomething() method doesn't have to be declared as throwing a RemoteException, since it isn't a remotely callable method. Only the methods declared in the remote interface can be invoked remotely. Any other methods you include in your implementation class are considered nonremote (i.e., they are only callable from within the local Java virtual machine where the object exists). 3.2.1. Key RMI Classes for Remote Object Implementations You probably noticed that our ThisOrThatServer Imp] class also extends the UnicastRemoteObject Class. This is a class in the java.rmi.server package that extends java.rmi.server .RemoteServer, which itself extends java.rmi.ser- ver .RemoteObject, the base class for all RMI remote objects. There are four key classes related to writing server object implementations: RemoteObject RemoteObject implements both the Remote and java.rmi.server package, it is used by both the Serializable interfaces. Although the RemoteObject class is in the client and server portions of a remote object reference. Both client stubs and server implementations are subclassed (directly or indirectly) from RemoteObject. A RemoteObject contains the remote reference for a particular remote object. RemoteObject is an abstract class that reimplements the equals(), hashCode( ), and toString() methods inherited from Object in a way that makes sense and is practical for remote objects. The equals( ) method, for example, is implemented to return true if the internal remote references of the two RemoteObject objects are equal, (i.e., if they both point to the same server object). f RemoteServer RemoteServer is an abstract class that extends RemoteObject. It defines a set of static methods that are useful for implementing server objects in RMI, and it acts as a base class for classes that define various semantics for remote objects. In principle, a remote object can behave according to a simple point- to-point reference scheme; it can have replicated copies of itself scattered across the network that need to be kept synchronized; or any number of other scenarios. JDK 1.1 supported only point-to-point, nonpersistent remote references with the UnicastRemoteObject class. The Java 2 SDK 1.2 has introduced the RMI activation system, so it provides another subclass of RemoteServer, Activatable. UnicastRemoteObject This is a concrete subclass of RemoteServer that implements point-to-point remote references over TCP/IP networks. These references are nonpersistent: remote references to a server object are only valid during the lifetime of the server object. Before the server object is created (inside a virtual machine running on the host) or after the object has been destroyed, a client can't obtai remote references to the object. In addition, if the virtual machine containing the object exits (intentionally or otherwise), any existing remote references on clients become invalid and generate RemoteException objects if used. — Activatable “y uy This concrete subclass of RemoteServer is part of the new RMI object activation facility in Java 1.2 and can be found in the java.rmi. activation package. It implements a server object that supports persistent remote references. If a remote method request is received on the server host for an Activatable object, and the target object is not executing at the time, the object can be started automatically by the RMI activation daemon. Remote object activation ~)¢ in» 4 By Govind Seshadri ® 12/28/1999 4 yd Few deny that, today, RMI is among the most useful and versatile technologies within the Java pantheon Arguably, this has not always been the case, as there were some glaring lacunae within the RMI architecture implemented under earlier versions of Java. Under JDK 1.1, every RMI server had to be started manually prior to its usage by remote clients. Also, if a remote object crashed during operation, a client could not really resume “where it left off, as the previous state of the remote object was unrecoverable. But all that is history. | will examine the details behind a significant enhancement to the RMI architecture under the Java 2 platform that helps overcome these limitations. Understanding Remote Object Activation While it sounds simple, starting remote servers prior to their usage may not always be a practical proposition. For instance, itis highly inefficient to have hundreds of resource-intensive remote objects running in memory all the time—especially if they are used only on an occasional basis. What's worse, they may even turn out to be a logistical nightmare if your enterprise's distributed computing environment demands hundreds or thousands of remote objects. As we can see quite clearly, the old approach of prestarting an RMI server becomes unmanageable rather quickly in a reablife scenario. Luckily for us, however, with the advent of Java 2, the headaches associated with developing fairly large- scale distributed systems using RMI have now been greatly alleviated. Specifically, with the help of the new Remote Object Activation (ROA) feature, you no longer have to worry about starting up remote objects prior to their invocation. By extending java. rmi.act ivatable and making use of the new RMI activation daemon rmid, RMI developers can now register information about remote object implementations that should be instantiated only when necessary, rather than running all the time. The ROA mechanism accomplishes this by what is known as /azy activation. Lazy activation implies that the passive remote object is activated by mid, by loading its constituent classes into a JVM only on the receipt of the client's ivation.A first remote method invocation, and not until then, rmid—which runs on the same host as the activatable object and the RMI name server, regist ry~also needs to be configured with information that will allow it to locate and load the classes used by the remote object. This task is usually delegated to a separate setup or bootstrap program, after first starting up rmid and rmiregistry. Activatable objects can optionally be associated with a persistent state. This can be provided to the remote object during activation to reestablish its state information. The good news is, choosing to make your remote objects is strictly a server-side decision; RMI clients don't care whether the remote objects are activatable or not. Developing an Activatable Remote Object ROA can be a fairly complex topic for the novice RMI develope If you are not familiar with Java remote objects, it may be useful for you to read my earlier article on distributed Java and RMI. ' Assuming you are familiar with the basics of RMI, let us take a closer look at activatable remote objects. The best way to understand ROA is to see it in action via a concrete examph As with any RMI object, the activatable object in our example implements a remote interface. Here, Count Inter face (shown in Listing 1), contains a single remote method getCount () and basically allows a client to obtain a count of the number of times the remote object has been accessed. The class for our activatable server object is shown in Listing 2. Count Server not only maintains a running count of the number of accesses by RMI clients, but is also activatable—i.e,, its instantiation is, deferred until the first remote method invocation. Further, it also makes use of MarshalledObject to regain its state—even if it is restarted after a server shutdown. Now, let us dissect the activatable remote server a little and see how it differs from a conventional RMI server. Consider the line: public class CountServer extends Activatable implements Count Interface Unlike regular remote objects that subclass java. rmi.server.UnicastRemoteObject, the class f an activatable object usually needs to extend java.rmi.activation.Activatable. (lemphasize usually, because you can also create an activatable server object by exporting a remote object using the static method Act ivatable .exportObject().) Observe the constructor for Count Server: public CountServer( ActivationID id, MarshalledObject data) throws RemoteException, ClassNotFoundException, IOException { va The constructor is automatically invoked by the RMI activation daemon rmid whenever a new instance ¢ this remote object needs to be created, and really serves two purposes. It first registers the remote obje with the activation system, and then exports it on an anonymous port. The Act ivat ion 1D passed in by rmad represents a unique identifier for the remote object to be activated and includes a remote referenc to the object's activator. The MarshalledOb ject parameter here can also optionally contain any serialized object. In this, ‘example, we use it to supply the name of the file containing some previously persisted state informatior The great thing about using the Mar shal1ed0b ject is that now, your remote objects can preserve sta even between successive restarts. Observe that the constructor also checks whether the file passed via MarshalledObject exists, indicating the presence of a previously persisted state. If it does exist, the method rest oreState ( ) is invoked, causing the activatable object to initialize itself from the persiste data. Because our remote object implements Count Interface, it defines getCount ( |, which increments a counter specifying the number of times the object was accessed. For each client invocation, this method updates and saves the count data to the file that was passed as the MarshalledOb ject, before returning the count to the client. You may also have noticed that unlike a conventional RMI server, an activatable remote object does not contain a main () method. There is no need for one, as the server is now instantiated by rm id, instead of being manually started from the command line. Setup Program Before activatable objects can be instantiated, the rmid and rmiregistry services must be properly initialized with information regarding these objects. First, we need to initialize rmid with some URL- based information that will allow it to locate and load the remote object's constituent classes before instantiating them. Second, we have to register a remote reference (an instance of the generated stub class) for the activatable class within the rmiregist ry, and associate it with an URL-based identifier. A setup program that is executed for each activatable object usually performs this configuration work. Listl ng 3 demonstrates the setup program for our activatable remote object. Because our activatable object serializes its state information to a file, we have to explicitly grant this level of access, bypassing the default security sandbox. For testing, you can give carte blanche access to the setup program by disabling the security mechanism with the following policy file: grant { permission java.security.AllPermission hs Once we have created the policy file, we need to initialize the java. security. policy property with its location. For example: props.put("java.security.policy’,"/policy") assumes that the policy file exists in the root directory (i.e., C:\ on win32 systems). The activation system provides great flexibility when activating remote objects through the use of activation groups. Each activation group is associated with its own JVM. Thus, it is possible to run groups of activatable objects within different JVMs by associating them with a distinct ActivationGroupID: ActivationGroupDesc .CommandEnvironment ace = ( null; a“ ActivationGroupID agi= ActivationGroup.getSystem().registerGroup( ” new ActivationGroupDesc(props, ace)); Consider the line: MarshalledObject data- new MarshalledObject ( new File(*/Count.ser*)); Here, MarshalledObject is indicated as being the file Count . ser, which is created in the root directory. This file is then used by our activatable remote object to persist the updated count data after each client invocation. Although we could have probably hard-coded the filename within the remote object itself, MarshalledOb ject provides a flexible mechanism for passing any kind of initialization object to the activatable remote object All the information required to activate a remote object is stored within an instance of the activation descriptor Act ivat ionDesc.. This activation descriptor is then interrogated by rmid to obtain the necessary information during object activation. The line: ActivationDesc desc = new ActivationDesc( agi, ‘cup.chap6.activation.CountServer" location, data); creates an Act ivat ionDese object that stipulates the fully qualified class name for the activatable remote object, the location of its code, the object's group identifier, as well as any object-specific initialization data. The activatable object is then registered with rmid by passing the activation descripto as a parameter to the static method Act ivatable. register() as: counter = (Count Interface)Act ivatable.register(desc) ; The register() invocation retums the Remote stub or client-side proxy for the activatable object Basic RMI mechanics dictate that the client must obtain a handle to the remote object by performing a lookup on the rmi registry. Thus, we bind the stub for the remote object within rmiregistry and permit its access via an URL-based name through: “a3 a / x The Client Applet Listing 4 shows the client applet which is used to access the activatable remote object. Notice that ther: is nothing new happening here-the RMI client remains the same for a regular remote object as well as for an activatable one. Demonstrating Remote Object Activation Each time a client makes a remote method invocation, the activatable remote object updates its counter and stores the state in the form of serialized data to the file specified by the MarshalledObject . Now that you have the requisite example programs ready, you can compile them and generate the RMI stub and skeleton files as usual: javac -d . *.java rmic -d . com.jr.active.CountServer Before you can run the setup program, you need to start the RMI name server rmiregistry and the activation daemon rmid: start rmid Y ns a start rmiregistry Assuming that you had compiled the example source files in the path c:\examples and placed the security policy file in the root directory, you can execute the setup program as: start java -Djava.security.policy=\policy -Djava.rmi.server.codebase=file:\examples com. jr.active.BootStrap The counter. htm! file shown in Listing 5 should work fine for the purpose of loading your applet. The RMI applet can then be started as: start appletviewer counter.html Note that you do not have to first start up the remote server. The activatable object was already registered within the registry when we ran the setup program. If you keep executing CounterApplet repeatedly for a minute or two, you should eventually see the output shown in Figure 1 learigure 1 _ Figure 1. Output of repeated execution of CounterApplet. 6 = Now let us observe an interesting phenomenon. Without closing the applet vlewer wifidow, shut down rmiregistry and rmid, After that, restart both of them, and run the setup program as before. You should observe that the applet can resume where it left off—i.e., the RMI stub reference holds well even if the server goes down. Also, the activation system automatic- ally restarts the remote server, while reading in the previous state from the serialized file passed via Marshal ledOb ject Design Considerations Remote object activation is an extremely powerful feature and should empower us to build much more flexible distributed systems, Although activatable and non-activatable remote objects can coexist in the same environment, they introduce a new level of complexity to the remote object lifecycle management issues. For instance, activatable remote objects can be deactivated via the method Activatable.unexportObject (). If the target remote object happens to be the last one present within its activation group, then one of the consequences of this deactivation call would result in the shutting down of the JVM! This could in turn lead to RMI clients that access non-activatable remote objects left with dangling stub references. Conclusion Using the Act ivatable class and the activation daemon rmid, you can design remote object implementation that can be executed on-demand, rather than running all the time. This feature allows us to design highly scalable distributed systems while making efficient use of system resources. rmid provides a default JVM from which other JVM instances may be automatically spawned. We have also seen how the Marshal led0bject can be used for passing initialization data through the activation descriptor, without having to hard-code these values within the remote object itself Serializable Objects To serialize an object means to convert its state to a byte stream so that the byte stream can be reverted back into a copy of the object. A Java object is serializable if its class or any of its superclasses implements either the java.io.Serializable interface or its subinterface, java. io.Externalizable. Deserialization is the process of converting the serialized form of an object back into a copy of the object. For example, the java.awt .Button class implements the Serializable interface, so you can serialize a java.awt .Button object and store that serialized state in a file. Later, you can read back the serialized state and deserialize into a java. awt.Button object. The Java platform specifies a default way by which serializable 7Ycbiects are serialized. A (Java) class can override this default serialization and define its own way of serializing objects of that class. The Object Serialization Specification describes object serialization in detail. When an object is serialized, information that identifies its class is recorded in the serialized stream. However, the class's definition (‘class file’) itself is not recorded. It is the responsibility of the system that is deserializing the object to determine how to locate and load the necessary class files. For example, a Java application might include in its classpath a JAR file that contains the class files of the serialized object(s) or load the class definitions by using information stored in the directory, as explained later in this lesson. Binding a Serializable Object You can store a serializable object in the directory if the underlying service provider supports that action, as does Oracle's LDAP service provider. The following example invokes Context .bind to bind an AWT button to the name "cn=Button". To associate attributes with the new binding, you use DirContext . bind. To overwrite an existing binding, use Context. rebind and DirContext.rebind. 11 Create the object to be bound Button b = new Button(*Push me") 11 Bertorm the bind ctx.bind(“en-Button, b) You can then read the object back using Context. Lookup, as follows. 11 Check that 4¢ 4s bound Button b2 = (Button)etx. lookup("en=Button") System.out.printin(b2) Running this example produces the following output. # java Seroby java.ant.Button{button0,0,0,0x0, invalid, Label=Push me] Specifying a Codebase Note: The procedures described here are for binding a serializable object in a directory service that follows the schema defined in RFC 2713. These procedures might not be generally applicable to other naming and directory services that support binding a serializable object with a specified codebase. When a serialized object is bound in the directory as shown in the previous example, applications that read the serialized object from the directory must have access to the class definitions necessary to deserialize the object. Alternatively, you can record a codebase with the serialized object in the directory, either when you bind the object or subsequently by adding an attribute by using DirContext .modifyAttributes. You can use any attribute to record this codebase and have your application read that attribute from the directory and use it appropriately. Or you can use the "javaCodebase" attribute specified in . In the latter case, Oracle's LDAP service provider will automatically use the attribute to load the class definitions as needed. "javaCodebase" should contain the URL of a codebase directory or a JAR file. If the codebase contains more than one URL, then each URL must be separated by a space character. The following example resembles the one for binding a java.awt Button. It differs in that it uses a user-defined Serializable class, Flower, and supplies a "javaCodebase" attribute that contains the location of Flower's class definition. Here's the code that does the binding. String codsbare = 11 Create the object to be bound Flower f= new Flower(*rose™, "pink". 11 Perform the bind and specify the codebase cte.bind("en-Flower™, f, new BasicAttributes("javaCodebase", codebase)) When yourun this example, you must supply the URL of the location at which the class file Flower .class was installed. For example, if Flower .class was installed at the Web server web1, in the directory example/classes, then you would run this example as follows. 1 java SerOojMithCodebase nttp://webt/example/classes/ Afterward, you may remove Flower .class from your classpath and run any program that looks up or lists this object without directly referencing the Flower class. If your program references Flower directly, then you must make its class file available for compilation and execution. JavaSpaces is a new distributed object system being proposed by Sun as a package at a higher level than the existing RMI and object serialization facilities built into Java. JavaSpaces provides a distributed, persistent object system that is roughly modeled after earlier shared memory systems, such as LINDA. While it has some analogies with parallel shared memory systems such as the Posix shm_xxx library and shared memory facilities in parallel languages like Python, it also has some important differences. This appendix provides an overview of the general JavaSpace architecture as currently described in the draft JavaSpace specification. It doesn't go into detail about how to use the JavaSpace API, since the API isn't fully available yet. This appendix only includes the core elements of the specification, without discussing any proposed features that may or may not be in the final API. C.1. Overview of JavaSpaces The distributed application paradigm supported by JavaSpaces is one in which remote agents interact with each other indirectly through shared data object spaces. Objects are stored in a JavaSpace in the form of entries. Clients write entries into the space, read entries from the space, or take entries from the space, as shown in Figure C-1. “Bh. cB JavaSpace Serialized object entries . ( = read E Figure C-1. JavaSpaces general architecture Access to the entries in JavaSpaces is through a small set of basic operations read Read an entry from the space that matches a template. write Add an entry to the space. take Read and remove an entry from the space. notify Send a notification through a given event handler if entries that match a template are added to the space. A notification request has a time-out period associated with it: if a matching entry isn't added within the time-out period, the notify request fails and is dropped from the JavaSpace. Multiple basic operations can be assembled into transactions that group basic operations into a single, atomic aggregate operation. There can be many clients and many JavaSpaces in a given distributed application. One client, and even one transaction from one client, can access multiple JavaSpaces. So instead of one agent sending a message to another, or invoking a remote method directly on another object within an agent, agents interact by writing and reading objects in JavaSpaces. An important feature of the JavaSpaces specification is that all operations on a given JavaSpace are considered unordered. If you have multiple threads or multiple remote agents issuing operations on a JavaSpace, and for some reason you want to impose some order on the operations, then it's up to you to synchronize your threads or agents as needed. Each JavaSpace holds data in the form of entries, which can either be read, written, or taken from a JavaSpace. Each entry has one or more fields that are used to match incoming requests from clients. Each request to read, take, or be notified about an entry includes a template for the entry to match. In order for an entry in the JavaSpace to match, the entry must be of the same type as the template object. Each field in the template can either have a non-nul1 value, which must match the fields in a matching entry in the JavaSpace, or a null value, which matches any value in that field. All operations on JavaSpaces are "transactionally secure,” which means that each operation or transaction is either entirely committed or entirely noncommitted to a JavaSpace. So if a write to a JavaSpace succeeds, then you can be assured that the Entry was written and will appear in the next client read or take operation on the space. An operation on JavaSpaces can be either in the form of a simple operation, or a group of operations within a single Transaction. remote method directly on another object within an agent, agents interact by writing and reading objects in JavaSpaces. An important feature of the JavaSpaces specification is that all operations on a given JavaSpace are considered unordered. If you have multiple threads or multiple remote agents issuing operations on a JavaSpace, and for some reason you want to impose some order on the operations, then it's up to you to synchronize your threads or agents as needed. Each JavaSpace holds data in the form of entries, which can either be read, written, or taken from a JavaSpace. Each entry has one or more fields that are used to match incoming requests from clients. Each request to read, take, or be notified about an entry includes a template for the entry to match. In order for an entry in the JavaSpace to match, the entry must be of the same type as the template object. Each field in the template can either have a non-nu11 value, which must match the fields in a matching entry in the JavaSpace, or a null value, which matches any value in that field. 4 \ All operations on JavaSpaces are "transactionally secure,’ which means that each operation or transaction is either entirely committed or entirely noncommitted to a JavaSpace. So if a write to a JavaSpace succeeds, then you can be assured that the Entry was written and will appear in the next client read or take operation on the space. An operation on JavaSpaces can be either in the form of a simple operation, or a group of operations within a single Transaction. The authors of the JavaSpace specification make a point of distinguishing JavaSpaces from a distributed database system. A JavaSpace knows the type of its entries, and can compare field values, but it doesn't understand anything about the structure of the data in its entries. It also isn't meant to provide opaque read/write access to persistent data. An entry in a JavaSpace is a serialized copy of the object written to the space, and entries returned to clients as a result of read or take operations are separate copies of the objects in the space. JavaSpaces f Z C.2. Entry and EntryRep ‘ NX Ly a Every JavaSpace consists solely of entries, which are represented by instances of the Entry class. An entry is a group of object references, which represent the fields it the Entry. When an entry is added to a JavaSpace, the entry is stored in serialized form by independently serializing each field in the entry. Because of this, every field in an entry has to be public, has to be Serializable, and has to be an Object (not a primitive type). EntryRep s act as the conduit for Entrys into and out of JavaSpaces. They serialize Entrys before going into a JavaSpace during a write operation, and de-serialize Entrys returned as the result of read, take, or notify operations. A given EntryRep can be written multiple times to the same JavaSpace, which would result in multiple identical entries in the space. EntryReps are used to specify JavaSpace entries in read or take operations. A client creates an Entry with the values and wildcards that it wants to match in a JavaSpace. Then it wraps it in an EntryRep, which generates the serialized form of the template Entry and passes it to the JavaSpace as an argument of the operation. The JavaSpace compares the serialized bytes of the template Entry to its own Entrys, and matches on the first one whose serialized bytes are the same as those of the non-null fields in the template Entry. Another benefit of serializing each field of an Entry independently is that it allows fo fault-tolerant retrieval of entries from the space. If a read, take, or notify operation finds a match and an error occurs while deserializing it, an UnusableEntryException is thrown. The exception object contains a list of the fields from the entry that were successfully deserialized from the JavaSpace, along with a list of the unusable field: and a list of nested exceptions that explain why each unusable field failed to be deserialized. Some reasons for failed deserialization are missing class files on the client, or a RemoteException caused by a remote reference on the Entry that isn't valid any more. Your client can react in different ways to an UnusableEntryException it can try to use the partial entry that it received, it can ignore the partial entry and try to read or take another entry, or it can give up altogether. The authors of the JavaSpaces specification make a point of mentioning that, since at this time the Java API doesn't support persistent server objects, it's dangerous to put remote references into a JavaSpace as part of an Entry. If the server object behind the remote reference is destroyed for some reason (e.g., server restart, serve crash, etc.), then the remote reference becomes invalid; however, this won't be discovered until a client tries to get the entry from the JavaSpace. The authors suggest that you use metadata about the remote object, namely its remote host and registry name, in the Entry, and let the client establish its own remote reference to the server object. Ul O < C.3. Transactions Ly “L a A Transaction is a group of basic operations that act as an atomic operation on one or more JavaSpaces. A Transaction is atomic in the sense that all or none of the operations in the Transaction will be carried out. If any of the operations within the Transaction fail (e.g., a read fails to match an entry, or a notify times out before it is triggered), then the entire Transaction fails and any sub-operations that had succeeded are "rolled back," and the JavaSpace is left in the same state it would have been in if the Transact ion had never been attempted.

You might also like