/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.catalog.impl; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; import java.util.function.Predicate; import javax.annotation.Nullable; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogFacade; import org.geoserver.catalog.CatalogInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.LockingCatalogFacade; import org.geoserver.catalog.MapInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.util.CloseableIterator; import org.geoserver.catalog.util.CloseableIteratorAdapter; import org.geoserver.ows.util.OwsUtils; import org.geotools.feature.NameImpl; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; import org.opengis.filter.sort.SortBy; import org.opengis.filter.sort.SortOrder; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; /** * Default catalog facade implementation in which all objects are stored in memory. * * @author Justin Deoliveira, OpenGeo * * TODO: look for any exceptions, move them back to catalog as they indicate logic */ public class DefaultCatalogFacade extends AbstractCatalogFacade implements CatalogFacade { /** * The name uses the workspace id as it does not need to be updated when the workspace is renamed */ static final Function<StoreInfo, Name> STORE_NAME_MAPPER = s -> new NameImpl(s.getWorkspace().getId(), s.getName()); /** * The name uses the namspace id as it does not need to be updated when the namespace is renamed */ static final Function<ResourceInfo, Name> RESOURCE_NAME_MAPPER = r -> new NameImpl(r.getNamespace().getId(), r.getName()); /** * Like LayerInfo, actually delegates to the resource logic */ static final Function<LayerInfo, Name> LAYER_NAME_MAPPER = l -> RESOURCE_NAME_MAPPER.apply(l.getResource()); /** * The name uses the workspace id as it does not need to be updated when the workspace is renamed */ static final Function<LayerGroupInfo, Name> LAYERGROUP_NAME_MAPPER = lg -> new NameImpl(lg.getWorkspace() != null ? lg.getWorkspace().getId() : null, lg.getName()); static final Function<NamespaceInfo, Name> NAMESPACE_NAME_MAPPER = n -> new NameImpl(n.getPrefix()); static final Function<WorkspaceInfo, Name> WORKSPACE_NAME_MAPPER = w -> new NameImpl(w.getName()); static final Function<StyleInfo, Name> STYLE_NAME_MAPPER = s -> new NameImpl(s.getWorkspace() != null ? s.getWorkspace().getId() : null, s.getName()); static final class LayerInfoLookup extends CatalogInfoLookup<LayerInfo> { public LayerInfoLookup() { super(LAYER_NAME_MAPPER); } public void update(ResourceInfo proxiedValue) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(proxiedValue); ResourceInfo actualValue = (ResourceInfo) h.getProxyObject(); Name oldName = RESOURCE_NAME_MAPPER.apply(actualValue); Name newName = RESOURCE_NAME_MAPPER.apply(proxiedValue); if(!oldName.equals(newName)) { Map<Name, LayerInfo> nameMap = getMapForValue(nameMultiMap, LayerInfoImpl.class); LayerInfo value = nameMap.remove(oldName); // handle case of feature type without a corresponding layer if(value != null) { nameMap.put(newName, value); } } } } /** * Contains the stores keyed by implementation class */ protected CatalogInfoLookup<StoreInfo> stores = new CatalogInfoLookup<>(STORE_NAME_MAPPER); /** * The default store keyed by workspace id */ protected Map<String, DataStoreInfo> defaultStores = new ConcurrentHashMap<String, DataStoreInfo>(); /** * resources */ protected CatalogInfoLookup<ResourceInfo> resources = new CatalogInfoLookup<>(RESOURCE_NAME_MAPPER); /** * The default namespace */ protected volatile NamespaceInfo defaultNamespace; /** * namespaces */ protected CatalogInfoLookup<NamespaceInfo> namespaces = new CatalogInfoLookup<>(NAMESPACE_NAME_MAPPER); /** * The default workspace */ protected volatile WorkspaceInfo defaultWorkspace; /** * workspaces */ protected CatalogInfoLookup<WorkspaceInfo> workspaces = new CatalogInfoLookup<>(WORKSPACE_NAME_MAPPER); /** * layers */ protected LayerInfoLookup layers = new LayerInfoLookup(); /** * maps */ protected List<MapInfo> maps = new CopyOnWriteArrayList<MapInfo>(); /** * layer groups */ protected CatalogInfoLookup<LayerGroupInfo> layerGroups = new CatalogInfoLookup<>(LAYERGROUP_NAME_MAPPER); /** * styles */ protected CatalogInfoLookup<StyleInfo> styles = new CatalogInfoLookup<>(STYLE_NAME_MAPPER); /** * the catalog */ private CatalogImpl catalog; public DefaultCatalogFacade(Catalog catalog) { setCatalog(catalog); } public void setCatalog(Catalog catalog) { this.catalog = (CatalogImpl) catalog; } public Catalog getCatalog() { return catalog; } // // Stores // public StoreInfo add(StoreInfo store) { resolve(store); stores.add(store); return ModificationProxy.create(store, StoreInfo.class); } public void remove(StoreInfo store) { store = unwrap(store); synchronized(stores) { stores.remove(store); } } public void save(StoreInfo store) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(store); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(store, propertyNames, oldValues, newValues); stores.update(store); commitProxy(store); afterSaved(store, propertyNames, oldValues, newValues); } public <T extends StoreInfo> T detach(T store) { return store; } public <T extends StoreInfo> T getStore(String id, Class<T> clazz) { T store = stores.findById(id, clazz); return wrapInModificationProxy(store, clazz); } public <T extends StoreInfo> T getStoreByName(WorkspaceInfo workspace, String name, Class<T> clazz) { T result; if (workspace == ANY_WORKSPACE) { result = stores.findFirst(clazz, s -> name.equals(s.getName())); } else { Name qname = new NameImpl((workspace != null) ? workspace.getId() : null, name); result = stores.findByName(qname, clazz); } return wrapInModificationProxy(result, clazz); } public <T extends StoreInfo> List<T> getStoresByWorkspace( WorkspaceInfo workspace, Class<T> clazz) { //TODO: support ANY_WORKSPACE? WorkspaceInfo ws; if ( workspace == null ) { ws = getDefaultWorkspace(); } else { ws = workspace; } List<T> matches = stores.list(clazz, s -> ws.equals(s.getWorkspace())); return ModificationProxy.createList(matches,clazz); } public <T extends StoreInfo> List<T> getStores(Class<T> clazz) { return ModificationProxy.createList(stores.list(clazz, CatalogInfoLookup.TRUE) , clazz); } public DataStoreInfo getDefaultDataStore(WorkspaceInfo workspace) { if(defaultStores.containsKey(workspace.getId())) { DataStoreInfo defaultStore = defaultStores.get(workspace.getId()); return ModificationProxy.create(defaultStore, DataStoreInfo.class); } else { return null; } } public void setDefaultDataStore(WorkspaceInfo workspace, DataStoreInfo store) { DataStoreInfo old = defaultStores.get(workspace.getId()); //fire modify event before change catalog.fireModified(catalog, Arrays.asList("defaultDataStore"), Arrays.asList(old), Arrays.asList(store)); synchronized(defaultStores) { if (store != null) { defaultStores.put(workspace.getId(), store); } else { defaultStores.remove(workspace.getId()); } } //fire postmodify event after change catalog.firePostModified(catalog, Arrays.asList("defaultDataStore"), Arrays.asList(old), Arrays.asList(store)); } // // Resources // public ResourceInfo add(ResourceInfo resource) { resolve(resource); synchronized(resources) { resources.add(resource); } return ModificationProxy.create(resource, ResourceInfo.class); } public void remove(ResourceInfo resource) { resource = unwrap(resource); synchronized(resources) { resources.remove(resource); } } public void save(ResourceInfo resource) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(resource); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(resource, propertyNames, oldValues, newValues); resources.update(resource); layers.update(resource); commitProxy(resource); afterSaved(resource, propertyNames, oldValues, newValues); } public <T extends ResourceInfo> T detach(T resource) { return resource; } public <T extends ResourceInfo> T getResource(String id, Class<T> clazz) { T result = resources.findById(id, clazz); return wrapInModificationProxy(result, clazz); } public <T extends ResourceInfo> T getResourceByName(NamespaceInfo namespace, String name, Class<T> clazz) { T result; if (namespace == ANY_NAMESPACE) { result = resources.findFirst(clazz, r -> name.equals(r.getName())); } else { Name qname = new NameImpl(namespace != null ? namespace.getId() : null, name); result = resources.findByName(qname, clazz); } return wrapInModificationProxy(result, clazz); } public <T extends ResourceInfo> List<T> getResources(Class<T> clazz) { return ModificationProxy.createList(resources.list(clazz, CatalogInfoLookup.TRUE), clazz); } public <T extends ResourceInfo> List<T> getResourcesByNamespace(NamespaceInfo namespace, Class<T> clazz) { // TODO: support ANY_NAMESPACE? NamespaceInfo ns; if ( namespace == null ) { ns = getDefaultNamespace(); } else { ns = namespace; } List<T> matches = resources.list(clazz, r -> ns.equals(r.getNamespace())); return ModificationProxy.createList( matches, clazz ); } public <T extends ResourceInfo> T getResourceByStore(StoreInfo store, String name, Class<T> clazz) { T resource = null; NamespaceInfo ns = null; if(store.getWorkspace() != null && store.getWorkspace().getName() != null && (ns = getNamespaceByPrefix(store.getWorkspace().getName())) != null) { resource = resources.findByName(new NameImpl(ns.getId(), name), clazz); if(resource != null && !(store.equals(resource.getStore()))) { return null; } } else { // should not happen, but some broken test code sets up namespaces without equivalent workspaces // or stores without workspaces resource = resources.findFirst(clazz, r -> name.equals( r.getName() ) && store.equals( r.getStore())); } return wrapInModificationProxy(resource, clazz); } private <T extends CatalogInfo> T wrapInModificationProxy(T ci, Class<T> clazz) { if (ci != null) { return ModificationProxy.create(ci, clazz); } else { return null; } } public <T extends ResourceInfo> List<T> getResourcesByStore( StoreInfo store, Class<T> clazz) { List<T> matches = resources.list(clazz, r -> store.equals(r.getStore())); return ModificationProxy.createList( matches, clazz ); } // // Layers // public LayerInfo add(LayerInfo layer) { resolve(layer); layers.add(layer); return ModificationProxy.create(layer, LayerInfo.class); } public void remove(LayerInfo layer) { layers.remove(unwrap(layer)); } public void save(LayerInfo layer) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(layer); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(layer, propertyNames, oldValues, newValues); layers.update(layer); commitProxy(layer); afterSaved(layer, propertyNames, oldValues, newValues); } public LayerInfo detach(LayerInfo layer) { return layer; } public LayerInfo getLayer(String id) { LayerInfo li = layers.findById(id, LayerInfo.class); return wrapInModificationProxy(li, LayerInfo.class); } public LayerInfo getLayerByName(String name) { LayerInfo result = layers.findFirst(LayerInfo.class, li -> name.equals( li.getName())); return wrapInModificationProxy(result, LayerInfo.class); } public List<LayerInfo> getLayers(ResourceInfo resource) { // in the current setup we cannot have multiple layers associated to the same // resource, as they would all share the same name (the one of the resource) so // a direct lookup becomes possible Name name = RESOURCE_NAME_MAPPER.apply(resource); LayerInfo layer = layers.findByName(name, LayerInfo.class); if(layer == null) { return Collections.emptyList(); } else { List<LayerInfo> matches = new ArrayList<>(); matches.add(layer); return ModificationProxy.createList(matches, LayerInfo.class); } // we check the id first as it's faster to compare than a full blown equals // String id = resource.getId(); // List<LayerInfo> matches = layers.list(LayerInfo.class, li -> id.equals(li.getResource().getId()) && resource.equals(li.getResource())); // return ModificationProxy.createList(matches,LayerInfo.class); } public List<LayerInfo> getLayers(StyleInfo style) { List<LayerInfo> matches = layers.list(LayerInfo.class, li -> style.equals(li.getDefaultStyle()) || li.getStyles().contains(style)); return ModificationProxy.createList(matches,LayerInfo.class); } public List<LayerInfo> getLayers() { return ModificationProxy.createList( new ArrayList<>(layers.values()), LayerInfo.class ); } // // Maps // public MapInfo add(MapInfo map) { resolve(map); synchronized(maps) { maps.add(map); } return ModificationProxy.create(map, MapInfo.class); } public void remove(MapInfo map) { synchronized(maps) { maps.remove(unwrap(map)); } } public void save(MapInfo map) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(map); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(map, propertyNames, oldValues, newValues); commitProxy(map); afterSaved(map, propertyNames, oldValues, newValues); } public MapInfo detach(MapInfo map) { return map; } 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 ); } // // Layer groups // public LayerGroupInfo add (LayerGroupInfo layerGroup) { resolve(layerGroup); synchronized(layerGroups) { layerGroups.add( layerGroup ); } return ModificationProxy.create(layerGroup, LayerGroupInfo.class); } /* (non-Javadoc) * @see org.geoserver.catalog.impl.CatalogDAO#remove(org.geoserver.catalog.LayerGroupInfo) */ public void remove(LayerGroupInfo layerGroup) { synchronized(layerGroups) { layerGroups.remove( unwrap(layerGroup) ); } } /* (non-Javadoc) * @see org.geoserver.catalog.impl.CatalogDAO#save(org.geoserver.catalog.LayerGroupInfo) */ public void save(LayerGroupInfo layerGroup) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(layerGroup); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(layerGroup, propertyNames, oldValues, newValues); layerGroups.update(layerGroup); commitProxy(layerGroup); afterSaved(layerGroup, propertyNames, oldValues, newValues); } public LayerGroupInfo detach(LayerGroupInfo layerGroup) { return layerGroup; } public List<LayerGroupInfo> getLayerGroups() { return ModificationProxy.createList( new ArrayList<>(layerGroups.values()), LayerGroupInfo.class ); } public List<LayerGroupInfo> getLayerGroupsByWorkspace(WorkspaceInfo workspace) { //TODO: support ANY_WORKSPACE? WorkspaceInfo ws; if ( workspace == null ) { ws = getDefaultWorkspace(); } else { ws = workspace; } Predicate<LayerGroupInfo> predicate; if(workspace == NO_WORKSPACE) { predicate = lg -> lg.getWorkspace() == null; } else { predicate = lg -> ws.equals(lg.getWorkspace()); } List<LayerGroupInfo> matches = layerGroups.list(LayerGroupInfo.class, predicate); return ModificationProxy.createList(matches,LayerGroupInfo.class); } public LayerGroupInfo getLayerGroup(String id) { LayerGroupInfo result = layerGroups.findById(id, LayerGroupInfo.class); return wrapInModificationProxy(result, LayerGroupInfo.class); } @Override public LayerGroupInfo getLayerGroupByName(String name) { return getLayerGroupByName(NO_WORKSPACE, name); } @Override public LayerGroupInfo getLayerGroupByName(WorkspaceInfo workspace, String name) { LayerGroupInfo match; if(workspace == NO_WORKSPACE) { match = layerGroups.findByName(new NameImpl(null, name), LayerGroupInfo.class); } else if(ANY_WORKSPACE == workspace) { match = layerGroups.findFirst(LayerGroupInfo.class, lg -> name.equals(lg.getName())); } else { match = layerGroups.findByName(new NameImpl(workspace.getId(), name), LayerGroupInfo.class); } return wrapInModificationProxy(match, LayerGroupInfo.class); } // // Namespaces // public NamespaceInfo add(NamespaceInfo namespace) { resolve(namespace); namespaces.add(namespace); return ModificationProxy.create(namespace, NamespaceInfo.class); } public void remove(NamespaceInfo namespace) { if (namespace.equals(defaultNamespace)) { defaultNamespace = null; } namespaces.remove(namespace); } public void save(NamespaceInfo namespace) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(namespace); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(namespace, propertyNames, oldValues, newValues); namespaces.update(namespace); commitProxy(namespace); afterSaved(namespace, propertyNames, oldValues, newValues); } public NamespaceInfo detach(NamespaceInfo namespace) { return namespace; } public NamespaceInfo getDefaultNamespace() { return wrapInModificationProxy(defaultNamespace, NamespaceInfo.class); } public void setDefaultNamespace(NamespaceInfo defaultNamespace) { NamespaceInfo old = this.defaultNamespace; //fire modify event before change catalog.fireModified(catalog, Arrays.asList("defaultNamespace"), Arrays.asList(old), Arrays.asList(defaultNamespace)); this.defaultNamespace = defaultNamespace; //fire postmodify event after change catalog.firePostModified(catalog, Arrays.asList("defaultNamespace"), Arrays.asList(old), Arrays.asList(defaultNamespace)); } public NamespaceInfo getNamespace(String id) { NamespaceInfo ns = namespaces.findById(id, NamespaceInfo.class); return wrapInModificationProxy(ns, NamespaceInfo.class); } public NamespaceInfo getNamespaceByPrefix(String prefix) { NamespaceInfo ns = namespaces.findByName(new NameImpl(prefix), NamespaceInfo.class); return wrapInModificationProxy(ns, NamespaceInfo.class); } public NamespaceInfo getNamespaceByURI(String uri) { NamespaceInfo result = namespaces.findFirst(NamespaceInfo.class, ns -> uri.equals(ns.getURI())); return wrapInModificationProxy(result, NamespaceInfo.class); } public List<NamespaceInfo> getNamespaces() { return ModificationProxy.createList(new ArrayList<>(namespaces.values()), NamespaceInfo.class ); } // // Workspaces // // Workspace methods public WorkspaceInfo add(WorkspaceInfo workspace) { resolve(workspace); workspaces.add(workspace); return ModificationProxy.create(workspace, WorkspaceInfo.class); } public void remove(WorkspaceInfo workspace) { if(workspace.equals(this.defaultWorkspace)) { this.defaultWorkspace = null; } workspaces.remove(workspace); } public void save(WorkspaceInfo workspace) { // need to synch up the default store lookup ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(workspace); WorkspaceInfo ws = (WorkspaceInfo) h.getProxyObject(); if (!workspace.getName().equals(ws.getName())) { DataStoreInfo ds = defaultStores.remove(ws.getName()); if(ds != null) { defaultStores.put(workspace.getName(), ds); } } // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(workspace, propertyNames, oldValues, newValues); workspaces.update(workspace); commitProxy(workspace); afterSaved(workspace, propertyNames, oldValues, newValues); } public WorkspaceInfo detach(WorkspaceInfo workspace) { return workspace; } public WorkspaceInfo getDefaultWorkspace() { return defaultWorkspace; } public void setDefaultWorkspace(WorkspaceInfo workspace) { WorkspaceInfo old = defaultWorkspace; //fire modify event before change catalog.fireModified(catalog, Arrays.asList("defaultWorkspace"), Arrays.asList(old), Arrays.asList(workspace)); this.defaultWorkspace = workspace; //fire postmodify event after change catalog.firePostModified(catalog, Arrays.asList("defaultWorkspace"), Arrays.asList(old), Arrays.asList(workspace)); } public List<WorkspaceInfo> getWorkspaces() { return ModificationProxy.createList( new ArrayList<>(workspaces.values()), WorkspaceInfo.class ); } public WorkspaceInfo getWorkspace(String id) { WorkspaceInfo ws = workspaces.findById(id, WorkspaceInfo.class); return wrapInModificationProxy(ws, WorkspaceInfo.class); } public WorkspaceInfo getWorkspaceByName(String name) { WorkspaceInfo ws = workspaces.findByName(new NameImpl(name), WorkspaceInfo.class); return wrapInModificationProxy(ws, WorkspaceInfo.class); } // // Styles // public StyleInfo add(StyleInfo style) { resolve(style); synchronized(styles) { styles.add(style); } return ModificationProxy.create(style, StyleInfo.class); } public void remove(StyleInfo style) { synchronized(styles) { styles.remove(unwrap(style)); } } public void save(StyleInfo style) { ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(style); // figure out what changed List<String> propertyNames = h.getPropertyNames(); List<Object> newValues = h.getNewValues(); List<Object> oldValues = h.getOldValues(); beforeSaved(style, propertyNames, oldValues, newValues); styles.update(style); commitProxy(style); afterSaved(style, propertyNames, oldValues, newValues); } public StyleInfo detach(StyleInfo style) { return style; } public StyleInfo getStyle(String id) { StyleInfo match = styles.findById(id, StyleInfo.class); return wrapInModificationProxy(match, StyleInfo.class); } public StyleInfo getStyleByName(String name) { StyleInfo match = styles.findByName(new NameImpl(null, name), StyleInfo.class); if(match == null) { match = styles.findFirst(StyleInfo.class, s -> name.equals(s.getName())); } return wrapInModificationProxy(match, StyleInfo.class); } @Override public StyleInfo getStyleByName(WorkspaceInfo workspace, String name) { if (null == workspace) { throw new NullPointerException("workspace"); } if (null == name) { throw new NullPointerException("name"); } if (workspace == ANY_WORKSPACE) { return getStyleByName(name); } else { Name sn = new NameImpl(workspace == null ? null : workspace.getId(), name); StyleInfo match = styles.findByName(sn, StyleInfo.class); return wrapInModificationProxy(match, StyleInfo.class); } } public List<StyleInfo> getStyles() { return ModificationProxy.createList(new ArrayList<>(styles.values()), StyleInfo.class); } public List<StyleInfo> getStylesByWorkspace(WorkspaceInfo workspace) { //TODO: support ANY_WORKSPACE? List<StyleInfo> matches; if (workspace == NO_WORKSPACE) { matches = styles.list(StyleInfo.class, s -> s.getWorkspace() == null); } else { WorkspaceInfo ws; if ( workspace == null ) { ws = getDefaultWorkspace(); } else { ws = workspace; } matches = styles.list(StyleInfo.class, s -> ws.equals(s.getWorkspace())); } return ModificationProxy.createList(matches,StyleInfo.class); } public void dispose() { if ( stores != null ) stores.clear(); if ( defaultStores != null ) defaultStores.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(); } 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 CatalogInfoLookup<>(WORKSPACE_NAME_MAPPER); } for ( WorkspaceInfo ws : workspaces.values() ) { resolve(ws); } //namespaces if ( namespaces == null ) { namespaces = new CatalogInfoLookup<>(NAMESPACE_NAME_MAPPER); } for ( NamespaceInfo ns : namespaces.values() ) { resolve(ns); } //stores if ( stores == null ) { stores = new CatalogInfoLookup<>(STORE_NAME_MAPPER); } for ( Object o : stores.values() ) { resolve((StoreInfoImpl)o); } //styles if ( styles == null ) { styles = new CatalogInfoLookup<>(STYLE_NAME_MAPPER); } for ( StyleInfo s : styles.values() ) { resolve(s); } //resources if ( resources == null ) { resources = new CatalogInfoLookup<>(RESOURCE_NAME_MAPPER); } for( Object o : resources.values() ) { resolve((ResourceInfo)o); } // layers if ( layers == null ) { layers = new LayerInfoLookup(); } for ( LayerInfo l : layers.values() ) { resolve(l); } //layer groups if ( layerGroups == null ) { layerGroups = new CatalogInfoLookup<>(LAYERGROUP_NAME_MAPPER); } for (LayerGroupInfo lg : layerGroups.values()) { resolve(lg); } //maps if ( maps == null ) { maps = new ArrayList<MapInfo>(); } for ( MapInfo m : maps ) { resolve(m); } } public void syncTo(CatalogFacade dao) { dao = ProxyUtils.unwrap(dao, LockingCatalogFacade.class); if (dao instanceof DefaultCatalogFacade) { //do an optimized sync DefaultCatalogFacade other = (DefaultCatalogFacade) dao; other.stores = stores; other.defaultStores = defaultStores; other.resources = resources; other.defaultNamespace = defaultNamespace; other.namespaces = namespaces; other.defaultWorkspace = defaultWorkspace; other.workspaces = workspaces; other.layers = layers; other.maps = maps; other.layerGroups = layerGroups; other.styles = styles; } else { //do a manual import for (WorkspaceInfo ws : workspaces.values()) { dao.add(ws); } for (NamespaceInfo ns : namespaces.values()) { dao.add(ns); } for (StoreInfo store : stores.values()) { dao.add(store); } for (ResourceInfo resource : resources.values()) { dao.add(resource); } for (StyleInfo s : styles.values()) { dao.add(s); } for (LayerInfo l : layers.values()) { dao.add(l); } for (LayerGroupInfo lg : layerGroups.values()) { dao.add(lg); } for (MapInfo m : maps) { dao.add(m); } if (defaultWorkspace != null) { dao.setDefaultWorkspace(defaultWorkspace); } if (defaultNamespace != null) { dao.setDefaultNamespace(defaultNamespace); } for (Map.Entry<String, DataStoreInfo> e : defaultStores.entrySet()) { WorkspaceInfo ws = workspaces.findById(e.getKey(), WorkspaceInfo.class); if (null != ws) { dao.setDefaultDataStore(ws, e.getValue()); } } } } @Override public <T extends CatalogInfo> int count(final Class<T> of, final Filter filter) { return Iterables.size(iterable(of, filter, null)); } /** * This default implementation supports sorting against properties (could be nested) that are * either of a primitive type or implement {@link Comparable}. * * @param type the type of object to sort * @param propertyName the property name of the objects of type {@code type} to sort by * @see org.geoserver.catalog.CatalogFacade#canSort(java.lang.Class, java.lang.String) */ @Override public boolean canSort(final Class<? extends CatalogInfo> type, final String propertyName) { final String[] path = propertyName.split("\\."); Class<?> clazz = type; for (int i = 0; i < path.length; i++) { String property = path[i]; Method getter; try { getter = OwsUtils.getter(clazz, property, null); } catch (RuntimeException e) { return false; } clazz = getter.getReturnType(); if (i == path.length - 1) { boolean primitive = clazz.isPrimitive(); boolean comparable = Comparable.class.isAssignableFrom(clazz); boolean canSort = primitive || comparable; return canSort; } } throw new IllegalStateException("empty property name"); } @Override public <T extends CatalogInfo> CloseableIterator<T> list(final Class<T> of, final Filter filter, @Nullable Integer offset, @Nullable Integer count, @Nullable SortBy sortOrder) { SortBy[] sortOrderList = null; if (sortOrder != null) { sortOrderList = new SortBy[] { sortOrder }; } return list(of, filter, offset, count, sortOrderList); } @Override public <T extends CatalogInfo> CloseableIterator<T> list(final Class<T> of, final Filter filter, @Nullable Integer offset, @Nullable Integer count, @Nullable SortBy... sortOrder) { if (sortOrder != null) { for (SortBy so : sortOrder) { if (sortOrder != null && !canSort(of, so.getPropertyName().getPropertyName())) { throw new IllegalArgumentException( "Can't sort objects of type "+of.getName()+" by "+so.getPropertyName()); } } } Iterable<T> iterable = iterable(of, filter, sortOrder); if (offset != null && offset.intValue() > 0) { iterable = Iterables.skip(iterable, offset.intValue()); } if (count != null && count.intValue() >= 0) { iterable = Iterables.limit(iterable, count.intValue()); } Iterator<T> iterator = iterable.iterator(); return new CloseableIteratorAdapter<T>(iterator); } @SuppressWarnings("unchecked") public <T extends CatalogInfo> Iterable<T> iterable(final Class<T> of, final Filter filter, final SortBy[] sortByList) { List<T> all; T t = null; if (NamespaceInfo.class.isAssignableFrom(of)) { all = (List<T>) namespaces.list(of, toPredicate(filter)); } else if (WorkspaceInfo.class.isAssignableFrom(of)) { all = (List<T>) workspaces.list(of, toPredicate(filter)); } else if (StoreInfo.class.isAssignableFrom(of)) { all = (List<T>) stores.list(of, toPredicate(filter)); } else if (ResourceInfo.class.isAssignableFrom(of)) { all = (List<T>) resources.list(of, toPredicate(filter)); } else if (LayerInfo.class.isAssignableFrom(of)) { all = (List<T>) layers.list(of, toPredicate(filter)); } else if (LayerGroupInfo.class.isAssignableFrom(of)) { all = (List<T>) layerGroups.list(of, toPredicate(filter)); } else if (PublishedInfo.class.isAssignableFrom(of)) { all = new ArrayList<>(); all.addAll((List<T>) layers.list(LayerInfo.class, toPredicate(filter))); all.addAll((List<T>) layerGroups.list(LayerGroupInfo.class, toPredicate(filter))); } else if (StyleInfo.class.isAssignableFrom(of)) { all = (List<T>) styles.list(of, toPredicate(filter)); } else if (MapInfo.class.isAssignableFrom(of)) { all = (List<T>) new ArrayList<>(maps); } else { throw new IllegalArgumentException("Unknown type: " + of); } if (null != sortByList) { for (int i = sortByList.length - 1; i >= 0; i--) { SortBy sortBy = sortByList[i]; Ordering<Object> ordering = Ordering.from(comparator(sortBy)); if (SortOrder.DESCENDING.equals(sortBy.getSortOrder())) { ordering = ordering.reverse(); } all = ordering.sortedCopy(all); } } return ModificationProxy.createList(all, of); } private <T> Predicate<T> toPredicate(Filter filter) { if(filter != null && filter != Filter.INCLUDE) { return o -> filter.evaluate(o); } else { return CatalogInfoLookup.TRUE; } } private Comparator<Object> comparator(final SortBy sortOrder) { return new Comparator<Object>() { @Override public int compare(Object o1, Object o2) { Object v1 = OwsUtils.get(o1, sortOrder.getPropertyName().getPropertyName()); Object v2 = OwsUtils.get(o2, sortOrder.getPropertyName().getPropertyName()); if (v1 == null) { if (v2 == null) { return 0; } else { return -1; } } else if (v2 == null) { return 1; } Comparable c1 = (Comparable) v1; Comparable c2 = (Comparable) v2; return c1.compareTo(c2); } }; } }