Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Mastering Enterprise JavaBeans™ and the Java 2 Platform, Enterprise Edition - Roman E

..pdf
Скачиваний:
41
Добавлен:
24.05.2014
Размер:
6.28 Mб
Скачать

Understanding Java Remote Method Invocation (RMI) 523

Is UnicastRemoteObject My Only Choice?

If you want your objects to become remote objects, you have several options besides using java.rmi.server.UnicastRemoteObject.

1.You can extend java.rmi.activation.Activatable, which allows your objects to be kicked into memory when needed (called “activation”). For more information about activation, see the book’s accompanying Web site.

2.You can also extend javax.rmi.PortableRemoteObject, which allows you to use the Internet Inter-ORB Protocol, a robust protocol for distributed object communications. This is very handy for CORBA interoperability. For more information, see Chapter 11.

so that it is available to be invoked on by remote hosts. To export your object, call java.rmi.server.UnicastRemoteObject.exportObject().

A Simple Example

We now present a simple example illustrating the basics of RMI. In this example, a remote object exposes one method, flip(). flip() takes an integer as a parameter and negates it, returning the result. You can’t get much simpler than this.

The IFlip Interface

First, we must create a remote interface containing every method that our remote object will expose to remote hosts. This interface must extend java.rmi.Remote. The code is shown in Source A.1.

package com.wiley.compBooks.roman.rmi.flip;

import java.rmi.Remote;

import java.rmi.RemoteException;

/**

*The remote interface for the remote object. Remote hosts use this

*remote interface to perform any operations on the remote object. */

public interface IFlip extends Remote {

public int flip(int i) throws RemoteException;

}

Source A.1 IFlip.java.

Go back to the first page for a quick link to buy this book online!

524 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

The Flip Class

Now let’s create the remote object class. This class implements the IFlip interface, and it is shown in Source A.2.

We have a quick main() program that starts things up. First, we start an RMI Registry (if needed). We then construct our remote object. In our remote object constructor, we export our remote object and bind it to an RMI registry. Once the remote object’s constructor is complete, this object will be available forever for any Virtual Machine to invoke on, by contacting the RMI registry listening at the specified port.

package com.wiley.compBooks.roman.rmi.flip;

import java.rmi.*;

import java.rmi.registry.*; import java.rmi.server.*;

/**

*The remote object that performs the integer negation.

*Notice that we extend UnicastRemoteObject, which will

*automatically export our remote object for us.

*/

public class Flip extends UnicastRemoteObject implements IFlip {

/**

* Our main() method starts things up */

public static void main(String args[]) throws Exception {

/*

*If the user called this program incorrectly,

*report an error

*/

if (args.length != 2) {

System.err.println("Syntax: Flip <true|false> <port>");

System.err.println();

System.err.println("true demarcates to start RMI Registry, " + "false demarcates to bind to existing one.");

System.err.println("port is the port # for RMI Registry.");

System.exit(-1);

}

/*

* If desired, start RMI Registry at specified port. */

Source A.2 Flip.java (continues).

Go back to the first page for a quick link to buy this book online!

Understanding Java Remote Method Invocation (RMI) 525

int port = new Integer(args[1]).intValue(); Registry reg = null;

if (args[0].equals("true")) {

reg = LocateRegistry.createRegistry(port); System.out.println("Successfully created registry.");

}

/*

*Otherwise, acquire a reference to an existing

*registry which the user may have started up.

*/

else {

reg = LocateRegistry.getRegistry(port); System.out.println("Connected to existing registry.");

}

/*

*Start up our Flip remote object. It will

*auto-bind itself to the RMI Registry.

*/

Flip flip = new Flip(reg);

}

/*

* Our remote object's constructor performs RMI Initialization. */

public Flip(Registry reg) throws Exception, RemoteException {

/*

*Since we extend UnicastRemoteObject, the super

*class will export our remote object here.

*/

super();

/*

* Bind our Flip remote object to the RMI registry */

reg.rebind("Flip", this); System.out.println("Flip object bound.");

}

/*

*Our single business logic method, flip(), is callable over

*the network. Notice that it throws a RemoteException,

*which is required for all remote methods.

*/

public int flip(int i) throws RemoteException { return i * -1;

}

}

Source A.2 Flip.java (continued).

Go back to the first page for a quick link to buy this book online!

526 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

The Client

Finally, let’s take a look at the client code calling the remote object. First, we perform a lookup on the remote host’s RMI registry. This lookup returns a remote stub for the remote object we’d like to invoke upon. Next, our client simply calls the remote object’s flip() method, which flips the value of 5 to –5 and prints the result. Note that the client operates on the IFlip interface rather than on Flip itself, as RMI dictates. The code is depicted in Source A.3.

package com.wiley.compBooks.roman.rmi.flip;

import java.rmi.Remote;

import java.rmi.RemoteException; import java.rmi.Naming;

public class FlipClient {

public static void main (String[] args) {

IFlip flip = null;

/*

* Do some parameter checking.. */

if (args.length != 2) { System.err.println(

"Usage: Flip <hostname of Flip remote object> <port>"); System.exit(-1);

}

/*

*Set the security manager (required because we may

*be downloading implementation)

*/ try {

System.setSecurityManager(new java.rmi.RMISecurityManager());

}

catch (java.rmi.RMISecurityException exc) { System.err.println("Security violation " + exc.toString()); System.exit(-1);

}

/*

* Get a handle to a remote Flip object. */

Source A.3 FlipClient.java (continues).

Go back to the first page for a quick link to buy this book online!

Understanding Java Remote Method Invocation (RMI) 527

try { /*

*We use an RMI URL to locate the remote object.

*We construct the RMI URL from the

*parameters passed from the end user.

*/

String targetMachine = "rmi://" + args[0] + ":" + args[1] + "/Flip";

System.out.println("Attempting to contact " + targetMachine);

/*

* Get the object from the remote RMI Registry */

Remote remoteObject = Naming.lookup(targetMachine);

/*

*Perform a quick check to make sure the object

*is of the expected IFlip interface type.

*/

if (remoteObject instanceof IFlip) { flip = (IFlip) remoteObject;

}

else {

throw new Exception(

"Bad object returned from remote machine");

}

}

catch (Exception e) {

System.err.println("Error in lookup() " + e.toString());

System.exit(-1);

}

/*

*Print the result of flipping 5. We're actually

*invoking on a stub here, which delegates across the

*network to the real object.

*/ try {

System.err.println(flip.flip(5));

}

catch (RemoteException e) {

System.err.println("Remote error: " + e.toString());

}

}

}

Source A.3 FlipClient.java (continued).

Go back to the first page for a quick link to buy this book online!

528 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

Compiling the Program

We compile the program using the normal java compiler, javac. The additional step we need is to generate the stub (and optionally skeleton) using rmic:

rmic com.wiley.compBooks.roman.rmi.flip.Flip

This generates the stub and skeleton files, Flip_Stub.class and Flip_Skeleton.class.

Running the Program

To start the server, we type:

rmiregistry 1000

java com.wiley.compBooks.roman.rmi.flip.Flip false 1000

The first line starts a new RMI Registry at port 1000. The second line starts up our Flip server, which binds to the RMI Registry we started at port 1000.

Finally, we run the client program:

java com.wiley.compBooks.roman.rmi.flip.FlipClient localhost 1000

When running the client, we need to specify the target machine where the Flip server is running. We use localhost to denote that the client and server are on the same machine. We also need to specify the port (1000) at which it should look for the RMI Registry that contains an entry for our Flip object.

The following is the output of the server:

Connected to existing registry.

Flip object bound.

And the following is the output of the client:

Attempting to contact rmi://localhost:1000/Flip

-5

As you can see, the remote object correctly negated 5 to –5. This completes our basic example.

Dealing with Remote Exceptions

When designing object models with RMI, you usually want to have as much of your code decoupled from RMI as possible. Ideally, you want to separate your

Go back to the first page for a quick link to buy this book online!

Understanding Java Remote Method Invocation (RMI) 529

business logic from your networking code as much as possible. This is very difficult to do with RMI because of all those annoying RemoteExceptions being thrown, which you need to wrap try..catch blocks around (see our previous example). How can you effectively separate the two?

One common design pattern with RMI is to construct a “gateway” remote object that is dependent on RMI. Once a client has acquired a handle to the gateway remote object, the gateway can serve as a conduit into an entire suite of objects running within a JVM. All method invocations can happen through the gateway. When the gateway receives a request, it delegates calls to other local objects on its machine—those objects providing the business logic. In the simplest case, only one reference to one remote object on another machine is needed in order to access that machine’s functionality. This technique also reduces the number of times you need to form that initial RMI connection. We depict this in Figure A.9.

Of course, you can never fully separate your application from the network. Somewhere, at some point, you’ll need to deal with Remote Exceptions being thrown due to networking issues. Some may consider this a limitation of RMI because the network is not entirely “seamless,” because Remote Exceptions force you to differentiate a local method from a remote method. But in some ways, this is an advantage of RMI as well. Interlacing your code with Remote Exceptions forces you to think about the network and encourages distributed object developers to consider issues such as the network failing, the size of parameters going across the network, and more.

RMI’s Remote Exception limitations

As we’ve said, Remote Exceptions are useful for detecting nasty networking issues, such as application death, machine crashes, and network failures. But how robust are Remote Exceptions for detecting problems in enterprise-class deployments?

Let’s investigate this matter by using RMI as a communications mechanism for performing banking operations. Banking operations are a perfect example because they are mission-critical. Any errors in a bank statement could result in millions in lost funds.

We’ll implement a bank operator as a distributed object based on the RMI standard. One of the methods our banking operator exposes is withdraw(), which is used for withdrawing money. Seems simple enough, right?

Wrong. There is a serious danger in doing this. Let’s say you call withdraw() remotely, but the network dies at some point:

Go back to the first page for a quick link to buy this book online!

530 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

RMI Client

invoke()

Gateway Stub

Network

Gateway Skeleton

invoke()

Gateway Remote

Object

Implementation

<<delegate>> <<delegate>> <<delegate>>

Local Object A

 

Local Object B

 

Local Object C

 

 

 

 

 

Figure A.9 Using gateway objects to mask RMI from your business objects.

■■If the network dies before the method was invoked, then the money will not be withdrawn.

■■If the network dies after the method was invoked, then the money will be withdrawn.

Go back to the first page for a quick link to buy this book online!

Understanding Java Remote Method Invocation (RMI) 531

As you can see, these two scenarios produce drastically different results. There are other problematic scenarios as well, such as processes crashing or databases crashing. How can you deal with this in your RMI code? The blunt answer is that with straight RMI, you can’t do this in any clean way. You will never know for sure if your method finished properly.

These types of problems are really what transactions were made to assist with. If a networking problem happens during a transaction, the transaction will be rolled back, undoing any changes (such as a withdrawal) that may have happened. In this sense, transactions are acting as a sophisticated form of exception handling. Similarly, if a database crashes, transactions guarantee that the database will be in a consistent state.

Because of this, and because of the host of other middleware necessary, RMI by itself is not sufficient for enterprise-class deployments. But RMI does have its purpose in the Java 2 Platform, Enterprise Edition (J2EE)—as a networking technology that allow for distributed objects to communicate, which complements the other Enterprise Java APIs.

RMI Tips

The following are helpful hints for using RMI:

■■Design your remote interface and object interface separately.

■■To minimize the number of objects you register with the RMI Registry, you can register one object and have that object be a factory for other objects you need. Clients contact the RMI Registry once, get a handle to the remote factory, and have the factory generate the objects you really want. Because the factory generates the real objects, they don’t clutter up the RMI Registry. For more details about factories, see the link provided on this book’s Web site.

■■You can debug your RMI programs by using RMI’s built-in logging facility. You can turn on logging by setting java.rmi.logCalls=true as a system property when launching your VM.

■■If you need to cross a firewall with RMI, this is also possible, as described in the accompanying sidebar.

Advanced RMI

So far, in this chapter we’ve touched the basics of RMI. Let’s move on to some advanced issues. Specifically, we’ll show you how to do the following:

Go back to the first page for a quick link to buy this book online!

532 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

RMI and Firewalls

A firewall is a barrier that controls network traffic for the purposes of security. Although necessary for a secure intranet, a firewall can be a real hassle to deal with when you start talking about distributed computing. Many corporate intranets will let users through the firewall only to browse the Web, which happens at port 80. And that port is reserved for Web traffic.

It would seem that firewalls make it impossible to do any form of client/server com- munication—a pretty harsh restriction. Imagine if you code a Java applet to be downloaded by clients who are sitting behind their corporate intranet firewalls. If your applet needs any communication with your central server, you’re out of luck.

Out of luck, that is, unless you could somehow get the use of port 80. What if you sent all client/server traffic as regular HTTP requests/responses? This blatant hack is exactly how RMI really solves the firewall problem. RMI passes through firewalls by simulating an RMI Registry listening at port 80. It wraps all transported data in Web tags, so that they look like normal Web communications. The masking of a request as an HTTP request on port 80 is known as HTTP tunneling through firewalls. By tunneling through and pretending that you’re doing normal HTTP communications, you can reach your target RMI Registry.

To use this tunneling, a Web server must be deployed on the same machine on which the target RMI Registry sits. That Web server acts as a router—it takes requests that come in at port 80 (which the firewall permits) and routes them to the actual port where the RMI Registry sits on that machine. This is accomplished by a CGI script included with the Sun’s Java 2 platform. By this mechanism, clients can get through their firewalls because their firewalls think they are making normal HTTP port 80 communications. RMI accomplishes this transparently—you don’t need to worry about these details. All you need to do is install the java-rmi.cgi script included with the JDK.

Note that firewalls will not allow any incoming connections from outside the firewall to be established. This means that if you’re behind a firewall, you can’t start an RMI Registry to listen for incoming connections. You must initiate all connections—external VMs may not make remote invocations to your machine, due to the firewall’s blockade.

For more information on this subject, see the link provided on this book’s Web site.

1.Take RMI to a bit more depth by building a simple message queuing product on top of RMI

2.Throw exceptions across the network using RMI

3.Pass objects by value as well as by reference

4.Exploit RMI’s distributed garbage collector

We’ll explain these concepts in hands-on examples.

Go back to the first page for a quick link to buy this book online!