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

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

.pdf
Скачиваний:
45
Добавлен:
24.05.2014
Размер:
6 Мб
Скачать
☆

 

 

Understanding Java Remote Method Invocation (RMI)

 

513

 

 

 

 

 

 

 

Client

 

Client

 

 

 

 

 

 

 

 

RMI facility to

lookup remote Stub objects

Network

 

Network

 

 

Once a stub is

 

 

acquired...

 

Remote

Skeleton

RMI Registry

Objects

 

Remote Object

Figure A.3 Bootstrapping with the RMI Registry.

3.Finally, append the name of the remote object to which you wish to acquire a remote reference. For example, if your remote object is bound as the name “myObject”, you can reference it from any machine in the world with the RMI URL rmi://foobar.baz.com/myObject.

Looking Up a Remote Object

So far, our discussion has touched the following concepts:

■■An RMI Registry is a remote object used by clients for bootstrapping, or initially connecting, to your remote objects. Remote objects register themselves with the registry, which clients connect to at a well-known port.

■■RMI URLs are used to identify the locations of objects over the network.

How do clients use these two abstractions to actually connect to remote objects? This is what the java.rmi.Naming class is used for. When a client wants to acquire a reference to a remote object, he or she goes through java.rmi.Naming.

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

514 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 URLs versus JNDI

RMI URLs are great for identifying object locations across the network, but they are fairly inflexible for enterprise computing. You need to hard-code RMI URLs into your Java code (or provide them in properties files). If the locations of your distributed objects change, you must change and possibly redeploy each client of those distributed objects. In essence, there is no location transparency of distributed objects.

A much more flexible system of looking up distributed objects is to use naming and directory services through the Java Naming and Directory Interface (JNDI). Naming and directory services allow for your client code to escape hard-coding the locations of distributed objects, yielding a much more robust deployment. We’ll study how that’s done in Appendix B.

java.rmi.Naming is responsible for connecting to RMI registries for the purposes of finding remote objects.

The most important method that java.rmi.Naming exposes is the lookup() method, which takes an RMI URL, connects to an RMI registry on a target JVM, and returns a java.lang.Object representing the remote object. The returned object is actually the remote stub, and it can be cast into your remote object’s interface type.

RMIC—The RMI Compiler

The stubs and skeletons we described earlier provide a screen to block networking issues and make it appear as if things are happening locally. Both stubs and skeletons must implement your custom remote objects’ interfaces. Because of

Skeletons in the Java 2 Platform

The RMI runtime in Java 2 contains generic code that will perform the function of skeletons for you. Hence, skeleton generation is optional in Java 2. Stubs, on the other hand, are still required to be generated for each remote object.

This makes sense because a skeleton is responsible for delegating invocations to a remote object. It doesn’t matter to what interface the skeleton code conforms. A stub, on the other hand, is accessed directly by your client code and hence must implement your remote object’s remote interface. This gives clients the illusion that they are invoking on a local object implementation.

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

Understanding Java Remote Method Invocation (RMI) 515

 

.java

 

 

 

.class

 

 

.class

Remote object

 

Remote object

 

Stub

implementation

Java compiler

implementation

RMI Compiler

 

 

 

 

 

 

 

.java

 

 

.class

 

.class

Remote interface

Remote interface

Skeleton

Figure A.4 Generating stubs and skeletons with RMIC.

 

this, no two stubs or skeletons are the same, and they must be generated anew for each remote object class that you create. Fortunately, Sun ships a tool to perform this generation for you. The name of the tool is rmic, the RMI compiler. It takes as input your remote object implementation (the class that implements your remote interface), and it creates a stub and skeleton for that remote object. This is shown in Figure A.4. The stub mimics each method exposed by the remote object, copying the method signatures exactly, so clients can call that stub locally as if the remote object itself were there.

Object Serialization and Parameter Passing

This next section discusses how parameters are passed in Java RMI. We also explore the power of object serialization and how it assists in parameter passing and object marshalling/unmarshalling.

Passing by Value

When invoking a method using RMI, all parameters to the remote method are passed by value. This means that all parameters are copied from one machine

Taking a Look at the Generated Proxy Code

You can use the -keepgenerated option of rmic to see the generated stub (or “proxy”) code. This is useful if you want to learn how RMI works internally. It’s also great if you want to rewrite your own stub/skeleton layer of RMI. You can use these generated proxies as a basis for your code.

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

516 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

to the other when the target method is invoked.

Passing objects by value is in stark contrast with the Java programming language, which uses a pass-by-reference calling convention for an object’s state. In Java, when calling a method normally with an Object parameter, the reference to the object is copied, but the actual object’s data is not.

There’s a big problem with passing by value. If you’re trying to pass an object over the network and that object contains references to other objects, how are those references resolved on the target machine? A memory address on one machine does not map to the same memory address on another machine. Also, the referenced object may not even exist on the target machine. How do we get around this?

Object Serialization

Java introduces the concept of object serialization to handle this problem. Serialization is the conversion of a Java object into a bit-blob representation of that object. Once in bit-blob form, the object can be sent anywhere—onto your hard disk or across the network. When you’re ready to use the object again, you must deserialize the bit-blob back into a Java object. Then it’s magically usable again.

The Java language handles the low-level details of serialization. In most cases, you don’t need to worry about any of it. In order to tell Java that your object is serializable, your object must implement the java.lang.Serializable interface. That’s all there is to it—take this one simple step, and let Java handle the rest. java.lang.Serializable defines no methods at all—it’s simply a marker interface that identifies your object as something that can be serialized and deserialized.

How Objects Are Serialized

Once your objects implement java.lang.Serializable, they can be serialized into a stream of data. The serialization process is accomplished automatically by Java Serialization. You can provide your own custom serialization by implementing the writeObject() method on your object. This might be useful if, for example, you’d like to perform some sort of compression on your data before your object is converted into a bit-blob.

Similarly, deserialization is also automatic in most cases. If you want to, you can provide your own deserialization method as well. This method is called readObject(), and it is called automatically whenever your object is deserialized. This might be useful if, for example, you’d like to decompress your object’s data during the deserialization process.

This process is shown in Figure A.5.

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

Understanding Java Remote Method Invocation (RMI) 517

java.io.Serializable

My Class

writeObject() is responsible for saving the state of the class.

readObject() is responsible for restoring the state of the class.

These two methods will be called automatically when an object instance is being serialized or deserialized.

If you choose not to define these methods, then the default serialization mechanisms will be applied. The default mechanisms are good enough for most situations.

Figure A.5 The Java serialization process.

Rules for Serialization

Java serialization has the following rules regarding member variables held in serialized objects:

■■Any basic primitive type (int, char, etc.) is automatically serialized with the object and is available when deserialized.

■■Java Objects can be included with the serialized bit-blob or not; it’s your choice. The way you make your choice is as follows:

■■Objects marked with the transient keyword are not serialized with the object and are not available when deserialized.

■■Any object that is not marked with the transient keyword must implement java.lang.Serializable. These objects are converted to bit-blob format along with the original object. If your Java objects neither are transient nor implement java.lang.Serializable, a NotSerializableException is thrown when writeObject() is called.

Thus, when you serialize an object, you also serialize all nontransient subobjects as well. This means you also serialize all nontransient sub-subobjects (the objects referenced from the subobjects). This is repeated recursively for every object until the entire reference graph of objects is serialized. This recursion is

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

java.io.Serializable

 

Class2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Class3 c

 

 

 

 

 

MyClass

 

 

 

 

 

int a

 

 

 

 

 

 

transient long b

 

 

 

 

 

String s

 

 

java.io.Serializable

 

 

 

Class2 c

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

518

 

 

 

 

 

java.io.Serializable

 

java.io.Serializable

Class3

java.lang.String

When serializing MyClass, Object Serialization will recurse through the dependencies shown, packaging up the entire graph of objects as a stream. In this diagram, everything will get serialized except for transient long b, since it is marked as transient.

Figure A.6 Object serialization.

handled automatically by Java serialization, as shown in Figure A.6. You simply need to make sure that each of your member objects implements the java.lang. Serializable interface.

What Should You Make Transient?

How do you know which member variables should be marked transient and which should not? Here are some good reasons why you would want to mark an object as transient:

■■The object is very large. Large objects may not be suitable for serialization because operations you do with the serialized blob may be very intensive.

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

Understanding Java Remote Method Invocation (RMI) 519

Examples here include saving the blob to disk or transporting the blob across the network.

■■The object represents a resource that cannot be reconstructed on the target machine. Some examples of such resources are database connections and sockets.

■■The object represents sensitive information that you do not want to pass in a serialized stream.

Note that object serialization is not free—it is a very heavyweight operation for large graphs of objects. Make sure you take this into account when designing your distributed object application.

Uses of Object Serialization

Object serialization is the mechanism used when passing parameters by value using RMI. When you pass an argument using RMI, RMI checks whether your object is serializable. If it is, it serializes the object and sends it over the network. The recipient takes the object and deserializes it, making it available for use. When the object is deserialized, all of its references to other objects are automatically reconstructed because those objects referred to were also part of the bit-blob. There’s no danger of a reference being invalid after an invocation because references are dynamically reconstructed in real time. This is shown in Figure A.7.

Thus, object serialization assists Java RMI with the marshalling (packaging) of parameters and the unmarshalling (unpackaging) of parameters. But serialization has a wide variety of other purposes as well. For instance, object serialization provides an instant file format for your objects. All you need to do is serialize them and write the serialized bit-blob to disk. It’s a quick and easy way to make your state persistent. For more information about Object Serialization, please see the book’s accompanying Web site.

How RMI Simulates Pass by Reference

As we’ve said, Java RMI relies on Object Serialization for passing parameters via remote method invocations. The scenario we’ve described is pass by value, where an entire graph of objects is serialized into a bit-blob, sent across the network, and then deserialized on the target machine.

But passing parameters by value can lead to inefficiencies. What if your referenced graph of objects is very large? What if you have lots of state to send across the network? The ensuing network lag from performing the invocation may be unacceptable.

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

520 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

java.io.Serializable

java.io.Serializable

Obj2 : MyClass2

Class3 Obj3 = new Class3();

MyObject : MyClass

java.io.Serializable

s : java.lang.String

java.io.Serializable

Obj3 : MyClass3

This is what MyObject's object graph could look like. Notice that every field and sub-field is a valid type for Java serialization.

RMI Client

1: invoke(MyObject)

Remote Object

Stub

2: Since the MyObject parameter implements Serializable, serialize MyObject's object graph.

This is the process that occurs when MyObject is sent over the network through Java RMI.

Remote Object

Implementation

4: invoke(MyObject2)

Remote Object

Skeleton

3: Deserialize the parameter. The deserialized parameter, MyObject2, contains the same state as MyObject, except the field b does not have the value 3 since b is transient.

Network

Figure A.7 Java RMI and Object Serialization.

Because of this, there is another way to pass arguments. RMI simulates a pass- by-reference convention, which means the arguments are not copied over. Rather, the remote method works with the original object.

If you want a parameter by reference, the parameter itself must be a remote object—that is, an object that is callable remotely. Let’s consider the scenario where a remote host acquires a reference to this remote object, perhaps as a parameter to one of its own methods. That remote host can then invoke operations on this remote object, the operations will occur on the local host, rather

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

Understanding Java Remote Method Invocation (RMI) 521

than on the remote host. That is, the operation is itself a remote method invocation that runs over the network.

But what is actually passed as a parameter when passing a remote object by reference? After all, we are not copying that remote object over the network. Yet the remote host still needs some object on which to operate. The answer is to pass that remote object’s stub object, as shown in Figure A.8. Stubs are networkaware references to remote objects. Just as in regular Java you have references to objects, in RMI you have remote references to objects. java.rmi.RemoteStub objects are the manifestation of those remote references. These are the exact same stubs we described earlier that are generated by the rmic tool.

Java RMI simulates pass by reference only if the object parameter being passed is itself a remote object and is callable from remote hosts. In this case, the stub for the remote object is passed, rather than the object itself being serialized.

java.rmi.Remote

MyRemoteClass

RMI Client

This is the process that occurs 1: invoke(MyRemoteObject) when MyRemoteObject, an

instance of MyRemoteClass, is sent over the network through

Java RMI.

Remote Object

Stub

2: Since the MyRemoteObject parameter is a remote object, serialize a

stub to MyRemoteObject.

Remote Object

Implementation

4: invoke(stub to MyRemoteObject)

Remote Object

Skeleton

3: Deserialize the parameter. The deserialized parameter is a stub back to the original MyRemoteObject.

Network

Figure A.8 Pass by reference with Java RMI.

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

522 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

Because Java RMI stubs are also serializable, they are passable over the network as a bit-blob. This is why earlier we said that all parameters in Java RMI are passed by-value. Thus, Java RMI only simulates pass by reference by passing a serializable stub, rather than serializing the original object. By making your parameters remote objects, you can effectively avoid the network lag in passing large objects.

In summary, we have the following rules for passing objects using Java RMI:

■■All Java basic primitives are passed by value when calling methods remotely. This means copies are made of the parameters. Any changes to the data on the remote host are not reflected in the original data.

■■If you want to pass an object over the network by value, it must implement java.lang.Serializable. Anything referenced from within that object must follow the rules for Java Serialization. Again, any changes to the data on the remote host are not reflected in the original data.

■■If you want to pass an object over the network by reference, it must be a remote object, and it must implement java.rmi.Remote. A stub for the remote object will be serialized and passed to the remote host. The remote host can then use that stub to invoke callbacks on your remote object. There is only one copy of the data at any time, which means that all hosts are updating the same data.

Exporting Your Remote Objects

To make your object a remote object available to be invoked on by remote hosts, your remote class must perform one of the following steps:

Extend the class java.rmi.server.UnicastRemoteObject. UnicastRemoteObject is a base class from which you can derive your remote objects. When your remote object is constructed, it will automatically call the UnicastRemoteObject’s constructor, which will make the object available to be called remotely. You can see this by looking at the code for UnicastRemoteObject constructor, included with the Java 2 platform (formerly known as JDK Java Development Kit 1.2):

protected UnicastRemoteObject() throws RemoteException {

exportObject((Remote)this);

}

Don’t extend java.rmi.server.UnicastRemoteObject. Perhaps your objects are inheriting implementation from another class. In this case, because Java does not allow for multiple implementation inheritance, you cannot extend UnicastRemoteObject. If you do this, you must manually export your object

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