Friday, November 6, 2009

Proxy




















Chapter 4 -
Fundamental Design Patterns
Patterns in Java, Volume 1: A Catalog of Reusable Design Patterns Illustrated with UML, Second Edition
by Mark Grand
John Wiley & Sons � 2002



























Proxy



Proxy is a very general pattern that occurs in many other patterns but never by itself in its pure form. The Proxy pattern was described previously in [GoF95].




Synopsis


The Proxy pattern forces method calls to an object to occur indirectly through a proxy object that acts as a surrogate for the other object, delegating method calls to that object. Classes for proxy objects are declared in a way that usually eliminates the client object’s awareness that it is dealing with a proxy.






Context


A proxy object is an object that receives method calls on behalf of another object. Client objects call the proxy object’s method. The proxy object’s methods do not directly provide the service that its clients expect; instead, they call the methods of the object that provides the actual service. Figure 4.18 shows this structure.






Figure 4.18: Method calls through a proxy object.

Although a proxy object’s methods do not directly provide the service its clients expect, the proxy object provides some management of those services. Proxy objects share a common interface with the service-providing object. Whether client objects directly access a service-providing object or a proxy object, they access it through the common interface rather than an instance of a particular class. Doing so allows client objects to be unaware that they call the methods of a proxy object rather than the methods of the actual service-providing object. Transparent management of another object’s services is the basic reason for using a proxy object.


A proxy object can be used to provide many types of service management. Some of the more important types are documented elsewhere in this work as patterns in their own right. The following are some of the more common uses for proxies:




















l



Create the appearance that a method that takes a long time to complete returns immediately.




l



Create the illusion that an object on a different machine is an ordinary local object. This kind of proxy—called a remote proxy, or stub—is used by RMI, CORBA and other ORBs (object request brokers). Stub classes are described as part of the discussion of ORBs in Patterns in Java, Volume 3.




l



Control access to a service-providing object based on a security policy. This use of proxies is described as the Protection Proxy pattern in Patterns in Java, Volume 3.




l



Create the illusion that a service object exists before it actually does. Doing so can be useful if a service object is expensive to create and its services may not be needed. This use of proxies is documented as the Virtual Proxy pattern.








Forces























J



It is not possible for a service-providing object to provide a service at a convenient time or place.




J



Gaining visibility to an object is complex and you want to hide that complexity.




J



Access to a service-providing object must be controlled without adding complexity to the service-providing object or coupling the service to the access control policy.




J



The management of a service should be provided in a way that is transparent to the clients of that service.




J



The clients of a service-providing object do not care about the identity of the object’s class or which instance of its class they are working with.








Solution


Transparent management of a service-providing object can be accomplished by forcing all access to the service-providing object to be accomplished through a proxy object. For the management to be transparent, both the proxy object and the service-providing object must either implement a common interface or be instances of a common superclass.



Figure 4.19 shows the organization of the Proxy pattern but not details for implementing any particular access management policy. The Proxy pattern is not very useful unless it implements some particular access management policy. The Proxy pattern is so commonly used with some access management policies that these combinations are described elsewhere as patterns in their own right.






Figure 4.19: Proxy class diagram.





Implementation


Without any specific management policy, the implementation of the Proxy pattern simply involves creating a class that shares a common superclass or interface with a service-providing class and delegates operations to instances of the service-providing class.






Consequences














J



The service provided by a service-providing object is managed in a manner transparent to the object and its clients.




J



Unless the use of proxies introduces new failure modes, there is normally no need for the code of client classes to reflect the use of proxies.








Code Example


The Proxy pattern is not useful in its pure form; it must be combined with a service management behavior to accomplish anything useful. This example of the Proxy pattern uses proxies to defer an expensive operation until it is actually needed. If the operation is not needed, the operation is never performed.


The example is a proxy for instances of classes such as java.util. HashMap that implement both the java.util.Map and the java.long. Cloneable interfaces. The purpose of the proxy is to delay cloning the underlying Map object until it is known that this expensive operation is actually needed.








One reason for cloning a Map object is to avoid holding a lock on the object for a long time when all that is desired is to fetch multiple key-value pairs. In a multi-threaded program, to ensure that a Map object is in a consistent state while fetching key-value pairs from it, you can use a synchronized method to obtain exclusive access to the Map object. While that is happening, other threads wait to gain access to the same Map object. Such waiting may be unacceptable


Not every class that implements the Map interface permits its instances to be cloned. However, many Map classes, such as java.util. HashMap and java.util.TreeMap, do permit their instances to be cloned.


Cloning a Map object prior to fetching values out of it is a defensive measure. Cloning the Map object avoids the need to obtain a synchronization lock on a Hashtable beyond the time it takes for the clone operation to complete. When you have a freshly cloned copy of a Map object, you can be sure that no other thread has access to the copy. Because no other thread has access to the copy, you will be able to fetch key-value pairs from the copy without any interference from other threads.


If after you clone a Map object no subsequent modification to the original Map object occurs, the time and memory spent in creating the clone was wasted. The point of this example is to avoid waste. It accomplishes this by delaying the cloning of a Map object until a modification to it actually occurs.


The name of the proxy class is LazyCloneMap. Instances of Lazy CloneMap are a copy-on-write proxy for a Map object. When a proxy’s clone method is called, it returns a copy of the proxy but does not copy the underlying Map object. At this point, both the original and the copy of the proxy refer to the same underlying Map object. When one of the proxies is asked to modify the underlying Map object, it will recognize that it uses a shared underlying Map object; it will clone the underlying Map object before it makes the modification. Figure 4.20 shows the structure of the Lazy CloneMap class.






Figure 4.20: LazyCloneMap.

What follows is the beginning of a listing of the LazyCloneMap class.



public class LazyCloneMap implements Map, Cloneable {
/**
* The Map object that this object is a proxy for.
*/
    private Map underlyingMap;

Proxy classes have a way for a proxy object to refer to the object it is a proxy for. This proxy class has an instance variable that refers to the underlying Map object. LazyCloneMap objects know when they share their underlying Map object with other LazyCloneMap objects by keeping a count of how many refer to the same underlying Map object. They keep this count in an instance of a class named MutableInteger. The MutableInteger object is shared or not by the same LazyCloneMap objects that share the underlying Map object. The listing for MutableInteger appears later.



    /**
* This is the number of proxy objects that share the same
* underlying map.
*/
    private MutableInteger refCount;


The clone method that classes inherit from the Object class is protected. There is no interface that declares the public clone method implemented by classes such as HashMap and TreeMap. For this reason, the Lazy- CloneMap class must access the clone method of underlying Map objects through an explicit java.lang.reflect.Method object.



    /**
* This is used to invoke the clone method of the
* underlying Map object.
*/
    private Method cloneMethod;

    private static Class[] cloneParams = new Class[0];

/**
* Constructor
*
* @param underlyingMap
* The Map object that this object should be a
* proxy for.
* @throws NoSuchMethodException
* If the underlyingMap object does not have a
* public clone() method.
* @throws InvocationTargetException
* The object constructed by this constructor uses
* a clone of the given Map object. If the Map
* object's clone method throws an exception, this
* constructor throws an InvocationTargetException
* whose getCause method returns the original *
* exception.
*/
    public LazyCloneMap(Map underlyingMap)
                             throws NoSuchMethodException,
                                   InvocationTargetException {
        Class mapClass = underlyingMap.getClass();
        cloneMethod = mapClass.getMethod("clone",cloneParams);
try {
            this.underlyingMap =
              (Map)cloneMethod.invoke(underlyingMap, null);
} catch (IllegalAccessException e) {
// This should not happen.
} // try
        refCount = new MutableInteger(1);
    }  // constructor(Map)


This class’s clone method does not copy the underlying Map object. It does increment the reference count that is also shared by the original LazyCloneMap object and the copy. Incrementing the value allows the LazyCloneMap objects to know that they share their underlying Map object.



    public Object clone() {
LazyCloneMap theClone;
try {
            Cloneable original = (Cloneable)underlyingMap;
            theClone = (LazyCloneMap)super.clone();
} catch (CloneNotSupportedException e) {
// this should never happen
theClone = null;
} // try
        refCount.setValue(1+refCount.getValue());
        return theClone;
    }  // clone()

The following private method is called by public methods of the LazyCloneMap class, such as put and clear, that modify the underlying Map object. The public methods call this private method before modifying the underlying Map object to ensure that they are not sharing the underlying Map object.




    private void ensureUnderlyingMapNotShared() {
        if (refCount.getValue()>1) {
try {
                underlyingMap =
                  (Map)cloneMethod.invoke(underlyingMap, null);
                refCount.setValue(refCount.getValue()-1);
                refCount = new MutableInteger(1);
} catch (IllegalAccessException e) {
// This should not happen.
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
throw new RuntimeException("clone failed",
cause);
} // try
} // if
    } // ensureUnderlyingMapNotShared()


The ensureUnderlyingMapNotShared method begins by determining whether the value of the reference count is greater than one. If it is greater than one, it will know that this LazyCloneMap object shares its underlying Map object with other LazyCloneMap objects. The ensureUnderlyingMap NotShared method clones the underlying Map object so this LazyCloneMap object will have its own copy of the underlying Map object that it does not share with any other LazyCloneMap object. It decrements the reference count so that the other LazyCloneMap objects with which this object shared an underlying Map object will know that they no longer share the same underlying Map object with this object. Also, it creates a new object to contain the reference count of 1 indicating that this LazyCloneMap object does not share its underlying Map object.


The rest of the methods in the LazyCloneMap class delegate their work to the corresponding method of the underlying Map object.



    public int size(){
        return underlyingMap.size();
    }

    public boolean isEmpty(){
        return underlyingMap.isEmpty();
    }

    public boolean containsKey(Object key){
        return underlyingMap.containsKey(key);
    }

    public boolean containsValue(Object value){
        return underlyingMap.containsValue(value);
    }

    public Object get(Object key){
        return underlyingMap.get(key);
    }

Because the next four methods modify the underlying Map object, they call the ensureUnderlyingMapNotShared method before they delegate their work to the underlying Map object.



    public Object put(Object key, Object value){
        ensureUnderlyingMapNotShared();
        return underlyingMap.put(key, value);
    }

    public Object remove(Object key){
        ensureUnderlyingMapNotShared();
        return underlyingMap.remove(key);
    }

    public void putAll(Map m){
        ensureUnderlyingMapNotShared();
        underlyingMap.putAll(m);
    }

    public void clear(){
        ensureUnderlyingMapNotShared();
        underlyingMap.clear();
    }

    public Set keySet(){
        return underlyingMap.keySet();
    }

    public Collection values(){
return underlyingMap.values();
      }

    public Set entrySet(){
        return underlyingMap.entrySet();
    }

    public boolean equals(Object that){
        return underlyingMap.equals(that);
    }

    public int hashCode(){
        return underlyingMap.hashCode();
    }
}

The following is a listing of the MutableInteger class that the LazyCloneMap class uses:



public class MutableInteger {
    public int val;

    public MutableInteger( int value ) {
        setValue( value );
    }

    public int getValue() {
        return( val );
    }

    public void setValue( int value ) {
        val = value;
    }
    ...
}





Related Patterns



Protection Proxy.  The Protection Proxy pattern (described in Patterns in Java, Volume 3) uses a proxy to enforce a security policy on access to a service-providing object.



Façade.  The Façade pattern uses a single object as a front end to a set of interrelated objects rather than as a front end to a single object.



Object Request Broker.  The Object Request Broker pattern (described in Patterns in Java, Volume 3) uses a proxy to hide the fact that a service object is located on a different machine than the client objects that want to use it.



Virtual Proxy.  The Virtual Proxy pattern uses a proxy to create the illusion that a service-providing object exists before it has actually been created. The Virtual Proxy pattern is useful if the object is expensive to create and its services may not be needed. The copy-on-write proxy discussed under the Code Example heading for the Proxy pattern is a kind of virtual proxy.



Decorator.  The Decorator pattern is structurally similar to the Proxy pattern in that it forces access to a service-providing object to be done indirectly through another object. The difference is a matter of intent. Instead of trying to manage the service, the indirection object in some way enhances the service.


















No comments:

Post a Comment