/* 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.impl; 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.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; 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.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogException; import org.geoserver.catalog.CatalogFactory; 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.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; /** * A default catalog implementation that is memory based. * * @author Justin Deoliveira, The Open Planning Project * */ public class CatalogImpl implements Catalog { /** * logger */ private static final Logger LOGGER = Logging.getLogger(CatalogImpl.class); /** * stores */ protected MultiHashMap/* <Class> */stores = new MultiHashMap(); /** * resources */ protected MultiHashMap/* <Class> */resources = new MultiHashMap(); /** * namespaces */ protected HashMap<String, NamespaceInfo> namespaces = new HashMap<String, NamespaceInfo>(); /** * workspaces */ protected HashMap<String, WorkspaceInfo> workspaces = new HashMap<String, WorkspaceInfo>(); /** * layers */ protected List<LayerInfo> layers = new ArrayList(); /** * maps */ protected List<MapInfo> maps = new ArrayList<MapInfo>(); /** * layer groups */ protected List<LayerGroupInfo> layerGroups = new ArrayList<LayerGroupInfo>(); /** * styles */ protected List<StyleInfo> styles = new ArrayList(); /** * listeners */ protected List listeners = new ArrayList(); /** * resources */ protected ResourcePool resourcePool; protected GeoServerResourceLoader resourceLoader; public CatalogImpl() { resourcePool = new ResourcePool(this); } public String getId() { return "catalog"; } public CatalogFactory getFactory() { return new CatalogFactoryImpl( this ); } // Store methods public void add(StoreInfo store) { if ( store.getWorkspace() == null ) { store.setWorkspace( getDefaultWorkspace() ); } validate(store, true); resolve(store); stores.put(store.getClass(), store); 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); stores.remove(store.getClass(),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); } public <T extends StoreInfo> T getStore(String id, Class<T> clazz) { List l = lookup(clazz, stores); for (Iterator i = l.iterator(); i.hasNext();) { StoreInfo store = (StoreInfo) i.next(); if (id.equals(store.getId())) { return ModificationProxy.create( (T) store, clazz ); //return store; } } return null; } public <T extends StoreInfo> T getStoreByName(String name, Class<T> clazz) { T store = getStoreByName( (WorkspaceInfo) null, name, clazz ); if ( store != null ) { return store; } //look for secondary match List l = lookup(clazz, stores); ArrayList matches = new ArrayList(); for (Iterator i = l.iterator(); i.hasNext();) { store = (T) i.next(); if ( name.equals( store.getName() ) ) { matches.add( store ); } } if ( matches.size() == 1 ) { return ModificationProxy.create( (T) matches.get( 0 ), clazz); } return null; } public <T extends StoreInfo> T getStoreByName(WorkspaceInfo workspace, String name, Class<T> clazz) { if ( workspace == null ) { workspace = getDefaultWorkspace(); } List l = lookup(clazz, stores); for (Iterator i = l.iterator(); i.hasNext();) { StoreInfo store = (StoreInfo) i.next(); if (name.equals(store.getName()) && store.getWorkspace().equals( workspace )) { return ModificationProxy.create( (T) store, clazz ); } } return null; } 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 = getDefaultWorkspace(); } List all = lookup(clazz, stores); List matches = new ArrayList(); for (Iterator s = all.iterator(); s.hasNext();) { StoreInfo store = (StoreInfo) s.next(); if (workspace.equals(store.getWorkspace())) { matches.add(store); } } return ModificationProxy.createList(matches,clazz); } public List getStores(Class clazz) { return ModificationProxy.createList(lookup(clazz, stores) , clazz); } 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 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 getCoverageStores() { return getStores(CoverageStoreInfo.class); } // Resource methods public void add(ResourceInfo resource) { if ( resource.getNamespace() == null ) { //default to default namespace resource.setNamespace( getDefaultNamespace() ); } validate(resource,true); resolve(resource); resources.put(resource.getClass(), resource); 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); resources.remove(resource.getClass(), resource); removed(resource); } public void save(ResourceInfo resource) { validate(resource,false); saved(resource); } public <T extends ResourceInfo> T getResource(String id, Class<T> clazz) { List l = lookup(clazz, resources); for (Iterator i = l.iterator(); i.hasNext();) { ResourceInfo resource = (ResourceInfo) i.next(); if (id.equals(resource.getId())) { return ModificationProxy.create((T) resource, clazz ); } } return null; } 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 ); } } 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 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 ) { 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 ); } 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); } } return ModificationProxy.createList( matches, clazz ); } 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) { 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) { 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 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 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); added(layer); } void validate( LayerInfo layer, boolean isNew) { // TODO: bring back when the layer/publishing split is in act // 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 : layerGroups ) { 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)); removed(layer); } public void save(LayerInfo layer) { validate( layer, false ); saved(layer); } public LayerInfo getLayer(String id) { for (Iterator l = layers.iterator(); l.hasNext();) { LayerInfo layer = (LayerInfo) l.next(); if (id.equals(layer.getId())) { return ModificationProxy.create( layer, LayerInfo.class ); } } return null; } 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; int colon = name.indexOf( ':' ); if ( colon != -1 ) { //search by resource name prefix = name.substring( 0, colon ); resource = name.substring( colon + 1 ); for (Iterator l = layers.iterator(); l.hasNext();) { LayerInfo layer = (LayerInfo) l.next(); ResourceInfo r = layer.getResource(); if ( prefix.equals( r.getNamespace().getPrefix() ) && resource.equals( r.getName() ) ) { return ModificationProxy.create( layer, LayerInfo.class ); } } } else { //search by layer name for (Iterator l = layers.iterator(); l.hasNext();) { LayerInfo layer = (LayerInfo) l.next(); if ( name.equals( layer.getName() ) ) { return ModificationProxy.create( layer, LayerInfo.class ); } } } return null; } public List<LayerInfo> getLayers(ResourceInfo resource) { List<LayerInfo> matches = new ArrayList<LayerInfo>(); for (Iterator l = layers.iterator(); l.hasNext();) { LayerInfo layer = (LayerInfo) l.next(); if ( resource.equals( layer.getResource() ) ) { matches.add( layer ); } } return ModificationProxy.createList(matches,LayerInfo.class); } public List<LayerInfo> getLayers(StyleInfo style) { List<LayerInfo> matches = new ArrayList<LayerInfo>(); for (Iterator l = layers.iterator(); l.hasNext();) { LayerInfo layer = (LayerInfo) l.next(); if ( style.equals( layer.getDefaultStyle() ) || layer.getStyles().contains( style ) ) { matches.add( layer ); } } return ModificationProxy.createList(matches,LayerInfo.class); } public List getLayers() { return ModificationProxy.createList( new ArrayList(layers), LayerInfo.class ); } // Map methods public MapInfo getMap(String id) { for (MapInfo map : maps) { if (id.equals(map.getId())) { return ModificationProxy.create(map,MapInfo.class); } } return null; } public MapInfo getMapByName(String name) { for (MapInfo map : maps) { if (name.equals(map.getName())) { return ModificationProxy.create(map,MapInfo.class); } } return null; } public List<MapInfo> getMaps() { return ModificationProxy.createList( new ArrayList(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); } } layerGroups.add( 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) { layerGroups.remove( unwrap(layerGroup) ); removed( layerGroup ); } public void save(LayerGroupInfo layerGroup) { validate(layerGroup,false); saved(layerGroup); } public List<LayerGroupInfo> getLayerGroups() { return ModificationProxy.createList( new ArrayList(layerGroups), LayerGroupInfo.class ); } public LayerGroupInfo getLayerGroup(String id) { for (LayerGroupInfo layerGroup : layerGroups ) { if ( id.equals( layerGroup.getId() ) ) { return ModificationProxy.create(layerGroup,LayerGroupInfo.class); } } return null; } public LayerGroupInfo getLayerGroupByName(String name) { for (LayerGroupInfo layerGroup : layerGroups ) { if ( name.equals( layerGroup.getName() ) ) { return ModificationProxy.create(layerGroup,LayerGroupInfo.class); } } return null; } public void add(MapInfo map) { resolve(map); maps.add(map); added(map); } public void remove(MapInfo map) { maps.remove(unwrap(map)); removed(map); } public void save(MapInfo map) { saved( map ); } // Namespace methods public NamespaceInfo getNamespace(String id) { for (NamespaceInfo namespace : namespaces.values() ) { if (id.equals(namespace.getId())) { return ModificationProxy.create( namespace, NamespaceInfo.class ); } } return null; } public NamespaceInfo getNamespaceByPrefix(String prefix) { NamespaceInfo ns = namespaces.get( prefix ); return ns != null ? ModificationProxy.create(ns, NamespaceInfo.class ) : null; } public NamespaceInfo getNamespaceByURI(String uri) { for (NamespaceInfo namespace : namespaces.values() ) { if (uri.equals(namespace.getURI())) { return ModificationProxy.create( namespace, NamespaceInfo.class ); } } return null; } public List getNamespaces() { ArrayList<NamespaceInfo> ns = new ArrayList<NamespaceInfo>(); for ( Map.Entry<String,NamespaceInfo> e : namespaces.entrySet() ) { if ( e.getKey() == null ) continue; ns.add( e.getValue() ); } return ModificationProxy.createList( ns, NamespaceInfo.class ); } public void add(NamespaceInfo namespace) { validate(namespace,true); resolve(namespace); synchronized (namespaces) { namespaces.put(namespace.getPrefix(),namespace); if ( namespaces.get( null ) == null ) { namespaces.put( null, namespace ); //fire the event fireModified(this, Arrays.asList("defaultNamespace"), Collections.singletonList(null), Arrays.asList(namespace)); } } added(namespace); } void validate(NamespaceInfo namespace, boolean isNew) { if ( isNull(namespace.getPrefix()) ) { throw new NullPointerException( "Namespace prefix must not be null"); } NamespaceInfo existing = getNamespaceByPrefix( namespace.getPrefix() ); if ( existing != null && !existing.getId().equals( namespace.getId() ) ) { throw new IllegalArgumentException( "Namespace with prefix '" + namespace.getPrefix() + "' already exists."); } 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 ( !getResourcesByNamespace(namespace, ResourceInfo.class ).isEmpty() ) { throw new IllegalArgumentException( "Unable to delete non-empty namespace."); } NamespaceInfo defaultNamespace = getDefaultNamespace(); if (namespace.equals(defaultNamespace)) { namespaces.remove(null); } namespaces.remove(namespace.getPrefix()); removed(namespace); } public void save(NamespaceInfo namespace) { validate(namespace,false); ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(namespace); NamespaceInfo ns = (NamespaceInfo) h.getProxyObject(); if ( !namespace.getPrefix().equals( ns.getPrefix() ) ) { synchronized (namespaces) { namespaces.remove( ns.getPrefix() ); namespaces.put( namespace.getPrefix(), ns ); } } saved(namespace); } public NamespaceInfo getDefaultNamespace() { return namespaces.containsKey(null) ? ModificationProxy.create(namespaces.get( null ),NamespaceInfo.class) : null; } public void setDefaultNamespace(NamespaceInfo defaultNamespace) { NamespaceInfo ns = namespaces.get( defaultNamespace.getPrefix() ); if ( ns == null ) { throw new IllegalArgumentException( "No such namespace: '" + defaultNamespace.getPrefix() + "'" ); } NamespaceInfo old = namespaces.get(null); namespaces.put( null, ns ); //fire change event fireModified(this, Arrays.asList("defaultNamespace"), Arrays.asList(old), Arrays.asList(defaultNamespace)); } // Workspace methods public void add(WorkspaceInfo workspace) { validate(workspace,true); if ( workspaces.containsKey( workspace.getName() ) ) { throw new IllegalArgumentException( "Workspace with name '" + workspace.getName() + "' already exists."); } resolve(workspace); synchronized (workspaces) { workspaces.put( workspace.getName(), workspace ); // if there is no default workspace use this one as the default if ( workspaces.get( null ) == null ) { workspaces.put( null, workspace ); //fire the event fireModified(this, Arrays.asList("defaultWorkspace"), Collections.singletonList(null), Arrays.asList(workspace)); } } added( workspace ); } void validate(WorkspaceInfo workspace, boolean isNew) { if ( isNull(workspace.getName()) ) { throw new NullPointerException( "workspace name must not be null"); } WorkspaceInfo existing = 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 ( 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."); } workspaces.remove( workspace.getName() ); WorkspaceInfo defaultWorkspace = getDefaultWorkspace(); if (workspace.equals(defaultWorkspace)) { workspaces.remove(null); //default removed, choose another workspace to become default if (!workspaces.isEmpty()) { setDefaultWorkspace(workspaces.values().iterator().next()); } } removed( workspace ); } public void save(WorkspaceInfo workspace) { validate(workspace,false); ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(workspace); WorkspaceInfo ws = (WorkspaceInfo) h.getProxyObject(); if ( !workspace.getName().equals( ws.getName() ) ) { synchronized (workspaces) { workspaces.remove( ws.getName() ); workspaces.put( workspace.getName(), ws ); } } saved(workspace); } public WorkspaceInfo getDefaultWorkspace() { return workspaces.containsKey( null ) ? ModificationProxy.create( workspaces.get( null ), WorkspaceInfo.class ) : null; } public void setDefaultWorkspace(WorkspaceInfo workspace) { WorkspaceInfo old = workspaces.get(null); workspaces.put( null, workspace ); //fire change event fireModified(this, Arrays.asList("defaultWorkspace"), Arrays.asList(old), Arrays.asList(workspace)); } public List<WorkspaceInfo> getWorkspaces() { ArrayList<WorkspaceInfo> ws = new ArrayList<WorkspaceInfo>(); //strip out default namespace for ( Map.Entry<String, WorkspaceInfo> e : workspaces.entrySet() ) { if ( e.getKey() == null ) { continue; } ws.add( e.getValue() ); } return ModificationProxy.createList( ws, WorkspaceInfo.class ); } public WorkspaceInfo getWorkspace(String id) { for ( WorkspaceInfo ws : workspaces.values() ) { if ( id.equals( ws.getId() ) ) { return ModificationProxy.create(ws,WorkspaceInfo.class); } } return null; } public WorkspaceInfo getWorkspaceByName(String name) { return workspaces.containsKey(name) ? ModificationProxy.create( workspaces.get( name ), WorkspaceInfo.class ) : null; } // Style methods public StyleInfo getStyle(String id) { for (Iterator s = styles.iterator(); s.hasNext();) { StyleInfo style = (StyleInfo) s.next(); if (id.equals(style.getId())) { return ModificationProxy.create(style,StyleInfo.class); } } return null; } public StyleInfo getStyleByName(String name) { for (Iterator s = styles.iterator(); s.hasNext();) { StyleInfo style = (StyleInfo) s.next(); if (name.equals(style.getName())) { return ModificationProxy.create(style,StyleInfo.class); } } return null; } public List getStyles() { return ModificationProxy.createList(styles,StyleInfo.class); } public void add(StyleInfo style) { validate(style,true); resolve(style); styles.add(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 : layers ) { if ( style.equals( l.getDefaultStyle() ) || l.getStyles().contains( style )) { throw new IllegalArgumentException( "Unable to delete style referenced by '"+ l.getName()+"'"); } } styles.remove(unwrap(style)); 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 Iterator search(String cql) { // TODO Auto-generated method stub return null; } 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 ( stores != null ) stores.clear(); if ( resources != null ) resources.clear(); if ( namespaces != null ) namespaces.clear(); if ( workspaces != null ) workspaces.clear(); if ( layers != null ) layers.clear(); if ( layerGroups != null ) layerGroups.clear(); if ( maps != null ) maps.clear(); if ( styles != null ) styles.clear(); if ( listeners != null ) listeners.clear(); if ( resourcePool != null ) resourcePool.dispose(); } List lookup(Class clazz, MultiHashMap map) { ArrayList result = new ArrayList(); for (Iterator k = map.keySet().iterator(); k.hasNext();) { Class key = (Class) k.next(); if (clazz.isAssignableFrom(key)) { result.addAll(map.getCollection(key)); } } return result; } protected void added(CatalogInfo object) { fireAdded( object ); } protected void fireAdded(CatalogInfo object) { CatalogAddEventImpl event = new CatalogAddEventImpl(); event.setSource(object); event(event); } protected void saved(CatalogInfo object) { //this object is a proxy 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) { CatalogException toThrow = null; for (Iterator l = listeners.iterator(); l.hasNext();) { try { CatalogListener listener = (CatalogListener) l.next(); 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 t) { if ( t instanceof CatalogException && toThrow == null) { toThrow = (CatalogException) t; } else { LOGGER.log(Level.WARNING, "Catalog listener threw exception handling event.", t); } } } if (toThrow != null) { throw toThrow; } } /** * Implementation method for resolving all {@link ResolvingProxy} instances. */ public void resolve() { //JD creation checks are done here b/c when xstream depersists // some members may be left null //workspaces if ( workspaces == null ) { workspaces = new HashMap<String, WorkspaceInfo>(); } for ( WorkspaceInfo ws : workspaces.values() ) { resolve(ws); } //namespaces if ( namespaces == null ) { namespaces = new HashMap<String, NamespaceInfo>(); } for ( NamespaceInfo ns : namespaces.values() ) { resolve(ns); } //stores if ( stores == null ) { stores = new MultiHashMap(); } for ( Object o : stores.values() ) { resolve((StoreInfoImpl)o); } //styles if ( styles == null ) { styles = new ArrayList<StyleInfo>(); } for ( StyleInfo s : styles ) { resolve(s); } //resources if ( resources == null ) { resources = new MultiHashMap(); } for( Object o : resources.values() ) { resolve((ResourceInfo)o); } //layers if ( layers == null ) { layers = new ArrayList<LayerInfo>(); } for ( LayerInfo l : layers ) { resolve(l); } //layer groups if ( layerGroups == null ) { layerGroups = new ArrayList<LayerGroupInfo>(); } for ( LayerGroupInfo lg : layerGroups ) { resolve(lg); } //maps if ( maps == null ) { maps = new ArrayList<MapInfo>(); } for ( MapInfo m : maps ) { resolve(m); } if ( listeners == null ) { listeners = new ArrayList<CatalogListener>(); } if ( resourcePool == null ) { resourcePool = new ResourcePool(this); } } protected void resolve(WorkspaceInfo workspace) { setId(workspace); resolveCollections(workspace); } protected void resolve(NamespaceInfo namespace) { setId(namespace); resolveCollections(namespace); } protected void resolve(StoreInfo store) { setId(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) { setId(resource); ResourceInfoImpl r = (ResourceInfoImpl) resource; //resolve the store StoreInfo resolved = ResolvingProxy.resolve( this, r.getStore() ); if ( resolved != null ) { 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) { setId(layer); if (layer.getAttribution() == null) { layer.setAttribution(getFactory().createAttribution()); } resolveCollections(layer); } protected void resolve(LayerGroupInfo layerGroup) { setId(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 ); 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 ); lg.getStyles().set( i, resolved ); } } } protected void resolve(StyleInfo style) { setId(style); ((StyleInfoImpl)style).setCatalog( this ); } protected void resolve(MapInfo map) { setId(map); } /** * 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 void sync( CatalogImpl other ) { stores = other.stores; resources = other.resources; namespaces = other.namespaces; workspaces = other.workspaces; layers = other.layers; maps = other.maps; layerGroups = other.layerGroups; styles = other.styles; listeners = other.listeners; if ( resourcePool != other.resourcePool ) { resourcePool.dispose(); resourcePool = other.resourcePool; } resourceLoader = other.resourceLoader; } public static <T> T unwrap(T obj) { return ModificationProxy.unwrap(obj); } public void accept(CatalogVisitor visitor) { visitor.visit(this); } }