/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.catalog.impl; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Utility class for working with proxies. * * @author Justin Deoliveira, OpenGeo * */ public class ProxyUtils { /** * Avoids the cost of looking up over and over the same proxy class */ static final Map<ProxyClassConstructorKey, Constructor> PROXY_CLASS_CACHE = new ConcurrentHashMap<>(); static final class ProxyClassConstructorKey { Class c1; Class c2; public ProxyClassConstructorKey(Class c1, Class c2) { this.c1 = c1; this.c2 = c2; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((c1 == null) ? 0 : c1.hashCode()); result = prime * result + ((c2 == null) ? 0 : c2.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ProxyClassConstructorKey other = (ProxyClassConstructorKey) obj; if (c1 == null) { if (other.c1 != null) { return false; } } else if (!c1.equals(other.c1)) { return false; } if (c2 == null) { if (other.c2 != null) { return false; } } else if (!c2.equals(other.c2)) { return false; } return true; } } /** * Creates a proxy for the specified object. * * @param proxyObject The object to proxy. * @param clazz The explicit interface to proxy. * @param h The invocation handler to intercept method calls. */ public static <T> T createProxy(T proxyObject, Class<T> clazz, InvocationHandler h) { ProxyClassConstructorKey key = new ProxyClassConstructorKey(proxyObject.getClass(), clazz); Constructor<?> constructor = PROXY_CLASS_CACHE.get(key); T proxy; try { if(constructor == null) { // proxy all interfaces implemented by the source object List<Class> proxyInterfaces = (List) Arrays.asList( proxyObject.getClass().getInterfaces() ); // ensure that the specified class is included boolean add = true; for ( Class interfce : proxyObject.getClass().getInterfaces() ) { if ( clazz.isAssignableFrom( interfce) ) { add = false; break; } } if( add ) { // make the list mutable (Arrays.asList is not) and then add the extra interfaces proxyInterfaces = new ArrayList<Class>(proxyInterfaces); proxyInterfaces.add( clazz ); } Class proxyClass = Proxy.getProxyClass( clazz.getClassLoader(), proxyInterfaces.toArray(new Class[proxyInterfaces.size()]) ); constructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); PROXY_CLASS_CACHE.put(key, constructor); } proxy = (T) constructor.newInstance(new Object[] { h } ); } catch( Exception e ) { throw new RuntimeException( e ); } return proxy; } /** * Unwraps a proxy returning the underlying object, if one exists. * <p> * This method handles two cases, the first is the case of a {@link WrappingProxy} in which the * underlying proxy object is returned.. The second is the {@link ProxyList} case in which the * underlying list is returned. * </p> * @param object The proxy object. * @param handlerClass The invocation handler class. * * @return The underlying proxied object, or the object passed in if no underlying object is * recognized. */ public static <T> T unwrap( T object, Class<? extends InvocationHandler> handlerClass) { if ( object instanceof Proxy ) { InvocationHandler h = handler( object, handlerClass ); if ( h != null && h instanceof WrappingProxy) { return (T) ((WrappingProxy) h).getProxyObject(); } } if ( object instanceof ProxyList ) { return (T) ((ProxyList)object).proxyList; } return object; } /** * Returns the invocation handler from a proxy object. * * @param object The proxy object. * @param handlerClass The class of invocation handler to return. * * @return THe invocation handler, or null if non matchining the specified class can be found. */ public static <H extends InvocationHandler> H handler( Object object, Class<H> handlerClass ) { if ( object instanceof Proxy ) { InvocationHandler h = Proxy.getInvocationHandler( object ); if (handlerClass.isInstance(h)) { return (H) h; } } return null; } }