Difference between revisions of "Proxy"

From CDOT Wiki
Jump to: navigation, search
 
(34 intermediate revisions by the same user not shown)
Line 1: Line 1:
The <i>Proxy</i> pattern is used in software development to create a placeholder for an object. The object is not actually created until the information that the object holds is required. This extra layer of abstraction saves time when a program must access a database or a disk for the information.
+
The <i>Proxy</i> pattern is used in software development to create a placeholder for an object. Proxies provide an interface to another object such as a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.
<br />
 
<br />
 
If the information is never required, the database/disk will never be queried and the system will run more efficiently with less slowdowns for unnecessary materializations.
 
 
<br />
 
<br />
 
<br />
 
<br />
Line 9: Line 6:
 
==Proxy Design Pattern==
 
==Proxy Design Pattern==
  
When a request is made to a Proxy, the real object is then instantiated. From then on, any further requests are made to the real object. There are four common situations where a Proxy pattern is required:
+
"<i>Any operations performed on the proxies are forwarded to the original object. Once all instances of the proxy are out of scope, the complex object's memory may be deallocated.</i>" - <b>Proxy Pattern, Wikipedia.org</b>
 +
<br />
 +
There are four common situations where a Proxy pattern is required:
 
<br />
 
<br />
  
Line 37: Line 36:
 
<br />
 
<br />
  
===UML Diagram===
+
==UML Diagram==
 
Here is a UML depiction of the Proxy pattern:
 
Here is a UML depiction of the Proxy pattern:
 
<br />
 
<br />
 
[[Image:ProxyUML.gif|UML Diagram for the Proxy Design Pattern]]
 
[[Image:ProxyUML.gif|UML Diagram for the Proxy Design Pattern]]
 +
<br />
 +
Source: Downloaded from http://www.developer.com/design/article.php/3309461
 +
<br />
 +
<br />
 +
A more complete UML diagram of the Proxy pattern:
 +
<br />
 +
[[Image:Proxy.gif|A Complete UML Diagram for the Proxy Design Pattern]]
 +
<br />
 +
Source: Downloaded from http://www.dofactory.com/Patterns/PatternProxy.aspx
 
<br />
 
<br />
 
<br />
 
<br />
Line 46: Line 54:
 
<br />
 
<br />
 
[[Image:ProxyDCD.jpg|Design Class Diagram for the Proxy Design Pattern]]
 
[[Image:ProxyDCD.jpg|Design Class Diagram for the Proxy Design Pattern]]
 +
<br />
 +
Source: Downloaded from http://home.earthlink.net/~huston2/dp/all_uml.html
 +
<br />
 +
 +
==Code Samples==
 +
These code samples will demonstrate real world uses of the Proxy Design Pattern. Each code sample is from a real working software system.
 +
<br />
 +
===Virtual Proxy - Python===
 +
This Sample code is from a Python Remote Method Invocation program. It is copyrighted by <b>Irmen de Jong</b> and licensed by <b>[http://www.google.com/codesearch?hl=en&q=show:dgvZt53GNuE:9VmFCU8eSwo:nSqKvMM2F_M&sa=N&cd=13&ct=rl&cs_p=http://gentoo.osuosl.org/distfiles/Pyro-3.4.tar.gz&cs_f=Pyro-3.4/Pyro/EventService/Clients.py MIT]</b>. The code in this example is located in the <i>Clients.py</i> file of the <b>Pyro</b> system. It allows a user to write a module containing a class to be accessed remotely. The class the user created is registered by the Pyro Name Server. The client creates proxies for the remote objects to be invoked when a call is made to the object. The Virtual Proxy pattern can be seen throughout this code file as highlighted:
 +
<br />
 +
<pre>
 +
import Pyro.core, Pyro.naming, Pyro.constants
 +
import Pyro.EventService.Server
 +
from Pyro.errors import *
 +
 +
# SUBSCRIBER: subscribes to certain events.
 +
class Subscriber(Pyro.core.CallbackObjBase):
 +
def __init__(self, ident=None):
 +
Pyro.core.ObjBase.__init__(self)
 +
Pyro.core.initServer()
 +
Pyro.core.initClient()
 +
daemon = Pyro.core.Daemon()
 +
locator = Pyro.naming.NameServerLocator(identification=ident)
 +
self.NS = locator.getNS(host=Pyro.config.PYRO_NS_HOSTNAME)
 +
daemon.useNameServer(self.NS)
 +
daemon.connect(self)  #  will also set self.daemon...
 +
uri = self.NS.resolve(Pyro.constants.EVENTSERVER_NAME)
 +
                # creates a proxy for the remote class to be called on later to access the
 +
                # remote object and saves making an expensive call to a DB/disk if it isn't
 +
                # required.
 +
self.eventservice=Pyro.core.getProxyForURI(uri)
 +
self.eventservice._setIdentification(ident)
 +
self.abortListen=0
 +
self.daemon=daemon # make sure daemon doesn't get garbage collected now
 +
 +
def subscribe(self,subjects):
 +
# Subscribe to one or more subjects.
 +
# It is safe to call this multiple times.
 +
                # gets a proxy for the service so that an expensive call isn't require until
 +
                # the actual service is needed.
 +
self.eventservice.subscribe(subjects, self.getProxy())
 +
def subscribeMatch(self,subjectPatterns):
 +
# Subscribe to one or more subjects (by pattern)
 +
# It is safe to call this multiple times.
 +
                # gets a proxy for the service so that an expensive call isn't require until
 +
                # the actual service is needed.
 +
self.eventservice.subscribeMatch(subjectPatterns, self.getProxy())
 +
def unsubscribe(self, subjects):
 +
# Unsubscribe the subscriber for the given subject(s).
 +
                # gets a proxy for the service so that an expensive call isn't require until
 +
                # the actual service is needed.
 +
self.eventservice.unsubscribe(subjects, self.getProxy())
 +
 +
def abort(self):
 +
self.abortListen=1
 +
 +
def setThreading(self, threaded):
 +
self.getDaemon().threaded=threaded
 +
 +
def listen(self):
 +
self.getDaemon().requestLoop(lambda s=self: not s.abortListen)
 +
 +
def event(self, event): # callback, override this!
 +
print event
 +
 +
# PUBLISHER: publishes events.
 +
class Publisher:
 +
def __init__(self, ident=None):
 +
Pyro.core.initClient()
 +
locator = Pyro.naming.NameServerLocator(identification=ident)
 +
ns = locator.getNS(host=Pyro.config.PYRO_NS_HOSTNAME)
 +
uri = ns.resolve(Pyro.constants.EVENTSERVER_NAME)
 +
                # creates a proxy for the remote class to be called on later to access the
 +
                # remote object and saves making an expensive call to a DB/disk if it isn't
 +
                # required.
 +
self.eventservice=Pyro.core.getProxyForURI(uri)
 +
self.eventservice._setIdentification(ident)
 +
 +
def publish(self, subjects, msg):
 +
self.eventservice.publish(subjects,msg)
 +
</pre>
 +
<br />
 +
 +
===Virtual Proxy - Java===
 +
This sample code is from the Apache Geronimo server runtime framework written in J2EE. It is copyrighted by <b>IBM</b> and licensed by <b>[http://www.google.com/codesearch?hl=en&q=show:x-Vlj2phf84:hfeVps_3TKA:KxOJN1EfkwE&sa=N&ct=rd&cs_p=http://archive.apache.org/dist/geronimo/1.0-M5/geronimo-1.0-M5-src.tar.gz&cs_f=geronimo-1.0-M5/LICENSE.txt Apache]</b>. The code in this example is located in the <i>GBeanCollectionReference.java</i> of the <b>Geronimo</b> framework. It allows a user to create their own J2EE 1.4 certified application server. It can deploy servlets, XML, and other web applications. The Virtual Proxy pattern can be seen throughout this code file as highlighted:
 +
<br />
 +
<pre>
 +
/**
 +
*
 +
* Copyright 2003-2004 The Apache Software Foundation
 +
*
 +
*  Licensed under the Apache License, Version 2.0 (the "License");
 +
*  you may not use this file except in compliance with the License.
 +
*  You may obtain a copy of the License at
 +
*
 +
*    http://www.apache.org/licenses/LICENSE-2.0
 +
*
 +
*  Unless required by applicable law or agreed to in writing, software
 +
*  distributed under the License is distributed on an "AS IS" BASIS,
 +
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +
*  See the License for the specific language governing permissions and
 +
*  limitations under the License.
 +
*/
 +
 +
package org.apache.geronimo.gbean.runtime;
 +
 +
import javax.management.ObjectName;
 +
 +
import org.apache.geronimo.gbean.GReferenceInfo;
 +
import org.apache.geronimo.gbean.InvalidConfigurationException;
 +
import org.apache.geronimo.kernel.DependencyManager;
 +
import org.apache.geronimo.kernel.Kernel;
 +
import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
 +
import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
 +
 +
/**
 +
* @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
 +
*/
 +
public class GBeanCollectionReference extends AbstractGBeanReference {
 +
    public GBeanCollectionReference(GBeanInstance gbeanInstance, GReferenceInfo referenceInfo,
 +
      Kernel kernel, DependencyManager dependencyManager) throws InvalidConfigurationException {
 +
        super(gbeanInstance, referenceInfo, kernel, dependencyManager);
 +
    }
 +
 +
    public synchronized boolean start() {
 +
        // We only need to start if there are patterns and we don't already have a proxy
 +
        if (!getPatterns().isEmpty() && getProxy() == null) {
 +
            // add a dependency on our target and create the proxy
 +
            /* sets a collection of proxies for each object instead of making an expensive
 +
              call to a DB/disk to retrieve all of the object information when it isn't
 +
              required. */
 +
            setProxy(new ProxyCollection(getName(), getReferenceType(),
 +
                      getKernel().getProxyManager(), getTargets()));
 +
        }
 +
        return true;
 +
    }
 +
 +
    /* Destroys all of the proxies for the objects as the system is deconstructing. */
 +
    public synchronized void stop() {
 +
        ProxyCollection proxy = (ProxyCollection) getProxy();
 +
        if (proxy != null) {
 +
            proxy.destroy();
 +
            setProxy(null);
 +
        }
 +
    }
 +
 +
    /* Adds a target object to the proxy to be instantiated if or when it is
 +
      required. */
 +
    protected synchronized void targetAdded(ObjectName target) {
 +
        ProxyCollection proxy = (ProxyCollection) getProxy();
 +
        if (proxy != null) {
 +
            proxy.addTarget(target);
 +
        }
 +
    }
 +
 +
    /* Removes a target object from the proxy to to open it's use up to another
 +
      object. */
 +
    protected synchronized void targetRemoved(ObjectName target) {
 +
        ProxyCollection proxy = (ProxyCollection) getProxy();
 +
        if (proxy != null) {
 +
            proxy.removeTarget(target);
 +
        }
 +
    }
 +
 +
    protected LifecycleListener createLifecycleListener() {
 +
        return new LifecycleAdapter() {
 +
                    public void running(ObjectName objectName) {
 +
                        addTarget(objectName);
 +
                    }
 +
 +
                    public void stopping(ObjectName objectName) {
 +
                        removeTarget(objectName);
 +
                    }
 +
 +
                    public void stopped(ObjectName objectName) {
 +
                        removeTarget(objectName);
 +
                    }
 +
 +
                    public void failed(ObjectName objectName) {
 +
                        removeTarget(objectName);
 +
                    }
 +
 +
                    public void unloaded(ObjectName objectName) {
 +
                        removeTarget(objectName);
 +
                    }
 +
                };
 +
    }
 +
}
 +
</pre>
  
 
==References==
 
==References==
Line 56: Line 253:
 
   <li>[http://home.earthlink.net/~huston2/dp/all_uml.html Design Class Diagrams - Proxy UML Diagram]</li>
 
   <li>[http://home.earthlink.net/~huston2/dp/all_uml.html Design Class Diagrams - Proxy UML Diagram]</li>
 
   <li>[http://www.dofactory.com/Patterns/PatternProxy.aspx Information on the Proxy Pattern with a UML Diagram]</li>
 
   <li>[http://www.dofactory.com/Patterns/PatternProxy.aspx Information on the Proxy Pattern with a UML Diagram]</li>
 +
  <li>[http://www.google.com/codesearch?hl=en&q=show:dgvZt53GNuE:9VmFCU8eSwo:nSqKvMM2F_M&sa=N&cd=13&ct=rl&cs_p=http://gentoo.osuosl.org/distfiles/Pyro-3.4.tar.gz&cs_f=Pyro-3.4/Pyro/EventService/Clients.py Python Remote Method Invocation]</li>
 +
  <li>[http://www.google.com/codesearch?hl=en&q=show:hfeVps_3TKA:OPU6OVZro5s&sa=N&ct=rdp&cs_p=http://archive.apache.org/dist/geronimo/1.0-M5/geronimo-1.0-M5-src.tar.gz&cs_f=geronimo-1.0-M5 Geronimo Server Runtime Framework]</li>
 
</ul>
 
</ul>
 
<br />
 
<br />

Latest revision as of 15:48, 22 March 2007

The Proxy pattern is used in software development to create a placeholder for an object. Proxies provide an interface to another object such as a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.

Proxy Design Pattern

"Any operations performed on the proxies are forwarded to the original object. Once all instances of the proxy are out of scope, the complex object's memory may be deallocated." - Proxy Pattern, Wikipedia.org
There are four common situations where a Proxy pattern is required:

  • Remote Proxy - provides a local representative that is located in a different address space.
  • Virtual Proxy - creates expensive objects only when that object's information is required.
  • Protection Proxy - provides access controls for different clients to a specified target object.
  • Smart Reference Proxy - performs additional actions when a specific object is referenced such as counting the number of references, loading an object into memory when first referenced, and checking that the object is locked before access so that no other object may change it.


There are several other Proxy pattern situations less common in design:

  • Copy-on-Write Proxy
  • Cache Proxy
  • Firewall Proxy
  • Synchronization Proxy


UML Diagram

Here is a UML depiction of the Proxy pattern:
UML Diagram for the Proxy Design Pattern
Source: Downloaded from http://www.developer.com/design/article.php/3309461

A more complete UML diagram of the Proxy pattern:
A Complete UML Diagram for the Proxy Design Pattern
Source: Downloaded from http://www.dofactory.com/Patterns/PatternProxy.aspx

Here is a design class diagram using UML of how te Proxy pattern would be used in a system:
Design Class Diagram for the Proxy Design Pattern
Source: Downloaded from http://home.earthlink.net/~huston2/dp/all_uml.html

Code Samples

These code samples will demonstrate real world uses of the Proxy Design Pattern. Each code sample is from a real working software system.

Virtual Proxy - Python

This Sample code is from a Python Remote Method Invocation program. It is copyrighted by Irmen de Jong and licensed by MIT. The code in this example is located in the Clients.py file of the Pyro system. It allows a user to write a module containing a class to be accessed remotely. The class the user created is registered by the Pyro Name Server. The client creates proxies for the remote objects to be invoked when a call is made to the object. The Virtual Proxy pattern can be seen throughout this code file as highlighted:

import Pyro.core, Pyro.naming, Pyro.constants
import Pyro.EventService.Server
from Pyro.errors import *

# SUBSCRIBER: subscribes to certain events.
class Subscriber(Pyro.core.CallbackObjBase):
	def __init__(self, ident=None):
		Pyro.core.ObjBase.__init__(self)
		Pyro.core.initServer()
		Pyro.core.initClient()
		daemon = Pyro.core.Daemon()
		locator = Pyro.naming.NameServerLocator(identification=ident)
		self.NS = locator.getNS(host=Pyro.config.PYRO_NS_HOSTNAME)
		daemon.useNameServer(self.NS)
		daemon.connect(self)  #  will also set self.daemon...
		uri = self.NS.resolve(Pyro.constants.EVENTSERVER_NAME)
                # creates a proxy for the remote class to be called on later to access the
                # remote object and saves making an expensive call to a DB/disk if it isn't
                # required.
		self.eventservice=Pyro.core.getProxyForURI(uri)
		self.eventservice._setIdentification(ident)
		self.abortListen=0
		self.daemon=daemon	# make sure daemon doesn't get garbage collected now

	def subscribe(self,subjects):
		# Subscribe to one or more subjects.
		# It is safe to call this multiple times.
                # gets a proxy for the service so that an expensive call isn't require until
                # the actual service is needed.
		self.eventservice.subscribe(subjects, self.getProxy())
	def subscribeMatch(self,subjectPatterns):
		# Subscribe to one or more subjects (by pattern)
		# It is safe to call this multiple times.
                # gets a proxy for the service so that an expensive call isn't require until
                # the actual service is needed.
		self.eventservice.subscribeMatch(subjectPatterns, self.getProxy())
	def unsubscribe(self, subjects):
		# Unsubscribe the subscriber for the given subject(s).
                # gets a proxy for the service so that an expensive call isn't require until
                # the actual service is needed.
		self.eventservice.unsubscribe(subjects, self.getProxy())

	def abort(self):
		self.abortListen=1

	def setThreading(self, threaded):
		self.getDaemon().threaded=threaded

	def listen(self):
		self.getDaemon().requestLoop(lambda s=self: not s.abortListen)

	def event(self, event):					# callback, override this!
		print event

# PUBLISHER: publishes events.
class Publisher:
	def __init__(self, ident=None):
		Pyro.core.initClient()
		locator = Pyro.naming.NameServerLocator(identification=ident)
		ns = locator.getNS(host=Pyro.config.PYRO_NS_HOSTNAME)
		uri = ns.resolve(Pyro.constants.EVENTSERVER_NAME)
                # creates a proxy for the remote class to be called on later to access the
                # remote object and saves making an expensive call to a DB/disk if it isn't
                # required.
		self.eventservice=Pyro.core.getProxyForURI(uri)
		self.eventservice._setIdentification(ident)

	def publish(self, subjects, msg):
		self.eventservice.publish(subjects,msg)


Virtual Proxy - Java

This sample code is from the Apache Geronimo server runtime framework written in J2EE. It is copyrighted by IBM and licensed by Apache. The code in this example is located in the GBeanCollectionReference.java of the Geronimo framework. It allows a user to create their own J2EE 1.4 certified application server. It can deploy servlets, XML, and other web applications. The Virtual Proxy pattern can be seen throughout this code file as highlighted:

/**
 *
 * Copyright 2003-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.geronimo.gbean.runtime;

import javax.management.ObjectName;

import org.apache.geronimo.gbean.GReferenceInfo;
import org.apache.geronimo.gbean.InvalidConfigurationException;
import org.apache.geronimo.kernel.DependencyManager;
import org.apache.geronimo.kernel.Kernel;
import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
import org.apache.geronimo.kernel.lifecycle.LifecycleListener;

/**
 * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
 */
public class GBeanCollectionReference extends AbstractGBeanReference {
    public GBeanCollectionReference(GBeanInstance gbeanInstance, GReferenceInfo referenceInfo, 
      Kernel kernel, DependencyManager dependencyManager) throws InvalidConfigurationException {
        super(gbeanInstance, referenceInfo, kernel, dependencyManager);
    }

    public synchronized boolean start() {
        // We only need to start if there are patterns and we don't already have a proxy
        if (!getPatterns().isEmpty() && getProxy() == null) {
            // add a dependency on our target and create the proxy
            /* sets a collection of proxies for each object instead of making an expensive
               call to a DB/disk to retrieve all of the object information when it isn't
               required. */
            setProxy(new ProxyCollection(getName(), getReferenceType(), 
                      getKernel().getProxyManager(), getTargets()));
        }
        return true;
    }

    /* Destroys all of the proxies for the objects as the system is deconstructing. */
    public synchronized void stop() {
        ProxyCollection proxy = (ProxyCollection) getProxy();
        if (proxy != null) {
            proxy.destroy();
            setProxy(null);
        }
    }

    /* Adds a target object to the proxy to be instantiated if or when it is
       required. */
    protected synchronized void targetAdded(ObjectName target) {
        ProxyCollection proxy = (ProxyCollection) getProxy();
        if (proxy != null) {
            proxy.addTarget(target);
        }
    }

    /* Removes a target object from the proxy to to open it's use up to another
       object. */
    protected synchronized void targetRemoved(ObjectName target) {
        ProxyCollection proxy = (ProxyCollection) getProxy();
        if (proxy != null) {
            proxy.removeTarget(target);
        }
    }

    protected LifecycleListener createLifecycleListener() {
        return new LifecycleAdapter() {
                    public void running(ObjectName objectName) {
                        addTarget(objectName);
                    }

                    public void stopping(ObjectName objectName) {
                        removeTarget(objectName);
                    }

                    public void stopped(ObjectName objectName) {
                        removeTarget(objectName);
                    }

                    public void failed(ObjectName objectName) {
                        removeTarget(objectName);
                    }

                    public void unloaded(ObjectName objectName) {
                        removeTarget(objectName);
                    }
                };
    }
}

References


Links

BTP600