220 likes | 290 Views
Chapter 53. How to Activate Remote Computations. The reasons why you might want your computer, a client, to run a method on another computer, a server, include the following: The method requires a great deal of computation, and the server is a faster computer.
E N D
Chapter 53 How to Activate Remote Computations
The reasons why you might want your computer, a client, to run a method on another computer, a server, include the following: • The method requires a great deal of computation, and the server is a faster computer. • The method requires a great deal of computation, and you have managed to break up the computation into multiple pieces, which you can ship off to multiple server computers. • The method is maintained by someone else; rather than enduring reinstallations, you simply run the method remotely, on the server, where the method is maintained. • The method makes use of information that is available on a server, but is not available on your computer.
To understand how remote method invocation works, you first imagine that there is a class, the RatingServer class, which defines a magical method for computing movie ratings, superior to all other known methods. Suspending disbelief, you learn that the magical method merely finds the best of the individual ratings; evidently, the method is for positive-attitude movie watchers. public class RatingServer { public int serverRating (Movie m) { System.out.println("RatingServer asked for a rating"); int s = m.getScript(); int a = m.getActing(); int d = m.getDirection(); return 3 * Math.max(Math.max(s, a), d); } }
With the RatingServer class defined, if only you had access to an instance of that class, and did not mind running serverRating on your own computer, you could substitute serverRating wherever your program would otherwise use the rating method defined in the Movie class. • For example, the observer uses the rating method, so you could make the following substitution, with serverRating replacing rating. // Replaced // row.add(new Integer(movie.rating())); // Substituted in row.add(new Integer((new RatingServer()).serverRating(movie)));
Of course, while you are testing the RatingServer class, you probably want to avoid the complexity of a complete application, so you might define just a stub, such as ClientStub: import java.rmi.*; import java.math.*; public class ClientStub { public static void main(String args[]) { // Construct a movie for testing Movie movie = new Movie(2, 3, 8, "Psycho"); // Construct a rating server for testing RatingServer ratingServer = new RatingServer(); // Test and print int rating = ratingServer.serverRating(movie); System.out.println("The server returned a rating of " + rating); } } --- Result --- RatingServer asked for a rating The server returned a rating of 24
Now, suppose that you want to move the RatingServer instance to another computer—a server. You need answers to several questions: • How do you inform the server that a particular class instance is to be made available remotely? • How does the client find an instance that has been made available remotely? • How much do the client and server computers need to know about each other's classes at compile time? How do you supply that information? • How much do the client and server computers need to know about each other's classes at run time? How is that information exchanged? • How do you supply to the client and server information about each other's classes at compile time and at run time?
At the time that the RatingServer class is compiled, on the server computer, all that the compiler needs to know about the Movie class is that there are various methods with various signatures, and obtaining that knowledge requires access to only a modified version of the MovieInterface. • The modification specifies that implementers, such as the Movie class, are serializable. The reason for introducing serializability is that the server needs access to the actual compiled definitions of Movie methods, at run time, rather than to just definition signatures, because the RatingServer method, serverRating, calls Movie methods. To enable that access, the compiled Movie class is shipped off to the server, from the client, at run time, in serialized form, and we say that the client and server, at this point, are tightly coupled.
import java.io.*; public interface MovieInterface extends Serializable { // Setters public abstract void setScript (int i) ; public abstract void setActing (int i) ; public abstract void setDirection (int i) ; // Getters public abstract int getScript () ; public abstract int getActing () ; public abstract int getDirection () ; public abstract String getTitle () ; public abstract String getPoster () ; // Miscellaneous methods public abstract int rating () ; public abstract void changed () ; }
Similarly, at the time that the MovieDataObserverForTable or ClientStub class are compiled, on the client computer, all the compiler needs to know about the RatingServer class is that there is a serverRating method, with a particular signature, which is provided adequately by an interface, RatingServerInterface. • Note that the RatingServerInterface not only specifies the serverRating method, but also extends the Remote interface, which informs the compiler that not only must implementing classes implement the specified method, but also that the method is to be called by a client and run on a server. import java.io.*; import java.rmi.*; public interface RatingServerInterface extends Remote { public abstract int serverRating(MovieInterface m) throws RemoteException ; }
The serverRating method defined previously throws a RemoteException, recognizing that a remote method invocation can produce errors that occur because a network is involved, and that the invocation is, in fact, remote.
With RatingServerInterface and MovieInterface defined, you can make the following changes in the evolving RatingServer and ClientStub classes. Note that both a parameter and a variable are typed by interface names, rather than by class names:
import java.rmi.*; public class RatingServer implements RatingServerInterface { public int serverRating (MovieInterface m) throws RemoteException { System.out.println("RatingServer asked for a rating"); int s = m.getScript(); int a = m.getActing(); int d = m.getDirection(); return 3 * Math.max(Math.max(s, a), d); } }
import java.rmi.*; import java.math.*; public class ClientStub { public static void main(String args[]) throws RemoteException { // Construct a movie for testing Movie movie = new Movie(2, 3, 8, "Psycho"); // Construct a rating server for testing RatingServerInterface ratingServer = new RatingServer(); // Test and print int rating = ratingServer.serverRating(movie); System.out.println("The server returned " + rating); } }
You need to do more work to prepare the RatingServer for remote method invocation. For example, RatingServer must extend UnicastRemoteObject, rather than extending Object, because the constructors and methods of UnicastRemoteObject perform all sorts of wizardry that enable remote activation. RatingServer must also implement the Serializable interface. • Also, you now must define a zero-argument constructor that calls the zero-argument UnicastRemoteObject constructor and throws the RemoteException exception. • The definition is required because the zero-argument constructor in UnicastRemoteObject happens to throw the RemoteException exception.
import java.rmi.*; import java.rmi.server.*; public class RatingServer extends UnicastRemoteObject implements RatingServerInterface, Serializable { public RatingServer () throws RemoteException { super(); } public int serverRating (MovieInterface m) throws RemoteException { System.out.println("RatingServer asked for a rating"); int s = m.getScript(); int a = m.getActing(); int d = m.getDirection(); return 3 * Math.max(Math.max(s, a), d); } }
Clients and servers communicate with each other via a registry running on the server computer. • The registry needs to be informed about instance that are to be accessed by clients. Thus, to create a RatingServer instance that is ready to receive method calls from a client computer, you must establish a connection with the server computer's running registry program. • The connection is done via a class method, rebind, of the Naming class, which connects a name with a remotely accessible instance. The rebind method takes two arguments: one is a host identifier, combined with a name of your choice. The second is the remotely accessible instance. • The host identifier is a specification, such as whitney.ai.mit.edu, that specifies the computer on which the registry runs. Typically, this computer is the same one on which the server runs, in which case the host identifier is localhost. The client uses a name of your choice—such as ratingService—to tell the registry what the client seeks. • Because both rebind and the RatingServer constructor throw exceptions, both the rebind call and the RatingServer construction must appear in a try–catch combination.
import java.rmi.*; import java.rmi.server.*; public class RatingServer extends UnicastRemoteObject implements RatingServerInterface { public RatingServer () throws RemoteException { super(); } public int serverRating (MovieInterface m) throws RemoteException { System.out.println("RatingServer asked for a rating"); int s = m.getScript(); int a = m.getActing(); int d = m.getDirection(); return 3 * Math.max(Math.max(s, a), d); } public static void main(String[] args) { try { Naming.rebind("//localhost/ratingService", new RatingServer()); System.out.println("Rating server connected to server"); } catch (Exception e) { System.err.println("RatingServer exception: " + e.getMessage()); e.printStackTrace(); } } }
You compile the server program with the Java compiler, javac: javac RatingServer.java • In addition, once you have compiled the server program, you must perform a second compilation step, using rmic, the remote method invocation compiler.rmic RatingServer
Now that you have completed the definition of the server-side RatingServer class, you move back to the client side to modify the ClientStub class to access a remote—rather than local—instance of the RatingServer class. • Another class method, lookup, of the Naming class provides the required access using a host identifier, such as whitney.ai.mit.edu, and the name with which you have chosen to identify the remote instance. • As you did on the server side, you need a try–catch combination to deal with thrown exceptions.
import java.rmi.*; import java.math.*; public class ClientStub { public static void main(String args[]) { // Construct a movie for testing Movie movie = new Movie(2, 3, 8, "Psycho"); // Construct a rating server for testing try { String computer = "whitney.ai.mit.edu"; System.out.println("The client is asking server, " + computer + ", for a rating"); RatingServerInterface ratingServer = (RatingServerInterface) (Naming.lookup("//" + computer +"/ratingService")); int rating = ratingServer.serverRating(movie); System.out.println("The server, " + computer + ", returned " + rating); } catch (Exception e) { System.err.println("Rating client exception: " + e.getMessage()); e.printStackTrace(); } } }
Now you have server defined, compiled with javac and rmic, and the client defined, compiled with javac; you are ready to test both. • First, on the server computer you open a window in which you start the registry. If you use the code-testing registry program supplied by Sun Microsystems, Inc., then you use the rmiregistry command: rmiregistry • Next, on the server computer, you start the server program in another window: java RatingServer Rating server connected to server • Then, with the server started, you can start the client stub in a window on the client computer: java ClientStub The client is asking server, whitney.ai.mit.edu, for a rating The server, whitney.ai.mit.edu, returned 24 • Plainly, the server has returned a result. Returning to the server window, you see that the client has come calling. java RatingServer Rating server connected to server Rating server asked for rating
To run the client and server on the same computer, for testing, you need only to replace a full internet address with localhost in the client stub: ... // Replaced // String computer = "whitney.ai.mit.edu"; String computer = "localhost"; ... • Everything on the server side is unchanged.