Advanced Java Programming Unit Three: Remote Objects
Download
Report
Transcript Advanced Java Programming Unit Three: Remote Objects
Remote Objects
From notes originally prepared by
Gareth Lee
Department of Electrical and Electronic Engineering,
University of Western Australia
Overview
• First - look at remote loading of software
components
• Then - adapt the example to use RMI
• Finally - the RemoteWorker system
• Using a network of PEs for solving problems
• Distributes a task from a master to a set of
workers (slaves)
• Programmers write a Solver class
• Actually solves the problem (or sub-problem)
• System handles distribution of these classes
• Slaves can be started on remote machines
and left running
Network becomes a powerful computing
resource!
Example Application - Image viewer
• Requirements
• Able to display a wide range of image formats
• Imaging a picture library
• Extensible
• Support new image formats without
redistributing the entire application
• New image decoders prepared by the IT
department (or external suppliers)
• Allow bug fixes to the existing code without
updating every desk machine
Software components
• Allows designers to build applications
from substitutable (reusable) components
• Components can be loaded as they are
needed, not when the application is started
•
•
•
•
Minimizes memory footprint at any time
Maximizes versatility of application
Makes an application extensible
Minimises development cost (by allowing
reuse)
Remote Class loading
• Remote Class Loading is a half way house
• Deploy a simple shell application onto
clients’ desks
• This shell loads software components on
demand from a shared server
• Users never need know that this is going
on behind the scenes
• Demonstrated by the picture viewer
example
Implementing components
• We need a way of abstracting Java classes
• We cannot mention a component class
directly otherwise it will be loaded at the
outset
• An indirect way of creating components
• Components must implement agreed interface
• We must be able to create a component without
hard-coding its name in the source
• Here we create a specific family of
software component
• Java generalizes components as JavaBeans
The ImageCreator interface
• We need to create a family of software
components that know how to decode
various image file formats
• ImageCreator is the standard interface
for accessing components. . .
• . . . so each of the components must
implement the interface
The ImageCreator interface
/**
This provides an interface for components which are
able to create image objects from files.
@author Gareth Lee
@version 0.1, March 11th, 2000.
*/
public interface ImageCreator {
/**
Read the contents of the specified source file
and create an Image object containing the file
contents
*/
public Image createImage(File source);
}
Abstracting Java classes
• Java provides us with a pool of objects of
type java.lang.Class
• These abstract the data types that Java
supports
• Class types as found in the standard libraries
(API classes)
• User defined classes
• Software components written by users
• Built in primitive types
• int, boolean, etc
Remote Class Loading
? Deploy applications using applets
• Overcomes distributions costs for bug
fixes and upgrades
• Downside:
• Most browsers don’t trust applets and impose
severe restrictions
• Severe UI restrictions when running applets
• Difficult to deploy groups of applets which are
able to cooperate (for example a word
processor and a spell checker)
java.lang.Class
• Allows types (classes) to be accessed by name
• Strings giving the fully qualified name
• Class name must be prefixed by the package in
which it is loaded
• eg java.lang.Integer
• Objects may be created (instantiated) from the
instances of the Class
• Zero argument construction is trivial
• Constructors with arguments a little trickier
• java.lang.Class API
• Has methods which allow properties of the
class type to be obtained
• Constructors, fields, methods, etc
java.lang.Class
• Constructing (loading) a class
• Class forName(String qualifiedName)
eg
• Class c = Class.forName( “RemoteWorker” );
• Reflection API methods
•
•
•
•
•
Object newInstance() // construct obj
Constructor[] getConstructors()
Method[] getMethods()
Field[] getFields()
Class[] getInterfaces()
. . but this is local loading
• When we call Class.forName(String) we use
the default class loader
• The JVM uses objects of type
java.lang.ClassLoader to load classes
dynamically
• The default loader uses the standard class path
• Specified by
• Environment variable CLASSPATH
• Command line option -classpath
Remote class loading - using HTTP
• To load classes from some remote source
we must create a new class loader that can
load remotely
• java.net.URLClassLoader is able to
load classes from URLs
• Allows us to load classes using the HTTP
protocol from any web server
java.net.URLClassLoader
• Constructor
• URLClassLoader(URL[] classPath)
• Useful methods:
• Class findClass(String qualifiedName)
• Does the same job as Class.forName()
• URL[] getURLs()
• … and a few others
java.net.URL
• Abstracts a universal resource locator
• Constructor
• URL(String urlName)
eg new URL(“http://asp/index.html”)
• Methods to take the URL apart,
such as
• String getHost()
• int getPort()
• String getPath()
Different ImageCreators
• Assume we have different ImageCreator
objects available on some remote system
• ImageCreator_GIF
• Decodes GIF image formats
• ImageCreator_JPG
• Decodes JPEG format images
• ImageCreator_BMP
• Decodes (256 colour) MS-Windows Device
Independent Bitmap files
• We want to be able to download and use the
appropriate one on demand
Loading Viewer classes
public ImageCreator loadImageCreator(String type) {
try {
// Get a URL as a text string from ...
String urlTextString = sourceURLText.getText();
// Form an array of URLs
URL[] sourceURLs =
new URL[] { new URL(urlTextString) };
// Construct the class loader
URLClassLoader loader =
new URLClassLoader(sourceURLs);
// Load the class
Class componentType = loader.loadClass(
"ImageCreator_" + type);
// Make an object of the class
return (ImageCreator) componentType.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
Exceptions, exceptions!
• java.net.MalformedURLException
• The (complex) format of the URL is invalid
• java.lang.ClassNotFoundException
• The class could not be loaded from the remote
location
• java.lang.ClassCastException
• The loaded class does not implement the
interface that we require
Remote loading alternative ...
• Remote Procedure Calls
• Idea has been around for at least 20 years
since Sun introduced RPC to SunOS
• RPC involves hiding a network communication
protocol so that programmers appear to simply
be calling procedures
• This idea adapts very easily to OO
programming as distributed objects
A Little History
• IBM lead the way with DSOM (late 80s)
• Microsoft developed this into the
distributed common object model (DCOM)
(ca.1995)
• Common Object Request Broker
Architecture (CORBA) developed by OMG
to provide platform and language
independence (1991/94)
• Sun developed Remote Method Invocation
(RMI) as a simpler alternative (1997)
Remote Method Invocation
• First introduced into JDK 1.1
• Java specific protocol aimed at simplicity
• Java is already platform independent
• Closely linked to the idea of client/server
• A server application provides some form of
service to zero or more clients
• A client program uses the service
• A Java interface defines the contract between
the two
The Object Registry
• Server advertises its services using a service
label
• A string naming the service
• It registers the label with a object registry
(rmiregistry) which runs as a daemon
• Clients connect to the object registry and ask
for the service they require
• Identifying it with the service label
• Clients must know where the object registry is
located (unlike in JINI)
• Clients obtain a reference to the desired
interface and can then call its methods
Loading remote components using RMI
• As an alternative we can use RMI to allow
us to load remotely
• We will look at the following parts:
• The service interface
• ImageCreatorService
• A server implementation class
• RMIComponentServer
• A client implementation class
• RMIClientViewer
RMI versus HTTP
• HTTP is really intended for document
downloads
• Using HTTP POST you can provide
parameters as part of a request
• Too specific for communication between
distributed objects
• RMI allows any object to
• Call any method in another object (locally or
remotely)
• Pass any list of parameters (not just Strings)
• Receive back any Java type as the return value
The ImageCreatorService interface
public interface ImageCreatorService
extends java.rmi.Remote
{
/**
Deliver the byte codes for an ImageCreator
component which will deal with images of the
specified type.
*/
public byte[] loadClassData(String compName)
throws RemoteException;
}
The component server
• Just a few simple steps to write the server:
• STEP ONE: Create a server class which
implements your interface and subclasses
java.rmi.server.UnicastRemoteObject
• STEP TWO: Make the server register itself
with the object registry
• STEP THREE: Implement the methods
cited in the service interface
The RMIComponentServer class
STEP ONE
public class RMIComponentServer
extends java.rmi.server.UnicastRemoteObject
implements ImageCreatorService
{
STEP TWO
public RMIComponentServer()
throws RemoteException
{
Service label
try {
java.rmi.Naming.rebind(
"rmi://localhost/ImageCreatorService", this);
} catch (MalformedURLException mue) {
mue.printStackTrace();
}
}
. . . .
The RMIComponentServer class
public byte[] loadClassData(String className)
throws RemoteException
{ // Implementation edited out for brevity. . .
}
public static void main(String[] args)
{
try {
System.out.println("RMIComponentServer: started.");
RMIComponentServer server =
new RMIComponentServer();
}
catch (Exception e) {
STEP THREE
e.printStackTrace();
}
}
The client viewer implementation
• This is in the form of RMIClientViewer
but the interesting part is the inner class
called RMIClassLoader
• This subclasses java.lang.ClassLoader
• The parent of all classes capable loading byte
codes
• RMIClassLoader loads byte codes with RMI
• Rather than HTTP (as in the previous example)
• Distributed memory space (all parameters are
passed by value, rather than by reference)
• RMI requires a different design discipline
Reliability Issues
• When calling a method locally there are
only two possible outcomes:
• The method succeeds
• The method fails with an exception
• When calling an RMI method there is a
third category:
• It was impossible to call the method, since the
RMI mechanism failed in some way
How does RMI work?
• It uses proxy classes called stubs and
skeletons
• RMI calls from the client are intercepted by
a proxy class called a stub
• They are passed to another proxy called a
skeleton residing on the server which calls
the server class on the client’s behalf
• Stubs and skeletons can be generated
using rmic
Proxy stubs
• Implements the chosen service interface
so it looks like the remote server as far as
the client is concerned
• Packages up (marshals) arguments for
dispatch across the network
• Sends them using the Java Remote
Method Protocol (JRMP)
• Waits for and unpacks the return value
Proxy skeletons
• Waits for JRMP request to be received
from a TCP/IP connection
• Unmarshals the arguments and passes
them on to server implementation
• Awaits a return value and marshals it to be
returned through the TCP/IP connection
• Not needed by JDK 1.2
How it all fits together
Client VM
Server VM
Client Obj
x=if.m()
Stub
Call
Skeleton
TCP/IP
link
int m() {
....
}
Retn
Service Interface
Server Impl.
Service Interface
rmic
• Remote interface compiler
• Specify your server implementation class
as a command line argument
eg rmic -classpath . RMIComponentServer
• Ascertains the service interfaces
• Creates a stub and a skeleton class for
each interface
eg RMIComponentServer_Stub.class
RMIComponentServer_Skel.class
• Recreate stubs and skeletons when you
modify the interfaces for your server
RMI Method Arguments
• What restrictions are there on RMI
arguments?
• By default parameters are passed by value
• NOTE: This is the opposite of the conventions
when calling a local method
• This is the normal behavior for primitive
types: byte, short, char, int,
long, float, double, boolean
• Objects must be pickled before being sent!
• They are marked as implementing the
Serializable interface
java.io.Serializable
• Contains no methods!
• Just a marker for those classes which are
may be serialized
• Into a file or
• Sent across the net to a different VM by RMI
• Many Java classes implement
java.io.Serializable
• Some notable exceptions: Runtime system
and Reflection API classes and some I/O
classes
Passing objects by reference
• It is possible to pass objects be reference,
but only if you design them specifically for
RMI
• STEP ONE: Make the object implement the
java.rmi.Remote interface
• STEP TWO: Export the object by
registering it using methods in
java.rmi.Naming
• In other words you are turning the object
into another RMI server in its own right
Inner class RMIClassLoader
• Part of the client side application which
wants to load the appropriate viewer class
• STEP ONE
• Create a reference to the required service
interface
• STEP TWO
• Lookup the service by connecting to the
chosen object registry
• STEP THREE
• Call a method through the interface
sea.picturelib.RMIClientViewer
public class RMIClassLoader extends ClassLoader
{
ImageCreatorService remoteService = null;
public RMIClassLoader(String serverName) STEP ONE
throws NotBoundException, MalformedURLException,
RemoteException
{
remoteService = (ImageCreatorService)
java.rmi.Naming.lookup(
"rmi://" + serverName + "/ImageCreatorService");
}
. . . .
STEP TWO
sea.picturelib.RMIClientViewer
public Class findClass(String name)
throws ClassNotFoundException
STEP THREE
{
try {
byte[] byteCodes =
remoteService.loadClassData(name);
return defineClass(
name, byteCodes, 0, byteCodes.length);
}
catch (RemoteException re) {
throw new ClassNotFoundException(
"failed to load class " + name + " using RMI");
}
}
}
It’s that simple. . .
• STEP ONE
• Define the service interface
• STEP TWO
• Write a server that implements the interface
• STEP THREE
• Make the server register with an object registry
• STEP FOUR
• Write a client that looks up the desired service
in the object registry
• STEP FIVE
• The client can call methods in its interface
. . . not quite!
• Some general RMI issues …
• RPC (RMI) can lull the designer into a false
sense of security!
• Beware! They may look like method calls but
they’re really network communications
• You will need a design that take into account
• Reliability issues
• Latency issues
Reliability Issues
• When a remote method cannot be called it throws
an exception in the java.rmi.RemoteException
hierarchy
• java.rmi.ConnectionException when the
network connection could not be established
• java.rmi.ConnectionIOException when the
network connection failed
• . . . and there are plenty of things that can go
wrong (17 other exception types)
Latency Issues
• Calling methods locally
• 100% reliable and
• only requires a few nanoseconds
• Not true for RMI
• Network might be congested (or down)
• Remote server may be busy (or down)
• RMI calls take a long time:
• About 1 ms in the best scenario (one million
times as long as a local method call!)
• Up to a 3 minute time-out
• DNS lookup fails
Latency Issues
• It is unwise to make RMI method calls from
any time-sensitive code
• User interface callback handlers
• Process/hardware controllers
• Control the network environment
• Make RMI calls in a separate thread
• Design an interface with coarse-grained
methods to minimize round trip delays
Accessing remote objects
• Why does an object need to be called
remotely?
• Only because it needs some resource that
the remote machine can offer
•
•
•
•
Some custom peripheral (a scanner?)
CPU/Memory resources
Intimate access to a database
Some other resource that you wish to manage
centrally
• If not, then copy back the bytes codes and
execute locally!
• This is starting to sound like JINI
Design Issues
• Work out where objects need to be within
the system architecture
• Why not get the RMI server to support the
ImageCreator interface directly?
• We could have passed the File object
across to the server but not the file!
• File implements Serializable
• We could need to create a remotely accessible
stream object
• Must then pass the image object back to
the client
• It is expensive to transfer large arrays of bytes
Other sources of information
• Lots of RMI information at Sun’s site
http://www.javasoft.com/products/jdk/rmi
• JRMP (RMI’s wire protocol) is described in
the downloadable documentation available
for each Java 1.2.X or 1.3.X VM or from
http://java.sun.com/j2se/1.3/docs/guide/rmi/..
spec/rmi-protocol#60
RemoteWorker
• Model
•
•
•
•
Master + n slave processors
Slaves do the work
Master distributes it and receives results
Work is done in a class which implements the
Solver interface
RemoteWorker
• Solver methods
• public void init( TaskDescriptor init_parms )
• Set up initial parameters (if any)
• public Result executeTask( TaskDescriptor td
)
• Solve the problem
• TaskDescriptor
• Marker interface
• Wrapper for arguments for init and executeTask
• Result
• Marker interface
• Wrapper for return value(s) from executeTask
RemoteWorker
• Setting up
• Start rmiregistry on each slave
• start rmiregistry
• Start RemoteWorker on each slave
• java -cp .
scl.RemoteWorker.RemoteWorker
• On master
• Run program which
• creates task descriptors
• puts them in a Q
• fires up a proxy for each slave
• send solver class to slave
• extract task descriptors from Q and send them to slaves
Creating a Class object
public ImageCreator loadCreator(String className)
throws ClassNotFoundException
{
Class type = Class.forName(className);
Class imageCreatorType = ImageCreator.class;
ImageCreator component = null;
Class[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(imageCreatorType)) {
return (ImageCreator) type.newInstance();
}
}
return null;
}
Alternative approaches
• There are two other ways to test this:
if (ImageCreator.class.isAssignableFrom(type))
return (ImageCreator) type.newInstance();
else
return null;
• Or:
try {
return (ImageCreator) type.newInstance();
} catch (java.lang.ClassCastException cce) {
return null;
}
The price of deferred loading
• Compiler errors become exceptions!
• java.lang.ClassNotFoundException
• no (byte code) definition for the specified class
can be found
• java.lang.IllegalAccessException
• the constructor/method is private
• java.lang.InstantiationException
• the class does not have a valid constructor or
an attempt was made to instantiate a primitive:
• int i = new int(); // this is an error!