Ever play good cop, bad cop? You’re the good cop and you provide all your services in a nice and friendly manner, but you don’t want everyone asking you for services, so you have the bad cop control access to you. That’s what proxies do: control and manage access. As you’re going to see, there are lots of ways in which proxies stand in for the objects they proxy. Proxies have been known to haul entire method calls over the Internet for their proxied objects; they’ve also been known to patiently stand in the place for some pretty lazy objects.
All we need to do is create a report that can be printed out and sent back to the CEO. Hmmm, we should probably add a location field to each gumball machine as well; that way the CEO can keep the machines straight.
Let’s just jump in and code this. We’ll impress the CEO with a very fast turnaround.
Let’s start by adding support to the GumballMachine class so that it can handle locations:
Now let’s create another class, GumballMonitor, that retrieves the machine’s location, inventory of gumballs, and current machine state and prints them in a nice little report:
We implemented that in no time. The CEO is going to be thrilled and amazed by our development skills.
Now we just need to instantiate a GumballMonitor and give it a machine to monitor:
Joe: Remote proxy. Think about it: we’ve already got the monitor code written, right? We give the GumballMonitor a reference to a machine and it gives us a report. The problem is that the monitor runs in the same JVM as the gumball machine and the CEO wants to sit at his desk and remotely monitor the machines! So what if we left our GumballMonitor class as is, but handed it a proxy to a remote object?
Frank: I’m not sure I get it.
Jim: Me neither.
Joe: Let’s start at the beginning... a proxy is a stand in for a real object. In this case, the proxy acts just like it is a Gumball Machine object, but behind the scenes it is communicating over the network to talk to the real, remote GumballMachine.
Jim: So you’re saying we keep our code as it is, and we give the monitor a reference to a proxy version of the GumballMachine...
Frank: And this proxy pretends it’s the real object, but it’s really just communicating over the net to the real object.
Joe: Yeah, that’s pretty much the story.
Frank: It sounds like something that is easier said than done.
Joe: Perhaps, but I don’t think it’ll be that bad. We have to make sure that the gumball machine can act as a service and accept requests over the network; we also need to give our monitor a way to get a reference to a proxy object, but we’ve got some great tools already built into Java to help us. Let’s talk a little more about remote proxies first...
A remote proxy acts as a local representative to a remote object. What’s a “remote object”? It’s an object that lives in the heap of a different Java Virtual Machine (or more generally, a remote object that is running in a different address space). What’s a “local representative”? It’s an object that you can call local methods on and have them forwarded on to the remote object.
Your client object acts like it’s making remote method calls. But what it’s really doing is calling methods on a heap-local ‘proxy’ object that handles all the low-level details of network communication.
Before going further, think about how you’d design a system to enable remote method invocation. How would you make it easy on the developer so that she has to write as little code as possible? How would you make the remote invocation look seamless?
Hmmm. Well, you can’t get a reference to something on another heap, right? In other words, you can’t say:
Duck d = <object in another heap>
Whatever the variable
d is referencing must be in the same heap space as the code running the statement. So how do we approach this? Well, that’s where Java’s Remote Method Invocation comes in... RMI gives us a way to find objects in a remote JVM and allows us to invoke their methods.
You may have encountered RMI in Head First Java; if not, take a slight detour and get up to speed on RMI before adding the proxy support to the Gumball Machine code.
So, here’s what we’re going to do:
① First, we’re going to take the RMI Detour and check RMI out. Even if you are familiar with RMI, you might want to follow along and check out the scenery.
② Then we’re going to take our GumballMachine and make it a remote service that provides a set of methods calls that can be invoked remotely.
③ Then, we going to create a proxy that can talk to a remote GumballMachine, again using RMI, and put the monitoring system back together so that the CEO can monitor any number of remote machines.
Let’s say we want to design a system that allows us to call a local object that forwards each request to a remote object. How would we design it? We’d need a couple of helper objects that actually do the communicating for us. The helpers make it possible for the client to act as though it’s calling a method on a local object (which in fact, it is). The client calls a method on the client helper, as if the client helper were the actual service. The client helper then takes care of forwarding that request for us.
In other words, the client object thinks it’s calling a method on the remote service, because the client helper is pretending to be the service object. Pretending to be the thing with the method the client wants to call.
But the client helper isn’t really the remote service. Although the client helper acts like it (because it has the same method that the service is advertising), the client helper doesn’t have any of the actual method logic the client is expecting. Instead, the client helper contacts the server, transfers information about the method call (e.g., name of the method, arguments, etc.), and waits for a return from the server.
On the server side, the service helper receives the request from the client helper (through a Socket connection), unpacks the information about the call, and then invokes the real method on the real service object. So, to the service object, the call is local. It’s coming from the service helper, not a remote client.
The service helper gets the return value from the service, packs it up, and ships it back (over a Socket’s output stream) to the client helper. The client helper unpacks the information and returns the value to the client object.
① Client object calls doBigThing() on the client helper object.
② Client helper packages up information about the call (arguments, method name, etc.) and ships it over the network to the service helper.
③ Service helper unpacks the information from the client helper, finds out which method to call (and on which object) and invokes the real method on the real service object.
④ The method is invoked on the service object, which returns some result to the service helper.
⑤ Service helper packages up information returned from the call and ships it back over the network to the client helper.
⑥ Client helper unpackages the returned values and returns them to the client object. To the client object, this was all transparent.
What RMI does for you is build the client and service helper objects, right down to creating a client helper object with the same methods as the remote service. The nice thing about RMI is that you don’t have to write any of the networking or I/O code yourself. With your client, you call remote methods (i.e., the ones the Real Service has) just like normal method calls on objects running in the client’s own local JVM.
RMI also provides all the runtime infrastructure to make it all work, including a lookup service that the client can use to find and access the remote objects.
There is one difference between RMI calls and local (normal) method calls. Remember that even though to the client it looks like the method call is local, the client helper sends the method call across the network. So there is networking and I/O. And what do we know about networking and I/O methods?
They’re risky! They can fail! And so, they throw exceptions all over the place. As a result, the client does have to acknowledge the risk. We’ll see how in a few pages.
RMI Nomenclature: in RMI, the client helper is a ‘stub’ and the service helper is a ‘skeleton’.
Now let’s go through all the steps needed to make an object into a service that can accept remote calls and also the steps needed to allow a client to make remote calls.
You might want to make sure your seat belt is fastened; there are a lot of steps and a few bumps and curves—but nothing to be too worried about.
This is an overview of the five steps for making the remote service. In other words, the steps needed to take an ordinary object and supercharge it so it can be called by a remote client. We’ll be doing this later to our GumballMachine. For now, let’s get the steps down and then we’ll explain each one in detail.
Make a Remote Interface
The remote interface defines the methods that a client can call remotely. It’s what the client will use as the class type for your service. Both the Stub and actual service will implement this!
Make a Remote Implementation
This is the class that does the Real Work. It has the real implementation of the remote methods defined in the remote interface. It’s the object that the client wants to call methods on (e.g., our GumballMachine!).
Start the RMI registry (rmiregistry)
The rmiregistry is like the white pages of a phone book. It’s where the client goes to get the proxy (the client stub/helper object).
Start the remote service
You have to get the service object up and running. Your service implementation class instantiates an instance of the service and registers it with the RMI registry. Registering it makes the service available for clients.
Step one: make a Remote interface
① Extend java.rmi.Remote
Remote is a ‘marker’ interface, which means it has no methods. It has special meaning for RMI, though, so you must follow this rule. Notice that we say ‘extends’ here. One interface is allowed to extend another interface.
② Declare that all methods throw a RemoteException
The remote interface is the one the client uses as the type for the service. In other words, the client invokes methods on something that implements the remote interface. That something is the stub, of course, and since the stub is doing networking and I/O, all kinds of Bad Things can happen. The client has to acknowledge the risks by handling or declaring the remote exceptions. If the methods in an interface declare exceptions, any code calling methods on a reference of that type (the interface type) must handle or declare the exceptions.
③ Be sure arguments and return values are primitives or Serializable
Arguments and return values of a remote method must be either primitive or Serializable. Think about it. Any argument to a remote method has to be packaged up and shipped across the network, and that’s done through Serialization. Same thing with return values. If you use primitives, Strings, and the majority of types in the API (including arrays and collections), you’ll be fine. If you are passing around your own types, just be sure that you make your classes implement Serializable.
Step two: make a Remote implementation
① Implement the Remote interface
Your service has to implement the remote interface—the one with the methods your client is going to call.
② Extend UnicastRemoteObject
In order to work as a remote service object, your object needs some functionality related to ‘being remote’. The simplest way is to extend UnicastRemoteObject (from the java.rmi.server package) and let that class (your superclass) do the work for you.
③ Write a no-arg constructor that declares a RemoteException
Your new superclass, UnicastRemoteObject, has one little problem—its constructor throws a RemoteException. The only way to deal with this is to declare a constructor for your remote implementation, just so that you have a place to declare the RemoteException. Remember, when a class is instantiated, its superclass constructor is always called. If your superclass constructor throws an exception, you have no choice but to declare that your constructor also throws an exception.
④ Register the service with the RMI registry
Now that you’ve got a remote service, you have to make it available to remote clients. You do this by instantiating it and putting it into the RMI registry (which must be running or this line of code fails). When you register the implementation object, the RMI system actually puts the stub in the registry, since that’s what the client really needs. Register your service using the static rebind() method of the java.rmi.Naming class.
① Bring up a terminal and start the rmiregistry.
Be sure you start it from a directory that has access to your classes. The simplest way is to start it from your
Step four: start the service
① Bring up another terminal and start your service
This might be from a main() method in your remote implementation class, or from a separate launcher class. In this simple example, we put the starter code in the implementation class, in a main method that instantiates the object and registers it with RMI registry.
Before Java 5, we had to generate static stubs and skeletons using rmic. Now, we don’t have to do this anymore and in fact, we shouldn’t do it anymore, because static stubs and skeletons are deprecated.
Instead, stubs and skeletons are generated dynamically. This happens automatically when we subclass the UnicastRemoteObject (like we’re doing for the MyRemoteImpl class).
The Remote interface:
The Remote service (the implementation):
The client has to get the stub object (our proxy), since that’s the thing the client will call methods on. And that’s where the RMI registry comes in. The client does a ‘lookup’, like going to the white pages of a phone book, and essentially says, “Here’s a name, and I’d like the stub that goes with that name.”
Let’s take a look at the code we need to look-up and retrieve a stub object.
How it works...
The things programmers do wrong with RMI are:
Forget to start rmiregistry before starting remote service (when the service is registered using Naming.rebind(), the rmiregistry must be running!)
Forget to make arguments and return types serializable (you won’t know until runtime; this is not something the compiler will detect.)
Okay, now that you have the RMI basics down, you’ve got the tools you need to implement the gumball machine remote proxy. Let’s take a look at how the GumballMachine fits into this framework:
The first step in converting our code to use the remote proxy is to enable the GumballMachine to service remote requests from clients. In other words, we’re going to make it into a service. To do that, we need to:
Create a remote interface for the GumballMachine. This will provide a set of methods that can be called remotely.
Make sure all the return types in the interface are serializable.
Implement the interface in a concrete class.
We’ll start with the remote interface:
We have one return type that isn’t Serializable: the State class. Let’s fix it up...
Actually, we’re not done with Serializable yet; we have one problem with State. As you may remember, each State object maintains a reference to a gumball machine so that it can call the gumball machine’s methods and change its state. We don’t want the entire gumball machine serialized and transferred with the State object. There is an easy way to fix this:
We’ve already implemented our GumballMachine, but we need to make sure it can act as a service and handle requests coming from over the network. To do that, we have to make sure the GumballMachine is doing everything it needs to implement the GumballMachineRemote interface.
As you’ve already seen in the RMI detour, this is quite simple; all we need to do is add a couple of things...
We’re going to add a little code to the test drive that will take care of this for us:
Let’s go ahead and get this running...
Okay, it’s time to put all this work together and give another demo. First let’s make sure a few gumball machines are running the new code:
By invoking methods on the proxy, we make a remote call across the wire, and get back a String, an integer, and a State object. Because we are using a proxy, the GumballMonitor doesn’t know, or care, that calls are remote (other than having to worry about remote exceptions).
We’ve already put a lot of pages behind us in this chapter; as you can see, explaining the Remote Proxy is quite involved. Despite that, you’ll see that the definition and class diagram for the Proxy Pattern is actually fairly straightforward. Note that Remote Proxy is one implementation of the general Proxy Pattern; there are actually quite a few variations of the pattern, and we’ll talk about them later. For now, let’s get the details of the general pattern down.
Here’s the Proxy Pattern definition:
Use the Proxy Pattern to create a representative object that controls access to another object, which may be remote, expensive to create, or in need of securing.
The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.
Well, we’ve seen how the Proxy Pattern provides a surrogate or placeholder for another object. We’ve also described the proxy as a “representative” for another object.
But what about a proxy controlling access? That sounds a little strange. No worries. In the case of the gumball machine, just think of the proxy controlling access to the remote object. The proxy needed to control access because our client, the monitor, didn’t know how to talk to a remote object. So in some sense the remote proxy controlled access so that it could handle the network details for us. As we just discussed, there are many variations of the Proxy Pattern, and the variations typically revolve around the way the proxy “controls access.” We’re going to talk more about this later, but for now here are a few ways proxies control access:
As we know, a remote proxy controls access to a remote object.
A virtual proxy controls access to a resource that is expensive to create.
A protection proxy controls access to a resource based on access rights.
Now that you’ve got the gist of the general pattern, check out the class diagram...
Let’s step through the diagram...
First we have a Subject, which provides an interface for the RealSubject and the Proxy. By implementing the same interface, the Proxy can be substituted for the RealSubject anywhere it occurs.
The RealSubject is the object that does the real work. It’s the object that the Proxy represents and controls access to.
The Proxy holds a reference to the RealSubject. In some cases, the Proxy may be responsible for creating and destroying the RealSubject. Clients interact with the RealSubject through the Proxy. Because the Proxy and RealSubject implement the same interface (Subject), the Proxy can be substituted anywhere the subject can be used. The Proxy also controls access to the RealSubject; this control may be needed if the Subject is running on a remote machine, if the Subject is expensive to create in some way or if access to the subject needs to be protected in some way.
Now that you understand the general pattern, let’s look at some other ways of using proxy beyond the Remote Proxy...
Okay, so far you’ve seen the definition of the Proxy Pattern and you’ve taken a look at one specific example: the Remote Proxy. Now we’re going to take a look at a different type of proxy, the Virtual Proxy. As you’ll discover, the Proxy Pattern can manifest itself in many forms, yet all the forms follow roughly the general proxy design. Why so many forms? Because the Proxy Pattern can be applied to a lot of different use cases. Let’s check out the Virtual Proxy and compare it to Remote Proxy:
With Remote Proxy, the proxy acts as a local representative for an object that lives in a different JVM. A method call on the proxy results in the call being transferred over the wire, invoked remotely, and the result being returned back to the proxy and then to the Client.
Virtual Proxy acts as a representative for an object that may be expensive to create. The Virtual Proxy often defers the creation of the object until it is needed; the Virtual Proxy also acts as a surrogate for the object before and while it is being created. After that, the proxy delegates requests directly to the RealSubject.
Let’s say you want to write an application that displays your favorite compact disc covers. You might create a menu of the CD titles and then retrieve the images from an online service like Amazon.com. If you’re using Swing, you might create an Icon and ask it to load the image from the network. The only problem is, depending on the network load and the bandwidth of your connection, retrieving a CD cover might take a little time, so your application should display something while you are waiting for the image to load. We also don’t want to hang up the entire application while it’s waiting on the image. Once the image is loaded, the message should go away and you should see the image.
An easy way to achieve this is through a virtual proxy. The virtual proxy can stand in place of the icon, manage the background loading, and before the image is fully retrieved from the network, display “Loading CD cover, please wait...”. Once the image is loaded, the proxy delegates the display to the Icon.
Before writing the code for the CD Cover Viewer, let’s look at the class diagram. You’ll see this looks just like our Remote Proxy class diagram, but here the proxy is used to hide an object that is expensive to create (because we need to retrieve the data for the Icon over the network) as opposed to an object that actually lives somewhere else on the network.
① ImageProxy first creates an ImageIcon and starts loading it from a network URL.
② While the bytes of the image are being retrieved, ImageProxy displays “Loading CD cover, please wait...”.
③ When the image is fully loaded, ImageProxy delegates all method calls to the image icon, including paintIcon(), getWidth() and getHeight().
④ If the user requests a new image, we’ll create a new proxy and start the process over.
① Use the menu to load different CD covers; watch the proxy display “loading” until the image has arrived.
② Resize the window as the “loading” message is displayed. Notice that the proxy is handling the loading without hanging up the Swing window.
③ Add your own favorite CDs to the ImageProxyTestDrive.
Java’s got its own proxy support right in the java.lang.reflect package. With this package, Java lets you create a proxy class on the fly that implements one or more interfaces and forwards method invocations to a class that you specify. Because the actual proxy class is created at runtime, we refer to this Java technology as a dynamic proxy.
We’re going to use Java’s dynamic proxy to create our next proxy implementation (a protection proxy), but before we do that, let’s quickly look at a class diagram that shows how dynamic proxies are put together. Like most things in the real world, it differs slightly from the classic definition of the pattern:
Because Java creates the Proxy class for you, you need a way to tell the Proxy class what to do. You can’t put that code into the Proxy class like we did before, because you’re not implementing one directly. So, if you can’t put this code in the Proxy class, where do you put it? In an InvocationHandler. The job of the InvocationHandler is to respond to any method calls on the proxy. Think of the InvocationHandler as the object the Proxy asks to do all the real work after it’s received the method calls.
Okay, let’s step through how to use the dynamic proxy...
Every town needs a matchmaking service, right? You’ve risen to the task and implemented a dating service for Objectville. You’ve also tried to be innovative by including a “Hot or Not” feature in the service where participants can rate each other—you figure this keeps your customers engaged and looking through possible matches; it also makes things a lot more fun.
Your service revolves around a PersonBean that allows you to set and get information about a person:
Now let’s check out the implementation...
While we suspect other factors may be keeping Elroy from getting dates, he is right: you shouldn’t be able to vote for yourself or to change another customer’s data. The way our PersonBean is defined, any client can call any of the methods.
This is a perfect example of where we might be able to use a Protection Proxy. What’s a Protection Proxy? It’s a proxy that controls access to an object based on access rights. For instance, if we had an employee object, a Protection Proxy might allow the employee to call certain methods on the object, a manager to call additional methods (like setSalary()), and a human resources employee to call any method on the object.
In our dating service we want to make sure that a customer can set his own information while preventing others from altering it. We also want to allow just the opposite with the HotOrNot ratings: we want the other customers to be able to set the rating, but not that particular customer. We also have a number of getter methods in the PersonBean, and because none of these return private information, any customer should be able to call them.
The Internet bubble seems a distant memory; those were the days when all you needed to do to find a better, higher-paying job was to walk across the street. Even agents for software developers were in vogue...
We have a couple of problems to fix: customers shouldn’t be changing their own HotOrNot rating and customers shouldn’t be able to change other customers’ personal information. To fix these problems we’re going to create two proxies: one for accessing your own PersonBean object and one for accessing another customer’s PersonBean object. That way, the proxies can control what requests can be made in each circumstance.
To create these proxies we’re going to use the Java API’s dynamic proxy that you saw a few pages back. Java will create two proxies for us; all we need to do is supply the handlers that know what to do when a method is invoked on the proxy.
Create two InvocationHandlers.
InvocationHandlers implement the behavior of the proxy. As you’ll see, Java will take care of creating the actual proxy class and object; we just need to supply a handler that knows what to do when a method is called on it.
Write the code that creates the dynamic proxies.
We need to write a little bit of code to generate the proxy class and instantiate it. We’ll step through this code in just a bit.
Wrap any PersonBean object with the appropriate proxy.
When we need to use a PersonBean object, either it’s the object of the customer himself (in that case, will call him the “owner”), or it’s another user of the service that the customer is checking out (in that case we’ll call him “non-owner”).
In either case, we create the appropriate proxy for the PersonBean.
We know we need to write two invocation handlers, one for the owner and one for the non-owner. But what are invocation handlers? Here’s the way to think about them: when a method call is made on the proxy, the proxy forwards that call to your invocation handler, but not by calling the invocation handler’s corresponding method. So, what does it call? Have a look at the InvocationHandler interface:
There’s only one method, invoke(), and no matter what methods get called on the proxy, the invoke() method is what gets called on the handler. Let’s see how this works:
When invoke() is called by the proxy, how do you know what to do with the call? Typically, you’ll examine the method that was called on the proxy and make decisions based on the method’s name and possibly its arguments. Let’s implement the OwnerInvocationHandler to see how this works:
Now, all we have left is to dynamically create the Proxy class and instantiate the proxy object. Let’s start by writing a method that takes a PersonBean and knows how to create an owner proxy for it. That is, we’re going to create the kind of proxy that forwards its method calls to the OwnerInvocationHandler. Here’s the code:
You now know about the remote, virtual and protection proxies, but out in the wild you’re going to see lots of mutations of this pattern. Over here in the Proxy corner of the zoo we’ve got a nice collection of wild proxy patterns that we’ve captured for your study.
Our job isn’t done; we are sure you’re going to see more variations of this pattern in the real world, so give us a hand in cataloging more proxies. Let’s take a look at the existing collection:
Your design toolbox is almost full; you’re prepared for almost any design problem that comes your way.