/* (c) 2014 - 2016 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.Method; import java.lang.reflect.Proxy; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WorkspaceInfo; /** * A proxy which holds onto an identifier which will later be * resolved into a real object. * * @author Justin Deoliveira, The Open Planning Project * */ public class ResolvingProxy extends ProxyBase { /** * Avoids the cost of looking up over and over the same proxy class */ static final Map<Class, Constructor> PROXY_CLASS_CONSTRUCTOR_CACHE = new ConcurrentHashMap<>(); /** * Wraps an object in the proxy. * * @throws RuntimeException If creating the proxy fails. */ public static <T> T create( String ref, Class<T> clazz ) { return create(ref, null, clazz); } /** * Wraps an object in the proxy, specifying a prefix for the reference. * * @throws RuntimeException If creating the proxy fails. */ public static <T> T create( String ref, String prefix, Class<T> clazz ) { InvocationHandler h = new ResolvingProxy(ref, prefix); T proxy; try { Constructor<T> constructor = PROXY_CLASS_CONSTRUCTOR_CACHE.get(clazz); if(constructor == null) { Class proxyClass = Proxy.getProxyClass( clazz.getClassLoader(), clazz ); constructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); } proxy = (T) constructor.newInstance(new Object[] { h } ); } catch( Exception e ) { throw new RuntimeException( e ); } return proxy; } @SuppressWarnings("unchecked") public static <T> T resolve( Catalog catalog, T object ) { if ( object instanceof Proxy ) { InvocationHandler h = Proxy.getInvocationHandler( object ); if ( h instanceof ResolvingProxy ) { String ref = ((ResolvingProxy)h).getRef(); String pre = ((ResolvingProxy)h).getPrefix(); if ( object instanceof WorkspaceInfo ) { Object ws = catalog.getWorkspace( ref ); if ( ws == null ) { ws = catalog.getWorkspaceByName( ref ); } return (T) ws; } if ( object instanceof NamespaceInfo ) { Object ns = catalog.getNamespace( ref ); if ( ns == null ) { ns = catalog.getNamespaceByPrefix( ref ); } return (T) ns; } if ( object instanceof StoreInfo ) { if ( object instanceof DataStoreInfo ) { return (T) catalog.getDataStore( ref ); } if ( object instanceof CoverageStoreInfo ) { return (T) catalog.getCoverageStore( ref ); } T resolved = (T) catalog.getStore( ref, StoreInfo.class ); if (resolved == null) { if (ref.indexOf(":") > 0) { String[] qualifiedName = ref.split(":"); resolved = (T) catalog.getStoreByName( qualifiedName[0], qualifiedName[1], StoreInfo.class ); } else { resolved = (T) catalog.getStoreByName( ref, StoreInfo.class ); } } return resolved; } if ( object instanceof ResourceInfo ) { if ( object instanceof FeatureTypeInfo ) { Object r = catalog.getFeatureType( ref ); if ( r == null ) { r = catalog.getFeatureTypeByName( ref ); } return (T) r; } if ( object instanceof CoverageInfo ) { Object r = catalog.getCoverage( ref ); if ( r == null ) { r = catalog.getCoverageByName( ref ); } return (T) r; } Object r = catalog.getResource( ref, ResourceInfo.class ); if ( r == null ) { r = catalog.getResourceByName( ref, ResourceInfo.class ); } return (T) r; } if ( object instanceof LayerInfo ) { Object l = catalog.getLayer( ref ); if ( l == null ) { l = catalog.getLayerByName( ref ); } return (T) l; } if ( object instanceof LayerGroupInfo ) { Object g = catalog.getLayerGroup( ref ); if ( g == null ) { g = catalog.getLayerGroupByName( ref ); } return (T) g; } if ( object instanceof StyleInfo ) { Object s = catalog.getStyle( ref ); if ( s == null ) { if (pre != null) { //look up in workspace s = catalog.getStyleByName(pre, ref); } } if (s == null) { //still no luck s = catalog.getStyleByName( ref ); } return (T) s; } } } return object; } /** * the reference */ String ref; /** * optional prefix, used to reference by name inside of a workspace */ String prefix; public ResolvingProxy(String ref) { this(ref, null); } public ResolvingProxy(String ref, String prefix) { this.ref = ref; this.prefix = prefix; } public String getRef() { return ref; } public String getPrefix() { return prefix; } @Override protected Object handleGetUnSet(Object proxy, Method method, String property) throws Throwable { if ( "id".equalsIgnoreCase( property ) ) { return ref; } return null; } @Override protected Object handleOther(Object proxy, Method method, Object[] args) throws Throwable { // if we get here the reference is dangling, have it use the proxy hashcode and equals // to allow comparing the references with no cryptic exceptions that would not // help debugging the broken reference final String methodName = method.getName(); if(methodName.equals("hashCode")) { return hashCode(); } else if(methodName.equals("equals")) { // allows an object with dangling reference to be compared with itself by equality return args[0] == null || equals(args[0]); } return null; } }