Skip to main content

Proxies

3 replies [Last post]
subanark
Offline
Joined: 2004-11-26

Here is a rather simple suggestion:
Add a method to the Proxy class that does not take an invocation handler instead an object, and instead create a default one that simply fowards calls. This would allow someone to easly restrict access by allowing only access to a given interface.

E.g.

<br />
public interface ReadableValue<br />
{<br />
   public Object getValue();<br />
}<br />
public class Value implements ReadableValue<br />
{<br />
   private Object value;<br />
   public Value(Object value)<br />
   {<br />
       this.value = value;<br />
   }<br />
   public Object getValue()<br />
   {<br />
      return value;<br />
   }<br />
   public void setValue(Object value)<br />
   {<br />
      this.value = value;<br />
   }<br />
}<br />

Lets say you use this class but only want the consumer to have read access to it. If this proxy method was implemented (hopefully not as an InvocationHandler, but rather as part of the generated code). Then one could mearly:
<br />
return Proxy.newProxyInstance(null,new Class[]{ReadableValue.class},myValue);<br />

Then you would be sure that the client could not change myValue.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
subanark
Offline
Joined: 2004-11-26

I have an example of such a class:
[code]
/*
* ProxyRegistry.java
*
* Created on January 16, 2005, 10:06 AM
*/

package proxyregistry;
import java.util.*;
import java.lang.reflect.*;
/**
*
* @author artsh
*/
public class ProxyRegistry
{
private ArrayList packages = new ArrayList();
private ArrayList subPackages;
private WeakHashMap

proxyCache = new WeakHashMap(); /** Creates a new instance of ProxyRegistry */ public ProxyRegistry() { } public synchronized void addHiddenPackages(Package... packages) { this.packages.ensureCapacity(packages.length+this.packages.size()); for(Package pack: packages) { this.packages.add(pack); } } public synchronized void addHiddenSubPackages(String... packageNames) { subPackages.ensureCapacity(packageNames.length+subPackages.size()); for(String packageName: packageNames) { subPackages.add(packageName); } } public Object wrap(Object obj) { Object proxy = proxyCache.get(obj); if(proxy != null) return proxy; Class objClass = obj.getClass(); if(isHidden(objClass)) { proxyCache.put(obj,obj); return obj; } Class[] declaredInterfaces = objClass.getInterfaces(); ArrayList> interfaces = new ArrayList>(declaredInterfaces.length); for(Class clazz:declaredInterfaces) { if(!java.lang.reflect.Modifier.isPublic(clazz.getModifiers()) || !isHidden(clazz)) interfaces.add(clazz); } proxy = Proxy.newProxyInstance(null, interfaces.toArray(new Class[interfaces.size()]),new ProxyRegistryHandler(obj,interfaces,this)); proxyCache.put(obj,proxy); return proxy; } protected boolean isHidden(Class objClass) { Package pack = objClass.getPackage(); PackageLookup: if(!packages.contains(pack)) { String packageName = pack.getName(); while(packageName != null) { if(subPackages.contains(packageName)) break PackageLookup; int dotIndex = packageName.lastIndexOf('.'); if(dotIndex == -1) packageName = null; else packageName = packageName.substring(0,dotIndex); } return false; } return true; } } /* * ProxyRegistryHandler.java * * Created on January 16, 2005, 10:43 AM */ package proxyregistry; import java.lang.reflect.*; import java.util.*; /** * * @author artsh */ public class ProxyRegistryHandler implements InvocationHandler { ArrayList> interfaces; ProxyRegistry registry; Object obj; /** Creates a new instance of ProxyRegistryHandler */ ProxyRegistryHandler(Object obj,ArrayList> interfaces,ProxyRegistry registry) { this.interfaces = interfaces; this.registry = registry; this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class clazz = method.getDeclaringClass(); if(clazz == Object.class || interfaces.contains(clazz)) { Object retValue = method.invoke(obj,args); return registry.wrap(retValue); } throw new IllegalArgumentException(method+" is invalid"); } } [/code] Note that I should be using a WeakIdentityHashtable, but since one does not exist... just pretend I am.
tackline
Offline
Joined: 2003-06-19

Couldn't you just write the method yourself?

Not that it would universally work. As a simple example, consider types that return this to facilitate chaining. In general, any non-value returned or passed to an actual argument is going to cause issues.

subanark
Offline
Joined: 2004-11-26

Yes I guess that does raise a probem...
Despite that, although I could write a method that does that myself, having the VM generate byte code instead of using reflection would reduce the slow down seen with this.

This problem could be solved using a (somewhat complex) class web in which methods return proxy instances of return values. Successfully implementing this may require deep knowledge of the api you are using though.