org.geoserver.catalog.hibernate.HibCatalogImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.catalog.hibernate.HibCatalogImpl.java

Source

/* Copyright (c) 2001 - 2008 TOPP - www.openplans.org. All rights reserved.
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */

package org.geoserver.catalog.hibernate;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.rmi.server.UID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.collections.MultiHashMap;
import org.apache.commons.io.FilenameUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogInfo;
import org.geoserver.catalog.CatalogVisitor;
import org.geoserver.catalog.CoverageDimensionInfo;
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.MapInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.ResourcePool;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.event.CatalogAddEvent;
import org.geoserver.catalog.event.CatalogEvent;
import org.geoserver.catalog.event.CatalogListener;
import org.geoserver.catalog.event.CatalogModifyEvent;
import org.geoserver.catalog.event.CatalogPostModifyEvent;
import org.geoserver.catalog.event.CatalogRemoveEvent;
import org.geoserver.catalog.event.impl.CatalogAddEventImpl;
import org.geoserver.catalog.event.impl.CatalogModifyEventImpl;
import org.geoserver.catalog.event.impl.CatalogPostModifyEventImpl;
import org.geoserver.catalog.event.impl.CatalogRemoveEventImpl;
import org.geoserver.catalog.impl.CoverageDimensionImpl;
import org.geoserver.catalog.impl.CoverageInfoImpl;
import org.geoserver.catalog.impl.FeatureTypeInfoImpl;
import org.geoserver.catalog.impl.LayerGroupInfoImpl;
import org.geoserver.catalog.impl.ModificationProxy;
import org.geoserver.catalog.impl.NamespaceInfoImpl;
import org.geoserver.catalog.impl.ResolvingProxy;
import org.geoserver.catalog.impl.ResourceInfoImpl;
import org.geoserver.catalog.impl.StoreInfoImpl;
import org.geoserver.catalog.impl.StyleInfoImpl;
import org.geoserver.catalog.impl.WorkspaceInfoImpl;
import org.geoserver.hibernate.dao.CatalogDAO;
import org.geoserver.ows.util.ClassProperties;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geotools.util.logging.Logging;
import org.opengis.feature.type.Name;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.vfny.geoserver.global.GeoserverDataDirectory;

/**
 * A {@link Catalog} implementation based on Hibernate
 * 
 * @author ETj
 */

public class HibCatalogImpl implements Catalog, Serializable, ApplicationContextAware {

    /**
    * 
    */
    private static final long serialVersionUID = -33839247662351582L;

    private final static Logger LOGGER = Logging.getLogger(HibCatalogImpl.class);

    /**
     *
     */
    private CatalogDAO catalogDAO;

    /**
     * Flag indicating whether events are fired on commit or as they happen
     */
    private boolean fireEventsOnCommit = false;

    /**
     * listeners
     */
    private List<CatalogListener> listeners = new ArrayList<CatalogListener>();

    /**
     * events
     * 
     * TODO: ETJ rework this: what do we need the key for in this map?
     */
    private Map<HibCatalogImpl, CatalogEvent> events = Collections.synchronizedMap(new MultiHashMap());

    /**
     * resources
     */
    private ResourcePool resourcePool;

    // really needed??
    private GeoServerResourceLoader resourceLoader;

    private HibCatalogImpl() {
        super();
        resourcePool = new ResourcePool(this);
        listeners.add(new HibCatalogUpdater());
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        GeoserverDataDirectory.init((WebApplicationContext) applicationContext);
    }

    public String getId() {
        return "hibcatalog";
    }

    /**
     * @see Catalog#getFactory()
     * @see HibernateCatalogFactory
     */
    public HibCatalogFactoryImpl getFactory() {
        return new HibCatalogFactoryImpl(this);
    }

    /**
     * Sets whether to fire events on commits, intended to be used by unit tests only?
     */
    void setFireEventsOnCommit(boolean fireEventsOnCommit) {
        this.fireEventsOnCommit = fireEventsOnCommit;
    }

    boolean isFireEventsOnCommit() {
        return fireEventsOnCommit;
    }

    // Store methods
    public void add(StoreInfo store) {

        if (LOGGER.isLoggable(Level.INFO))
            LOGGER.info("Adding store " + store.getClass().getSimpleName() + " " + store.getName());

        if (store.getWorkspace() == null) {
            store.setWorkspace(catalogDAO.getDefaultWorkspace());
        }

        validate(store, true);
        resolve(store);
        StoreInfo unwrapped = ModificationProxy.unwrap(store);
        unwrapped.setWorkspace(ModificationProxy.unwrap(unwrapped.getWorkspace()));
        catalogDAO.save(unwrapped);
        ((StoreInfoImpl) store).setId(store.getId()); // make the ModificationProxy aware of the id
        if (LOGGER.isLoggable(Level.INFO))
            LOGGER.info("Added store " + store.getClass().getSimpleName() + "[id: " + store.getId() + " name:"
                    + store.getName() + "]");
        added(store);
    }

    void validate(StoreInfo store, boolean isNew) {
        if (isNull(store.getName())) {
            throw new IllegalArgumentException("Store name must not be null");
        }
        if (store.getWorkspace() == null) {
            throw new IllegalArgumentException("Store must be part of a workspace");
        }

        WorkspaceInfo workspace = store.getWorkspace();
        StoreInfo existing = getStoreByName(workspace, store.getName(), StoreInfo.class);
        if (existing != null && !existing.getId().equals(store.getId())) {
            String msg = "Store '" + store.getName() + "' already exists in workspace '" + workspace.getName()
                    + "'";
            throw new IllegalArgumentException(msg);
        }
    }

    public void remove(StoreInfo store) {
        if (!getResourcesByStore(store, ResourceInfo.class).isEmpty()) {
            throw new IllegalArgumentException("Unable to delete non-empty store.");
        }
        store = unwrap(store);
        catalogDAO.delete(store);
        removed(store);
    }

    public void save(StoreInfo store) {
        validate(store, false);

        if (store.getId() == null) {
            // add it instead of saving
            add(store);
            return;
        }

        saved(store);
    }

    private Class<? extends StoreInfo> mapStoreClass(StoreInfo store) {
        if (store instanceof DataStoreInfo)
            return DataStoreInfo.class;
        else if (store instanceof CoverageStoreInfo)
            return CoverageStoreInfo.class;
        else
            return StoreInfo.class; // should not happen
    }

    private Class<? extends ResourceInfo> mapResourceClass(ResourceInfo resource) {
        if (resource instanceof FeatureTypeInfo)
            return FeatureTypeInfo.class;
        else if (resource instanceof CoverageInfo)
            return CoverageInfo.class;
        else
            return ResourceInfo.class; // should not happen
    }

    protected <T> T createProxy(T obj, Class<T> clazz) {
        return obj;
    }

    protected <T> List<T> createProxyList(List<T> list, Class<T> clazz) {
        return list;
    }

    public <T extends StoreInfo> T getStore(String id, Class<T> clazz) {
        StoreInfo store = catalogDAO.getStore(id, clazz);
        if (store == null) {
            return null;
        } else {
            resolve(store);
            return createProxy((T) store, (Class<T>) mapStoreClass(store));
        }
    }

    public <T extends StoreInfo> T getStoreByName(String name, Class<T> clazz) {
        T freestore = getStoreByName((WorkspaceInfo) null, name, clazz);
        if (freestore != null) {
            return freestore;
        }

        // look for secondary match
        List matches = catalogDAO.getStoresByName(name, clazz);
        if (matches.size() == 1) {
            StoreInfo store = (StoreInfo) matches.get(0);
            resolve(store);
            return createProxy((T) store, (Class<T>) mapStoreClass(store));
        }

        return null;
    }

    public <T extends StoreInfo> T getStoreByName(WorkspaceInfo workspace, String name, Class<T> clazz) {

        if (workspace == null) {
            workspace = catalogDAO.getDefaultWorkspace();
        } else {
            workspace = ModificationProxy.unwrap(workspace);
        }

        StoreInfo store = catalogDAO.getStoreByName(workspace, name, clazz);
        if (store == null) {
            return null;
        } else {
            resolve(store);
            return createProxy((T) store, (Class<T>) mapStoreClass(store));
        }

    }

    public <T extends StoreInfo> T getStoreByName(String workspaceName, String name, Class<T> clazz) {
        return getStoreByName(workspaceName != null ? getWorkspaceByName(workspaceName) : null, name, clazz);
    }

    public <T extends StoreInfo> List<T> getStoresByWorkspace(String workspaceName, Class<T> clazz) {

        WorkspaceInfo workspace = null;
        if (workspaceName != null) {
            workspace = getWorkspaceByName(workspaceName);
            if (workspace == null) {
                return Collections.EMPTY_LIST;
            }
        }

        return getStoresByWorkspace(workspace, clazz);
    }

    public <T extends StoreInfo> List<T> getStoresByWorkspace(WorkspaceInfo workspace, Class<T> clazz) {

        if (workspace == null) {
            workspace = catalogDAO.getDefaultWorkspace();
        } else
            workspace = ModificationProxy.unwrap(workspace);

        List matches = catalogDAO.getStoresByWorkspace(workspace, clazz);

        for (StoreInfo store : (List<StoreInfo>) matches) {
            resolve(store);
        }

        Class proxyclass = matches.isEmpty() ? clazz : mapStoreClass((StoreInfo) matches.get(0));
        return createProxyList(matches, proxyclass);
    }

    public List getStores(Class clazz) {
        List stores = catalogDAO.getStores(clazz);
        for (StoreInfo store : (List<StoreInfo>) stores) {
            resolve(store);
        }
        Class proxyclass = stores.isEmpty() ? clazz : mapStoreClass((StoreInfo) stores.get(0));
        return createProxyList(stores, proxyclass);
    }

    public DataStoreInfo getDataStore(String id) {
        return (DataStoreInfo) getStore(id, DataStoreInfo.class);
    }

    public DataStoreInfo getDataStoreByName(String name) {
        return (DataStoreInfo) getStoreByName(name, DataStoreInfo.class);
    }

    public DataStoreInfo getDataStoreByName(String workspaceName, String name) {
        return (DataStoreInfo) getStoreByName(workspaceName, name, DataStoreInfo.class);
    }

    public DataStoreInfo getDataStoreByName(WorkspaceInfo workspace, String name) {
        return (DataStoreInfo) getStoreByName(workspace, name, DataStoreInfo.class);
    }

    public List<DataStoreInfo> getDataStoresByWorkspace(String workspaceName) {
        return getStoresByWorkspace(workspaceName, DataStoreInfo.class);
    }

    public List<DataStoreInfo> getDataStoresByWorkspace(WorkspaceInfo workspace) {
        return getStoresByWorkspace(workspace, DataStoreInfo.class);
    }

    public List<DataStoreInfo> getDataStores() {
        return getStores(DataStoreInfo.class);
    }

    public CoverageStoreInfo getCoverageStore(String id) {
        return (CoverageStoreInfo) getStore(id, CoverageStoreInfo.class);
    }

    public CoverageStoreInfo getCoverageStoreByName(String name) {
        return (CoverageStoreInfo) getStoreByName(name, CoverageStoreInfo.class);
    }

    public CoverageStoreInfo getCoverageStoreByName(String workspaceName, String name) {
        return getStoreByName(workspaceName, name, CoverageStoreInfo.class);
    }

    public CoverageStoreInfo getCoverageStoreByName(WorkspaceInfo workspace, String name) {
        return getStoreByName(workspace, name, CoverageStoreInfo.class);
    }

    public List<CoverageStoreInfo> getCoverageStoresByWorkspace(String workspaceName) {
        return getStoresByWorkspace(workspaceName, CoverageStoreInfo.class);
    }

    public List<CoverageStoreInfo> getCoverageStoresByWorkspace(WorkspaceInfo workspace) {
        return getStoresByWorkspace(workspace, CoverageStoreInfo.class);
    }

    public List<CoverageStoreInfo> getCoverageStores() {
        return getStores(CoverageStoreInfo.class);
    }

    // Resource methods
    public void add(ResourceInfo resource) {
        if (resource.getNamespace() == null) {
            // default to default namespace
            resource.setNamespace(catalogDAO.getDefaultNamespace());
        }

        validate(resource, true);
        resolve(resource);
        // resources.put(resource.getClass(), resource);
        ResourceInfo unwrappedResource = ModificationProxy.unwrap(resource);
        unwrappedResource.setNamespace(ModificationProxy.unwrap(unwrappedResource.getNamespace()));
        StoreInfo unwrappedStore = ModificationProxy.unwrap(resource.getStore());
        unwrappedResource.setStore(unwrappedStore);
        unwrappedStore.setWorkspace(ModificationProxy.unwrap(unwrappedStore.getWorkspace()));

        fixNativeName(unwrappedResource);

        catalogDAO.save(unwrappedResource);
        added(resource);
    }

    void validate(ResourceInfo resource, boolean isNew) {
        if (isNull(resource.getName())) {
            throw new NullPointerException("Resource name must not be null");
        }
        if (resource.getStore() == null) {
            throw new IllegalArgumentException("Resource must be part of a store");
        }
        if (resource.getNamespace() == null) {
            throw new IllegalArgumentException("Resource must be part of a namespace");
        }

        StoreInfo store = resource.getStore();
        ResourceInfo existing = getResourceByStore(store, resource.getName(), ResourceInfo.class);
        if (existing != null && !existing.getId().equals(resource.getId())) {
            String msg = "Resource named '" + resource.getName() + "' already exists in store: '" + store.getName()
                    + "'";
            throw new IllegalArgumentException(msg);
        }

        NamespaceInfo namespace = resource.getNamespace();
        existing = getResourceByName(namespace, resource.getName(), ResourceInfo.class);
        if (existing != null && !existing.getId().equals(resource.getId())) {
            String msg = "Resource named '" + resource.getName() + "' already exists in namespace: '"
                    + namespace.getPrefix() + "'";
            throw new IllegalArgumentException(msg);
        }

    }

    public void remove(ResourceInfo resource) {
        // ensure no references to the resource
        if (!getLayers(resource).isEmpty()) {
            throw new IllegalArgumentException("Unable to delete resource referenced by layer");
        }
        resource = unwrap(resource);
        catalogDAO.delete(resource);
        removed(resource);
    }

    public void save(ResourceInfo resource) {
        validate(resource, false);
        saved(resource);
    }

    public <T extends ResourceInfo> T getResource(String id, Class<T> clazz) {
        ResourceInfo resource = catalogDAO.getResource(id, clazz);
        if (resource == null)
            return null;
        else {
            resolve(resource);
            return createProxy((T) resource, clazz);
        }

    }

    public <T extends ResourceInfo> T getResourceByName(String ns, String name, Class<T> clazz) {

        NamespaceInfo namespace = null;
        if ("".equals(ns)) {
            ns = null;
        }
        if (ns == null) {
            // if namespace was null, try the default namespace
            if (getDefaultNamespace() != null) {
                namespace = getDefaultNamespace();
            }
        } else {
            namespace = getNamespaceByPrefix(ns);
            if (namespace == null) {
                namespace = getNamespaceByURI(ns);
            }
        }

        if (namespace != null) {
            ResourceInfo resource = catalogDAO.getResourceByName(namespace.getName(), name, clazz);
            if (resource != null) {
                resolve(resource);
                // return ModificationProxy.create( (T) resource, clazz );
                return createProxy((T) resource, (Class<T>) mapResourceClass(resource));
            }
        }

        // List l = lookup(clazz, resources);
        // if ( namespace != null ) {
        // for (Iterator i = l.iterator(); i.hasNext();) {
        // ResourceInfo resource = (ResourceInfo) i.next();
        // if (name.equals(resource.getName())) {
        // NamespaceInfo namespace1 = resource.getNamespace();
        // if (namespace1 != null && namespace1.equals( namespace )) {
        // return ModificationProxy.create( (T) resource, clazz );
        // }
        // }
        // }
        // }

        if (ns == null) {
            // no namespace was specified, so do an exhaustive lookup
            List<ResourceInfo> matches = (List<ResourceInfo>) catalogDAO.getResourcesByName(name, clazz);
            // List matches = new ArrayList();
            // for (Iterator i = l.iterator(); i.hasNext();) {
            // ResourceInfo resource = (ResourceInfo) i.next();
            // if (name.equals(resource.getName())) {
            // matches.add( resource );
            // }
            // }

            if (matches.size() == 1) {
                ResourceInfo ret = matches.get(0);
                resolve(ret);
                return createProxy((T) ret, (Class<T>) mapResourceClass(ret));
                // return ModificationProxy.create( (T) matches.get( 0 ), clazz );
            }
        }
        return null;
    }

    public <T extends ResourceInfo> T getResourceByName(NamespaceInfo ns, String name, Class<T> clazz) {
        return getResourceByName(ns != null ? ns.getPrefix() : null, name, clazz);
    }

    public <T extends ResourceInfo> T getResourceByName(Name name, Class<T> clazz) {
        return getResourceByName(name.getNamespaceURI(), name.getLocalPart(), clazz);
    }

    public <T extends ResourceInfo> T getResourceByName(String name, Class<T> clazz) {
        ResourceInfo resource;

        // check is the name is a fully qualified one
        int colon = name.indexOf(':');
        if (colon != -1) {
            String ns = name.substring(0, colon);
            String localName = name.substring(colon + 1);
            return getResourceByName(ns, localName, clazz);
        } else {
            return getResourceByName((String) null, name, clazz);
        }
    }

    public List getResources(Class clazz) {
        // return ModificationProxy.createList( lookup(clazz,resources), clazz );
        // return ModificationProxy.createList( catalogDAO.getResources(clazz), clazz );
        List<ResourceInfo> list = catalogDAO.getResources(clazz);
        for (ResourceInfo resourceInfo : list) {
            resolve(resourceInfo);
        }

        return createProxyList(list, clazz);
    }

    public List getResourcesByNamespace(NamespaceInfo namespace, Class clazz) {
        // List all = lookup(clazz, resources);
        // List matches = new ArrayList();

        if (namespace == null) {
            namespace = getDefaultNamespace();
        }

        // for (Iterator r = all.iterator(); r.hasNext();) {
        // ResourceInfo resource = (ResourceInfo) r.next();
        // if (namespace != null ) {
        // if (namespace.equals(resource.getNamespace())) {
        // matches.add( resource );
        // }
        // }
        // else if ( resource.getNamespace() == null ) {
        // matches.add(resource);
        // }
        // }

        List<ResourceInfo> matches = catalogDAO.getResourcesByNamespace(namespace, clazz);
        for (ResourceInfo resource : matches) {
            resolve(resource);
        }
        Class proxyclass = matches.isEmpty() ? clazz : mapResourceClass((ResourceInfo) matches.get(0));
        // return ModificationProxy.createList( matches, clazz );
        return createProxyList(matches, proxyclass);
    }

    public <T extends ResourceInfo> List<T> getResourcesByNamespace(String namespace, Class<T> clazz) {
        if (namespace == null) {
            return getResourcesByNamespace((NamespaceInfo) null, clazz);
        }

        NamespaceInfo ns = getNamespaceByPrefix(namespace);
        if (ns == null) {
            ns = getNamespaceByURI(namespace);
        }
        if (ns == null) {
            return Collections.EMPTY_LIST;
        }

        return getResourcesByNamespace(ns, clazz);
    }

    public <T extends ResourceInfo> T getResourceByStore(StoreInfo store, String name, Class<T> clazz) {

        store = ModificationProxy.unwrap(store);
        ResourceInfo resource = catalogDAO.getResourceByStore(store, name, clazz);
        if (resource == null)
            return null;
        else {
            resolve(resource);
            return createProxy((T) resource, (Class<T>) mapResourceClass(resource));
        }

        // List all = lookup(clazz,resources);
        // for (Iterator r = all.iterator(); r.hasNext(); ) {
        // ResourceInfo resource = (ResourceInfo) r.next();
        // if ( name.equals( resource.getName() ) && store.equals( resource.getStore() ) ) {
        // return ModificationProxy.create((T)resource, clazz);
        // }
        //
        // }
        //
        // return null;
    }

    public <T extends ResourceInfo> List<T> getResourcesByStore(StoreInfo store, Class<T> clazz) {
        store = ModificationProxy.unwrap(store);
        // refine clazz type searching
        Class typedClazz = clazz;
        if (clazz.equals(ResourceInfo.class)) {
            if (store instanceof DataStoreInfo) {
                typedClazz = FeatureTypeInfo.class;
            } else if (store instanceof CoverageStoreInfo) {
                typedClazz = CoverageInfo.class;
            } else
                throw new IllegalArgumentException("Unknown store type " + store.getClass().getName());
        }
        List<T> resources = catalogDAO.getResourcesByStore(store, (Class<T>) typedClazz);
        for (ResourceInfo resource : resources) {
            resolve(resource);
        }
        Class proxyclazz = resources.isEmpty() ? typedClazz : mapResourceClass(resources.get(0));
        // return ModificationProxy.createList( resources, proxyclazz );
        return createProxyList(resources, proxyclazz);

        // List all = lookup(clazz,resources);
        // List matches = new ArrayList();
        //
        // for (Iterator r = all.iterator(); r.hasNext();) {
        // ResourceInfo resource = (ResourceInfo) r.next();
        // if (store.equals(resource.getStore())) {
        // matches.add(resource);
        // }
        // }
        //
        // return ModificationProxy.createList( matches, clazz );
    }

    public FeatureTypeInfo getFeatureType(String id) {
        return (FeatureTypeInfo) getResource(id, FeatureTypeInfo.class);
    }

    public FeatureTypeInfo getFeatureTypeByName(String ns, String name) {
        return (FeatureTypeInfo) getResourceByName(ns, name, FeatureTypeInfo.class);
    }

    public FeatureTypeInfo getFeatureTypeByName(NamespaceInfo ns, String name) {
        return getResourceByName(ns, name, FeatureTypeInfo.class);
    }

    public FeatureTypeInfo getFeatureTypeByName(Name name) {
        return getResourceByName(name, FeatureTypeInfo.class);
    }

    public FeatureTypeInfo getFeatureTypeByName(String name) {
        return (FeatureTypeInfo) getResourceByName(name, FeatureTypeInfo.class);
    }

    public List<FeatureTypeInfo> getFeatureTypes() {
        return getResources(FeatureTypeInfo.class);
    }

    public List getFeatureTypesByNamespace(NamespaceInfo namespace) {
        return getResourcesByNamespace(namespace, FeatureTypeInfo.class);
    }

    public FeatureTypeInfo getFeatureTypeByStore(DataStoreInfo dataStore, String name) {
        return getFeatureTypeByDataStore(dataStore, name);
    }

    public FeatureTypeInfo getFeatureTypeByDataStore(DataStoreInfo dataStore, String name) {
        return getResourceByStore(dataStore, name, FeatureTypeInfo.class);
    }

    public List<FeatureTypeInfo> getFeatureTypesByStore(DataStoreInfo store) {
        return getFeatureTypesByDataStore(store);
    }

    public List<FeatureTypeInfo> getFeatureTypesByDataStore(DataStoreInfo store) {
        return getResourcesByStore(store, FeatureTypeInfo.class);
    }

    public CoverageInfo getCoverage(String id) {
        return (CoverageInfo) getResource(id, CoverageInfo.class);
    }

    public CoverageInfo getCoverageByName(String ns, String name) {
        return (CoverageInfo) getResourceByName(ns, name, CoverageInfo.class);
    }

    public CoverageInfo getCoverageByName(NamespaceInfo ns, String name) {
        return (CoverageInfo) getResourceByName(ns, name, CoverageInfo.class);
    }

    public CoverageInfo getCoverageByName(Name name) {
        return getResourceByName(name, CoverageInfo.class);
    }

    public CoverageInfo getCoverageByName(String name) {
        return (CoverageInfo) getResourceByName(name, CoverageInfo.class);
    }

    public List<CoverageInfo> getCoverages() {
        return getResources(CoverageInfo.class);
    }

    public List getCoveragesByNamespace(NamespaceInfo namespace) {
        return getResourcesByNamespace(namespace, CoverageInfo.class);
    }

    public List<CoverageInfo> getCoveragesByStore(CoverageStoreInfo store) {
        return getResourcesByStore(store, CoverageInfo.class);
    }

    public CoverageInfo getCoverageByCoverageStore(CoverageStoreInfo coverageStore, String name) {
        return getResourceByStore(coverageStore, name, CoverageInfo.class);
    }

    public List<CoverageInfo> getCoveragesByCoverageStore(CoverageStoreInfo store) {
        return getResourcesByStore(store, CoverageInfo.class);
    }

    // Layer methods
    public void add(LayerInfo layer) {
        validate(layer, true);
        resolve(layer);

        if (layer.getType() == null) {
            if (layer.getResource() instanceof FeatureTypeInfo) {
                layer.setType(LayerInfo.Type.VECTOR);
            } else if (layer.getResource() instanceof CoverageInfo) {
                layer.setType(LayerInfo.Type.RASTER);
            } else {
                String msg = "Layer type not set and can't be derived from resource";
                throw new IllegalArgumentException(msg);
            }
        }

        // layers.add(layer);
        LayerInfo unwrapped = unwrap(layer);
        ResourceInfo resource = unwrapped.getResource();
        if (resource != null) {
            resource = unwrap(resource);
            unwrapped.setResource(resource);
        }

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("SAVING LAYER id:" + unwrapped.getId() + " name:" + unwrapped.getName());
            LOGGER.fine("  layer.resource " + unwrapped.getResource().getClass().getSimpleName() + "[" + "id:"
                    + unwrapped.getResource().getId() + " name:" + unwrapped.getResource().getName() + "]");

            if (unwrapped.getDefaultStyle() != null)
                LOGGER.fine("  layer.style " + "[" + "id:" + unwrapped.getDefaultStyle().getId() + " name:"
                        + unwrapped.getDefaultStyle().getName() + "]");
        }
        fixNativeName(resource);

        catalogDAO.save(unwrapped);
        added(layer);
    }

    /**
     * <B>FIXME This is a workaround to fill in nativeName in coverages.</B> Please investigate why
     * its value is not assigned.
     * 
     * @deprecated this is a workaround
     */
    private void fixNativeName(ResourceInfo resource) {
        if (resource != null && resource.getNativeName() == null) {
            if (resource instanceof CoverageInfo) {
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.fine("FIXME Coverage is missing nativeName (" + "id:" + resource.getId() + " name:"
                            + resource.getName() + ")");
                String storeurl = ((CoverageInfo) resource).getStore().getURL();
                String name = FilenameUtils.getBaseName(storeurl);
                resource.setNativeName(name);
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.fine("FIXME setting coverage nativeName to " + name);
            }
        }
    }

    void validate(LayerInfo layer, boolean isNew) {
        if (isNull(layer.getName())) {
            throw new NullPointerException("Layer name must not be null");
        }

        LayerInfo existing = getLayerByName(layer.getName());
        if (existing != null && !existing.getId().equals(layer.getId())) {
            // JD: since layers are not qualified by anything (yet), check
            // namespace of the resource, if they are different then allow the
            // layer to be added
            if (existing.getResource().getNamespace().equals(layer.getName())) {
                throw new IllegalArgumentException("Layer named '" + layer.getName() + "' already exists.");
            }
        }

        if (layer.getResource() == null) {
            throw new NullPointerException("Layer resource must not be null");
        }
        // (JD): not sure if default style should be mandatory
        // if ( layer.getDefaultStyle() == null ){
        // throw new NullPointerException( "Layer default style must not be null" );
        // }
    }

    public void remove(LayerInfo layer) {
        // ensure no references to the layer
        for (LayerGroupInfo lg : catalogDAO.getLayerGroups()) {
            if (lg.getLayers().contains(layer)) {
                String msg = "Unable to delete layer referenced by layer group '" + lg.getName() + "'";
                throw new IllegalArgumentException(msg);
            }
        }
        // layers.remove(unwrap(layer));
        catalogDAO.delete(layer);
        removed(layer);
    }

    public void save(LayerInfo layer) {
        validate(layer, false);
        saved(layer);
    }

    public LayerInfo getLayer(String id) {
        LayerInfo layer = catalogDAO.getLayer(id);
        if (layer == null)
            return null;
        else {
            resolve(layer);
            return createProxy(layer, LayerInfo.class);
        }

    }

    public LayerInfo getLayerByName(Name name) {
        if (name.getNamespaceURI() != null) {
            NamespaceInfo ns = getNamespaceByURI(name.getNamespaceURI());
            if (ns != null) {
                return getLayerByName(ns.getPrefix() + ":" + name.getLocalPart());
            }
        }

        return getLayerByName(name.getLocalPart());
    }

    public LayerInfo getLayerByName(String name) {
        String prefix = null;
        String resource = null;

        LayerInfo layer;

        int colon = name.indexOf(':');
        if (colon != -1) {
            // search by resource name
            prefix = name.substring(0, colon);
            resource = name.substring(colon + 1);

            // todo: check original code: it compared vs the resource name

            layer = catalogDAO.getLayerByName(prefix, resource);

        } else {
            // search by layer name
            layer = catalogDAO.getLayerByName(name);

        }

        if (layer != null) {
            resolve(layer);
            return createProxy(layer, LayerInfo.class);
        } else
            return null;
    }

    public List<LayerInfo> getLayers(ResourceInfo resource) {

        List<LayerInfo> layers = catalogDAO.getLayersByResourceId(resource.getId());
        for (LayerInfo layer : layers) {
            resolve(layer);
        }
        return createProxyList(layers, LayerInfo.class);

    }

    public List<LayerInfo> getLayers(StyleInfo style) {
        List<LayerInfo> matches = new ArrayList<LayerInfo>();
        for (LayerInfo layer : catalogDAO.getLayers()) {
            if (style.equals(layer.getDefaultStyle()) || layer.getStyles().contains(style)) {
                resolve(layer);
                matches.add(layer);
            }
        }

        return createProxyList(matches, LayerInfo.class);
    }

    public List<LayerInfo> getLayers() {
        List<LayerInfo> layers = catalogDAO.getLayers();
        for (LayerInfo layerInfo : layers) {
            resolve(layerInfo);
        }
        return createProxyList(layers, LayerInfo.class);
    }

    // Map methods
    public MapInfo getMap(String id) {
        MapInfo map = catalogDAO.getMap(id);
        if (map != null) {
            resolve(map);
            return createProxy(map, MapInfo.class);
        }
        return null;

    }

    public MapInfo getMapByName(String name) {
        MapInfo map = catalogDAO.getMapByName(name);
        if (map != null) {
            resolve(map);
            return createProxy(map, MapInfo.class);
        }
        return null;

    }

    public List<MapInfo> getMaps() {
        List<MapInfo> maps = catalogDAO.getMaps();
        for (MapInfo map : maps) {
            resolve(map);
        }

        return createProxyList(maps, MapInfo.class);
    }

    public void add(LayerGroupInfo layerGroup) {
        validate(layerGroup, true);
        resolve(layerGroup);

        if (layerGroup.getStyles().isEmpty()) {
            for (LayerInfo l : layerGroup.getLayers()) {
                // default style
                layerGroup.getStyles().add(null);
            }
        }

        layerGroup = ModificationProxy.unwrap(layerGroup);
        catalogDAO.save(layerGroup);
        added(layerGroup);
    }

    void validate(LayerGroupInfo layerGroup, boolean isNew) {
        if (isNull(layerGroup.getName())) {
            throw new NullPointerException("Layer group name must not be null");
        }

        LayerGroupInfo existing = getLayerGroupByName(layerGroup.getName());
        if (existing != null && !existing.getId().equals(layerGroup.getId())) {
            throw new IllegalArgumentException("Layer group named '" + layerGroup.getName() + "' already exists.");
        }

        if (!isNew) {
            if (layerGroup.getLayers() == null || layerGroup.getLayers().isEmpty()) {
                throw new NullPointerException("Layer group must not be empty");
            }
        }

        if (layerGroup.getStyles() != null && !layerGroup.getStyles().isEmpty()
                && !(layerGroup.getStyles().size() == layerGroup.getLayers().size())) {
            throw new IllegalArgumentException("Layer group has different number of styles than layers");
        }
    }

    public void remove(LayerGroupInfo layerGroup) {
        catalogDAO.delete(unwrap(layerGroup));
        removed(layerGroup);
    }

    public void save(LayerGroupInfo layerGroup) {
        validate(layerGroup, false);
        saved(layerGroup);
    }

    public List<LayerGroupInfo> getLayerGroups() {
        final List<LayerGroupInfo> layerGroups = catalogDAO.getLayerGroups();
        for (LayerGroupInfo layerGroupInfo : layerGroups) {
            resolve(layerGroupInfo);
        }
        return createProxyList(layerGroups, LayerGroupInfo.class);
    }

    public LayerGroupInfo getLayerGroup(String id) {
        final LayerGroupInfo layerGroup = catalogDAO.getLayerGroup(id);
        if (layerGroup != null) {
            resolve(layerGroup);
            return createProxy(layerGroup, LayerGroupInfo.class);
        } else
            return null;

    }

    public LayerGroupInfo getLayerGroupByName(String name) {
        LayerGroupInfo layerGroup = catalogDAO.getLayerGroupByName(name);
        if (layerGroup != null) {
            resolve(layerGroup);
            return createProxy(layerGroup, LayerGroupInfo.class);
        } else
            return null;
    }

    public void add(MapInfo map) {
        resolve(map);
        catalogDAO.save(ModificationProxy.unwrap(map));
        added(map);
    }

    public void remove(MapInfo map) {
        catalogDAO.delete(unwrap(map));
        removed(map);
    }

    public void save(MapInfo map) {
        saved(map);
    }

    // Namespace methods
    public NamespaceInfo getNamespace(String id) {
        final NamespaceInfo namespace = catalogDAO.getNamespace(id);
        if (namespace != null) {
            resolve(namespace);
            return createProxy(namespace, NamespaceInfo.class);
        } else
            return null;

    }

    public NamespaceInfo getNamespaceByPrefix(String prefix) {
        NamespaceInfo namespace = catalogDAO.getNamespaceByPrefix(prefix);
        if (namespace != null) {
            resolve(namespace);
            return createProxy(namespace, NamespaceInfo.class);
        } else
            return null;
    }

    public NamespaceInfo getNamespaceByURI(String uri) {
        NamespaceInfo namespace = catalogDAO.getNamespaceByURI(uri);
        if (namespace != null) {
            resolve(namespace);
            return createProxy(namespace, NamespaceInfo.class);
        } else
            return null;

    }

    public List<NamespaceInfo> getNamespaces() {
        final List<NamespaceInfo> namespaces = catalogDAO.getNamespaces();
        for (NamespaceInfo namespace : namespaces) {
            resolve(namespace);
        }
        return createProxyList(namespaces, NamespaceInfo.class);
    }

    public void add(NamespaceInfo namespace) {
        validate(namespace, true);
        resolve(namespace);

        boolean existsDefault = catalogDAO.getDefaultNamespace() != null;
        ((NamespaceInfoImpl) namespace).setDefault(!existsDefault);

        catalogDAO.save(namespace);

        added(namespace);
    }

    void validate(NamespaceInfo namespace, boolean isNew) {
        if (isNull(namespace.getPrefix())) {
            throw new NullPointerException("Namespace prefix must not be null");
        }

        NamespaceInfo existing = catalogDAO.getNamespaceByPrefix(namespace.getPrefix());
        if (existing != null && !existing.getId().equals(namespace.getId())) {
            throw new IllegalArgumentException(
                    "Namespace with prefix '" + namespace.getPrefix() + "' already exists.");
        }

        if (namespace.getURI() == null) {
            throw new NullPointerException("Namespace uri must not be null");
        }

        if (isNull(namespace.getURI())) {
            throw new NullPointerException("Namespace uri must not be null");
        }

        try {
            new URI(namespace.getURI());
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid URI syntax for '" + namespace.getURI() + "' in namespace '"
                    + namespace.getPrefix() + "'");
        }
    }

    public void remove(NamespaceInfo namespace) {
        if (!catalogDAO.getResourcesByNamespace(namespace, ResourceInfo.class).isEmpty()) {
            throw new IllegalArgumentException("Unable to delete non-empty namespace.");
        }
        NamespaceInfoImpl realns = catalogDAO.getNamespaceByPrefix(namespace.getPrefix());
        if (realns == null) {
            throw new IllegalArgumentException(
                    "Can't find namespace '" + namespace.getPrefix() + "' for deletion.");
        }

        catalogDAO.delete(realns);

        if (realns.isDefault()) {
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.fine("Removing default namespace '" + namespace.getName() + "'");
            // elect a random ns as default
            List<NamespaceInfo> nslist = catalogDAO.getNamespaces();
            if (!nslist.isEmpty()) {
                NamespaceInfoImpl ns0 = (NamespaceInfoImpl) nslist.get(0);
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.fine("Electing '" + namespace.getName() + "' to default namespace.");
                ns0.setDefault(true);
                catalogDAO.update(ns0);
            }
        }
        removed(namespace);
    }

    public void save(NamespaceInfo namespace) {
        validate(namespace, false);

        saved(namespace);
    }

    public NamespaceInfo getDefaultNamespace() {
        final NamespaceInfoImpl defaultNamespace = catalogDAO.getDefaultNamespace();
        resolve(defaultNamespace);
        return createProxy(defaultNamespace, NamespaceInfo.class);

    }

    public void setDefaultNamespace(NamespaceInfo defaultNamespace) {
        NamespaceInfo nsold = catalogDAO.getDefaultNamespace();
        NamespaceInfo nsnew = catalogDAO.getNamespaceByPrefix(defaultNamespace.getPrefix());
        if (nsnew == null) {
            throw new IllegalArgumentException("No such namespace: '" + defaultNamespace.getPrefix() + "'");
        }

        if (nsold != null && nsold.getPrefix().equals(nsnew.getPrefix())) // setting existing
                                                                          // default
            return;

        ((NamespaceInfoImpl) nsnew).setDefault(true);
        catalogDAO.update(nsnew);
        if (nsold != null) {
            ((NamespaceInfoImpl) nsold).setDefault(false);
            catalogDAO.update(nsold);
        }

    }

    // Workspace methods
    public void add(WorkspaceInfo workspace) {
        validate(workspace, true);
        if (catalogDAO.getWorkspaceByName(workspace.getName()) != null) {
            throw new IllegalArgumentException("Workspace with name '" + workspace.getName() + "' already exists.");
        }

        resolve(workspace);

        boolean existsDefault = catalogDAO.getDefaultWorkspace() != null;
        ((WorkspaceInfoImpl) workspace).setDefault(!existsDefault);

        catalogDAO.save(workspace);

        added(workspace);
    }

    void validate(WorkspaceInfo workspace, boolean isNew) {
        if (isNull(workspace.getName())) {
            throw new NullPointerException("workspace name must not be null");
        }

        WorkspaceInfo existing = catalogDAO.getWorkspaceByName(workspace.getName());
        if (existing != null && !existing.getId().equals(workspace.getId())) {
            throw new IllegalArgumentException("Workspace named '" + workspace.getName() + "' already exists.");
        }

    }

    public void remove(WorkspaceInfo workspace) {
        // JD: maintain the link between namespace and workspace, remove this when this is no
        // longer necessary
        if (catalogDAO.getNamespaceByPrefix(workspace.getName()) != null) {
            throw new IllegalArgumentException("Cannot delete workspace with linked namespace");
        }
        if (!getStoresByWorkspace(workspace, StoreInfo.class).isEmpty()) {
            throw new IllegalArgumentException("Cannot delete non-empty workspace.");
        }
        catalogDAO.delete(workspace);
        removed(workspace);
    }

    public void save(WorkspaceInfo workspace) {
        validate(workspace, false);

        saved(workspace);
    }

    public WorkspaceInfo getDefaultWorkspace() {
        WorkspaceInfo workspace = catalogDAO.getDefaultWorkspace();
        if (workspace != null) {
            resolve(workspace);
            return createProxy(workspace, WorkspaceInfo.class);
        } else
            return null;

    }

    public void setDefaultWorkspace(WorkspaceInfo workspace) {

        WorkspaceInfo wsold = catalogDAO.getDefaultWorkspace();
        WorkspaceInfo wsnew = catalogDAO.getWorkspaceByName(workspace.getName());
        if (wsnew == null) {
            throw new IllegalArgumentException("No such workspace: '" + workspace.getName() + "'");
        }

        if (wsold != null && wsold.getName().equals(wsnew.getName())) // setting existing default
            return;

        ((WorkspaceInfoImpl) wsnew).setDefault(true);
        catalogDAO.update(wsnew);
        if (wsold != null) {
            ((WorkspaceInfoImpl) wsold).setDefault(false);
            catalogDAO.update(wsold);
        }

    }

    public List<WorkspaceInfo> getWorkspaces() {
        final List<WorkspaceInfo> workspaces = catalogDAO.getWorkspaces();
        for (WorkspaceInfo workspace : workspaces) {
            resolve(workspace);
        }
        return createProxyList(workspaces, WorkspaceInfo.class);

    }

    public WorkspaceInfo getWorkspace(String id) {
        WorkspaceInfo workspace = catalogDAO.getWorkspace(id);
        if (workspace != null) {
            resolve(workspace);
            return createProxy(workspace, WorkspaceInfo.class);
        } else
            return null;

    }

    public WorkspaceInfo getWorkspaceByName(String name) {
        WorkspaceInfo workspace = catalogDAO.getWorkspaceByName(name);
        if (workspace != null) {
            resolve(workspace);
            return createProxy(workspace, WorkspaceInfo.class);
        } else
            return null;

    }

    // Style methods
    public StyleInfo getStyle(String id) {
        StyleInfo style = catalogDAO.getStyle(id);
        if (style != null) {
            resolve(style);
            return createProxy(style, StyleInfo.class);
        } else
            return null;

    }

    public StyleInfo getStyleByName(String name) {
        StyleInfo style = catalogDAO.getStyleByName(name);
        if (style != null) {
            resolve(style);
            return createProxy(style, StyleInfo.class);
        } else
            return null;

    }

    public List<StyleInfo> getStyles() {
        final List<StyleInfo> styles = catalogDAO.getStyles();
        for (StyleInfo style : styles) {
            resolve(style);
        }

        return createProxyList(styles, StyleInfo.class);
    }

    public void add(StyleInfo style) {
        validate(style, true);
        resolve(style);
        catalogDAO.save(style);
        added(style);
    }

    void validate(StyleInfo style, boolean isNew) {
        if (isNull(style.getName())) {
            throw new NullPointerException("Style name must not be null");
        }
        if (isNull(style.getFilename())) {
            throw new NullPointerException("Style fileName must not be null");
        }

        StyleInfo existing = getStyleByName(style.getName());
        if (existing != null && !existing.getId().equals(style.getId())) {
            throw new IllegalArgumentException("Style named '" + style.getName() + "' already exists.");
        }
    }

    public void remove(StyleInfo style) {
        // ensure no references to the style
        for (LayerInfo l : catalogDAO.getLayers()) {
            if (style.equals(l.getDefaultStyle()) || l.getStyles().contains(style)) {
                throw new IllegalArgumentException("Unable to delete style referenced by '" + l.getName() + "'");
            }
        }
        StyleInfo realObj = catalogDAO.getStyle(style.getId());
        catalogDAO.delete(realObj);
        removed(style);
    }

    public void save(StyleInfo style) {
        validate(style, false);
        saved(style);
    }

    // Event methods
    public Collection getListeners() {
        return Collections.unmodifiableCollection(listeners);
    }

    public void addListener(CatalogListener listener) {
        listeners.add(listener);

    }

    public void removeListener(CatalogListener listener) {
        listeners.remove(listener);
    }

    public ResourcePool getResourcePool() {
        return resourcePool;
    }

    public void setResourcePool(ResourcePool resourcePool) {
        this.resourcePool = resourcePool;
    }

    public GeoServerResourceLoader getResourceLoader() {
        return resourceLoader;
    }

    public void setResourceLoader(GeoServerResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void dispose() {
        if (listeners != null)
            listeners.clear();

        if (resourcePool != null)
            resourcePool.dispose();
    }

    protected void added(CatalogInfo object) {
        fireAdded(object);
    }

    protected void fireAdded(CatalogInfo object) {
        CatalogAddEventImpl event = new CatalogAddEventImpl();
        event.setSource(object);

        event(event);
    }

    @SuppressWarnings("unchecked")
    protected void saved(CatalogInfo object) {
        // this object is a proxy
        if (!Proxy.isProxyClass(object.getClass())) {
            LOGGER.severe("WORKAROUND: CatalogInfo object is a " + object.getClass().getSimpleName()
                    + " -- Faking delta values in fireModified()");

            // fire out what changed
            List propertyNames = new ArrayList();
            List newValues = new ArrayList();
            List oldValues = new ArrayList();

            // TODO: protect this original object, perhaps with another proxy
            fireModified(object, propertyNames, oldValues, newValues);
            firePostModified(object);
            return;
        }

        ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(object);

        // get the real object
        CatalogInfo real = (CatalogInfo) h.getProxyObject();

        // fire out what changed
        List propertyNames = h.getPropertyNames();
        List newValues = h.getNewValues();
        List oldValues = h.getOldValues();

        // TODO: protect this original object, perhaps with another proxy
        fireModified(real, propertyNames, oldValues, newValues);

        // commit to the original object
        h.commit();

        // resolve to do a sync on the object
        // syncIdWithName(real);

        // fire the post modify event
        firePostModified(real);
    }

    protected void fireModified(CatalogInfo object, List propertyNames, List oldValues, List newValues) {
        CatalogModifyEventImpl event = new CatalogModifyEventImpl();

        event.setSource(object);
        event.setPropertyNames(propertyNames);
        event.setOldValues(oldValues);
        event.setNewValues(newValues);

        event(event);
    }

    protected void firePostModified(CatalogInfo object) {
        CatalogPostModifyEventImpl event = new CatalogPostModifyEventImpl();
        event.setSource(object);

        event(event);
    }

    protected void removed(CatalogInfo object) {
        CatalogRemoveEventImpl event = new CatalogRemoveEventImpl();
        event.setSource(object);

        event(event);
    }

    protected void event(CatalogEvent event) {
        for (CatalogListener listener : listeners) {
            try {
                if (event instanceof CatalogAddEvent) {
                    listener.handleAddEvent((CatalogAddEvent) event);
                } else if (event instanceof CatalogRemoveEvent) {
                    listener.handleRemoveEvent((CatalogRemoveEvent) event);
                } else if (event instanceof CatalogModifyEvent) {
                    listener.handleModifyEvent((CatalogModifyEvent) event);
                } else if (event instanceof CatalogPostModifyEvent) {
                    listener.handlePostModifyEvent((CatalogPostModifyEvent) event);
                }
            } catch (Throwable e) {
                LOGGER.log(Level.WARNING, "Catalog listener threw exception handling event.", e);
            }

        }
    }

    /**
     * Implementation method for resolving all {@link ResolvingProxy} instances.
     */
    public void resolve() {
        if (listeners == null) {
            listeners = new ArrayList<CatalogListener>();
        }

        if (resourcePool == null) {
            resourcePool = new ResourcePool(this);
        }
    }

    protected void resolve(WorkspaceInfo workspace) {
        resolveCollections(workspace);
    }

    protected void resolve(NamespaceInfo namespace) {
        resolveCollections(namespace);
    }

    protected void resolve(StoreInfo store) {
        StoreInfoImpl s = (StoreInfoImpl) store;

        // resolve the workspace
        WorkspaceInfo resolved = ResolvingProxy.resolve(this, s.getWorkspace());
        if (resolved != null) {
            s.setWorkspace(resolved);
        } else {
            // this means the workspace has not yet been added to the catalog, keep the proxy around
        }
        resolveCollections(s);

        s.setCatalog(this);
    }

    protected void resolve(ResourceInfo resource) {
        ResourceInfoImpl r = (ResourceInfoImpl) resource;

        // resolve the store
        StoreInfo resolved = ResolvingProxy.resolve(this, r.getStore());
        if (resolved != null) {
            resolve(resolved);
            r.setStore(resolved);
        }

        if (resource instanceof FeatureTypeInfo) {
            resolve((FeatureTypeInfo) resource);
        }
        if (r instanceof CoverageInfo) {
            resolve((CoverageInfo) resource);
        }

        r.setCatalog(this);
    }

    private void resolve(CoverageInfo r) {
        CoverageInfoImpl c = (CoverageInfoImpl) r;
        if (c.getDimensions() == null) {
            c.setDimensions(new ArrayList<CoverageDimensionInfo>());
        } else {
            for (CoverageDimensionInfo dim : c.getDimensions()) {
                if (dim.getNullValues() == null)
                    ((CoverageDimensionImpl) dim).setNullValues(new ArrayList<Double>());
            }
        }
        resolveCollections(r);
    }

    /**
     * We don't want the world to be able and call this without going trough
     * {@link #resolve(ResourceInfo)}
     * 
     * @param featureType
     */
    private void resolve(FeatureTypeInfo featureType) {
        FeatureTypeInfoImpl ft = (FeatureTypeInfoImpl) featureType;
        resolveCollections(ft);
    }

    protected void resolve(LayerInfo layer) {
        if (layer.getAttribution() == null) {
            layer.setAttribution(getFactory().createAttribution());
        }
        if (layer.getDefaultStyle() != null) {
            resolve(layer.getDefaultStyle());
        }
        resolve(layer.getResource());
        resolveCollections(layer);
    }

    protected void resolve(LayerGroupInfo layerGroup) {
        resolveCollections(layerGroup);
        LayerGroupInfoImpl lg = (LayerGroupInfoImpl) layerGroup;

        for (int i = 0; i < lg.getLayers().size(); i++) {
            LayerInfo l = lg.getLayers().get(i);
            LayerInfo resolved = ResolvingProxy.resolve(this, l);
            resolve(resolved);
            lg.getLayers().set(i, resolved);
        }

        for (int i = 0; i < lg.getStyles().size(); i++) {
            StyleInfo s = lg.getStyles().get(i);
            if (s != null) {
                StyleInfo resolved = ResolvingProxy.resolve(this, s);
                ((StyleInfoImpl) resolved).setCatalog(this);
                lg.getStyles().set(i, resolved);
            }
        }

    }

    protected void resolve(StyleInfo style) {
        ((StyleInfoImpl) style).setCatalog(this);
    }

    protected void resolve(MapInfo map) {
        for (LayerInfo layerInfo : map.getLayers()) {
            resolve(layerInfo);
        }
    }

    /**
     * Method which reflectively sets all collections when they are null.
     */
    protected void resolveCollections(Object object) {
        ClassProperties properties = OwsUtils.getClassProperties(object.getClass());
        for (String property : properties.properties()) {
            Method g = properties.getter(property, null);
            if (g == null) {
                continue;
            }

            Class type = g.getReturnType();
            // only continue if this is a collection or a map
            if (!(Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type))) {
                continue;
            }

            // only continue if there is also a setter as well
            Method s = properties.setter(property, null);
            if (s == null) {
                continue;
            }

            // if the getter returns null, call the setter
            try {
                Object value = g.invoke(object, null);
                if (value == null) {
                    if (Map.class.isAssignableFrom(type)) {
                        if (MetadataMap.class.isAssignableFrom(type)) {
                            value = new MetadataMap();
                        } else {
                            value = new HashMap();
                        }
                    } else if (List.class.isAssignableFrom(type)) {
                        value = new ArrayList();
                    } else if (Set.class.isAssignableFrom(type)) {
                        value = new HashSet();
                    } else {
                        throw new RuntimeException("Unknown collection type:" + type.getName());
                    }

                    // initialize
                    s.invoke(object, value);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    protected void setId(Object o) {
        if (OwsUtils.get(o, "id") == null) {
            String uid = new UID().toString();
            OwsUtils.set(o, "id", o.getClass().getSimpleName() + "-" + uid);
        }
    }

    protected boolean isNull(String string) {
        return string == null || "".equals(string.trim());
    }

    public static <T> T unwrap(T obj) {
        return ModificationProxy.unwrap(obj);
    }

    public void setCatalogDAO(CatalogDAO catalogDAO) {
        this.catalogDAO = catalogDAO;
    }

    public void accept(CatalogVisitor visitor) {
        visitor.visit(this);
    }

    class HibCatalogUpdater implements CatalogListener {

        public void handleAddEvent(CatalogAddEvent event) {
        }

        public void handleRemoveEvent(CatalogRemoveEvent event) {
        }

        public void handleModifyEvent(CatalogModifyEvent event) {
        }

        public void handlePostModifyEvent(CatalogPostModifyEvent event) {
            CatalogInfo src = event.getSource();
            src.accept(catalogVisitor);
        }

        public void reloaded() {
        }

        private CatalogVisitor catalogVisitor = new CatalogVisitor() {

            public void visit(WorkspaceInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(NamespaceInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(DataStoreInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(CoverageStoreInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(FeatureTypeInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(CoverageInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(LayerInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(StyleInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(LayerGroupInfo obj) {
                obj = ModificationProxy.unwrap(obj);
                catalogDAO.update(obj);
            }

            public void visit(Catalog catalog) {
            }
        };

    }

}