Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Report of the case study in Sistemi Distribuiti A simple Java RMI application − Academic year 2012/13 − Vessio Gennaro Marzulli Giovanni Abstract In the ambit of distributed systems a key-role is played by the communication among cooperating processes. The Java remote method invocation (RMI) system represent an evolution of the classical remote procedure call (RPC) mechanism – riformulated in the terms of the object-oriented paradigm – which enables programmers to create distributed Java technology-based applications. The advantages of using this approach reside in its simplicity and the possibility to run applications on different platforms. The aim of this work is to implement a simple Java RMI application in order to show its internal behaviour and its well-known effectiveness. 1 Introduction Distributed systems require entities which reside in different address spaces, potentially on different machines, to communicate. Commonly, programming languages provide a basic communication mechanism: sockets [3]. While flexible and sufficient for general communication, the use of sockets requires the design of protocols which is cumbersome and can be error-prone. An alternative to sockets is remote procedure call (RPC). RPC [3] systems abstract the communication interface to the level of a procedure call. Thus, instead of having to deal directly with sockets, programmers have the illusion of calling a local procedure when, in practice, the arguments of the call are packaged up and shipped off to the remote target of the call. However, RPC was not designed for the object-oriented programming and, consequently, does not translate well into distributed object systems where communication between program-level objects residing in different address spaces is needed. In order to match the semantics of object invocation, distributed object systems require remote method invocation or RMI. In such systems, programmers have the illusion of invoking a method of an object, when in fact the invocation may act on a remote object (one not resident in the caller’s address space). Various RMI systems exist but they fall short of seamless integration due to their interoperability requirement with many languages. For instance, CORBA [4] presumes a heterogeneus, multi-language environment and thus must have a language neutral object model. In contrast, the Java RMI system [2, 5] assumes the homogeneus environment of the Java Virtual Machine (JVM), and the system can therefore follow the Java object model whenever possible. In order to show its internal behaviour and its well-known effectiveness, the aim of this work is to implement a simple Java RMI application in which a client sends to a server an array-list of real values and the server returns its mean, variance and standard deviation. 2 Java object model Java [1] is a strongly-typed object-oriented language with a C-like syntax. The main characteristic of Java is portability, i.e. the property of being platform-independent, which means that programs written in the Java language must run similarly on any hardware 1 or operating system platform. This is achieved by compiling code to an intermediate representation called bytecode, instead of directly to specific machine code. Bytecode instructions are analogous to machine code, but they are intended to be interpreted by a virtual machine written specifically for the host hardware. Clearly, portability is a highly desirable quality in a distributed system scenario because it allows to free programmers from worrying about the underlying architecture of the different machines cooperating in a distributed computation. Another interesting feature of Java is its separation of the notion of interface and class. An interface in Java describes a set of methods for an object, but provides no implementation. A class, on the other hand, can describe as well as implement methods. A class may also include fields to hold data, but interfaces cannot. Furthermore, a class may implement any number of interfaces. It is important to understand that there is no other “legal” way in which a process can have access to the data of an object which instantiates a class except by invoking the public methods of its interfaces. This separation is crucial in distributed systems because it allows programmers to place interfaces on one machine, i.e. on the client, and the real object on another, i.e. on the server. 3 Java RMI architecture RMI applications often comprise two separate programs, a server and a client. A typical server program creates some remote objects, makes references to these objects accessible and waits for clients to invoke methods on these objects. A typical client program obtains a remote reference to one or more remote objects on a server and then invokes methods on them. Distributed object applications need to do the following: • locate remote objects: applications can use various mechanisms to obtain references to remote objects. For example, an application can register its remote objects with RMI’s simple naming facility, the RMI registry. Alternatively, an application can pass and return remote object references as part of the remote invocation (in this case study it was used this second approach, as we will see in §5); • communicate with remote objects: details of the communications are handled by RMI. To programmers remote communication looks similar to regular Java method invocations; • load class definitions for objects that are passed around: because RMI enables objects to be passed back and forth, it provides mechanisms, called dynamic code loading, for loading an object’s class definition as well as for transmitting an object’s data. Like any other Java application, a distributed application built by using Java RMI is made up of interfaces and classes. The interface declare methods. The classes implement the methods declared in the interfaces. Objects with methods that can be invoked across Java Virtual Machines are called remote objects. An object becomes remote by implementing a remote interface which has the following characteristics: • it extends the interface java .rmi.Remote; • each method of the interface declares java .rmi.RemoteException in its throws clause, in addition to any application-specific exceptions. The RMI system consists of three basic layers (as shown in Figure 1): • stub/skeleton; • remote reference layer; • transport. Rather than making a copy of the implementation object in the receiving JVM, RMI passes a remote stub for a remote object. The stub acts as the local representative, or proxy, for the remote object and basically is, to the client, the remote reference. Similarly, the skeleton is the server-side counterpart of the stub. The stub/skeleton layer does not deal with specifics of 2 Figure 1: Java RMI architecture any transport, but transmits data to the remote reference layer via the abstraction of marshall streams. Marshall streams employ a mechanism which enables Java objects to be transmitted between address spaces. The remote reference layer is responsible for carrying out the semantics of the type of invocation. For example, this layer is responsible for handling unicast or multicast invocation to a server. Each remote object implementation chooses its own invocation semantics − whether communication to the server is unicast, or the server is part of a multicast group (to accomplish server replication). Finally, the transport layer is responsible for connection set-up with remote locations and connection management. A transport defines what the concrete representation of an end-point is, so multiple transports may exist. The design and implementation also allow multiple transports per address space (so both TCP and UDP can be supported in the same address space). Thus, client and server transports can negotiate to find a common transport between them. 4 Creating distributed applications by using RMI Using RMI to develop a distributed application involves these general steps: • designing and implementing the components of the application; • compiling sources; • making classes network accessible; • starting the application. The first step includes: • defining the remote interfaces: a remote interface specifies the methods that can be invoked remotely by a client. Clients program to remote interfaces, not to the implementation classes of those interfaces. The design of such interfaces includes the determination of the types of objects that will be used as the parameters and return values for these methods. Note that, in contrast to primitive types, it is possible to pass objects with arbitrary structure and complexity, provided that these objects are serializable, i.e. they implement the interface Serializable. Serialization consists in the automatic transformation of objects and structures into sequences of byte manipulated with various streams of the java.io package. • implementing the remote objects; • implementing the client. As with any Java program, it can be used the javac compiler to compile the source files. The source files contain the declarations of the remote interfaces, their implementations, any other server classes and the client classes. Note that with versions prior to Java 5.0, an additional step was required to build stub classes, by using the rmic compiler. However, this step is no 3 longer necessary. Making classes network accessible simply means that client and server must be able to communicate through a network connection and thus they must at least belong to a local ad hoc network. Finally, starting the application includes running the server and the client. 5 Implementation details The application is implemented in Java 6.0 (the source code is attached to this document). It consists of one remote interface, its implementation, a server class and a client class. The remote interface, called Calculator, simply defines the methods that can be invoked remotely. Note that this interface must reside also on the client because it has to know the syntax to invoke the remote methods. i m p o r t j a v a . rmi . Remote ; i m p o r t j a v a . rmi . RemoteException ; import java . u t i l . ArrayList ; public interface C a l c u l a t o r e x t e n d s Remote { v o i d i n p u t ( A r r a y L i s t <Double> l i s t ) t h r o w s RemoteException ; Double a v e r a g e ( ) t h r o w s RemoteException ; Double v a r i a n c e ( ) t h r o w s RemoteException ; Double s t a n d a r d D e v i a t i o n ( ) t h r o w s RemoteException ; A r r a y L i s t <Double> g e t L i s t ( ) t h r o w s RemoteException ; } The following is the implementation of the remote interface, called CalculatorImpl : import import import import java java java java . rmi . RemoteException ; . rmi . s e r v e r . S e r v e r N o t A c t i v e E x c e p t i o n ; . rmi . s e r v e r . U n i c a s t R e m o t e O b j e c t ; . u t i l . ArrayList ; p u b l i c c l a s s C a l c u l a t o r I m p l extends UnicastRemoteObject implements C a l c u l a t o r { p r i v a t e A r r a y L i s t <Double> l i s t ; p u b l i c C a l c u l a t o r I m p l ( ) t h r o w s RemoteException { } p u b l i c v o i d i n p u t ( A r r a y L i s t <Double> l i s t ) t h r o w s RemoteException { this . l i s t = l i s t ; try { System . o u t . p r i n t l n ( " R i c e v u t a l i s t a d i v a l o r i da " + UnicastRemoteObject . g e t C l i e n t H o s t ( ) ) ; } catch ( ServerNotActiveException e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . g e t M e s s a g e ( ) ) ; } } p u b l i c Double a v e r a g e ( ) t h r o w s RemoteException { Double sum = 0 . 0 ; f o r ( Double e l e m e n t : l i s t ) { sum += e l e m e n t ; } try { System . o u t . p r i n t l n ( " R i c h i e s t a d i c a l c o l o d e l l a media da " + UnicastRemoteObject . g e t C l i e n t H o s t ( ) ) ; } catch ( ServerNotActiveException e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . g e t M e s s a g e ( ) ) ; } r e t u r n sum/ l i s t . s i z e ( ) ; } p u b l i c Double v a r i a n c e ( ) t h r o w s RemoteException { Double sum = 0 . 0 ; f o r ( Double e l e m e n t : l i s t ) { sum += e l e m e n t ; } Double av = sum/ l i s t . s i z e ( ) ; 4 Double v a r = 0 . 0 ; f o r ( Double e l e m e n t : l i s t ) { v a r += Math . pow ( ( e l e m e n t − av ) , 2 ) ; } var = var /( l i s t . s i z e () −1); try { System . o u t . p r i n t l n ( " R i c h i e s t a d i v a r i a n z a da " + UnicastRemoteObject . g e t C l i e n t H o s t ( ) ) ; } catch ( ServerNotActiveException e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . g e t M e s s a g e ( ) ) ; } r e t u r n var ; } p u b l i c Double s t a n d a r d D e v i a t i o n ( ) t h r o w s RemoteException { Double v a r = t h i s . v a r i a n c e ( ) ; try { System . o u t . p r i n t l n ( " R i c h i e s t a d i d e v i a z i o n e s t a n d a r d da " + UnicastRemoteObject . g e t C l i e n t H o s t ( ) ) ; } catch ( ServerNotActiveException e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . g e t M e s s a g e ( ) ) ; } r e t u r n Math . s q r t ( v a r ) ; } p u b l i c A r r a y L i s t <Double> g e t L i s t ( ) { try { System . o u t . p r i n t l n ( " I n v i o l i s t a d i v a l o r i a " + UnicastRemoteObject . g e t C l i e n t H o s t ( ) ) ; } catch ( ServerNotActiveException e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . g e t M e s s a g e ( ) ) ; } return this . l i s t ; } } This is the Server class. Note that it will be running on port 1099: i m p o r t j a v a . rmi . r e g i s t r y . L o c a t e R e g i s t r y ; i m p o r t j a v a . rmi . r e g i s t r y . R e g i s t r y ; public class Server { p r i v a t e void s t a r t S e r v e r ( S t r i n g host , try { i n t port ){ System . s e t P r o p e r t y ( " j a v a . rmi . s e r v e r . hostname " , h o s t ) ; // c r e a t e on p o r t " p o r t " Registry r e g i s t r y = LocateRegistry . createRegistry ( port ) ; // c r e a t e a new s e r v i c e named c a l c u l a t o r r e g i s t r y . r e b i n d ( " c a l c u l a t o r " , new C a l c u l a t o r I m p l ( ) ) ; } catch ( Exception e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . g e t M e s s a g e ( ) ) ; } System . o u t . p r i n t l n ( " S e r v e r r e a d y " ) ; } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { S e r v e r s e r v e r = new S e r v e r ( ) ; server . startServer ( args [ 0 ] , Integer . parseInt ( args [ 1 ] ) ) ; } } Finally, this is the Client class. Note that it shows to the user a simple menu in which he can select the next action to execute server-side: import import import import import import java java java java java java . io . BufferedReader ; . i o . IOException ; . i o . InputStreamReader ; . rmi . r e g i s t r y . L o c a t e R e g i s t r y ; . rmi . r e g i s t r y . R e g i s t r y ; . u t i l . ArrayList ; public class Client { p r i v a t e void s t a r t C l i e n t ( S t r i n g host , try { i n t port ){ 5 // f i r e t o h o s t : p o r t R e g i s t r y myRegistry = L o c a t e R e g i s t r y . g e t R e g i s t r y ( host , port ) ; // s e a r c h f o r c a l c u l a t o r s e r v i c e C a l c u l a t o r impl = ( C a l c u l a t o r ) myRegistry . lookup (" c a l c u l a t o r " ) ; boolean e x i t = f a l s e ; // show menu while ( ! exit ) { System . o u t . System . o u t . System . o u t . System . o u t . System . o u t . System . o u t . System . o u t . System . o u t . p r i n t l n ( " Comandi d i s p o n i b i l i : " ) ; p r i n t l n ("1 − I n s e r i s c i v a l o r i " ) ; p r i n t l n ( " 2 − C a c o l a media a r i t m e n t i c a " ) ; p r i n t l n ("3 − Cacola v a r i a n z a " ) ; p r i n t l n ("4 − Cacola d e v i a z i o n e standard " ) ; p r i n t l n ( " 5 − Stampa v a l o r i i n s e r i t i " ) ; p r i n t l n ("0 − Esci " ) ; p r i n t ( " I n s e r i s c i comando : " ) ; I n p u t S t r e a m R e a d e r r e a d e r = new I n p u t S t r e a m R e a d e r ( System . i n ) ; B u f f e r e d R e a d e r i n p u t = new B u f f e r e d R e a d e r ( r e a d e r ) ; S t r i n g comandInput ; i n t command = 0 ; try { comandInput = i n p u t . r e a d L i n e ( ) ; command = I n t e g e r . p a r s e I n t ( comandInput ) ; } catch ( IOException e ){ System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . getMessage ( ) ) ; } s w i t c h ( command ) { //command 1 case 1:{ boolean stop = f a l s e ; A r r a y L i s t <Double> l i s t = new A r r a y L i s t ( ) ; while ( ! stop ) { System . o u t . p r i n t ( " I n s e r i s c i v a l o r e , E " + " per terminare l ’ inserimento : " ) ; String value ; try { value = input . readLine ( ) ; i f ( "E " . e q u a l s ( v a l u e ) ) s t o p=t r u e ; else l i s t . add ( Double . p a r s e D o u b l e ( v a l u e ) ) ; } catch ( IOException e ) { System . o u t . p r i n t l n ( " S i e ’ v e r i f i c a t o un e r r o r e : " + e . getMessage ( ) ) ; } } impl . input ( l i s t ) ; System . o u t . p r i n t l n ( " " ) ; break ; } //command 2 case 2: { System . o u t . p r i n t l n ( " Media a r t i m e t i c a : " + i m p l . a v e r a g e ( ) ) ; System . o u t . p r i n t l n ( " " ) ; break ; } //command 3 case 3: { System . o u t . p r i n t l n ( " V a r i a n z a : " + i m p l . v a r i a n c e ( ) ) ; System . o u t . p r i n t l n ( " " ) ; break ; } //command 4 case 4: { System . o u t . p r i n t l n ( " D e v i a z i o n e s t a n d a r d : " + impl . s t a nd a r dD e v ia t i o n ( ) ) ; System . o u t . p r i n t l n ( " " ) ; break ; } //command 5 case 5: { A r r a y L i s t <Double> l i s t S e n t = i m p l . g e t L i s t ( ) ; System . o u t . p r i n t l n ( " V a l o r i i n s e r i t i : " ) ; f o r ( Double e l e m e n t : l i s t S e n t ) { System . o u t . p r i n t ( " " + element ) ; 6 } System . o u t . p r i n t l n ( " " ) ; System . o u t . p r i n t l n ( " " ) ; break ; } //command 0 case 0: { exit = true ; break ; } } } } catch ( Exception e ) { System . o u t . p r i n t l n ( " S i e ’ e . getMessage ( ) ) ; } v e r i f i c a t o un e r r o r e : " + } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { C l i e n t c l i e n t = new C l i e n t ( ) ; c l i e n t . startClient ( args [ 0 ] , Integer . parseInt ( args [ 1 ] ) ) ; } } 6 Numerical results The following are screenshots of what happens by running the application on localhost. Remember that localhost1 is the standard hostname given to the address of the loopback network interface and that it is translated to an IPv4 address in the 127.0.0.1 net block. Figure 2: Starting server Figure 3: Example of running a client 7 Conclusions Java RMI leverages the basic assumptions of platform independence, which is very important in the area of distributed systems where machines are typically different. We can assume 1 Localhost is useful for programmers to test their software on their own machines. 7 independence due to the architecture neutrality that the Java Virtual Machine provides. In this case study we have seen a small application that demonstrates how the mechanism is actually simple and effective and how it allows to develop much more complex applications with equal ease. References [1] Oracle. Oracle’s Developer Resources for Java Technology. http://www.oracle.com/ technetwork/java/index.html. [2] Oracle. The Java RMI tutorial. http://docs.oracle.com/javase/tutorial/rmi/index. html. [3] A. Tanenbaum, M. Van Steen. Distributed Systems. Pearson-Prentice Hall, 2nd edition, 2007. [4] The Object Management Group. Common Object Request Broker: Architecture and Specification. OMG Document Number 91.12.1, 1991. [5] A. Wollrath, R. Riggs, J. Waldo. A Distributed Object Model for the Java System. Proceedings of the 2nd conference on USENIX Conference on Object-Oriented Technologies, 1996. 8