Drilling Down on Object Creation

The original design of the Web, with its stateless connections, serves as a good example of a distributed application that can tolerate almost any transient network failure. These three reasons imply that what is really needed is a way to copy objects and send them over the wire. That is, instead of turning arguments into implicit servers, arguments need to be completely copied so that no further network calls are needed to complete the remote method invocation. Put another way, we want the result of makeWithdrawal to involve creating a copy of the instance of Money on the server side. The runtime structure should resemble Figur e 10- 3 . Figure 10-3. Making a remote method call can create deep copies of the arguments and return values The desire to avoid unnecessary network dependencies has two significant consequences: Once an object is duplicated, the two objects are completely independent of each other. Any attempt to keep the copy and the original in sync would involve propagating changes over the network, entirely defeating the reason for making the copy in the first place. The copying mechanism must create deep copies. If the instance of Money references another instance, then copies must be made of both instances. Otherwise, when a method is called on the second object, the call must be relayed across the wire. Moreover, all the copies must be made immediately™we cant wait until the second object is accessed to make the copy because the original might change in the meantime. These two consequences have a very important third consequence: If an object is sent twice, in separate method calls, two copies of the object will be created. In addition to arguments to method calls, this holds for objects that are referenced by the arguments. If you pass object A, which has a reference to object C, and in another call you pass object B, which also has a reference to C, you will end up with two distinct copies of C on the receiving side.

10.1.1 Drilling Down on Object Creation

To see why this last point holds, consider a client that executes a withdrawal and then tries to cancel the transaction by making a deposit for the same amount of money. That is, the following lines of code are executed: server.makeWithdrawalamount; .... server.makeDepositamount; The client has no way of knowing whether the server still has a copy of amount . After all, the server may have used it and then thrown the copy away once it was done. This means that the client has to marshall amount and send it over the wire to the server. The RMI runtime can demarshall amount , which is the instance of Money the client sent. However, even if it has the previous object, it has no way unless equals has been overridden to tell whether the instance it just demarshalled is equal to the previous object. More generally, if the object being copied isnt immutable, then the server might change it. In this case, even if the two objects are currently equal, the RMI runtime has no way to tell if the two copies will always be equal and can potentially be replaced by a single copy. To see why, consider our Printer example again. At the end of Chapt er 3 , we considered a list of possible feature requests that could be made. One of them was the following: Managers will want to track resource consumption. This will involve logging print requests and, quite possibly, building a set of queries that can be run against the printers log. This can be implemented by adding a few more fields to DocumentDescription and having the server store an indexed log of all the DocumentDescription objects it has received. For example, we may add the following fields to DocumentDescription : public Time whenPrinted; public Person sender; public boolean printSucceeded; Now consider what happens when the user actually wants to print two copies of the same document. The client application could call: server.printDocumentdocument; twice with the same instance of DocumentDescription . And it would be an error for the RMI runtime to create only one instance of DocumentDescription on the server side. Even though the same object is passed into the server twice, it is passed as parts of distinct requests and therefore as different objects. This is true even if the runtime can tell that the two instances of DocumentDescription are equal when it finishes demarshalling. An implementation of a printer may well have a notion of a job queue that holds instances of DocumentDescription . So our client makes the first call, and the copy of document is placed in the queue say, at number 5, but not edited because the document hasnt been printed yet. Then our client makes the second call. At this point, the two copies of document are equal. However, we dont want to place the same object in the printer queue twice. We want to place distinct copies in the printer queue. Thus, we come to the following conclusion: network latency, and the desire to avoid vulnerability to partial failures, force us to have a deep copy mechanism for most arguments to a remote method invocation. This copying mechanism has to make deep copies, and it cannot perform any validation to eliminate extra copies across methods. While this discussion provides examples of implementation decisions that force two copies to occur, its important to note that, even without such examples, clients should be written as if the servers make independent copies. That is, clients are written to use interfaces. They should not, and cannot, make assumptions about server-side implementations of the interfaces.

10.2 Using Serialization