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 appli- cations.
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 com- munication 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 com- munication 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 ob- ject 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 inter- operability requirement with many languages. For instance, CORBA [4] presumes a hetero- geneus, 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
or operating system platform. This is achieved by compiling code to an intermediate repre- sentation 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 inter- faces 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 Ma- chines are called remote objects.
An object becomes remote by implementing a remote interface which has the following charac- teristics:
• 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
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 invoca- tion. 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 con- nection 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 in- voked remotely by a client. Clients program to remote interfaces, not to the implementa- tion 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 transforma- tion 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
longer necessary.
Making classes network accessible simply means that client and server must be able to com- municate 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 re- motely. 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 . R e m o t e E x c e p t i o n ; i m p o r t j a v a . u t i l . A r r a y L i s t ;
p u b l i c i n t e r f a c e 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 R e m o t e E x c e p t i o n ; Double a v e r a g e ( ) t h r o w s R e m o t e E x c e p t i o n ;
Double v a r i a n c e ( ) t h r o w s R e m o t e E x c e p t i o n ;
Double s t a n d a r d D e v i a t i o n ( ) t h r o w s R e m o t e E x c e p t i o n ; A r r a y L i s t <Double> g e t L i s t ( ) t h r o w s R e m o t e E x c e p t i o n ; }
The following is the implementation of the remote interface, called CalculatorImpl :
i m p o r t j a v a . rmi . R e m o t e E x c e p t i o n ;
i m p o r t j a v a . 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 ; i m p o r t j a v a . 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 ; i m p o r t j a v a . u t i l . A r r a y L i s t ;
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 e x t e n d s U n i c a s t R e m o t e O b j e c t i m p l e m e n t s 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 R e m o t e E x c e p t i o n { }
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 R e m o t e E x c e p t i o n { t h i s . l i s t = l i s t ;
t r y {
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 " + U n i c a s t R e m o t e O b j e c t . g e t C l i e n t H o s t ( ) ) ;
} c a t c h ( S e r v e r N o t A c t i v e E x c e p t i o n 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 R e m o t e E x c e p t i o n { 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 ;
} t r y {
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 " + U n i c a s t R e m o t e O b j e c t . g e t C l i e n t H o s t ( ) ) ;
} c a t c h ( S e r v e r N o t A c t i v e E x c e p t i o n 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 R e m o t e E x c e p t i o n { 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 ( ) ;
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 ) ; }
v a r = v a r / ( l i s t . s i z e ( ) − 1 ) ; t r y {
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 " + U n i c a s t R e m o t e O b j e c t . g e t C l i e n t H o s t ( ) ) ;
} c a t c h ( S e r v e r N o t A c t i v e E x c e p t i o n 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 v a r ; }
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 R e m o t e E x c e p t i o n { Double v a r = t h i s . v a r i a n c e ( ) ;
t r y {
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 " + U n i c a s t R e m o t e O b j e c t . g e t C l i e n t H o s t ( ) ) ;
} c a t c h ( S e r v e r N o t A c t i v e E x c e p t i o n 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 ( ) { t r y {
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 " + U n i c a s t R e m o t e O b j e c t . g e t C l i e n t H o s t ( ) ) ;
} c a t c h ( S e r v e r N o t A c t i v e E x c e p t i o n 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 t h i s . 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 ; p u b l i c c l a s s S e r v e r {
p r i v a t e v o i d s t a r t S e r v e r ( S t r i n g h o s t , i n t p o r t ) { t r y {
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 "
R e g i s t r y r e g i s t r y = L o c a t e R e g i s t r y . c r e a t e R e g i s t r y ( p o r t ) ; // 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 ( ) ) ; } c a t c h ( E x c e p t i o n 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 ( ) ;
s e r v e r . s t a r t S e r v e r ( a r g s [ 0 ] , I n t e g e r . p a r s e I n t ( a r g s [ 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:
i m p o r t j a v a . i o . B u f f e r e d R e a d e r ; i m p o r t j a v a . i o . I O E x c e p t i o n ; i m p o r t j a v a . i o . I n p u t S t r e a m R e a d e r ; 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 ; i m p o r t j a v a . u t i l . A r r a y L i s t ; p u b l i c c l a s s C l i e n t {
p r i v a t e v o i d s t a r t C l i e n t ( S t r i n g h o s t , i n t p o r t ) { t r y {
// f i r e t o h o s t : p o r t
R e g i s t r y m y R e g i s t r y = 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 ( h o s t , p o r t ) ; // 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 i m p l = ( C a l c u l a t o r ) m y R e g i s t r y . l o o k u p ( " c a l c u l a t o r " ) ; b o o l e a n e x i t = f a l s e ;
// show menu w h i l e ( ! e x i t ) {
System . o u t . p r i n t l n ( " Comandi d i s p o n i b i l i : " ) ; System . o u t . p r i n t l n ( " 1 − I n s e r i s c i v a l o r i " ) ;
System . o u t . 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 " ) ; System . o u t . p r i n t l n ( " 3 − C a c o l a v a r i a n z a " ) ;
System . o u t . p r i n t l n ( " 4 − C a c o l a d e v i a z i o n e s t a n d a r d " ) ; System . o u t . p r i n t l n ( " 5 − Stampa v a l o r i i n s e r i t i " ) ; System . o u t . p r i n t l n ( " 0 − E s c i " ) ;
System . o u t . 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 ; t r y {
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 ) ; } c a t c h ( I O E x c e p t i o n 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 ( ) ) ;
}
s w i t c h ( command ) { //command 1 c a s e 1 : {
b o o l e a n s t o p = 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 ( ) ; w h i l e ( ! s t o p ) {
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 " + "
p e r t e r m i n a r e l ’ i n s e r i m e n t o : " ) ; S t r i n g v a l u e ;
t r y {
v a l u e = i n p u t . r e a d L i n e ( ) ; i f ( "E " . e q u a l s ( v a l u e ) )
s t o p=t r u e ; e l s e
l i s t . add ( Double . p a r s e D o u b l e ( v a l u e ) ) ; } c a t c h ( I O E x c e p t i o n 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 ( ) ) ;
} }
i m p l . i n p u t ( l i s t ) ; System . o u t . p r i n t l n ( " " ) ; b r e a k ;
}
//command 2 c a s e 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 ( " " ) ;
b r e a k ; }
//command 3 c a s e 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 ( " " ) ;
b r e a k ; }
//command 4 c a s e 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 : " + i m p l . s t a n d a r d D e v i a t i o n ( ) ) ;
System . o u t . p r i n t l n ( " " ) ; b r e a k ;
}
//command 5 c a s e 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 ( " " + e l e m e n t ) ;
}
System . o u t . p r i n t l n ( " " ) ; System . o u t . p r i n t l n ( " " ) ; b r e a k ;
}
//command 0 c a s e 0 : {
e x i t = t r u e ; b r e a k ; }
} }
} c a t c h ( E x c e p t i o n 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 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 . s t a r t C l i e n t ( a r g s [ 0 ] , I n t e g e r . p a r s e I n t ( a r g s [ 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 net- work 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
1Localhost is useful for programmers to test their software on their own machines.
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 Spec- ification. OMG Document Number 91.12.1, 1991.
[5] A. Wollrath, R. Riggs, J. Waldo. A Distributed Object Model for the Java System. Pro- ceedings of the 2nd conference on USENIX Conference on Object-Oriented Technologies, 1996.