diff --git a/extension/restconfig/src/main/java/org/geoserver/catalog/rest/AbstractCatalogListResource.java b/extension/restconfig/src/main/java/org/geoserver/catalog/rest/AbstractCatalogListResource.java index efe2d78..541db32 100644 --- a/extension/restconfig/src/main/java/org/geoserver/catalog/rest/AbstractCatalogListResource.java +++ b/extension/restconfig/src/main/java/org/geoserver/catalog/rest/AbstractCatalogListResource.java @@ -4,12 +4,18 @@ */ package org.geoserver.catalog.rest; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import org.geoserver.catalog.Catalog; import org.geoserver.config.util.XStreamPersister; import org.geoserver.ows.util.OwsUtils; +import org.geoserver.rest.format.ReflectiveJSONFormat; +import org.geoserver.rest.format.ReflectiveXMLFormat; import org.restlet.Context; import org.restlet.data.Request; import org.restlet.data.Response; @@ -36,16 +42,54 @@ public abstract class AbstractCatalogListResource extends CatalogResourceBase { protected abstract Collection handleListGet() throws Exception; + //JD: we create custom formats here because we need to set up the collection aliases + // correctly, basically whatever collection we get back we ant to alias to layers, featureTypes, + // coverages, styles, etc... + @Override + protected ReflectiveJSONFormat createJSONFormat(Request request, Response response) { + final ReflectiveJSONFormat f = super.createJSONFormat(request, response); + return new ReflectiveJSONFormat() { + @Override + public XStream getXStream() { + return f.getXStream(); + } + + @Override + protected void write(Object data, OutputStream output) throws IOException { + aliasCollection(data, f.getXStream()); + f.getXStream().toXML(data, output); + } + }; + } + + @Override + protected ReflectiveXMLFormat createXMLFormat(Request request, Response response) { + final ReflectiveXMLFormat f = super.createXMLFormat(request, response); + return new ReflectiveXMLFormat() { + @Override + public XStream getXStream() { + return f.getXStream(); + } + + @Override + protected void write(Object data, OutputStream output) throws IOException { + aliasCollection(data, f.getXStream()); + f.getXStream().toXML(data, output); + } + }; + } + @Override protected void configureXStream(XStream xstream) { XStreamPersister xp = xpf.createXMLPersister(); final String name = xp.getClassAliasingMapper().serializedClass( clazz ); - xstream.alias( name, clazz ); - aliasCollection( name + "s", xstream ); xstream.registerConverter( new CollectionConverter(xstream.getMapper()) { + public boolean canConvert(Class type) { + return Collection.class.isAssignableFrom(type); + }; @Override protected void writeItem(Object item, MarshallingContext context, @@ -100,7 +144,9 @@ public abstract class AbstractCatalogListResource extends CatalogResourceBase { * to work with a Set. *

*/ - protected void aliasCollection( String alias, XStream xstream ) { - xstream.alias( alias, Collection.class, ArrayList.class); + protected void aliasCollection( Object data, XStream xstream ) { + XStreamPersister xp = xpf.createXMLPersister(); + final String alias = xp.getClassAliasingMapper().serializedClass( clazz ); + xstream.alias(alias + "s", Collection.class, data.getClass()); } } diff --git a/extension/restconfig/src/main/java/org/geoserver/catalog/rest/StyleListResource.java b/extension/restconfig/src/main/java/org/geoserver/catalog/rest/StyleListResource.java index f80cb75..959c374 100644 --- a/extension/restconfig/src/main/java/org/geoserver/catalog/rest/StyleListResource.java +++ b/extension/restconfig/src/main/java/org/geoserver/catalog/rest/StyleListResource.java @@ -5,7 +5,6 @@ package org.geoserver.catalog.rest; import java.util.Collection; -import java.util.HashSet; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.StyleInfo; @@ -13,8 +12,6 @@ import org.restlet.Context; import org.restlet.data.Request; import org.restlet.data.Response; -import com.thoughtworks.xstream.XStream; - public class StyleListResource extends AbstractCatalogListResource { protected StyleListResource(Context context, Request request, @@ -22,22 +19,7 @@ public class StyleListResource extends AbstractCatalogListResource { super(context, request, response, StyleInfo.class, catalog); } - - @Override - protected void aliasCollection(String alias, XStream xstream) { - //JD: this is a bit of hack, we check when we alias as to - // property alias the correct collection type, since in teh case - // of listing all, a List is returned, but when referencing through - // style, a Set is returned - String layer = getAttribute("layer"); - if ( layer == null ) { - super.aliasCollection(alias, xstream); - } - else { - xstream.alias( alias, Collection.class, HashSet.class ); - } - } - + @Override protected Collection handleListGet() throws Exception { String layer = getAttribute("layer"); diff --git a/extension/restconfig/src/test/java/org/geoserver/catalog/rest/LayerGroupTest.java b/extension/restconfig/src/test/java/org/geoserver/catalog/rest/LayerGroupTest.java index d2eb79c..db3d647 100644 --- a/extension/restconfig/src/test/java/org/geoserver/catalog/rest/LayerGroupTest.java +++ b/extension/restconfig/src/test/java/org/geoserver/catalog/rest/LayerGroupTest.java @@ -40,6 +40,7 @@ public class LayerGroupTest extends CatalogRESTTestSupport { } public void testGetAsXML() throws Exception { + print(get("/rest/layergroups/sfLayerGroup.xml")); Document dom = getAsDOM( "/rest/layergroups/sfLayerGroup.xml"); assertEquals( "layerGroup", dom.getDocumentElement().getNodeName() ); assertXpathEvaluatesTo("sfLayerGroup", "/layerGroup/name", dom ); diff --git a/extension/restconfig/src/test/java/org/geoserver/catalog/rest/StyleTest.java b/extension/restconfig/src/test/java/org/geoserver/catalog/rest/StyleTest.java index 0e195e7..83f5f7f 100644 --- a/extension/restconfig/src/test/java/org/geoserver/catalog/rest/StyleTest.java +++ b/extension/restconfig/src/test/java/org/geoserver/catalog/rest/StyleTest.java @@ -33,6 +33,7 @@ public class StyleTest extends CatalogRESTTestSupport { public void testGetAllASJSON() throws Exception { JSON json = getAsJSON("/rest/styles.json"); + List styles = catalog.getStyles(); assertEquals( styles.size(), ((JSONObject) json).getJSONObject("styles").getJSONArray("style").size()); @@ -244,7 +245,7 @@ public class StyleTest extends CatalogRESTTestSupport { LayerInfo l2 = catalog.getLayerByName( "cite:BasicPolygons" ); assertEquals( nstyles+1, l2.getStyles().size() ); - assertEquals( catalog.getStyleByName( "Ponds"), l.getDefaultStyle() ); + assertEquals( catalog.getStyleByName( "Ponds"), l2.getDefaultStyle() ); } public void testPostByLayerExistingWithDefault() throws Exception { @@ -263,6 +264,6 @@ public class StyleTest extends CatalogRESTTestSupport { LayerInfo l2 = catalog.getLayerByName("cite:BasicPolygons"); assertEquals( nstyles, l2.getStyles().size() ); - assertEquals( catalog.getStyleByName( "Ponds"), l.getDefaultStyle() ); + assertEquals( catalog.getStyleByName( "Ponds"), l2.getDefaultStyle() ); } } diff --git a/extension/restconfig/src/test/java/org/geoserver/catalog/rest/WMSStoreTest.java b/extension/restconfig/src/test/java/org/geoserver/catalog/rest/WMSStoreTest.java index 0dcc6bd..b0f5b10 100644 --- a/extension/restconfig/src/test/java/org/geoserver/catalog/rest/WMSStoreTest.java +++ b/extension/restconfig/src/test/java/org/geoserver/catalog/rest/WMSStoreTest.java @@ -39,6 +39,7 @@ public class WMSStoreTest extends CatalogRESTTestSupport { public void testGetAllAsXML() throws Exception { Document dom = getAsDOM( "/rest/workspaces/sf/wmsstores.xml"); + assertEquals("wmsStores", dom.getDocumentElement().getNodeName()); assertEquals( catalog.getStoresByWorkspace( "sf", WMSStoreInfo.class ).size(), dom.getElementsByTagName( "wmsStore").getLength() ); } @@ -128,7 +129,6 @@ public class WMSStoreTest extends CatalogRESTTestSupport { public void testGetAsJSON() throws Exception { JSON json = getAsJSON( "/rest/workspaces/sf/wmsstores/demo.json" ); - print(json); JSONObject store = ((JSONObject)json).getJSONObject("wmsStore"); assertNotNull(store); diff --git a/gwc/src/main/resources/geowebcache-geoserver-context.xml b/gwc/src/main/resources/geowebcache-geoserver-context.xml index ce238c9..c8aeed8 100644 --- a/gwc/src/main/resources/geowebcache-geoserver-context.xml +++ b/gwc/src/main/resources/geowebcache-geoserver-context.xml @@ -34,6 +34,8 @@ + + diff --git a/gwc/src/test/java/org/geoserver/gwc/CatalogConfigurationTest.java b/gwc/src/test/java/org/geoserver/gwc/CatalogConfigurationTest.java index 82eb2d6..011fd58 100644 --- a/gwc/src/test/java/org/geoserver/gwc/CatalogConfigurationTest.java +++ b/gwc/src/test/java/org/geoserver/gwc/CatalogConfigurationTest.java @@ -127,14 +127,20 @@ public class CatalogConfigurationTest extends GeoServerTestSupport { // 5) Introducing new LayerInfo ResourceInfo resInfo = li.getResource(); + + //JD: not sure what this next line is really doing, disabling it because it changes the + // namespace and does not save it... and the catalog does not cascade changes + //resInfo.getNamespace().setPrefix("sf"); + resInfo.setName("hithere"); - resInfo.getNamespace().setPrefix("sf"); + cat.save(resInfo); + LayerInfo layerInfo = cat.getFactory().createLayer(); layerInfo.setResource(resInfo); layerInfo.setName(resInfo.getPrefixedName()); cat.add(layerInfo); - String newLayerName = layerInfo.getName(); + String newLayerName = layerInfo.getResource().getPrefixedName(); TileLayer tl3 = tld.getTileLayer(newLayerName); assertEquals(newLayerName, tl3.getName()); diff --git a/main/src/main/java/applicationContext.xml b/main/src/main/java/applicationContext.xml index b96c044..d79c227 100644 --- a/main/src/main/java/applicationContext.xml +++ b/main/src/main/java/applicationContext.xml @@ -39,7 +39,7 @@ - + diff --git a/main/src/main/java/org/geoserver/catalog/Catalog.java b/main/src/main/java/org/geoserver/catalog/Catalog.java index c977e05..e6a16fd 100644 --- a/main/src/main/java/org/geoserver/catalog/Catalog.java +++ b/main/src/main/java/org/geoserver/catalog/Catalog.java @@ -8,6 +8,9 @@ import java.util.Collection; import java.util.List; import org.geoserver.catalog.event.CatalogListener; +import org.geoserver.catalog.event.impl.CatalogAddEventImpl; +import org.geoserver.catalog.event.impl.CatalogModifyEventImpl; +import org.geoserver.catalog.event.impl.CatalogPostModifyEventImpl; import org.geoserver.platform.GeoServerResourceLoader; import org.opengis.feature.type.Name; @@ -132,6 +135,11 @@ public interface Catalog extends CatalogInfo { public static String DEFAULT = "default"; /** + * The dao used for data access. + */ + CatalogDAO getDAO(); + + /** * The factory used to create catalog objects. * */ @@ -153,6 +161,19 @@ public interface Catalog extends CatalogInfo { void save(StoreInfo store); /** + * Detaches the store from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + T detach(T store); + + /** * Returns the store with the specified id. *

* clazz is used to determine the implementation of StoreInfo @@ -595,6 +616,19 @@ public interface Catalog extends CatalogInfo { void save(ResourceInfo resource); /** + * Detatches the resource from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + T detach(T resource); + + /** * All resources in the catalog of the specified type. *

* The clazz parameter is used to filter the types of resources @@ -1009,6 +1043,19 @@ public interface Catalog extends CatalogInfo { void save(LayerInfo layer); /** + * Detatches the layer from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + LayerInfo detach(LayerInfo layer); + + /** * All coverages which are part of the specified store. */ List getCoveragesByStore(CoverageStoreInfo store); @@ -1073,6 +1120,19 @@ public interface Catalog extends CatalogInfo { * Saves a map which has been modified. */ void save( MapInfo map); + + /** + * Detatches the map from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + MapInfo detach(MapInfo map); /** * All maps in the catalog. @@ -1112,6 +1172,19 @@ public interface Catalog extends CatalogInfo { void save(LayerGroupInfo layerGroup); /** + * Detatches the layer group from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + LayerGroupInfo detach(LayerGroupInfo layerGroup); + + /** * All layer groups in the catalog. */ List getLayerGroups(); @@ -1145,6 +1218,19 @@ public interface Catalog extends CatalogInfo { void save(StyleInfo style); /** + * Detatches the style from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + StyleInfo detach(StyleInfo style); + + /** * Returns the style matching a particular id, or null if no * such style could be found. */ @@ -1185,6 +1271,19 @@ public interface Catalog extends CatalogInfo { void save(NamespaceInfo namespace); /** + * Detatches the namespace from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + NamespaceInfo detach(NamespaceInfo namespace); + + /** * Returns the namespace matching the specified id. * */ @@ -1246,6 +1345,19 @@ public interface Catalog extends CatalogInfo { void save(WorkspaceInfo workspace); /** + * Detatches the workspace from the catalog. + *

+ * This method does not remove the object from the catalog, it "unnattaches" the object + * resolving any proxies. + *

+ *

+ * In the even the specified object does not exist in the catalog it itself should be returned, + * this method should never return null. + *

+ */ + WorkspaceInfo detach(WorkspaceInfo workspace); + + /** * The default workspace for the catalog. */ WorkspaceInfo getDefaultWorkspace(); @@ -1277,7 +1389,7 @@ public interface Catalog extends CatalogInfo { * @param The name of the store, or null or {@link #DEFAULT} to get the default workspace */ WorkspaceInfo getWorkspaceByName( String name ); - + /** * catalog listeners. * @@ -1295,6 +1407,43 @@ public interface Catalog extends CatalogInfo { void removeListener(CatalogListener listener); /** + * Fires the event for an object being added to the catalog. + *

+ * This method should not be called by client code. It is meant to be called + * interally by the catalog subsystem. + *

+ */ + void fireAdded(CatalogInfo object); + + /** + * Fires the event for an object being modified in the catalog. + *

+ * This method should not be called by client code. It is meant to be called + * interally by the catalog subsystem. + *

+ */ + void fireModified(CatalogInfo object, List propertyNames, List oldValues, + List newValues); + + /** + * Fires the event for an object that was modified in the catalog. + *

+ * This method should not be called by client code. It is meant to be called + * interally by the catalog subsystem. + *

+ */ + void firePostModified(CatalogInfo object); + + /** + * Fires the event for an object being removed from the catalog. + *

+ * This method should not be called by client code. It is meant to be called + * interally by the catalog subsystem. + *

+ */ + void fireRemoved(CatalogInfo object); + + /** * Returns the pool or cache for resources. *

* This object is used to load physical resources like data stores, feature diff --git a/main/src/main/java/org/geoserver/catalog/CatalogBuilder.java b/main/src/main/java/org/geoserver/catalog/CatalogBuilder.java index 2c8bb61..28a6f09 100644 --- a/main/src/main/java/org/geoserver/catalog/CatalogBuilder.java +++ b/main/src/main/java/org/geoserver/catalog/CatalogBuilder.java @@ -252,64 +252,7 @@ public class CatalogBuilder { *

*/ void update(T original, T update, Class clazz) { - ClassProperties properties = OwsUtils.getClassProperties(clazz); - for (String p : properties.properties()) { - Method getter = properties.getter(p, null); - if (getter == null) { - continue; // should not really happen - } - - Class type = getter.getReturnType(); - Method setter = properties.setter(p, type); - - // do a check for read only before calling the getter to avoid an uneccesary call - if (setter == null - && !(Collection.class.isAssignableFrom(type) || Map.class - .isAssignableFrom(type))) { - // read only - continue; - } - - try { - Object newValue = getter.invoke(update, null); - if (newValue == null) { - continue; - // TODO: make this a flag whether to overwrite with null values - } - if (setter == null) { - if (Collection.class.isAssignableFrom(type)) { - updateCollectionProperty(original, (Collection) newValue, getter); - } else if (Map.class.isAssignableFrom(type)) { - updateMapProperty(original, (Map) newValue, getter); - } - continue; - } - - setter.invoke(original, newValue); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - /** - * Helper method for updating a collection based property. - */ - void updateCollectionProperty(Object object, Collection newValue, Method getter) - throws Exception { - Collection oldValue = (Collection) getter.invoke(object, null); - oldValue.clear(); - oldValue.addAll(newValue); - } - - /** - * Helper method for updating a map based property. - */ - - void updateMapProperty(Object object, Map newValue, Method getter) throws Exception { - Map oldValue = (Map) getter.invoke(object, null); - oldValue.clear(); - oldValue.putAll(newValue); + OwsUtils.copy(update, original, clazz); } /** diff --git a/main/src/main/java/org/geoserver/catalog/CatalogDAO.java b/main/src/main/java/org/geoserver/catalog/CatalogDAO.java new file mode 100644 index 0000000..265fc14 --- /dev/null +++ b/main/src/main/java/org/geoserver/catalog/CatalogDAO.java @@ -0,0 +1,713 @@ +package org.geoserver.catalog; + +import java.util.List; + +import org.geoserver.catalog.event.CatalogModifyEvent; + +/** + * Data access object for the catalog. + * + * @author ETj + * @author Justin Deoliveira, OpenGeo + */ +public interface CatalogDAO { + + /** + * The containing catalog. + */ + Catalog getCatalog(); + + /** + * Sets the containing catalog. + */ + void setCatalog(Catalog catalog); + + // + // Stores + // + /** + * Adds a store to persistent storage. + */ + StoreInfo add(StoreInfo store); + + /** + * Removes a store to persistent storage. + */ + void remove(StoreInfo store); + + /** + * Persists any modifications to a store to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(StoreInfo store); + + /** + * Detaches a store from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(StoreInfo) + */ + T detach(T store); + + /** + * Loads a store from persistent storage by specifying its identifier. + *

+ * The clazz parameter is used to type narrow the returned object to a + * specific type of store. If the type of store is unknown the client may simply + * specify StoreInfo.class. The dao should still create the correct type of store. + *

+ * @param id The unique identifier of the store + * @param clazz The class of the store. + * + * @return The store, or null if no such store exists. + */ + T getStore(String id, Class clazz); + + /** + * Loads a store from persistent storage by specifying its name and containing workspace. + *

+ * The clazz parameter is used to type narrow the returned object to a + * specific type of store. If the type of store is unknown the client may simply + * specify StoreInfo.class. The dao should still create the correct type of store. + *

+ * @param workspace THe containing workspace of the store. + * @param name The name of the store. + * @param clazz The class of the store. + * + * @return The store, or null if no such store exists. + */ + T getStoreByName(WorkspaceInfo workspace, String name, + Class clazz); + + /** + * Loads all stores from persistent storage in the specified workspace. + *

+ * The clazz parameter is used to type narrow/filter the returned objects + * to a specific type of store. Specifying StoreInfo.class will return all types of + * stores. + *

+ * @param workspace The containing workspace. + * @param clazz The class of the stores to return. + * + * @return A list of stores, possibly empty. + */ + List getStoresByWorkspace(WorkspaceInfo workspace, + Class clazz); + + /** + * Loads all stores from persistent storage. + *

+ * The clazz parameter is used to type narrow/filter the returned objects + * to a specific type of store. Specifying StoreInfo.class will return all types of + * stores. + *

+ * + * @param clazz The class of the stores to return. + * + * @return A list of stores, possibly empty. + */ + List getStores(Class clazz); + + /** + * Loads the default data store for the specified workspace. + * + * @param workspace The workspace. + * + * @return The default data store, or null if none is set. + */ + DataStoreInfo getDefaultDataStore(WorkspaceInfo workspace); + + /** + * Sets the default data store for the specified workspace. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. The source of the event is the + * workspace itself and changed property is "defaultDataStore". The changed old/new values should + * the old/new datastores respectively. + *

+ * @param workspace The workspace. + * @param store The default data store. + */ + void setDefaultDataStore(WorkspaceInfo workspace, DataStoreInfo store); + + // + // Resources + // + /** + * Adds a resource to persistent storage. + */ + ResourceInfo add(ResourceInfo resource); + + /** + * Removes a resource from persistent storage. + */ + void remove(ResourceInfo resource); + + /** + * Persists any modifications to a resource to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(ResourceInfo resource); + + /** + * Detaches a resource from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(ResourceInfo) + */ + T detach(T resource); + + /** + * Loads a resource from persistent storage by specifying its identifier. + *

+ * The clazz parameter is used to type narrow the returned object to a + * specific type of resource. If the type of resource is unknown the client may simply + * specify ResourceInfo.class. The dao should still create the correct type of resource. + *

+ * @param id The unique identifier of the resource + * @param clazz The class of the resource. + * + * @return The resource, or null if no such store exists. + */ + T getResource(String id, Class clazz); + + /** + * Loads a resource from persistent storage by specifying its qualified name. + *

+ * The clazz parameter is used to type narrow the returned object to a + * specific type of resource. If the type of resource is unknown the client may simply + * specify ResourceInfo.class. The dao should still create the correct type of resource. + *

+ * @param namespace The namespace of the resource + * @param name The local name of the resource + * @param clazz The class of the resource + * + * @return The resource, or null if no such store exists. + */ + T getResourceByName(NamespaceInfo namespace, + String name, Class clazz); + + /** + * Loads all resources from persistent storage. + *

+ * The clazz parameter is used to type narrow/filter the returned objects + * to a specific type of store. Specifying StoreInfo.class will return all types of + * stores. + *

+ * + * @param clazz The class of the resources to return. + * + * @return A list of resources, possibly empty. + */ + List getResources(Class clazz); + + /** + * Loads all resources from persistent storage contained with a specified namespace. + *

+ * The clazz parameter is used to type narrow/filter the returned objects + * to a specific type of store. Specifying StoreInfo.class will return all types of + * stores. + *

+ * + * @param namespace The namespace of resources + * @param clazz The class of the resources to return. + * + * @return A list of resources, possibly empty. + */ + List getResourcesByNamespace(NamespaceInfo namespace, Class clazz); + + /** + * Loads a resource from persistent storage by specifying its name and containing store. + *

+ * The clazz parameter is used to type narrow the returned object to a + * specific type of resource. If the type of resource is unknown the client may simply + * specify ResourceInfo.class. The dao should still create the correct type of resource. + *

+ * @param store The containing store + * @param name The local name of the resource + * @param clazz The class of the resource + * + * @return The resource, or null if no such store exists. + */ + T getResourceByStore(StoreInfo store, String name, + Class clazz); + + /** + * Loads all resources from persistent storage that are contained within a specified store. + *

+ * The clazz parameter is used to type narrow/filter the returned objects + * to a specific type of store. Specifying StoreInfo.class will return all types of + * stores. + *

+ * + * @param store The containing store + * @param clazz The class of the resources to return + * + * @return A list of resources, possibly empty. + */ + List getResourcesByStore(StoreInfo store, + Class clazz); + + // + // Layers + // + /** + * Adds a layer to persistent storage. + */ + LayerInfo add(LayerInfo layer); + + /** + * + * Removes a layer from persistent storage. + */ + void remove(LayerInfo layer); + + /** + * Persists any modifications to a layer to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(LayerInfo layer); + + /** + * Detaches a layer from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(LayerInfo) + */ + LayerInfo detach(LayerInfo layer); + + /** + * Loads a layer from persistent storage by specifying its identifier. + * + * @param id The unique identifier of the layer + * + * @return The layer, or null if no such layer exists. + */ + LayerInfo getLayer(String id); + + /** + * Loads a layer from persistent storage by specifying its name. + * + * @param name The name of the layer + * + * @return The layer, or null if no such layer exists. + */ + LayerInfo getLayerByName(String name); + + /** + * Loads all layers from persistent storage that publish a specified resource. + * + * @param resource The published resource + * + * @return List of layers, possibly empty + */ + List getLayers(ResourceInfo resource); + + /** + * Loads all layers from persistent storage that reference a specified style. + * + * @param style The referenced style + * + * @return List of layers, possibly empty + */ + List getLayers(StyleInfo style); + + /** + * Loads all layers from persistent storage. + * + * @return List of layers, possibly empty + */ + List getLayers(); + + // + // Maps + // + /** + * Adds a map to persistent storage. + */ + MapInfo add(MapInfo map); + + /** + * Removes a map from persistent storage. + */ + void remove(MapInfo map); + + /** + * Persists any modifications to a map to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(MapInfo map); + + /** + * Detaches a map from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(MapInfo) + */ + MapInfo detach(MapInfo map); + + /** + * Loads a map from persistent storage by its identifier. + * + * @param id The unique identifier of the map + * + * @return The map, or null if no such map exists + */ + MapInfo getMap(String id); + + /** + * Loads a map from persistent storage by its name. + * + * @param name The name of the map + * + * @return The map, or null if no such map exists + */ + MapInfo getMapByName(String name); + + /** + * Lists all maps from persistent storage. + * + * @return A list of maps, possibly empty + */ + List getMaps(); + + // + // Layer groups + // + /** + * Adds a layer group to persistent storage. + */ + LayerGroupInfo add(LayerGroupInfo layerGroup); + + /** + * + * Removes a layer group from persistent storage. + */ + void remove(LayerGroupInfo layerGroup); + + /** + * Persists any modifications to a layer group to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(LayerGroupInfo layerGroup); + + /** + * Detaches a layer group from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(LayerGroupInfo) + */ + LayerGroupInfo detach(LayerGroupInfo layerGroup); + + /** + * Loads a layer group from persistent storage by specifying its identifier. + * + * @param id The unique identifier for the layer group + * + * @return The layer group, or null if it does not exist + */ + LayerGroupInfo getLayerGroup(String id); + + /** + * Loads a layer group from persistent storage by specifying its name. + * + * @param name The name of the layer group. + * + * @return The layer group, or null getLayerGroups(); + + // + // Namespaces + // + /** + * Adds a namespace to persistent storage. + */ + NamespaceInfo add(NamespaceInfo namespace); + + /** + * Removes a namespace from persistent storage. + */ + void remove(NamespaceInfo namespace); + + /** + * Persists any modifications to a namespace to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(NamespaceInfo namespace); + + /** + * Detaches a namespace from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(NamespaceInfo) + */ + NamespaceInfo detach(NamespaceInfo namespace); + + /** + * Loads the default namespace from persistent storage. + * + * @return The namespace, or null if there is no default namespace set + */ + NamespaceInfo getDefaultNamespace(); + + /** + * Sets the default namespace. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. The source of the event is the + * catalog itself and changed property is "defaultNamespace". The changed old/new values should + * the old/new namespaces respectively. + *

+ */ + void setDefaultNamespace(NamespaceInfo defaultNamespace); + + /** + * Loads a namespace from persistent storage by specifying its identifier. + * + * @param id The unique identifier of the namespace. + * + * @return The namespace, or null if no such namespace exists + */ + NamespaceInfo getNamespace(String id); + + /** + * Loads a namespace from persistent storage by specifying its prefix. + * + * @param prefix The prefix of the namespace. + * + * @return The namespace, or null if no such namespace exists + */ + NamespaceInfo getNamespaceByPrefix(String prefix); + + /** + * Loads a namespace from persistent storage by specifying its uri. + * + * @param uri The uri of the namespace. + * + * @return The namespace, or null if no such namespace exists + */ + NamespaceInfo getNamespaceByURI(String uri); + + /** + * Loads all namespaces from persistent storage. + * + * @return A list of namespaces, possibilty empty + */ + List getNamespaces(); + + // + // Workspaces + // + /** + * Adds a workspace to persistent storage. + */ + WorkspaceInfo add(WorkspaceInfo workspace); + + /** + * Removes a workspace from persistent storage. + */ + void remove(WorkspaceInfo workspace); + + /** + * Persists any modifications to a workspace to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(WorkspaceInfo workspace); + + /** + * Detaches a workspace from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(WorkspaceInfo) + */ + WorkspaceInfo detach(WorkspaceInfo workspace); + + /** + * Loads the default workspace from persistent storage. + * + * @return The workspace, or null if there is no default workspace set + */ + WorkspaceInfo getDefaultWorkspace(); + + /** + * Sets the default workspace. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. The source of the event is the + * catalog itself and changed property is "defaultWorkspace". The changed old/new values should + * the old/new workspaces respectively. + *

+ */ + void setDefaultWorkspace(WorkspaceInfo workspace); + + /** + * Loads a workspace from persistent storage by specifying its identifier. + * + * @param id The unique identifier of the workspace. + * + * @return THe workspace, or null if no such workspace exists. + */ + WorkspaceInfo getWorkspace(String id); + + /** + * Loads a workspace from persistent storage by specifying its name. + * + * @param name The name of the workspace. + * + * @return THe workspace, or null if no such workspace exists. + */ + WorkspaceInfo getWorkspaceByName(String name); + + /** + * Loads all workspaces from persistent storage. + * + * @return A list of workspaces, possibly empty. + */ + List getWorkspaces(); + + // + // Styles + // + /** + * Adds a style to persistent storage. + */ + StyleInfo add(StyleInfo style); + + /** + * Removes a style from persistent storage. + */ + void remove(StyleInfo style); + + /** + * Persists any modifications to a style to persistent storage. + *

+ * DAO implementations are responsible for triggering a {@link CatalogModifyEvent} by calling + * {@link Catalog#fireModified(CatalogInfo, List, List, List)}. This is the responsibility of + * the dao because it is best suited to knowing and tracking which attributes of the object have + * changed. + *

+ */ + void save(StyleInfo style); + + /** + * Detaches a style from the underlying persistence layer. + *

+ * "Detaching" is specific to the underlying storage engine. But in general when an object + * is detached any proxies or uninitialized state of the object should be resolved. + *

+ * + * @see Catalog#detach(StyleInfo) + */ + StyleInfo detach(StyleInfo style); + + /** + * Loads a style from persistent storage by specifying its identifier. + * + * @param id The unique identifier of the style. + * + * @return The style, or null if no such style exists + */ + StyleInfo getStyle(String id); + + /** + * Loads a style from persistent storage by specifying its name. + * + * @param name The name of the style. + * + * @return The style, or null if no such style exists + */ + StyleInfo getStyleByName(String name); + + /** + * Loads all styles from persistent storage. + * + * @return A list of styles, possibly empty. + */ + List getStyles(); + + /** + * Disposes the dao. + *

+ * Called from {@link Catalog#dispose()} and forces the dao to release any resources. + *

+ */ + void dispose(); + + /** + * Called after the catalog has been initially started/loaded. + *

+ * This method gives the dao a chance to resolve any proxies or uninitialized state that are + * created during the loading process. + *

+ */ + void resolve(); + + /** + * Pushes the data stored by this dao into another dao. + */ + void syncTo(CatalogDAO other); +} diff --git a/main/src/main/java/org/geoserver/catalog/impl/CatalogImpl.java b/main/src/main/java/org/geoserver/catalog/impl/CatalogImpl.java index 9accffc..604a51e 100644 --- a/main/src/main/java/org/geoserver/catalog/impl/CatalogImpl.java +++ b/main/src/main/java/org/geoserver/catalog/impl/CatalogImpl.java @@ -5,24 +5,22 @@ 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.LinkedHashSet; 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.CatalogDAO; import org.geoserver.catalog.CatalogException; import org.geoserver.catalog.CatalogFactory; import org.geoserver.catalog.CatalogInfo; @@ -64,6 +62,10 @@ import org.opengis.feature.type.Name; * * @author Justin Deoliveira, The Open Planning Project * + * TODO: remove synchronized blocks, make setting of default workspace/namespace part + * of dao contract + * TODO: move resolve() to dao + * */ public class CatalogImpl implements Catalog { @@ -73,51 +75,11 @@ public class CatalogImpl implements Catalog { private static final Logger LOGGER = Logging.getLogger(CatalogImpl.class); /** - * Contains the stores keyed by implementation class + * dao */ - protected MultiHashMap/* */stores = new MultiHashMap(); + protected CatalogDAO dao; /** - * The default store keyed by workspace id - */ - protected Map defaultStores = new HashMap(); - - /** - * resources - */ - protected MultiHashMap/* */resources = new MultiHashMap(); - - /** - * namespaces - */ - protected HashMap namespaces = new HashMap(); - - /** - * workspaces - */ - protected HashMap workspaces = new HashMap(); - - /** - * layers - */ - protected List layers = new ArrayList(); - - /** - * maps - */ - protected List maps = new ArrayList(); - - /** - * layer groups - */ - protected List layerGroups = new ArrayList(); - - /** - * styles - */ - protected List styles = new ArrayList(); - - /** * listeners */ protected List listeners = new ArrayList(); @@ -129,9 +91,19 @@ public class CatalogImpl implements Catalog { protected GeoServerResourceLoader resourceLoader; public CatalogImpl() { + dao = new DefaultCatalogDAO(this); resourcePool = new ResourcePool(this); } + public CatalogDAO getDAO() { + return dao; + } + + public void setDAO(CatalogDAO dao) { + this.dao = dao; + dao.setCatalog(this); + } + public String getId() { return "catalog"; } @@ -148,9 +120,11 @@ public class CatalogImpl implements Catalog { } validate(store, true); - resolve(store); - synchronized (stores) { - stores.put(store.getClass(), store); + + //TODO: remove synchronized block, need transactions + synchronized (dao) { + dao.add(resolve(store)); + // if there is no default store use this one as the default if(getDefaultDataStore(store.getWorkspace()) == null && store instanceof DataStoreInfo) { setDefaultDataStore(store.getWorkspace(), (DataStoreInfo) store); @@ -179,18 +153,19 @@ public class CatalogImpl implements Catalog { if ( !getResourcesByStore(store, ResourceInfo.class).isEmpty() ) { throw new IllegalArgumentException( "Unable to delete non-empty store."); } - store = unwrap(store); - synchronized(stores) { - stores.remove(store.getClass(),store); + //TODO: remove synchronized block, need transactions + synchronized(dao) { + dao.remove(store); WorkspaceInfo workspace = store.getWorkspace(); DataStoreInfo defaultStore = getDefaultDataStore(workspace); if (store.equals(defaultStore)) { - defaultStores.remove(workspace.getId()); + //TODO: this will fire multiple events, we want to fire only one + setDefaultDataStore(workspace, null); // default removed, choose another store to become default if possible - List dstores = getDataStoresByWorkspace(workspace); + List dstores = getStoresByWorkspace(workspace, DataStoreInfo.class); if (!dstores.isEmpty()) { setDefaultDataStore(workspace, (DataStoreInfo) dstores.get(0)); } @@ -202,78 +177,52 @@ public class CatalogImpl implements Catalog { public void save(StoreInfo store) { validate(store, false); - - if ( store.getId() == null ) { - //add it instead of saving - add( store ); - return; - } - + dao.save(store); saved(store); } - public T getStore(String id, Class 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; - } - } + public T detach(T store) { + return detached(store, dao.detach(store)); + } - return null; + public T getStore(String id, Class clazz) { + return dao.getStore(id, clazz); } public T getStoreByName(String name, Class 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; + return getStoreByName((WorkspaceInfo) null, name, clazz); } public T getStoreByName(WorkspaceInfo workspace, String name, Class clazz) { - if ( workspace == null ) { - workspace = getDefaultWorkspace(); + WorkspaceInfo ws = workspace; + if (ws == null) { + ws = getDefaultWorkspace(); } - if(name == null || name.equals(DEFAULT)) { + if(DataStoreInfo.class == clazz && (name == null || name.equals(Catalog.DEFAULT))) { return (T) getDefaultDataStore(workspace); } - 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 ); - } + T store = dao.getStoreByName(ws, name, clazz); + if (store == null && workspace == null) { + store = dao.getStoreByName(DefaultCatalogDAO.ANY_WORKSPACE, name, clazz); } - - return null; + return store; } public T getStoreByName(String workspaceName, String name, Class clazz) { - return getStoreByName( - workspaceName != null ? getWorkspaceByName(workspaceName) : null, name, clazz); + if (workspaceName == null) { + return getStoreByName((WorkspaceInfo)null, name, clazz); + } + + WorkspaceInfo workspace = getWorkspaceByName(workspaceName); + if (workspace != null) { + return getStoreByName(workspace, name, clazz); + } + return null; } public List getStoresByWorkspace( @@ -294,25 +243,11 @@ public class CatalogImpl implements Catalog { public List getStoresByWorkspace( WorkspaceInfo workspace, Class 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); + return dao.getStoresByWorkspace(workspace, clazz); } public List getStores(Class clazz) { - return ModificationProxy.createList(lookup(clazz, stores) , clazz); + return dao.getStores(clazz); } public DataStoreInfo getDataStore(String id) { @@ -344,32 +279,23 @@ public class CatalogImpl implements Catalog { } 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; - } + return dao.getDefaultDataStore(workspace); } public void setDefaultDataStore(WorkspaceInfo workspace, DataStoreInfo store) { - // basic sanity check - if (store.getWorkspace() == null) { - throw new IllegalArgumentException("The store has not been assigned a workspace"); - } - - if (!store.getWorkspace().equals(workspace)) { - throw new IllegalArgumentException("Trying to mark as default " + "for workspace " - + workspace.getName() + " a store that " + "is contained in " - + store.getWorkspace().getName()); + if (store != null) { + // basic sanity check + if (store.getWorkspace() == null) { + throw new IllegalArgumentException("The store has not been assigned a workspace"); + } + + if (!store.getWorkspace().equals(workspace)) { + throw new IllegalArgumentException("Trying to mark as default " + "for workspace " + + workspace.getName() + " a store that " + "is contained in " + + store.getWorkspace().getName()); + } } - - DataStoreInfo old = defaultStores.get(workspace.getId()); - defaultStores.put(workspace.getId(), store); - - //fire change event - fireModified(this, - Arrays.asList("defaultDataStore"), Arrays.asList(old), Arrays.asList(store)); + dao.setDefaultDataStore(workspace, store); } public CoverageStoreInfo getCoverageStore(String id) { @@ -410,10 +336,12 @@ public class CatalogImpl implements Catalog { //default to default namespace resource.setNamespace( getDefaultNamespace() ); } - + if ( resource.getNativeName() == null ) { + resource.setNativeName(resource.getName()); + } validate(resource,true); - resolve(resource); - resources.put(resource.getClass(), resource); + + dao.add(resolve(resource)); added(resource); } @@ -421,6 +349,9 @@ public class CatalogImpl implements Catalog { if ( isNull(resource.getName()) ) { throw new NullPointerException( "Resource name must not be null"); } + if ( isNull(resource.getNativeName())) { + throw new NullPointerException( "Resource native name must not be null"); + } if ( resource.getStore() == null ) { throw new IllegalArgumentException( "Resource must be part of a store"); } @@ -449,80 +380,58 @@ public class CatalogImpl implements Catalog { if ( !getLayers( resource ).isEmpty() ) { throw new IllegalArgumentException( "Unable to delete resource referenced by layer"); } - resource = unwrap(resource); - resources.remove(resource.getClass(), resource); + dao.remove(resource); removed(resource); } public void save(ResourceInfo resource) { validate(resource,false); + dao.save(resource); saved(resource); } + public T detach(T resource) { + return detached(resource, dao.detach(resource)); + } + public T getResource(String id, Class 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; + return dao.getResource(id, clazz); } public T getResourceByName(String ns, String name, Class 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 ); - } + ns = null; } - 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 (ns != null) { + NamespaceInfo namespace = getNamespaceByPrefix(ns); + if (namespace == null) { + namespace = getNamespaceByURI(ns); } - if ( matches.size() == 1 ) { - return ModificationProxy.create( (T) matches.get( 0 ), clazz ); + if (namespace != null) { + return getResourceByName(namespace, name, clazz); } + + return null; } - return null; + + return getResourceByName((NamespaceInfo) null, name, clazz); + } public T getResourceByName(NamespaceInfo ns, String name, Class clazz) { - return getResourceByName( ns != null ? ns.getPrefix() : null , name, clazz); + + NamespaceInfo namespace = ns; + if (namespace == null) { + namespace = getDefaultNamespace(); + } + T resource = dao.getResourceByName(namespace, name, clazz); + if (resource == null && ns == null) { + resource = dao.getResourceByName(DefaultCatalogDAO.ANY_NAMESPACE, name, clazz); + } + return resource; } public T getResourceByName(Name name, Class clazz) { @@ -545,30 +454,11 @@ public class CatalogImpl implements Catalog { } public List getResources(Class clazz) { - return ModificationProxy.createList( lookup(clazz,resources), clazz ); + return dao.getResources(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 ); + return dao.getResourcesByNamespace(namespace, clazz); } public List getResourcesByNamespace( @@ -590,31 +480,12 @@ public class CatalogImpl implements Catalog { public T getResourceByStore(StoreInfo store, String name, Class 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; + return dao.getResourceByStore(store, name, clazz); } public List getResourcesByStore( StoreInfo store, Class 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 ); + return dao.getResourcesByStore(store, clazz); } public FeatureTypeInfo getFeatureType(String id) { @@ -708,7 +579,6 @@ public class CatalogImpl implements Catalog { // Layer methods public void add(LayerInfo layer) { validate(layer,true); - resolve(layer); if ( layer.getType() == null ) { if ( layer.getResource() instanceof FeatureTypeInfo ) { @@ -723,7 +593,7 @@ public class CatalogImpl implements Catalog { } } - layers.add(layer); + dao.add(resolve(layer)); added(layer); } @@ -754,30 +624,28 @@ public class CatalogImpl implements Catalog { public void remove(LayerInfo layer) { //ensure no references to the layer - for ( LayerGroupInfo lg : layerGroups ) { + for ( LayerGroupInfo lg : dao.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)); + dao.remove(layer); removed(layer); } public void save(LayerInfo layer) { validate( layer, false ); + dao.save(layer); saved(layer); } + + public LayerInfo detach(LayerInfo layer) { + return detached(layer, dao.detach(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; + return dao.getLayer(id); } @@ -802,79 +670,45 @@ public class CatalogImpl implements Catalog { 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 ); + ResourceInfo r = getResourceByName(prefix, resource, ResourceInfo.class); + if (r != null) { + List layers = getLayers(r); + if (layers.size() == 1) { + return layers.get(0); } + } + return null; } 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 dao.getLayerByName(name); } - return null; } public List getLayers(ResourceInfo resource) { - List matches = new ArrayList(); - 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); + return dao.getLayers(resource); } public List getLayers(StyleInfo style) { - List matches = new ArrayList(); - 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); + return dao.getLayers(style); } public List getLayers() { - return ModificationProxy.createList( new ArrayList(layers), LayerInfo.class ); + return dao.getLayers(); } // Map methods public MapInfo getMap(String id) { - for (MapInfo map : maps) { - if (id.equals(map.getId())) { - return ModificationProxy.create(map,MapInfo.class); - } - } - - return null; + return dao.getMap(id); } public MapInfo getMapByName(String name) { - for (MapInfo map : maps) { - if (name.equals(map.getName())) { - return ModificationProxy.create(map,MapInfo.class); - } - } - - return null; + return dao.getMapByName(name); } public List getMaps() { - return ModificationProxy.createList( new ArrayList(maps), MapInfo.class ); + return dao.getMaps(); } public void add(LayerGroupInfo layerGroup) { @@ -885,10 +719,10 @@ public class CatalogImpl implements Catalog { for ( LayerInfo l : layerGroup.getLayers() ) { // default style layerGroup.getStyles().add(null); - } + } } - layerGroups.add( layerGroup ); + layerGroup = dao.add(layerGroup); added( layerGroup ); } @@ -913,97 +747,80 @@ public class CatalogImpl implements Catalog { } public void remove(LayerGroupInfo layerGroup) { - layerGroups.remove( unwrap(layerGroup) ); + dao.remove(layerGroup); removed( layerGroup ); } public void save(LayerGroupInfo layerGroup) { validate(layerGroup,false); + dao.save(layerGroup); saved(layerGroup); } + public LayerGroupInfo detach(LayerGroupInfo layerGroup) { + return detached(layerGroup, dao.detach(layerGroup)); + } + public List getLayerGroups() { - return ModificationProxy.createList( new ArrayList(layerGroups), LayerGroupInfo.class ); + return dao.getLayerGroups(); } public LayerGroupInfo getLayerGroup(String id) { - for (LayerGroupInfo layerGroup : layerGroups ) { - if ( id.equals( layerGroup.getId() ) ) { - return ModificationProxy.create(layerGroup,LayerGroupInfo.class); - } - } - - return null; + return dao.getLayerGroup(id); } public LayerGroupInfo getLayerGroupByName(String name) { - for (LayerGroupInfo layerGroup : layerGroups ) { - if ( name.equals( layerGroup.getName() ) ) { - return ModificationProxy.create(layerGroup,LayerGroupInfo.class); - } - } - - return null; + return dao.getLayerGroupByName(name); } public void add(MapInfo map) { - resolve(map); - maps.add(map); + dao.add(resolve(map)); added(map); } public void remove(MapInfo map) { - maps.remove(unwrap(map)); + dao.remove(map); removed(map); } public void save(MapInfo map) { - saved( map ); + dao.save(map); + saved(map); + } + + public MapInfo detach(MapInfo map) { + return detached(map, dao.detach(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; + return dao.getNamespace(id); } 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 ); + if (prefix == null || Catalog.DEFAULT.equals(prefix)) { + NamespaceInfo ns = getDefaultNamespace(); + if (ns != null) { + prefix = ns.getPrefix(); } } + + return dao.getNamespaceByPrefix(prefix); + } - return null; + public NamespaceInfo getNamespaceByURI(String uri) { + return dao.getNamespaceByURI(uri); } public List getNamespaces() { - ArrayList ns = new ArrayList(); - for ( Map.Entry e : namespaces.entrySet() ) { - if ( e.getKey() == null || e.getKey().equals(DEFAULT)) - continue; - ns.add( e.getValue() ); - } - - return ModificationProxy.createList( ns, NamespaceInfo.class ); + return dao.getNamespaces(); } public void add(NamespaceInfo namespace) { validate(namespace,true); - resolve(namespace); - synchronized (namespaces) { - namespaces.put(namespace.getPrefix(),namespace); + synchronized (dao) { + dao.add(resolve(namespace)); if ( getDefaultNamespace() == null ) { setDefaultNamespace(namespace); } @@ -1048,67 +865,45 @@ public class CatalogImpl implements Catalog { throw new IllegalArgumentException( "Unable to delete non-empty namespace."); } - NamespaceInfo defaultNamespace = getDefaultNamespace(); - if (namespace.equals(defaultNamespace)) { - namespaces.remove(null); - namespaces.remove(DEFAULT); - } - - namespaces.remove(namespace.getPrefix()); + dao.remove(namespace); 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 ); - } - } - + dao.save(namespace); saved(namespace); } + + public NamespaceInfo detach(NamespaceInfo namespace) { + return detached(namespace, dao.detach(namespace)); + } public NamespaceInfo getDefaultNamespace() { - return namespaces.containsKey(null) ? - ModificationProxy.create(namespaces.get( null ),NamespaceInfo.class) : null; + return dao.getDefaultNamespace(); } public void setDefaultNamespace(NamespaceInfo defaultNamespace) { - NamespaceInfo ns = namespaces.get( defaultNamespace.getPrefix() ); + NamespaceInfo ns = getNamespaceByPrefix( defaultNamespace.getPrefix() ); if ( ns == null ) { throw new IllegalArgumentException( "No such namespace: '" + defaultNamespace.getPrefix() + "'" ); } - - NamespaceInfo old = namespaces.get(null); - namespaces.put( null, ns ); - namespaces.put( DEFAULT, ns ); - - //fire change event - fireModified(this, - Arrays.asList("defaultNamespace"), Arrays.asList(old), Arrays.asList(defaultNamespace)); - + dao.setDefaultNamespace(defaultNamespace); } // Workspace methods public void add(WorkspaceInfo workspace) { validate(workspace,true); - if ( workspaces.containsKey( workspace.getName() ) ) { + if ( getWorkspaceByName(workspace.getName()) != null ) { throw new IllegalArgumentException( "Workspace with name '" + workspace.getName() + "' already exists."); } - resolve(workspace); - synchronized (workspaces) { - workspaces.put( workspace.getName(), workspace ); + synchronized (dao) { + dao.add(resolve(workspace)); // if there is no default workspace use this one as the default - if ( workspaces.get( null ) == null ) { + if ( getDefaultWorkspace() == null ) { setDefaultWorkspace(workspace); } } @@ -1141,117 +936,83 @@ public class CatalogImpl implements Catalog { 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); - workspaces.remove(DEFAULT); - - //default removed, choose another workspace to become default - if (!workspaces.isEmpty()) { - setDefaultWorkspace(workspaces.values().iterator().next()); + + //TODO: remove synchronized block, need transactions + synchronized(dao) { + dao.remove(workspace); + + WorkspaceInfo defaultWorkspace = getDefaultWorkspace(); + if (workspace.equals(defaultWorkspace) || defaultWorkspace == null) { + List workspaces = dao.getWorkspaces(); + + defaultWorkspace = null; + if (!workspaces.isEmpty()) { + defaultWorkspace = workspaces.get(0); + } + + setDefaultWorkspace(defaultWorkspace); } } - - + 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 ); - } - } - + dao.save(workspace); saved(workspace); } + public WorkspaceInfo detach(WorkspaceInfo workspace) { + return detached(workspace, dao.detach(workspace)); + } + public WorkspaceInfo getDefaultWorkspace() { - return workspaces.containsKey( null ) ? - ModificationProxy.create( workspaces.get( null ), WorkspaceInfo.class ) : null; + return dao.getDefaultWorkspace(); } public void setDefaultWorkspace(WorkspaceInfo workspace) { - WorkspaceInfo old = workspaces.get(null); - workspaces.put( null, workspace ); - workspaces.put( "default", workspace ); - - //fire change event - fireModified(this, - Arrays.asList("defaultWorkspace"), Arrays.asList(old), Arrays.asList(workspace)); + if (workspace != null && dao.getWorkspaceByName(workspace.getName()) == null) { + dao.add(workspace); + } + dao.setDefaultWorkspace(workspace); } public List getWorkspaces() { - ArrayList ws = new ArrayList(); - - //strip out default namespace - for ( Map.Entry e : workspaces.entrySet() ) { - if ( e.getKey() == null || e.getKey().equals(DEFAULT) ) { - continue; - } - - ws.add( e.getValue() ); - } - - return ModificationProxy.createList( ws, WorkspaceInfo.class ); + return dao.getWorkspaces(); } public WorkspaceInfo getWorkspace(String id) { - for ( WorkspaceInfo ws : workspaces.values() ) { - if ( id.equals( ws.getId() ) ) { - return ModificationProxy.create(ws,WorkspaceInfo.class); - } - } - - return null; + return dao.getWorkspace(id); } public WorkspaceInfo getWorkspaceByName(String name) { - return workspaces.containsKey(name) ? - ModificationProxy.create( workspaces.get( name ), WorkspaceInfo.class ) : null; + if (name == null || Catalog.DEFAULT.equals(name)) { + WorkspaceInfo ws = getDefaultWorkspace(); + if (ws != null) { + name = ws.getName(); + } + } + return dao.getWorkspaceByName(name); } // 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; + return dao.getStyle(id); } 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; + return dao.getStyleByName(name); } public List getStyles() { - return ModificationProxy.createList(styles,StyleInfo.class); + return dao.getStyles(); } public void add(StyleInfo style) { validate(style,true); - resolve(style); - styles.add(style); + dao.add(resolve(style)); added(style); } @@ -1271,18 +1032,24 @@ public class CatalogImpl implements Catalog { public void remove(StyleInfo style) { //ensure no references to the style - for ( LayerInfo l : layers ) { + for ( LayerInfo l : dao.getLayers() ) { if ( style.equals( l.getDefaultStyle() ) || l.getStyles().contains( style )) { throw new IllegalArgumentException( "Unable to delete style referenced by '"+ l.getName()+"'"); } } - styles.remove(unwrap(style)); + + dao.remove(style); removed(style); } public void save(StyleInfo style) { validate(style,false); - saved( style ); + dao.save(style); + saved(style); + } + + public StyleInfo detach(StyleInfo style) { + return detached(style, dao.detach(style)); } // Event methods @@ -1319,70 +1086,27 @@ public class CatalogImpl implements Catalog { this.resourceLoader = resourceLoader; } 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(); + dao.dispose(); 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) { + protected void removed(CatalogInfo object) { + fireRemoved( object ); + } + + public 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, + public void fireModified(CatalogInfo object, List propertyNames, List oldValues, List newValues) { CatalogModifyEventImpl event = new CatalogModifyEventImpl(); @@ -1394,13 +1118,14 @@ public class CatalogImpl implements Catalog { event(event); } - protected void firePostModified(CatalogInfo object) { + public void firePostModified(CatalogInfo object) { CatalogPostModifyEventImpl event = new CatalogPostModifyEventImpl(); event.setSource( object); event(event); } - protected void removed(CatalogInfo object) { + + public void fireRemoved(CatalogInfo object) { CatalogRemoveEventImpl event = new CatalogRemoveEventImpl(); event.setSource(object); @@ -1437,76 +1162,20 @@ public class CatalogImpl implements Catalog { } } + protected void saved(CatalogInfo info) { + firePostModified(info); + } + + public static Object unwrap(Object obj) { + return obj; + } + /** * 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(); - } - for ( WorkspaceInfo ws : workspaces.values() ) { - resolve(ws); - } - - //namespaces - if ( namespaces == null ) { - namespaces = new HashMap(); - } - 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(); - } - 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(); - } - for ( LayerInfo l : layers ) { - resolve(l); - } - - //layer groups - if ( layerGroups == null ) { - layerGroups = new ArrayList(); - } - for ( LayerGroupInfo lg : layerGroups ) { - resolve(lg); - } - - //maps - if ( maps == null ) { - maps = new ArrayList(); - } - for ( MapInfo m : maps ) { - resolve(m); - } + dao.setCatalog(this); + dao.resolve(); if ( listeners == null ) { listeners = new ArrayList(); @@ -1517,42 +1186,29 @@ public class CatalogImpl implements Catalog { } } - protected void resolve(WorkspaceInfo workspace) { - setId(workspace); + protected WorkspaceInfo resolve(WorkspaceInfo workspace) { resolveCollections(workspace); + return workspace; } - protected void resolve(NamespaceInfo namespace) { - setId(namespace); + protected NamespaceInfo resolve(NamespaceInfo namespace) { resolveCollections(namespace); + return 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); + protected StoreInfo resolve(StoreInfo store) { + resolveCollections(store); + StoreInfoImpl s = (StoreInfoImpl) store; s.setCatalog( this ); + + return store; } - protected void resolve(ResourceInfo resource) { - setId(resource); - ResourceInfoImpl r = (ResourceInfoImpl) resource; + protected ResourceInfo resolve(ResourceInfo resource) { - //resolve the store - StoreInfo resolved = ResolvingProxy.resolve( this, r.getStore() ); - if ( resolved != null ) { - r.setStore( resolved ); - } + ResourceInfoImpl r = (ResourceInfoImpl) resource; + r.setCatalog(this); if ( resource instanceof FeatureTypeInfo ) { resolve( (FeatureTypeInfo) resource ); @@ -1563,20 +1219,21 @@ public class CatalogImpl implements Catalog { if(r instanceof WMSLayerInfo){ resolve((WMSLayerInfo) resource); } - r.setCatalog(this); + + return resource; } - private void resolve(CoverageInfo r) { + private CoverageInfo resolve(CoverageInfo r) { CoverageInfoImpl c = (CoverageInfoImpl)r; - if(c.getDimensions() == null) { - c.setDimensions(new ArrayList()); - } else { + if(c.getDimensions() != null) { for (CoverageDimensionInfo dim : c.getDimensions()) { - if(dim.getNullValues() == null) + if(dim.getNullValues() == null) { ((CoverageDimensionImpl) dim).setNullValues(new ArrayList()); + } } } resolveCollections(r); + return r; } /** @@ -1584,52 +1241,39 @@ public class CatalogImpl implements Catalog { * going trough {@link #resolve(ResourceInfo)} * @param featureType */ - private void resolve(FeatureTypeInfo featureType) { + private FeatureTypeInfo resolve(FeatureTypeInfo featureType) { FeatureTypeInfoImpl ft = (FeatureTypeInfoImpl) featureType; resolveCollections(ft); + return ft; } - private void resolve(WMSLayerInfo wmsLayer) { + private WMSLayerInfo resolve(WMSLayerInfo wmsLayer) { WMSLayerInfoImpl impl = (WMSLayerInfoImpl) wmsLayer; resolveCollections(impl); + return wmsLayer; } - protected void resolve(LayerInfo layer) { - setId(layer); + protected LayerInfo resolve(LayerInfo layer) { if (layer.getAttribution() == null) { layer.setAttribution(getFactory().createAttribution()); } resolveCollections(layer); + return layer; } - protected void resolve(LayerGroupInfo layerGroup) { - setId(layerGroup); + protected LayerGroupInfo 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 ); - 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 ); - } - } - + return layerGroup; } - protected void resolve(StyleInfo style) { - setId(style); + protected StyleInfo resolve(StyleInfo style) { ((StyleInfoImpl)style).setCatalog( this ); + return style; } - protected void resolve(MapInfo map) { - setId(map); + protected MapInfo resolve(MapInfo map) { + resolveCollections(map); + return map; } /** @@ -1687,27 +1331,16 @@ public class CatalogImpl implements Catalog { } } - 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() ); } + T detached(T original, T detached) { + return detached != null ? detached : original; + } + public void sync( CatalogImpl other ) { - stores = other.stores; - defaultStores = other.defaultStores; - resources = other.resources; - namespaces = other.namespaces; - workspaces = other.workspaces; - layers = other.layers; - maps = other.maps; - layerGroups = other.layerGroups; - styles = other.styles; + other.dao.syncTo(dao); listeners = other.listeners; if ( resourcePool != other.resourcePool ) { @@ -1718,10 +1351,6 @@ public class CatalogImpl implements Catalog { resourceLoader = other.resourceLoader; } - public static T unwrap(T obj) { - return ModificationProxy.unwrap(obj); - } - public void accept(CatalogVisitor visitor) { visitor.visit(this); } diff --git a/main/src/main/java/org/geoserver/catalog/impl/CoverageDimensionImpl.java b/main/src/main/java/org/geoserver/catalog/impl/CoverageDimensionImpl.java index 0fceffe..5021698 100644 --- a/main/src/main/java/org/geoserver/catalog/impl/CoverageDimensionImpl.java +++ b/main/src/main/java/org/geoserver/catalog/impl/CoverageDimensionImpl.java @@ -73,4 +73,55 @@ public class CoverageDimensionImpl implements CoverageDimensionInfo { public void setNullValues(List nullValues) { this.nullValues = nullValues; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((nullValues == null) ? 0 : nullValues.hashCode()); + result = prime * result + ((range == null) ? 0 : range.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CoverageDimensionImpl other = (CoverageDimensionImpl) obj; + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (nullValues == null) { + if (other.nullValues != null) + return false; + } else if (!nullValues.equals(other.nullValues)) + return false; + if (range == null) { + if (other.range != null) + return false; + } else if (!range.equals(other.range)) + return false; + return true; + } + + } diff --git a/main/src/main/java/org/geoserver/catalog/impl/DefaultCatalogDAO.java b/main/src/main/java/org/geoserver/catalog/impl/DefaultCatalogDAO.java new file mode 100644 index 0000000..08baea3 --- /dev/null +++ b/main/src/main/java/org/geoserver/catalog/impl/DefaultCatalogDAO.java @@ -0,0 +1,1102 @@ +package org.geoserver.catalog.impl; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.rmi.server.UID; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.collections.MultiHashMap; +import org.geoserver.catalog.Catalog; +import org.geoserver.catalog.CatalogDAO; +import org.geoserver.catalog.CatalogInfo; +import org.geoserver.catalog.CoverageDimensionInfo; +import org.geoserver.catalog.CoverageInfo; +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.StoreInfo; +import org.geoserver.catalog.StyleInfo; +import org.geoserver.catalog.WMSLayerInfo; +import org.geoserver.catalog.WorkspaceInfo; +import org.geoserver.ows.util.ClassProperties; +import org.geoserver.ows.util.OwsUtils; + +/** + * Default catalog dao implementation in which all objects are stored in memory. + * + * @author Justin Deoliveira, OpenGeo + * + * TODO: look for any exceptions, move them back to catlaog as they indicate logic + */ +public class DefaultCatalogDAO implements CatalogDAO { + + public static WorkspaceInfo ANY_WORKSPACE = any(WorkspaceInfo.class); + + public static NamespaceInfo ANY_NAMESPACE = any(NamespaceInfo.class); + + @SuppressWarnings("unchecked") + static T any(Class clazz) { + + Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz); + try { + return (T) proxyClass.getConstructor( + new Class[] { InvocationHandler.class }).newInstance(new Object[] { + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return null; + } + } + } ); + } + catch( Exception e ) { + throw new RuntimeException( e ); + } + } + + /** + * Contains the stores keyed by implementation class + */ + protected MultiHashMap/* */stores = new MultiHashMap(); + + /** + * The default store keyed by workspace id + */ + protected Map defaultStores = new HashMap(); + + /** + * resources + */ + protected MultiHashMap/* */resources = new MultiHashMap(); + + /** + * namespaces + */ + protected HashMap namespaces = new HashMap(); + + /** + * workspaces + */ + protected HashMap workspaces = new HashMap(); + + /** + * layers + */ + protected List layers = new ArrayList(); + + /** + * maps + */ + protected List maps = new ArrayList(); + + /** + * layer groups + */ + protected List layerGroups = new ArrayList(); + + /** + * styles + */ + protected List styles = new ArrayList(); + + /** + * the catalog + */ + private CatalogImpl catalog; + + public DefaultCatalogDAO(Catalog catalog) { + setCatalog(catalog); + } + + public void setCatalog(Catalog catalog) { + this.catalog = (CatalogImpl) catalog; + } + + public Catalog getCatalog() { + return null; + } + + // + // Stores + // + public StoreInfo add(StoreInfo store) { + resolve(store); + synchronized(stores) { + stores.put(store.getClass(), store); + } + return ModificationProxy.create(store, StoreInfo.class); + } + + public void remove(StoreInfo store) { + store = unwrap(store); + + synchronized(stores) { + stores.remove(store.getClass(),store); + } + } + + public void save(StoreInfo store) { + if ( store.getId() == null ) { + //add it instead of saving + add( store ); + return; + } + + saved(store); + } + + public T detach(T store) { + return store; + } + + public T getStore(String id, Class 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 null; + } + + public T getStoreByName(WorkspaceInfo workspace, + String name, Class clazz) { + + List l = lookup(clazz, stores); + if (workspace == ANY_WORKSPACE) { + //do an exhaustive search through all workspaces + ArrayList matches = new ArrayList(); + for (Iterator i = l.iterator(); i.hasNext();) { + T store = (T) i.next(); + if ( name.equals( store.getName() ) ) { + matches.add( store ); + } + } + + if ( matches.size() == 1 ) { + return ModificationProxy.create( (T) matches.get( 0 ), clazz); + } + } + else { + + 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 List getStoresByWorkspace( + WorkspaceInfo workspace, Class clazz) { + + //TODO: support ANY_WORKSPACE? + + 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 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()); + synchronized(defaultStores) { + if (store != null) { + defaultStores.put(workspace.getId(), store); + } + else { + defaultStores.remove(workspace.getId()); + } + } + + //fire change event + catalog.fireModified(catalog, + Arrays.asList("defaultDataStore"), Arrays.asList(old), Arrays.asList(store)); + } + + // + // Resources + // + public ResourceInfo add(ResourceInfo resource) { + resolve(resource); + synchronized(resources) { + resources.put(resource.getClass(), resource); + } + return ModificationProxy.create(resource, ResourceInfo.class); + } + + public void remove(ResourceInfo resource) { + resource = unwrap(resource); + synchronized(resources) { + resources.remove(resource.getClass(), resource); + } + } + + + public void save(ResourceInfo resource) { + saved(resource); + } + + public T detach(T resource) { + return resource; + } + + public T getResource(String id, Class 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 getResourceByName(NamespaceInfo namespace, String name, Class clazz) { + + List l = lookup(clazz, resources); + + if (namespace == ANY_NAMESPACE) { + //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 ); + } + } + else { + 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 ); + } + } + } + } + + return null; + } + + public List getResources(Class clazz) { + return ModificationProxy.createList( lookup(clazz,resources), clazz ); + } + + public List getResourcesByNamespace(NamespaceInfo namespace, Class clazz) { + //TODO: support ANY_NAMESPACE? + + 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 getResourceByStore(StoreInfo store, + String name, Class 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 List getResourcesByStore( + StoreInfo store, Class 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 ); + } + + // + // Layers + // + public LayerInfo add(LayerInfo layer) { + resolve(layer); + synchronized(layers) { + layers.add(layer); + } + + return ModificationProxy.create(layer, LayerInfo.class); + } + + public void remove(LayerInfo layer) { + synchronized(layers) { + layers.remove(unwrap(layer)); + } + } + + public void save(LayerInfo layer) { + saved(layer); + } + + public LayerInfo detach(LayerInfo layer) { + return 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(String 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 getLayers(ResourceInfo resource) { + List matches = new ArrayList(); + 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 getLayers(StyleInfo style) { + List matches = new ArrayList(); + 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 ); + } + + // + // 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) { + saved( map ); + } + + 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 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) { + saved(layerGroup); + } + + public LayerGroupInfo detach(LayerGroupInfo layerGroup) { + return layerGroup; + } + + public List 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; + } + + // + // Namespaces + // + public NamespaceInfo add(NamespaceInfo namespace) { + resolve(namespace); + synchronized(namespaces) { + namespaces.put(namespace.getPrefix(),namespace); + } + + return ModificationProxy.create(namespace, NamespaceInfo.class); + } + + public void remove(NamespaceInfo namespace) { + synchronized(namespaces) { + NamespaceInfo defaultNamespace = getDefaultNamespace(); + if (namespace.equals(defaultNamespace)) { + namespaces.remove(null); + namespaces.remove(Catalog.DEFAULT); + } + + namespaces.remove(namespace.getPrefix()); + } + } + + public void save(NamespaceInfo namespace) { + 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 detach(NamespaceInfo namespace) { + return 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()); + NamespaceInfo old = namespaces.get(null); + namespaces.put( null, ns ); + namespaces.put( Catalog.DEFAULT, ns ); + + //fire change event + catalog.fireModified(catalog, + Arrays.asList("defaultNamespace"), Arrays.asList(old), Arrays.asList(defaultNamespace)); + + } + + 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 ns = new ArrayList(); + for ( Map.Entry e : namespaces.entrySet() ) { + if ( e.getKey() == null || e.getKey().equals(Catalog.DEFAULT)) + continue; + ns.add( e.getValue() ); + } + + return ModificationProxy.createList( ns, NamespaceInfo.class ); + } + + // + // Workspaces + // + // Workspace methods + public WorkspaceInfo add(WorkspaceInfo workspace) { + resolve(workspace); + synchronized (workspaces) { + workspaces.put( workspace.getName(), workspace ); + } + return ModificationProxy.create(workspace, WorkspaceInfo.class); + } + + public void remove(WorkspaceInfo workspace) { + synchronized(workspaces) { + workspaces.remove( workspace.getName() ); + } + } + + public void save(WorkspaceInfo workspace) { + 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 detach(WorkspaceInfo workspace) { + return 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); + + synchronized(workspaces) { + if (workspace != null) { + WorkspaceInfo ws = workspaces.get(workspace.getName()); + workspaces.put( null, ws ); + workspaces.put( "default", ws ); + } + else { + workspaces.remove(null); + workspaces.remove("default"); + } + + } + + //fire change event + catalog.fireModified(catalog, + Arrays.asList("defaultWorkspace"), Arrays.asList(old), Arrays.asList(workspace)); + } + + public List getWorkspaces() { + ArrayList ws = new ArrayList(); + + //strip out default namespace + for ( Map.Entry e : workspaces.entrySet() ) { + if ( e.getKey() == null || e.getKey().equals(Catalog.DEFAULT) ) { + 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; + } + + // + // 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) { + saved( style ); + } + + public StyleInfo detach(StyleInfo style) { + return style; + } + + 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); + } + + // + // Utilities + // + public static T unwrap(T obj) { + return ModificationProxy.unwrap(obj); + } + + 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 + catalog.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 + catalog.firePostModified( real ); + } + + 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; + } + + 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 HashMap(); + } + for ( WorkspaceInfo ws : workspaces.values() ) { + resolve(ws); + } + + //namespaces + if ( namespaces == null ) { + namespaces = new HashMap(); + } + 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(); + } + 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(); + } + for ( LayerInfo l : layers ) { + resolve(l); + } + + //layer groups + if ( layerGroups == null ) { + layerGroups = new ArrayList(); + } + for ( LayerGroupInfo lg : layerGroups ) { + resolve(lg); + } + + //maps + if ( maps == null ) { + maps = new ArrayList(); + } + for ( MapInfo m : maps ) { + resolve(m); + } + } + + protected void resolve(WorkspaceInfo workspace) { + setId(workspace); + } + + protected void resolve(NamespaceInfo namespace) { + setId(namespace); + } + + protected void resolve(StoreInfo store) { + setId(store); + StoreInfoImpl s = (StoreInfoImpl) store; + + //resolve the workspace + WorkspaceInfo resolved = ResolvingProxy.resolve( catalog, s.getWorkspace()); + if ( resolved != null ) { + resolved = unwrap(resolved); + s.setWorkspace( resolved ); + } + else { + //this means the workspace has not yet been added to the catalog, keep the proxy around + } + } + + protected void resolve(ResourceInfo resource) { + setId(resource); + ResourceInfoImpl r = (ResourceInfoImpl) resource; + + //resolve the store + StoreInfo store = ResolvingProxy.resolve( catalog, r.getStore() ); + if ( store != null ) { + store = unwrap(store); + r.setStore(store); + } + + //resolve the namespace + NamespaceInfo namespace = ResolvingProxy.resolve( catalog, r.getNamespace() ); + if (namespace != null) { + namespace = unwrap(namespace); + r.setNamespace(namespace); + } + } + + protected void resolve(LayerInfo layer) { + setId(layer); + + ResourceInfo resource = ResolvingProxy.resolve(catalog, layer.getResource()); + if (resource != null) { + resource = unwrap(resource); + layer.setResource(resource); + } + + StyleInfo style = ResolvingProxy.resolve(catalog, layer.getDefaultStyle()); + if (style != null) { + style = unwrap(style); + layer.setDefaultStyle(style); + } + + LinkedHashSet styles = new LinkedHashSet(); + for (StyleInfo s : layer.getStyles()) { + s = ResolvingProxy.resolve(catalog, s); + s = unwrap(s); + styles.add(s); + } + ((LayerInfoImpl)layer).setStyles(styles); + } + + protected void resolve(LayerGroupInfo layerGroup) { + setId(layerGroup); + + LayerGroupInfoImpl lg = (LayerGroupInfoImpl) layerGroup; + + for ( int i = 0; i < lg.getLayers().size(); i++ ) { + LayerInfo l = lg.getLayers().get( i ); + LayerInfo resolved = unwrap(ResolvingProxy.resolve( catalog, 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 = unwrap(ResolvingProxy.resolve( catalog, s )); + lg.getStyles().set( i, resolved ); + } + } + + } + + protected void resolve(StyleInfo style) { + setId(style); + } + + protected void resolve(MapInfo map) { + setId(map); + } + + protected void setId( Object o ) { + if ( OwsUtils.get( o, "id") == null ) { + String uid = new UID().toString(); + OwsUtils.set( o, "id", o.getClass().getSimpleName() + "-"+uid ); + } + } + + public void syncTo(CatalogDAO dao) { + if (dao instanceof DefaultCatalogDAO) { + //do an optimized sync + DefaultCatalogDAO other = (DefaultCatalogDAO) dao; + + other.stores = stores; + other.defaultStores = defaultStores; + other.resources = resources; + other.namespaces = namespaces; + other.workspaces = workspaces; + other.layers = layers; + other.maps = maps; + other.layerGroups = layerGroups; + other.styles = styles; + } + else { + //do a manual import + for (Map.Entry e : workspaces.entrySet()) { + if (e.getKey() != null && !"default".equals(e.getKey())) { + dao.add(e.getValue()); + } + } + for (Map.Entry e : namespaces.entrySet()) { + if (e.getKey() != null && !"default".equals(e.getKey())) { + dao.add(e.getValue()); + } + } + + for (Iterator k = stores.keySet().iterator(); k.hasNext();) { + Class key = (Class) k.next(); + Collection val = stores.getCollection(key); + for (StoreInfo s : val) { + dao.add(s); + } + } + + for (Iterator k = resources.keySet().iterator(); k.hasNext();) { + Class key = (Class) k.next(); + Collection val = resources.getCollection(key); + for (ResourceInfo r : val) { + dao.add(r); + } + } + + for (StyleInfo s : styles) { dao.add(s); } + for (LayerInfo l : layers) { dao.add(l); } + for (LayerGroupInfo lg : layerGroups) { dao.add(lg); } + for (MapInfo m : maps) { dao.add(m); } + + if (workspaces.containsKey(null)) { + dao.setDefaultWorkspace(workspaces.get(null)); + } + if (namespaces.containsKey(null)) { + dao.setDefaultNamespace(namespaces.get(null)); + } + + for (Map.Entry e : defaultStores.entrySet()) { + WorkspaceInfo ws = workspaces.get(e.getKey()); + dao.setDefaultDataStore(ws, e.getValue()); + } + } + + } +} + diff --git a/main/src/main/java/org/geoserver/catalog/impl/LayerInfoImpl.java b/main/src/main/java/org/geoserver/catalog/impl/LayerInfoImpl.java index ea15632..317aa9d 100644 --- a/main/src/main/java/org/geoserver/catalog/impl/LayerInfoImpl.java +++ b/main/src/main/java/org/geoserver/catalog/impl/LayerInfoImpl.java @@ -56,6 +56,9 @@ public class LayerInfoImpl implements LayerInfo { } public String getName() { + if (resource == null) { + throw new NullPointerException("Unable to get Layer name without an underlying resource"); + } return resource.getName(); // TODO: uncomment back when resource/publish split is complete // return name; @@ -65,6 +68,10 @@ public class LayerInfoImpl implements LayerInfo { // TODO: remove this log and reinstate field assignment when resource/publish split is complete LOGGER.log(Level.FINE, "Warning, some code is setting the LayerInfo name, but that will be ignored"); this.name = name; + + if (resource == null) { + throw new NullPointerException("Layer name must not be set without an underlying resource"); + } resource.setName(name); } diff --git a/main/src/main/java/org/geoserver/catalog/impl/StoreInfoImpl.java b/main/src/main/java/org/geoserver/catalog/impl/StoreInfoImpl.java index 3929779..7b13caf 100644 --- a/main/src/main/java/org/geoserver/catalog/impl/StoreInfoImpl.java +++ b/main/src/main/java/org/geoserver/catalog/impl/StoreInfoImpl.java @@ -43,6 +43,8 @@ public abstract class StoreInfoImpl implements StoreInfo { protected Throwable error; + protected boolean _default; + protected StoreInfoImpl() { } @@ -151,6 +153,13 @@ public abstract class StoreInfoImpl implements StoreInfo { this.error = error; } + public boolean isDefault() { + return _default; + } + + public void setDefault(boolean _default) { + this._default = _default; + } public int hashCode() { final int prime = 31; int result = 1; diff --git a/main/src/main/java/org/geoserver/catalog/impl/WMSLayerInfoImpl.java b/main/src/main/java/org/geoserver/catalog/impl/WMSLayerInfoImpl.java index 49d2df2..df70e4f 100644 --- a/main/src/main/java/org/geoserver/catalog/impl/WMSLayerInfoImpl.java +++ b/main/src/main/java/org/geoserver/catalog/impl/WMSLayerInfoImpl.java @@ -15,6 +15,10 @@ import org.opengis.util.ProgressListener; @SuppressWarnings("serial") public class WMSLayerInfoImpl extends ResourceInfoImpl implements WMSLayerInfo { + + protected WMSLayerInfoImpl() { + } + public WMSLayerInfoImpl(CatalogImpl catalog) { super(catalog); } diff --git a/main/src/main/java/org/geoserver/catalog/impl/WMSStoreInfoImpl.java b/main/src/main/java/org/geoserver/catalog/impl/WMSStoreInfoImpl.java index f6fd588..deea114 100644 --- a/main/src/main/java/org/geoserver/catalog/impl/WMSStoreInfoImpl.java +++ b/main/src/main/java/org/geoserver/catalog/impl/WMSStoreInfoImpl.java @@ -16,6 +16,9 @@ public class WMSStoreInfoImpl extends StoreInfoImpl implements WMSStoreInfo { String capabilitiesURL; + protected WMSStoreInfoImpl() { + } + public WMSStoreInfoImpl(CatalogImpl catalog) { super(catalog); } diff --git a/main/src/main/java/org/geoserver/config/DefaultGeoServerDAO.java b/main/src/main/java/org/geoserver/config/DefaultGeoServerDAO.java new file mode 100644 index 0000000..a64d4a0 --- /dev/null +++ b/main/src/main/java/org/geoserver/config/DefaultGeoServerDAO.java @@ -0,0 +1,166 @@ +package org.geoserver.config; + +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +import org.geoserver.catalog.MetadataMap; +import org.geoserver.catalog.impl.ModificationProxy; +import org.geoserver.config.impl.CoverageAccessInfoImpl; +import org.geoserver.config.impl.GeoServerInfoImpl; + +public class DefaultGeoServerDAO implements GeoServerDAO { + + GeoServerInfo global; + LoggingInfo logging; + List services = new ArrayList(); + + GeoServer geoServer; + + public DefaultGeoServerDAO(GeoServer geoServer) { + this.geoServer = geoServer; + this.global = geoServer.getFactory().createGlobal(); + this.logging = geoServer.getFactory().createLogging(); + } + + public GeoServer getGeoServer() { + return geoServer; + } + + public void setGeoServer(GeoServer geoServer) { + this.geoServer = geoServer; + } + + public GeoServerInfo getGlobal() { + if ( global == null ) { + return null; + } + + return ModificationProxy.create( global, GeoServerInfo.class ); + } + + public void setGlobal(GeoServerInfo global) { + resolve(global); + this.global = global; + } + + public void save(GeoServerInfo global) { + ModificationProxy proxy = + (ModificationProxy) Proxy.getInvocationHandler( global ); + + List propertyNames = proxy.getPropertyNames(); + List oldValues = proxy.getOldValues(); + List newValues = proxy.getNewValues(); + + geoServer.fireGlobalModified(global, propertyNames, oldValues, newValues); + + proxy.commit(); + } + + public LoggingInfo getLogging() { + if ( logging == null ) { + return null; + } + + return ModificationProxy.create( logging, LoggingInfo.class ); + } + + public void setLogging(LoggingInfo logging) { + this.logging = logging; + } + + public void save(LoggingInfo logging) { + ModificationProxy proxy = + (ModificationProxy) Proxy.getInvocationHandler( logging ); + + List propertyNames = proxy.getPropertyNames(); + List oldValues = proxy.getOldValues(); + List newValues = proxy.getNewValues(); + + geoServer.fireLoggingModified(logging, propertyNames, oldValues, newValues); + + proxy.commit(); + } + + public void add(ServiceInfo service) { + //may be adding a proxy, need to unwrap + service = unwrap(service); + service.setGeoServer(geoServer); + services.add( service ); + } + + public void save(ServiceInfo service) { + ModificationProxy proxy = + (ModificationProxy) Proxy.getInvocationHandler( service ); + + List propertyNames = proxy.getPropertyNames(); + List oldValues = proxy.getOldValues(); + List newValues = proxy.getNewValues(); + + geoServer.fireServiceModified(service, propertyNames, oldValues, newValues); + + proxy.commit(); + } + + public void remove(ServiceInfo service) { + services.remove( service ); + } + + public T getService(Class clazz) { + for ( ServiceInfo si : services ) { + if( clazz.isAssignableFrom( si.getClass() ) ) { + return ModificationProxy.create( (T) si, clazz ); + } + } + + return null; + } + + public T getService(String id, Class clazz) { + for ( ServiceInfo si : services ) { + if( id.equals( si.getId() ) ) { + return ModificationProxy.create( (T) si, clazz ); + } + } + + return null; + } + + public T getServiceByName(String name, Class clazz) { + for ( ServiceInfo si : services ) { + if( name.equals( si.getName() ) ) { + return ModificationProxy.create( (T) si, clazz ); + } + } + + return null; + } + + public Collection getServices() { + return ModificationProxy.createList( services, ServiceInfo.class ); + } + + public void dispose() { + if ( global != null ) global.dispose(); + if ( services != null ) services.clear(); + } + + public static T unwrap(T obj) { + return ModificationProxy.unwrap(obj); + } + + protected void resolve(GeoServerInfo info) { + GeoServerInfoImpl global = (GeoServerInfoImpl) info; + if(global.getMetadata() == null) { + global.setMetadata(new MetadataMap()); + } + if(global.getClientProperties() == null) { + global.setClientProperties(new HashMap()); + } + if (global.getCoverageAccess() == null) { + global.setCoverageAccess(new CoverageAccessInfoImpl()); + } + } +} diff --git a/main/src/main/java/org/geoserver/config/DefaultGeoServerLoader.java b/main/src/main/java/org/geoserver/config/DefaultGeoServerLoader.java new file mode 100644 index 0000000..cadf278 --- /dev/null +++ b/main/src/main/java/org/geoserver/config/DefaultGeoServerLoader.java @@ -0,0 +1,84 @@ +package org.geoserver.config; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; + +import org.geoserver.catalog.Catalog; +import org.geoserver.catalog.MetadataMap; +import org.geoserver.catalog.impl.CatalogImpl; +import org.geoserver.config.impl.CoverageAccessInfoImpl; +import org.geoserver.config.impl.GeoServerInfoImpl; +import org.geoserver.config.util.LegacyConfigurationImporter; +import org.geoserver.config.util.XStreamPersister; +import org.geoserver.config.util.XStreamServiceLoader; +import org.geoserver.platform.GeoServerExtensions; +import org.geoserver.platform.GeoServerResourceLoader; + +/** + * Default GeoServerLoader which loads and persists configuration from the classic GeoServer data + * directory structure. + * + * @author Justin Deoliveira, OpenGeo + * + */ +public class DefaultGeoServerLoader extends GeoServerLoader { + + public DefaultGeoServerLoader(GeoServerResourceLoader resourceLoader) { + super(resourceLoader); + } + + protected void loadCatalog(Catalog catalog, XStreamPersister xp) throws Exception { + catalog.setResourceLoader(resourceLoader); + + readCatalog(catalog, xp); + + if ( !legacy ) { + //add the listener which will persist changes + catalog.addListener( new GeoServerPersister( resourceLoader, xp ) ); + } + } + + protected void loadGeoServer(final GeoServer geoServer, XStreamPersister xp) throws Exception { + + readConfiguration(geoServer, xp); + + //add event listener which persists changes + final List loaders = + GeoServerExtensions.extensions( XStreamServiceLoader.class ); + geoServer.addListener( + new ConfigurationListenerAdapter() { + @Override + public void handlePostServiceChange(ServiceInfo service) { + for ( XStreamServiceLoader l : loaders ) { + if ( l.getServiceClass().isInstance( service ) ) { + try { + l.save( service, geoServer ); + } catch (Throwable t) { + //TODO: log this + t.printStackTrace(); + } + } + } + } + } + ); + + geoServer.addListener( new GeoServerPersister( resourceLoader, xp ) ); + } + + @Override + protected void initializeStyles(Catalog catalog, XStreamPersister xp) throws IOException { + //add a persister temporarily in case the styles don't exist on disk + GeoServerPersister p = new GeoServerPersister(resourceLoader, xp); + catalog.addListener(p); + + super.initializeStyles(catalog, xp); + + catalog.removeListener(p); + } + +} diff --git a/main/src/main/java/org/geoserver/config/GeoServer.java b/main/src/main/java/org/geoserver/config/GeoServer.java index 8916918..73f48e0 100644 --- a/main/src/main/java/org/geoserver/config/GeoServer.java +++ b/main/src/main/java/org/geoserver/config/GeoServer.java @@ -5,6 +5,7 @@ package org.geoserver.config; import java.util.Collection; +import java.util.List; import org.geoserver.catalog.Catalog; @@ -41,6 +42,11 @@ public interface GeoServer { public static final Object CONFIGURATION_LOCK = new Object(); /** + * The configuration dao access object. + */ + GeoServerDAO getDAO(); + + /** * The global geoserver configuration. * * @uml.property name="configuration" @@ -175,6 +181,33 @@ public interface GeoServer { Collection getListeners(); /** + * Fires the event for the global configuration being modified. + *

+ * This method should not be called by client code. It is meant to be called + * internally by the configuration subsystem. + *

+ */ + void fireGlobalModified(GeoServerInfo global, List propertyNames, List oldValues, List newValues); + + /** + * Fires the event for the logging configuration being modified. + *

+ * This method should not be called by client code. It is meant to be called + * internally by the configuration subsystem. + *

+ */ + void fireLoggingModified(LoggingInfo logging, List propertyNames, List oldValues, List newValues); + + /** + * Fires the event for a service configuration being modified. + *

+ * This method should not be called by client code. It is meant to be called + * internally by the configuration subsystem. + *

+ */ + void fireServiceModified(ServiceInfo service, List propertyNames, List oldValues, List newValues); + + /** * Disposes the configuration. */ void dispose(); diff --git a/main/src/main/java/org/geoserver/config/GeoServerDAO.java b/main/src/main/java/org/geoserver/config/GeoServerDAO.java new file mode 100644 index 0000000..fd33b30 --- /dev/null +++ b/main/src/main/java/org/geoserver/config/GeoServerDAO.java @@ -0,0 +1,104 @@ +package org.geoserver.config; + +import java.util.Collection; + +/** + * Data access object for geoserver configuration. + * + * @author ETj + * @author Justin Deoliveira, OpenGeo + * + */ +public interface GeoServerDAO { + + GeoServer getGeoServer(); + + void setGeoServer(GeoServer geoServer); + + /** + * The global geoserver configuration. + */ + GeoServerInfo getGlobal(); + + /** + * Sets the global configuration. + */ + void setGlobal( GeoServerInfo global ); + + /** + * Saves the global geoserver configuration after modification. + */ + void save(GeoServerInfo geoServer); + + /** + * The logging configuration. + */ + LoggingInfo getLogging(); + + /** + * Sets logging configuration. + */ + void setLogging( LoggingInfo logging ); + + /** + * Saves the logging configuration. + */ + void save(LoggingInfo logging); + + /** + * Adds a service to the configuration. + */ + void add(ServiceInfo service); + + /** + * Removes a service from the configuration. + */ + void remove(ServiceInfo service); + + /** + * Saves a service that has been modified. + */ + void save(ServiceInfo service); + + /** + * GeoServer services. + * + */ + Collection getServices(); + + /** + * GeoServer services filtered by class. + * + * @param clazz + * The class of the services to return. + */ + T getService(Class clazz); + + /** + * Looks up a service by id. + * + * @param id + * The id of the service. + * @param clazz The type of the service. + * + * @return The service with the specified id, or null if no + * such service coud be found. + */ + T getService(String id, Class clazz); + + /** + * Looks up a service by name. + * + * @param name The name of the service. + * @param clazz The type of the service. + * + * @return The service with the specified name or null if no + * such service could be found. + */ + T getServiceByName(String name, Class clazz ); + + /** + * Disposes the configuration. + */ + void dispose(); +} diff --git a/main/src/main/java/org/geoserver/config/GeoServerLoader.java b/main/src/main/java/org/geoserver/config/GeoServerLoader.java index d023e4f..09b1cbd 100644 --- a/main/src/main/java/org/geoserver/config/GeoServerLoader.java +++ b/main/src/main/java/org/geoserver/config/GeoServerLoader.java @@ -13,8 +13,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -32,20 +30,16 @@ import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; -import org.geoserver.catalog.MetadataMap; import org.geoserver.catalog.NamespaceInfo; -import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WMSLayerInfo; import org.geoserver.catalog.WMSStoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.Wrapper; -import org.geoserver.catalog.event.CatalogListener; import org.geoserver.catalog.impl.CatalogImpl; import org.geoserver.catalog.util.LegacyCatalogImporter; import org.geoserver.catalog.util.LegacyCatalogReader; import org.geoserver.catalog.util.LegacyFeatureTypeInfoReader; -import org.geoserver.config.impl.CoverageAccessInfoImpl; import org.geoserver.config.impl.GeoServerInfoImpl; import org.geoserver.config.util.LegacyConfigurationImporter; import org.geoserver.config.util.XStreamPersister; @@ -65,19 +59,17 @@ import org.vfny.geoserver.global.GeoserverDataDirectory; /** * Initializes GeoServer configuration and catalog on startup. *

- * This class is registered in a spring context and post processes the - * singleton beans {@link Catalog} and {@link GeoServer}, populating them - * with data from the GeoServer data directory. + * This class post processes the singleton beans {@link Catalog} and {@link GeoServer}, populating + * them from stored configuration. *

* @author Justin Deoliveira, The Open Planning Project * */ -public class GeoServerLoader implements BeanPostProcessor, DisposableBean, - ApplicationContextAware { +public abstract class GeoServerLoader { static Logger LOGGER = Logging.getLogger( "org.geoserver" ); - GeoServerResourceLoader resourceLoader; + protected GeoServerResourceLoader resourceLoader; GeoServer geoserver; XStreamPersisterFactory xpf = new XStreamPersisterFactory(); @@ -122,6 +114,9 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, XStreamPersister xp = xpf.createXMLPersister(); xp.setCatalog( catalog ); loadCatalog( catalog, xp ); + + //initialize styles + initializeStyles(catalog, xp); } catch (Exception e) { throw new RuntimeException( e ); @@ -132,6 +127,9 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, geoserver = (GeoServer) bean; try { loadGeoServer( geoserver, xpf.createXMLPersister() ); + + //load initializers + loadInitializers(geoserver); } catch (Exception e) { throw new RuntimeException( e ); @@ -141,114 +139,12 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, return bean; } - - protected void loadCatalog(Catalog catalog, XStreamPersister xp) throws Exception { - catalog.setResourceLoader(resourceLoader); - //look for catalog.xml, if it exists assume we are dealing with - // an old data directory - File f = resourceLoader.find( "catalog.xml" ); - if ( f == null ) { - //assume 2.x style data directory - CatalogImpl catalog2 = (CatalogImpl) readCatalog( xp ); - ((CatalogImpl)catalog).sync( catalog2 ); - } else { - // import old style catalog, register the persister now so that we start - // with a new version of the catalog - CatalogImpl catalog2 = (CatalogImpl) readLegacyCatalog( f, xp ); - ((CatalogImpl)catalog).sync( catalog2 ); - } - - //initialize styles - initializeStyles(catalog, xp); - - if ( !legacy ) { - //add the listener which will persist changes - catalog.addListener( new GeoServerPersister( resourceLoader, xp ) ); - } - } - - protected void loadGeoServer(final GeoServer geoServer, XStreamPersister xp) throws Exception { - //add event listener which persists services - final List loaders = - GeoServerExtensions.extensions( XStreamServiceLoader.class ); - geoServer.addListener( - new ConfigurationListenerAdapter() { - @Override - public void handlePostServiceChange(ServiceInfo service) { - for ( XStreamServiceLoader l : loaders ) { - if ( l.getServiceClass().isInstance( service ) ) { - try { - l.save( service, geoServer ); - } catch (Throwable t) { - //TODO: log this - t.printStackTrace(); - } - } - } - } - } - ); - - //look for services.xml, if it exists assume we are dealing with - // an old data directory - File f = resourceLoader.find( "services.xml" ); - if ( f == null ) { - //assume 2.x style - f = resourceLoader.find( "global.xml"); - if ( f != null ) { - BufferedInputStream in = new BufferedInputStream( new FileInputStream( f ) ); - GeoServerInfoImpl global = (GeoServerInfoImpl) xpf.createXMLPersister().load( in, GeoServerInfo.class ); - // fill in default collection values if needed - //JD: this should not be here, it should be moved to a resolve() method - // on GeoServer, like the way the catalog does it - if(global.getMetadata() == null) - global.setMetadata(new MetadataMap()); - if(global.getClientProperties() == null) - global.setClientProperties(new HashMap()); - if (global.getCoverageAccess() == null){ - global.setCoverageAccess(new CoverageAccessInfoImpl()); - } - geoServer.setGlobal( global ); - - - } - - //load logging - f = resourceLoader.find( "logging.xml" ); - if ( f != null ) { - BufferedInputStream in = new BufferedInputStream( new FileInputStream( f ) ); - LoggingInfo logging = xpf.createXMLPersister().load( in, LoggingInfo.class ); - geoServer.setLogging( logging ); - } - //load services - for ( XStreamServiceLoader l : loaders ) { - try { - ServiceInfo s = l.load( geoServer ); - geoServer.add( s ); - - LOGGER.info( "Loaded service '" + s.getId() + "', " + (s.isEnabled()?"enabled":"disabled") ); - } - catch( Throwable t ) { - //TODO: log this - t.printStackTrace(); - } - } - } else { - //add listener now as a converter which will convert from the old style - // data directory to the new - GeoServerPersister p = new GeoServerPersister( resourceLoader, xp ); - geoServer.addListener( p ); - - //import old style services.xml - new LegacyConfigurationImporter(geoServer).imprt(resourceLoader.getBaseDirectory()); - - geoServer.removeListener( p ); - - //rename the services.xml file - f.renameTo( new File( f.getParentFile(), "services.xml.old" ) ); - } - + protected abstract void loadCatalog(Catalog catalog, XStreamPersister xp) throws Exception; + + protected abstract void loadGeoServer(final GeoServer geoServer, XStreamPersister xp) throws Exception; + + protected void loadInitializers(GeoServer geoServer) throws Exception { //load initializer extensions List initializers = GeoServerExtensions.extensions( GeoServerInitializer.class ); for ( GeoServerInitializer initer : initializers ) { @@ -260,72 +156,13 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, t.printStackTrace(); } } - - geoServer.addListener( new GeoServerPersister( resourceLoader, xp ) ); - } - - //JD: NOTE! This method is no longer used on trunk - protected void initialize() { - //load catalog - LegacyCatalogImporter catalogImporter = new LegacyCatalogImporter(); - catalogImporter.setResourceLoader(resourceLoader); - Catalog catalog = geoserver.getCatalog(); - if(catalog instanceof Wrapper && ((Wrapper) catalog).isWrapperFor(Catalog.class)) { - catalog = ((Wrapper) catalog).unwrap(Catalog.class); - } - catalogImporter.setCatalog(catalog); - - try { - catalogImporter.imprt( resourceLoader.getBaseDirectory() ); - } - catch(Exception e) { - throw new RuntimeException( e ); - } - - //load configuration - LegacyConfigurationImporter importer = new LegacyConfigurationImporter(); - importer.setConfiguration(geoserver); - - try { - importer.imprt( resourceLoader.getBaseDirectory() ); - } - catch (Exception e) { - throw new RuntimeException( e ); - } - - //load initializer extensions - List initializers = GeoServerExtensions.extensions( GeoServerInitializer.class ); - for ( GeoServerInitializer initer : initializers ) { - try { - initer.initialize( geoserver ); - } - catch( Throwable t ) { - //TODO: log this - t.printStackTrace(); - } - } - - //load listeners - List catalogListeners = GeoServerExtensions.extensions( CatalogListener.class ); - for ( CatalogListener l : catalogListeners ) { - catalog.addListener( l ); - } - List configListeners = GeoServerExtensions.extensions( ConfigurationListener.class ); - for ( ConfigurationListener l : configListeners ) { - geoserver.addListener( l ); - } } /** * Does some post processing on the catalog to ensure that the "well-known" styles * are always around. */ - void initializeStyles( Catalog catalog, XStreamPersister xp) throws IOException { - - //add a persister temporarily in case the styles don't exist on disk - GeoServerPersister p = new GeoServerPersister(resourceLoader, xp); - catalog.addListener(p); - + protected void initializeStyles( Catalog catalog, XStreamPersister xp) throws IOException { if ( catalog.getStyleByName( StyleInfo.DEFAULT_POINT ) == null ) { initializeStyle( catalog, StyleInfo.DEFAULT_POINT, "default_point.sld" ); } @@ -338,8 +175,6 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, if ( catalog.getStyleByName( StyleInfo.DEFAULT_RASTER ) == null ) { initializeStyle( catalog, StyleInfo.DEFAULT_RASTER, "default_raster.sld" ); } - - catalog.removeListener(p); } /** @@ -349,7 +184,7 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, //copy the file out to the data directory if necessary if ( resourceLoader.find( "styles", sld ) == null ) { - FileUtils.copyURLToFile(getClass().getResource(sld), + FileUtils.copyURLToFile(GeoServerLoader.class.getResource(sld), new File( resourceLoader.findOrCreateDirectory("styles" ), sld) ); } @@ -375,87 +210,20 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, loadCatalog( catalog, xp ); loadGeoServer( geoserver, xp); } - - //TODO: kill this method, it is not longer needed since persistance is event based - public void persist() throws Exception { - //TODO: make the configuration backend pluggable... or loadable - // from application context, or web.xml, or env variable, etc... - XStreamPersister p = xpf.createXMLPersister(); - BufferedOutputStream out = new BufferedOutputStream( - new FileOutputStream( resourceLoader.createFile( "catalog2.xml" ) ) - ); - - //persist catalog - Catalog catalog = geoserver.getCatalog(); - if( catalog instanceof Wrapper ) { - catalog = ((Wrapper)catalog).unwrap( Catalog.class ); - } - p.save( catalog, out ); - out.flush(); - out.close(); - - //persist resources - File workspaces = resourceLoader.findOrCreateDirectory( "workspaces" ); - for ( ResourceInfo r : catalog.getResources( ResourceInfo.class ) ) { - WorkspaceInfo ws = r.getStore().getWorkspace(); - File workspace = new File( workspaces, ws.getName() ); - if ( !workspace.exists() ) { - workspace.mkdir(); - } - - String dirName = r.getStore().getName() + "_" + r.getNativeName(); - //dirName = URLEncoder.encode( dirName, "UTF-8" ); - - File dir = new File( workspace, dirName ); - if ( !dir.exists() ) { - dir.mkdir(); - } - - File info = new File( dir, "resource.xml" ); - try { - persist( p, r, info ); - } - catch( Exception e ) { - LOGGER.log( Level.WARNING, "Error persisting '" + r.getName() + "'", e ); - } - - //persist layers publishing the resource - LayerInfo l = catalog.getLayers( r ).get( 0 ); - try { - persist( p, l, new File( dir, "layer.xml" ) ); - } - catch( Exception e ) { - LOGGER.log( Level.WARNING, "Error persisting layer '" + l.getName() + "'", e ); - } - } - - - //persist global - try { - persist( p, geoserver.getGlobal(), resourceLoader.createFile( "global.xml" ) ); - } - catch( Exception e ) { - LOGGER.log( Level.WARNING, "Error persisting global configuration.", e ); - } - - //persist services - Collection services = geoserver.getServices(); - List loaders = GeoServerExtensions.extensions( ServiceLoader.class ); - - for ( Iterator s = services.iterator(); s.hasNext(); ) { - ServiceInfo service = (ServiceInfo) s.next(); - for ( ServiceLoader loader : loaders ) { - if (loader.getServiceClass().isInstance( service ) ) { - try { - loader.save( service, geoserver ); - break; - } - catch( Throwable t ) { - LOGGER.warning( "Error persisting service: " + service.getId() ); - LOGGER.log( Level.INFO, "", t ); - } - } - } + + protected void readCatalog(Catalog catalog, XStreamPersister xp) throws Exception { + //look for catalog.xml, if it exists assume we are dealing with + // an old data directory + File f = resourceLoader.find( "catalog.xml" ); + if ( f == null ) { + //assume 2.x style data directory + CatalogImpl catalog2 = (CatalogImpl) readCatalog( xp ); + ((CatalogImpl)catalog).sync( catalog2 ); + } else { + // import old style catalog, register the persister now so that we start + // with a new version of the catalog + CatalogImpl catalog2 = (CatalogImpl) readLegacyCatalog( f, xp ); + ((CatalogImpl)catalog).sync( catalog2 ); } } @@ -835,6 +603,58 @@ public class GeoServerLoader implements BeanPostProcessor, DisposableBean, return catalog2; } + protected void readConfiguration(GeoServer geoServer, XStreamPersister xp) throws Exception { + //look for services.xml, if it exists assume we are dealing with + // an old data directory + File f = resourceLoader.find( "services.xml" ); + if ( f == null ) { + //assume 2.x style + f = resourceLoader.find( "global.xml"); + if ( f != null ) { + BufferedInputStream in = new BufferedInputStream( new FileInputStream( f ) ); + GeoServerInfoImpl global = (GeoServerInfoImpl) xpf.createXMLPersister().load( in, GeoServerInfo.class ); + geoServer.setGlobal( global ); + } + + //load logging + f = resourceLoader.find( "logging.xml" ); + if ( f != null ) { + BufferedInputStream in = new BufferedInputStream( new FileInputStream( f ) ); + LoggingInfo logging = xpf.createXMLPersister().load( in, LoggingInfo.class ); + geoServer.setLogging( logging ); + } + //load services + final List loaders = + GeoServerExtensions.extensions( XStreamServiceLoader.class ); + for ( XStreamServiceLoader l : loaders ) { + try { + ServiceInfo s = l.load( geoServer ); + geoServer.add( s ); + + LOGGER.info( "Loaded service '" + s.getId() + "', " + (s.isEnabled()?"enabled":"disabled") ); + } + catch( Throwable t ) { + //TODO: log this + t.printStackTrace(); + } + } + } else { + //add listener now as a converter which will convert from the old style + // data directory to the new + GeoServerPersister p = new GeoServerPersister( resourceLoader, xp ); + geoServer.addListener( p ); + + //import old style services.xml + new LegacyConfigurationImporter(geoServer).imprt(resourceLoader.getBaseDirectory()); + + geoServer.removeListener( p ); + + //rename the services.xml file + f.renameTo( new File( f.getParentFile(), "services.xml.old" ) ); + } + + } + /** * Helper method which uses xstream to persist an object as xml on disk. */ diff --git a/main/src/main/java/org/geoserver/config/GeoServerLoaderProxy.java b/main/src/main/java/org/geoserver/config/GeoServerLoaderProxy.java new file mode 100644 index 0000000..2bedb08 --- /dev/null +++ b/main/src/main/java/org/geoserver/config/GeoServerLoaderProxy.java @@ -0,0 +1,79 @@ +package org.geoserver.config; + +import org.geoserver.platform.GeoServerExtensions; +import org.geoserver.platform.GeoServerResourceLoader; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * A proxy for {@link GeoServerLoader} that loads the actual loader instance based on + * the spring context. + *

+ * This method will first attempt to lookup an instance of {@link GeoServerLoader} in the spring + * context and if none found will fall back on {@link DefaultGeoServerLoader}. + *

+ * + * @author Justin Deoliveira, OpenGeo + * + */ +public class GeoServerLoaderProxy implements BeanPostProcessor, DisposableBean, ApplicationContextAware { + + /** + * resource loader + */ + protected GeoServerResourceLoader resourceLoader; + + /** + * the actual loader + */ + GeoServerLoader loader; + + public GeoServerLoaderProxy(GeoServerResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.loader = lookupGeoServerLoader(applicationContext); + loader.setApplicationContext(applicationContext); + } + + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + if (loader != null) { + return loader.postProcessAfterInitialization(bean, beanName); + } + return bean; + } + + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + if (loader != null) { + return loader.postProcessBeforeInitialization(bean, beanName); + } + return bean; + } + + public void reload() throws Exception { + if (loader != null) { + loader.reload(); + } + } + public void destroy() throws Exception { + if (loader != null) { + loader.destroy(); + } + } + + protected GeoServerLoader lookupGeoServerLoader(ApplicationContext appContext) { + GeoServerLoader loader = GeoServerExtensions.bean(GeoServerLoader.class, appContext); + if (loader == null) { + loader = new DefaultGeoServerLoader(resourceLoader); + } + return loader; + } + + +} diff --git a/main/src/main/java/org/geoserver/config/GeoServerPropertyConfigurer.java b/main/src/main/java/org/geoserver/config/GeoServerPropertyConfigurer.java index 59fdf61..afd975d 100644 --- a/main/src/main/java/org/geoserver/config/GeoServerPropertyConfigurer.java +++ b/main/src/main/java/org/geoserver/config/GeoServerPropertyConfigurer.java @@ -59,6 +59,7 @@ public class GeoServerPropertyConfigurer extends PropertyPlaceholderConfigurer { GeoServerDataDirectory data; Resource location; boolean copyOutTemplate = true; + String comments; public GeoServerPropertyConfigurer(GeoServerDataDirectory data) { this.data = data; @@ -68,6 +69,10 @@ public class GeoServerPropertyConfigurer extends PropertyPlaceholderConfigurer { this.copyOutTemplate = copyOutTemplate; } + public void setComments(String comments) { + this.comments = comments; + } + @Override public void setLocation(Resource location) { try { @@ -103,7 +108,7 @@ public class GeoServerPropertyConfigurer extends PropertyPlaceholderConfigurer { f.getParentFile().mkdirs(); f.createNewFile(); FileOutputStream fout = new FileOutputStream(f); - props.store(fout, null); + props.store(fout, comments); fout.flush(); fout.close(); } diff --git a/main/src/main/java/org/geoserver/config/LegacyGeoServerLoader.java b/main/src/main/java/org/geoserver/config/LegacyGeoServerLoader.java index 4acbf4d..2d98f57 100644 --- a/main/src/main/java/org/geoserver/config/LegacyGeoServerLoader.java +++ b/main/src/main/java/org/geoserver/config/LegacyGeoServerLoader.java @@ -9,6 +9,7 @@ import java.io.File; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.util.LegacyCatalogImporter; import org.geoserver.config.util.LegacyConfigurationImporter; +import org.geoserver.config.util.XStreamPersister; import org.geoserver.platform.GeoServerResourceLoader; /** @@ -18,13 +19,14 @@ import org.geoserver.platform.GeoServerResourceLoader; * @author Justin Deoliveira, OpenGEO * */ -public class LegacyGeoServerLoader extends GeoServerLoader { +public class LegacyGeoServerLoader extends DefaultGeoServerLoader { public LegacyGeoServerLoader(GeoServerResourceLoader resourceLoader) { super(resourceLoader); } - protected void loadCatalog(Catalog catalog) throws Exception { + @Override + protected void readCatalog(Catalog catalog, XStreamPersister xp) throws Exception { catalog.setResourceLoader( resourceLoader ); //look for legacy catalog.xml @@ -41,7 +43,8 @@ public class LegacyGeoServerLoader extends GeoServerLoader { } } - protected void loadGeoServer(GeoServer geoServer) throws Exception { + @Override + protected void readConfiguration(GeoServer geoServer, XStreamPersister xp) throws Exception { //look for legacy services.xml File f = resourceLoader.find( "services.xml" ); if ( f != null ) { diff --git a/main/src/main/java/org/geoserver/config/LoggingInfo.java b/main/src/main/java/org/geoserver/config/LoggingInfo.java index 3b449f6..dd62847 100644 --- a/main/src/main/java/org/geoserver/config/LoggingInfo.java +++ b/main/src/main/java/org/geoserver/config/LoggingInfo.java @@ -4,12 +4,14 @@ */ package org.geoserver.config; +import org.geoserver.catalog.Info; + /** * Logging configuration. * * @author Justin Deoliveira, OpenGeo */ -public interface LoggingInfo { +public interface LoggingInfo extends Info { /** * The GeoServer logging level. diff --git a/main/src/main/java/org/geoserver/config/impl/ContactInfoImpl.java b/main/src/main/java/org/geoserver/config/impl/ContactInfoImpl.java index 2d2c680..f7428d3 100644 --- a/main/src/main/java/org/geoserver/config/impl/ContactInfoImpl.java +++ b/main/src/main/java/org/geoserver/config/impl/ContactInfoImpl.java @@ -8,7 +8,7 @@ import org.geoserver.config.ContactInfo; public class ContactInfoImpl implements ContactInfo { - String id; + String id = "contact"; String address; diff --git a/main/src/main/java/org/geoserver/config/impl/GeoServerImpl.java b/main/src/main/java/org/geoserver/config/impl/GeoServerImpl.java index 318d952..993fb6b 100644 --- a/main/src/main/java/org/geoserver/config/impl/GeoServerImpl.java +++ b/main/src/main/java/org/geoserver/config/impl/GeoServerImpl.java @@ -4,7 +4,6 @@ */ package org.geoserver.config.impl; -import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -12,9 +11,10 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.catalog.Catalog; -import org.geoserver.catalog.impl.ModificationProxy; import org.geoserver.config.ConfigurationListener; +import org.geoserver.config.DefaultGeoServerDAO; import org.geoserver.config.GeoServer; +import org.geoserver.config.GeoServerDAO; import org.geoserver.config.GeoServerFactory; import org.geoserver.config.GeoServerInfo; import org.geoserver.config.GeoServerLoader; @@ -28,14 +28,39 @@ public class GeoServerImpl implements GeoServer { private static final Logger LOGGER = Logging.getLogger(GeoServerImpl.class); + /** + * factory for creating objects + */ GeoServerFactory factory = new GeoServerFactoryImpl(this); - GeoServerInfo global = factory.createGlobal(); - LoggingInfo logging = factory.createLogging(); + + /** + * the catalog + */ Catalog catalog; - List services = new ArrayList(); + /** + * data access object + */ + GeoServerDAO dao; + + /** + * listeners + */ List listeners = new ArrayList(); + public GeoServerImpl() { + this.dao = new DefaultGeoServerDAO(this); + } + + public GeoServerDAO getDAO() { + return dao; + } + + public void setDAO(GeoServerDAO dao) { + this.dao = dao; + dao.setGeoServer(this); + } + public GeoServerFactory getFactory() { return factory; } @@ -53,31 +78,22 @@ public class GeoServerImpl implements GeoServer { } public GeoServerInfo getGlobal() { - if ( global == null ) { - return null; - } - - return ModificationProxy.create( global, GeoServerInfo.class ); + return dao.getGlobal(); } public void setGlobal(GeoServerInfo global) { - this.global = global; + dao.setGlobal(global); //fire the modification event fireGlobalPostModified(); } public LoggingInfo getLogging() { - if ( logging == null ) { - return null; - } - - return ModificationProxy.create( logging, LoggingInfo.class ); + return dao.getLogging(); } public void setLogging(LoggingInfo logging) { - this.logging = logging; - + dao.setLogging(logging); fireLoggingPostModified(); } @@ -85,112 +101,70 @@ public class GeoServerImpl implements GeoServer { if ( service.getId() == null ) { throw new NullPointerException( "service id must not be null" ); } - for ( ServiceInfo s : services ) { - if ( s.getId().equals( service.getId() ) ) { - throw new IllegalArgumentException( "service with id '" + s.getId() + "' already exists" ); - } + if ( dao.getService(service.getId(), ServiceInfo.class) != null) { + throw new IllegalArgumentException( "service with id '" + service.getId() + "' already exists" ); } - - //may be adding a proxy, need to unwrap - service = unwrap(service); - service.setGeoServer(this); - services.add( service ); + dao.add(service); //fire post modification event firePostServiceModified(service); } public static T unwrap(T obj) { - return ModificationProxy.unwrap(obj); + return DefaultGeoServerDAO.unwrap(obj); } public T getService(Class clazz) { - for ( ServiceInfo si : services ) { - if( clazz.isAssignableFrom( si.getClass() ) ) { - return ModificationProxy.create( (T) si, clazz ); - } - } - - return null; + return dao.getService(clazz); } public T getService(String id, Class clazz) { - for ( ServiceInfo si : services ) { - if( id.equals( si.getId() ) ) { - return ModificationProxy.create( (T) si, clazz ); - } - } - - return null; + return dao.getService(id, clazz); } public T getServiceByName(String name, Class clazz) { - for ( ServiceInfo si : services ) { - if( name.equals( si.getName() ) ) { - return ModificationProxy.create( (T) si, clazz ); - } - } - - return null; + return dao.getServiceByName(name, clazz); } public Collection getServices() { - return ModificationProxy.createList( services, ServiceInfo.class ); + return dao.getServices(); } public void remove(ServiceInfo service) { - services.remove( service ); + dao.remove(service); } public void save(GeoServerInfo geoServer) { - ModificationProxy proxy = - (ModificationProxy) Proxy.getInvocationHandler( geoServer ); - - List propertyNames = proxy.getPropertyNames(); - List oldValues = proxy.getOldValues(); - List newValues = proxy.getNewValues(); - - for ( ConfigurationListener l : listeners ) { - try { - l.handleGlobalChange( geoServer, propertyNames, oldValues, newValues); - } - catch( Exception e ) { - //log this - } - } - - proxy.commit(); + dao.save(geoServer); //fire post modification event fireGlobalPostModified(); } public void save(LoggingInfo logging) { - ModificationProxy proxy = - (ModificationProxy) Proxy.getInvocationHandler( logging ); - - List propertyNames = proxy.getPropertyNames(); - List oldValues = proxy.getOldValues(); - List newValues = proxy.getNewValues(); + dao.save(logging); + //fire post modification event + fireLoggingPostModified(); + } + + void fireGlobalPostModified() { for ( ConfigurationListener l : listeners ) { try { - l.handleLoggingChange( logging, propertyNames, oldValues, newValues); + l.handlePostGlobalChange( dao.getGlobal() ); } catch( Exception e ) { //log this } } + } + + public void fireGlobalModified(GeoServerInfo global, List changed, List oldValues, + List newValues) { - proxy.commit(); - - //fire post modification event - fireLoggingPostModified(); - } - void fireGlobalPostModified() { - for ( ConfigurationListener l : listeners ) { + for ( ConfigurationListener l : getListeners() ) { try { - l.handlePostGlobalChange( global ); + l.handleGlobalChange( global, changed, oldValues, newValues); } catch( Exception e ) { //log this @@ -198,10 +172,12 @@ public class GeoServerImpl implements GeoServer { } } - void fireLoggingPostModified() { - for ( ConfigurationListener l : listeners ) { + public void fireLoggingModified(LoggingInfo logging, List changed, List oldValues, + List newValues) { + + for ( ConfigurationListener l : getListeners() ) { try { - l.handlePostLoggingChange( logging ); + l.handleLoggingChange( logging, changed, oldValues, newValues); } catch( Exception e ) { //log this @@ -209,29 +185,37 @@ public class GeoServerImpl implements GeoServer { } } - public void save(ServiceInfo service) { - ModificationProxy proxy = - (ModificationProxy) Proxy.getInvocationHandler( service ); - - List propertyNames = proxy.getPropertyNames(); - List oldValues = proxy.getOldValues(); - List newValues = proxy.getNewValues(); - + void fireLoggingPostModified() { for ( ConfigurationListener l : listeners ) { try { - l.handleServiceChange( service, propertyNames, oldValues, newValues); + l.handlePostLoggingChange( dao.getLogging() ); } catch( Exception e ) { //log this } } - - proxy.commit(); + } + + public void save(ServiceInfo service) { + dao.save(service); //fire post modification event firePostServiceModified(service); } + public void fireServiceModified(ServiceInfo service, List changed, List oldValues, + List newValues) { + + for ( ConfigurationListener l : getListeners() ) { + try { + l.handleServiceChange( service, changed, oldValues, newValues); + } + catch( Exception e ) { + //log this + } + } + } + void firePostServiceModified(ServiceInfo service) { for ( ConfigurationListener l : listeners ) { try { @@ -266,9 +250,9 @@ public class GeoServerImpl implements GeoServer { } // internal cleanup - if ( global != null ) global.dispose(); + if ( catalog != null ) catalog.dispose(); - if ( services != null ) services.clear(); + if ( dao != null ) dao.dispose(); if ( listeners != null ) listeners.clear(); } diff --git a/main/src/main/java/org/geoserver/config/impl/GeoServerInfoImpl.java b/main/src/main/java/org/geoserver/config/impl/GeoServerInfoImpl.java index a761cc0..c9acc27 100644 --- a/main/src/main/java/org/geoserver/config/impl/GeoServerInfoImpl.java +++ b/main/src/main/java/org/geoserver/config/impl/GeoServerInfoImpl.java @@ -54,7 +54,7 @@ public class GeoServerInfoImpl implements GeoServerInfo { protected Boolean globalServices = true; - protected GeoServer geoServer; + protected transient GeoServer geoServer; public GeoServerInfoImpl(GeoServer geoServer) { this.geoServer = geoServer; diff --git a/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java b/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java index eec3346..8411809 100644 --- a/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java +++ b/main/src/main/java/org/geoserver/config/impl/JAIInfoImpl.java @@ -174,12 +174,65 @@ public class JAIInfoImpl implements Serializable, JAIInfo { this.tileCache = tileCache; } - public boolean isAllowNativeMosaic() { - return allowNativeMosaic; - } - - public void setAllowNativeMosaic(boolean allowNativeMosaic) { - this.allowNativeMosaic = allowNativeMosaic; - } - + public boolean isAllowNativeMosaic() { + return allowNativeMosaic; + } + + public void setAllowNativeMosaic(boolean allowNativeMosaic) { + this.allowNativeMosaic = allowNativeMosaic; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (allowInterpolation ? 1231 : 1237); + result = prime * result + (allowNativeMosaic ? 1231 : 1237); + result = prime * result + (imageIOCache ? 1231 : 1237); + result = prime * result + (jpegAcceleration ? 1231 : 1237); + long temp; + temp = Double.doubleToLongBits(memoryCapacity); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(memoryThreshold); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + (pngAcceleration ? 1231 : 1237); + result = prime * result + (recycling ? 1231 : 1237); + result = prime * result + tilePriority; + result = prime * result + tileThreads; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + JAIInfoImpl other = (JAIInfoImpl) obj; + if (allowInterpolation != other.allowInterpolation) + return false; + if (allowNativeMosaic != other.allowNativeMosaic) + return false; + if (imageIOCache != other.imageIOCache) + return false; + if (jpegAcceleration != other.jpegAcceleration) + return false; + if (Double.doubleToLongBits(memoryCapacity) != Double + .doubleToLongBits(other.memoryCapacity)) + return false; + if (Double.doubleToLongBits(memoryThreshold) != Double + .doubleToLongBits(other.memoryThreshold)) + return false; + if (pngAcceleration != other.pngAcceleration) + return false; + if (recycling != other.recycling) + return false; + if (tilePriority != other.tilePriority) + return false; + if (tileThreads != other.tileThreads) + return false; + return true; + } } diff --git a/main/src/main/java/org/geoserver/config/impl/LoggingInfoImpl.java b/main/src/main/java/org/geoserver/config/impl/LoggingInfoImpl.java index c1bbf9e..890fe7c 100644 --- a/main/src/main/java/org/geoserver/config/impl/LoggingInfoImpl.java +++ b/main/src/main/java/org/geoserver/config/impl/LoggingInfoImpl.java @@ -8,12 +8,22 @@ import org.geoserver.config.LoggingInfo; */ public class LoggingInfoImpl implements LoggingInfo { + String id; + String level; String location; boolean stdOutLogging; + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + public String getLevel() { return level; } diff --git a/main/src/main/java/org/geoserver/config/impl/ServiceInfoImpl.java b/main/src/main/java/org/geoserver/config/impl/ServiceInfoImpl.java index 8d52126..5ff165d 100644 --- a/main/src/main/java/org/geoserver/config/impl/ServiceInfoImpl.java +++ b/main/src/main/java/org/geoserver/config/impl/ServiceInfoImpl.java @@ -18,7 +18,7 @@ public class ServiceInfoImpl implements ServiceInfo { protected String id; - protected GeoServer geoServer; + protected transient GeoServer geoServer; protected boolean enabled = true; diff --git a/main/src/main/java/org/geoserver/config/util/LaxCollectionConverter.java b/main/src/main/java/org/geoserver/config/util/LaxCollectionConverter.java new file mode 100644 index 0000000..dada3ea --- /dev/null +++ b/main/src/main/java/org/geoserver/config/util/LaxCollectionConverter.java @@ -0,0 +1,27 @@ +package org.geoserver.config.util; + +import java.util.Collection; + +import com.thoughtworks.xstream.converters.collections.CollectionConverter; +import com.thoughtworks.xstream.mapper.Mapper; + +/** + * Custom collection converter. + *

+ * Used because because the basic only handles a handlful of collection implementations. And + * things like hibernate have there own implementation which leads to problems. + *

+ * @author Justin Deoliveira, OpenGeo + * + */ +public class LaxCollectionConverter extends CollectionConverter { + + public LaxCollectionConverter(Mapper mapper) { + super(mapper); + } + + @Override + public boolean canConvert(Class type) { + return Collection.class.isAssignableFrom(type); + } +} diff --git a/main/src/main/java/org/geoserver/config/util/XStreamPersister.java b/main/src/main/java/org/geoserver/config/util/XStreamPersister.java index 9c90778..552d069 100644 --- a/main/src/main/java/org/geoserver/config/util/XStreamPersister.java +++ b/main/src/main/java/org/geoserver/config/util/XStreamPersister.java @@ -47,6 +47,7 @@ import org.geoserver.catalog.impl.CoverageDimensionImpl; import org.geoserver.catalog.impl.CoverageInfoImpl; import org.geoserver.catalog.impl.CoverageStoreInfoImpl; import org.geoserver.catalog.impl.DataStoreInfoImpl; +import org.geoserver.catalog.impl.DefaultCatalogDAO; import org.geoserver.catalog.impl.FeatureTypeInfoImpl; import org.geoserver.catalog.impl.LayerGroupInfoImpl; import org.geoserver.catalog.impl.LayerInfoImpl; @@ -216,9 +217,9 @@ public class XStreamPersister { //control the order in which fields are sorted SortableFieldKeySorter sorter = new SortableFieldKeySorter(); - sorter.registerFieldOrder( CatalogImpl.class, new String[]{ "workspaces", "namespaces", "stores", "styles", + //sorter.registerFieldOrder( DefaultCatalogDAO.class, new String[]{ "workspaces", "namespaces", "stores", "styles", /* these we actually omit, but the sorter needs them specified */ - "layerGroups", "resources", "maps", "defaultStores", "listeners", "layers", "resourcePool", "resourceLoader", "LOGGER" } ); + // "layerGroups", "resources", "maps", "defaultStores", "listeners", "layers", "resourcePool", "resourceLoader", "LOGGER" } ); ReflectionProvider reflectionProvider = new CustomReflectionProvider( new FieldDictionary( sorter ) ); //new Sun14ReflectionProvider( new FieldDictionary( sorter ) ); @@ -275,17 +276,20 @@ public class XStreamPersister { // Catalog xs.omitField(impl(Catalog.class), "resourcePool"); xs.omitField(impl(Catalog.class), "resourceLoader"); - xs.omitField(impl(Catalog.class), "resources"); xs.omitField(impl(Catalog.class), "listeners"); - xs.omitField(impl(Catalog.class), "layers"); - xs.omitField(impl(Catalog.class), "maps"); - xs.omitField(impl(Catalog.class), "layerGroups"); xs.omitField(impl(Catalog.class), "LOGGER"); - xs.registerLocalConverter(impl(Catalog.class), "stores", + + xs.omitField(impl(DefaultCatalogDAO.class), "catalog"); + xs.omitField(impl(DefaultCatalogDAO.class), "resources"); + xs.omitField(impl(DefaultCatalogDAO.class), "layers"); + xs.omitField(impl(DefaultCatalogDAO.class), "maps"); + xs.omitField(impl(DefaultCatalogDAO.class), "layerGroups"); + + xs.registerLocalConverter(DefaultCatalogDAO.class, "stores", new StoreMultiHashMapConverter()); - xs.registerLocalConverter(impl(Catalog.class), "namespaces", + xs.registerLocalConverter(DefaultCatalogDAO.class, "namespaces", new SpaceMapConverter("namespace")); - xs.registerLocalConverter(impl(Catalog.class), "workspaces", + xs.registerLocalConverter(DefaultCatalogDAO.class, "workspaces", new SpaceMapConverter("workspace")); @@ -317,11 +321,18 @@ public class XStreamPersister { xs.registerLocalConverter( impl(ResourceInfo.class), "store", new ReferenceConverter(StoreInfo.class)); xs.registerLocalConverter( impl(ResourceInfo.class), "namespace", new ReferenceConverter(NamespaceInfo.class)); xs.registerLocalConverter( impl(ResourceInfo.class), "metadata", new MetadataMapConverter() ); + xs.registerLocalConverter( impl(ResourceInfo.class), "keywords", new LaxCollectionConverter(xs.getMapper())); // FeatureTypeInfo // CoverageInfo - + xs.registerLocalConverter( impl(CoverageInfo.class), "supportedFormats", new LaxCollectionConverter(xs.getMapper())); + xs.registerLocalConverter( impl(CoverageInfo.class), "requestSRS", new LaxCollectionConverter(xs.getMapper())); + xs.registerLocalConverter( impl(CoverageInfo.class), "responseSRS", new LaxCollectionConverter(xs.getMapper())); + xs.registerLocalConverter( impl(CoverageInfo.class), "interpolationMethods", new LaxCollectionConverter(xs.getMapper())); + xs.registerLocalConverter( impl(CoverageInfo.class), "dimensions", new LaxCollectionConverter(xs.getMapper())); + + // CoverageDimensionInfo xs.registerLocalConverter( impl(CoverageDimensionInfo.class), "range", new NumberRangeConverter()); @@ -601,6 +612,11 @@ public class XStreamPersister { } @Override + public boolean canConvert(Class type) { + //handle all types of maps + return Map.class.isAssignableFrom(type); + } + @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { @@ -790,6 +806,7 @@ public class XStreamPersister { return map; } } + /** * Converters which encodes an object by a reference, or its id. */ @@ -855,13 +872,13 @@ public class XStreamPersister { return CatalogImpl.unwrap( resolved ); } } - class ReferenceCollectionConverter extends CollectionConverter { + class ReferenceCollectionConverter extends LaxCollectionConverter { Class clazz; public ReferenceCollectionConverter(Class clazz) { super( getXStream().getMapper() ); this.clazz = clazz; } - + @Override protected void writeItem(Object item, MarshallingContext context, HierarchicalStreamWriter writer) { diff --git a/main/src/main/java/org/geoserver/data/GeoServerDataSource.java b/main/src/main/java/org/geoserver/data/GeoServerDataSource.java new file mode 100644 index 0000000..529728d --- /dev/null +++ b/main/src/main/java/org/geoserver/data/GeoServerDataSource.java @@ -0,0 +1,118 @@ +package org.geoserver.data; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +import org.apache.commons.dbcp.BasicDataSource; +import org.geoserver.config.GeoServerDataDirectory; + +/** + * A datasource that is user configurable via properties file stored in the + * geoserver data directory. + *

+ * Instances of this class are defined in a spring context as follows: + *

+ *   <bean id="myDataSource" class="org.geoserver.data.GeoServerDataSource">
+ *     <property name="dataDirectory" ref="dataDirectory"/>
+       <property name="file" value="mydatasource.properties"/>
+ *     <property name="defaultParameters">
+ *       <props>
+ *         <prop key="driver">org.h2.Driver</prop>
+ *         <prop key="url">jdbc:h2:file:%GEOSERVER_DATA_DIR%/mydb</prop>
+ *         <prop key="username">foo</prop>
+ *         <prop key="password">bar</prop>
+ *       </props>
+ *     </property>
+ *   </bean>
+ * 
+ *

+ * + * Note that any property values can contain "${GEOSERVER_DATA_DIR}" and it will be expanded out + * to the absolute path of the geoserver data directory. + * + * @author Justin Deoliveira, OpenGeo + * + */ +public class GeoServerDataSource extends BasicDataSource { + GeoServerDataDirectory dataDirectory; + + String file; + Properties defaultParameters; + + public void setDataDirectory(GeoServerDataDirectory dataDir) { + this.dataDirectory = dataDir; + } + + public void setFile(String file) { + this.file = file; + } + + public void setDefaultParameters(Properties defaultParameters) { + this.defaultParameters = defaultParameters; + } + + @Override + public Connection getConnection() throws SQLException { + if(getDriverClassName() == null) { + synchronized(this) { + if (getDriverClassName() == null) { + initializeDataSource(); + } + } + } + return super.getConnection(); + } + + void initializeDataSource() { + try { + File dbprops = new File(dataDirectory.root(), file); + + Properties db = new Properties(); + if (!dbprops.exists()) { + if (dbprops.getParentFile().exists()) { + dbprops.getParentFile().mkdirs(); + } + + //use the default parameters and save them out + FileOutputStream fout = new FileOutputStream(dbprops); + try { + defaultParameters.store(fout, null); + } + finally { + fout.close(); + } + db.putAll(defaultParameters); + } + else { + FileInputStream in = new FileInputStream(dbprops); + db.load(in); + in.close(); + } + + //TODO: check for nulls + setDriverClassName(db.getProperty("driver")); + setUrl(getURL(db)); + + if (db.containsKey("username")) { + setUsername(db.getProperty("username")); + } + if (db.containsKey("password")) { + setPassword(db.getProperty("password")); + } + + //TODO: make other parameters configurable + setMinIdle(1); + setMaxActive(4); + } catch (Exception e) { + throw new RuntimeException("Unexpected error setting up the datas source", e); + } + } + + String getURL(Properties db) { + return db.getProperty("url").replace("%GEOSERVER_DATA_DIR%", dataDirectory.root().getAbsolutePath()); + } +} diff --git a/main/src/main/java/org/geoserver/security/SecureCatalogImpl.java b/main/src/main/java/org/geoserver/security/SecureCatalogImpl.java index 5491ad5..b10fb57 100644 --- a/main/src/main/java/org/geoserver/security/SecureCatalogImpl.java +++ b/main/src/main/java/org/geoserver/security/SecureCatalogImpl.java @@ -14,7 +14,9 @@ import org.acegisecurity.Authentication; import org.acegisecurity.InsufficientAuthenticationException; import org.acegisecurity.context.SecurityContextHolder; import org.geoserver.catalog.Catalog; +import org.geoserver.catalog.CatalogDAO; import org.geoserver.catalog.CatalogFactory; +import org.geoserver.catalog.CatalogInfo; import org.geoserver.catalog.CatalogVisitor; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; @@ -848,36 +850,66 @@ public class SecureCatalogImpl extends AbstractDecorator implements Cat delegate.add(unwrap(layerGroup)); } + public LayerGroupInfo detach(LayerGroupInfo layerGroup) { + return delegate.detach(layerGroup); + } - public void add(LayerInfo layer) { delegate.add(unwrap(layer)); } + public LayerInfo detach(LayerInfo layer) { + return delegate.detach(layer); + } + public void add(MapInfo map) { delegate.add(map); } + + public MapInfo detach(MapInfo map) { + return delegate.detach(map); + } public void add(NamespaceInfo namespace) { delegate.add(namespace); } + public NamespaceInfo detach(NamespaceInfo namespace) { + return delegate.detach(namespace); + } + public void add(ResourceInfo resource) { delegate.add(unwrap(resource)); } + public T detach(T resource) { + return delegate.detach(resource); + } + public void add(StoreInfo store) { delegate.add(unwrap(store)); } + + public T detach(T store) { + return delegate.detach(store); + } public void add(StyleInfo style) { delegate.add(style); } + public StyleInfo detach(StyleInfo style) { + return delegate.detach(style); + } + public void add(WorkspaceInfo workspace) { delegate.add(workspace); } + public WorkspaceInfo detach(WorkspaceInfo workspace) { + return delegate.detach(workspace); + } + public void addListener(CatalogListener listener) { delegate.addListener(listener); } @@ -886,6 +918,10 @@ public class SecureCatalogImpl extends AbstractDecorator implements Cat delegate.dispose(); } + public CatalogDAO getDAO() { + return delegate.getDAO(); + } + public CatalogFactory getFactory() { return delegate.getFactory(); } @@ -894,6 +930,23 @@ public class SecureCatalogImpl extends AbstractDecorator implements Cat return delegate.getListeners(); } + public void fireAdded(CatalogInfo object) { + delegate.fireAdded(object); + } + + public void fireModified(CatalogInfo object, List propertyNames, List oldValues, + List newValues) { + delegate.fireModified(object, propertyNames, oldValues, newValues); + } + + public void firePostModified(CatalogInfo object) { + delegate.firePostModified(object); + } + + public void fireRemoved(CatalogInfo object) { + delegate.fireRemoved(object); + } + // TODO: why is resource pool being exposed??? public ResourcePool getResourcePool() { return delegate.getResourcePool(); diff --git a/main/src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java b/main/src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java index fb982fd..4e7a5d9 100644 --- a/main/src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java +++ b/main/src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java @@ -6,9 +6,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import org.geoserver.catalog.Catalog; import junit.framework.TestCase; +import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogException; import org.geoserver.catalog.CatalogFactory; import org.geoserver.catalog.CoverageInfo; @@ -31,7 +31,7 @@ import org.geoserver.catalog.event.CatalogRemoveEvent; public class CatalogImplTest extends TestCase { - CatalogImpl catalog; + protected Catalog catalog; WorkspaceInfo ws; NamespaceInfo ns; DataStoreInfo ds; @@ -44,7 +44,8 @@ public class CatalogImplTest extends TestCase { StyleInfo s; protected void setUp() throws Exception { - catalog = new CatalogImpl(); + catalog = createCatalog(); + CatalogFactory factory = catalog.getFactory(); ns = factory.createNamespace(); @@ -99,6 +100,61 @@ public class CatalogImplTest extends TestCase { l.setDefaultStyle( s ); } + protected Catalog createCatalog() { + return new CatalogImpl(); + } + + protected void addWorkspace() { + catalog.add(ws); + } + + protected void addNamespace() { + catalog.add(ns); + } + + protected void addDataStore() { + addWorkspace(); + catalog.add(ds); + } + + protected void addCoverageStore() { + addWorkspace(); + catalog.add(cs); + } + + protected void addWMSStore() { + addWorkspace(); + catalog.add(wms); + } + + protected void addFeatureType() { + addDataStore(); + addNamespace(); + catalog.add(ft); + } + + protected void addCoverage() { + addCoverageStore(); + addNamespace(); + catalog.add(cv); + } + + protected void addWMSLayer() { + addWMSStore(); + addNamespace(); + catalog.add(wl); + } + + protected void addStyle() { + catalog.add(s); + } + + protected void addLayer() { + addFeatureType(); + addStyle(); + catalog.add(l); + } + public void testAddNamespace() { assertTrue( catalog.getNamespaces().isEmpty() ); catalog.add( ns ); @@ -222,7 +278,8 @@ public class CatalogImplTest extends TestCase { assertEquals( "nsURI", ns3.getURI() ); catalog.save( ns2 ); - ns3 = catalog.getNamespaceByPrefix(ns.getPrefix()); + //ns3 = catalog.getNamespaceByPrefix(ns.getPrefix()); + ns3 = catalog.getNamespaceByPrefix("ns2Prefix"); assertEquals(ns2, ns3); assertEquals( "ns2Prefix", ns3.getPrefix() ); assertEquals( "ns2URI", ns3.getURI() ); @@ -250,7 +307,8 @@ public class CatalogImplTest extends TestCase { catalog.save( ns ); assertEquals( 2, l.modified.size() ); - assertTrue(l.modified.get(1).getPropertyNames().contains( "uRI" )); + assertEquals( 1, l.modified.get(1).getPropertyNames().size()); + assertTrue(l.modified.get(1).getPropertyNames().get(0).equalsIgnoreCase("uri" )); assertTrue(l.modified.get(1).getOldValues().contains( "ns2URI" )); assertTrue(l.modified.get(1).getNewValues().contains( "changed" )); @@ -268,7 +326,7 @@ public class CatalogImplTest extends TestCase { WorkspaceInfo ws2 = catalog.getFactory().createWorkspace(); try { - catalog.getNamespaces().add( ws2 ); + catalog.getWorkspaces().add( ws2 ); fail( "adding directly should throw an exception" ); } catch( Exception e ) { @@ -307,7 +365,8 @@ public class CatalogImplTest extends TestCase { } public void testRemoveDefaultWorkspace() { - catalog.add( ws ); + catalog.add( ws ); + assertNotNull(catalog.getDefaultWorkspace()); catalog.remove( ws ); assertNull(catalog.getDefaultWorkspace()); } @@ -421,7 +480,18 @@ public class CatalogImplTest extends TestCase { public void testAddDataStore() { assertTrue( catalog.getDataStores().isEmpty() ); - catalog.add( ds ); + + ds.setWorkspace(null); + try { + catalog.add( ds ); + fail( "adding with no workspace should throw exception" ); + } + catch( Exception e ) {} + + ds.setWorkspace(ws); + catalog.add(ws); + catalog.add(ds); + assertEquals( 1, catalog.getDataStores().size() ); DataStoreInfo retrieved = catalog.getDataStore(ds.getId()); @@ -442,12 +512,6 @@ public class CatalogImplTest extends TestCase { catch( Exception e ) { } - try { - catalog.add( ds2 ); - fail( "adding with no workspace should throw exception" ); - } - catch( Exception e ) { - } ds2.setWorkspace( ws ); catalog.add( ds2 ); @@ -465,7 +529,7 @@ public class CatalogImplTest extends TestCase { } public void testRemoveDataStore() { - catalog.add( ds ); + addDataStore(); assertEquals( 1, catalog.getDataStores().size() ); try { @@ -480,7 +544,7 @@ public class CatalogImplTest extends TestCase { } public void testGetDataStoreById() { - catalog.add( ds ); + addDataStore(); DataStoreInfo ds2 = catalog.getDataStore(ds.getId()); assertNotNull(ds2); @@ -489,8 +553,7 @@ public class CatalogImplTest extends TestCase { } public void testGetDataStoreByName() { - catalog.add( ws ); - catalog.add( ds ); + addDataStore(); DataStoreInfo ds2 = catalog.getDataStoreByName(ds.getName()); assertNotNull(ds2); @@ -514,7 +577,7 @@ public class CatalogImplTest extends TestCase { } public void testModifyDataStore() { - catalog.add( ds ); + addDataStore(); DataStoreInfo ds2 = catalog.getDataStoreByName(ds.getName()); ds2.setName( "dsName2" ); @@ -525,14 +588,14 @@ public class CatalogImplTest extends TestCase { assertEquals( "dsDescription", ds3.getDescription() ); catalog.save( ds2 ); - ds3 = catalog.getDataStoreByName(ds.getName()); + ds3 = catalog.getDataStoreByName("dsName2"); assertEquals(ds2, ds3); assertEquals( "dsName2", ds3.getName() ); assertEquals( "dsDescription2", ds3.getDescription() ); } public void testChangeDataStoreWorkspace() throws Exception { - catalog.add( ds ); + addDataStore(); WorkspaceInfo ws2 = catalog.getFactory().createWorkspace(); ws2.setName( "newWorkspace"); @@ -549,6 +612,8 @@ public class CatalogImplTest extends TestCase { } public void testDataStoreEvents() { + addWorkspace(); + TestListener l = new TestListener(); catalog.addListener( l ); @@ -585,12 +650,9 @@ public class CatalogImplTest extends TestCase { } public void testAddFeatureType() { - //set a default namespace - catalog.add( ns ); - assertTrue( catalog.getFeatureTypes().isEmpty() ); - catalog.add( ft ); + addFeatureType(); assertEquals( 1, catalog.getFeatureTypes().size() ); FeatureTypeInfo ft2 = catalog.getFactory().createFeatureType(); @@ -601,6 +663,7 @@ public class CatalogImplTest extends TestCase { catch( Exception e ) {} ft2.setName("ft2Name"); + try { catalog.add(ft2); fail( "adding with no store should throw exception"); @@ -621,12 +684,10 @@ public class CatalogImplTest extends TestCase { public void testAddCoverage() { //set a default namespace - catalog.add( ns ); - assertNotNull(catalog.getCoverages()); assertTrue( catalog.getCoverages().isEmpty() ); - catalog.add( cv ); + addCoverage(); assertEquals( 1, catalog.getCoverages().size() ); CoverageInfo cv2 = catalog.getFactory().createCoverage(); @@ -663,16 +724,13 @@ public class CatalogImplTest extends TestCase { public void testAddWMSLayer() { //set a default namespace - catalog.add( ns ); - assertTrue( catalog.getResources(WMSLayerInfo.class).isEmpty() ); - - catalog.add( wl ); + addWMSLayer(); assertEquals( 1, catalog.getResources(WMSLayerInfo.class).size() ); } public void testRemoveFeatureType() { - catalog.add( ft ); + addFeatureType(); assertFalse( catalog.getFeatureTypes().isEmpty() ); try { @@ -686,7 +744,7 @@ public class CatalogImplTest extends TestCase { } public void testRemoveWMSLayer() { - catalog.add( wl ); + addWMSLayer(); assertFalse( catalog.getResources(WMSLayerInfo.class).isEmpty() ); catalog.remove( wl ); @@ -694,7 +752,7 @@ public class CatalogImplTest extends TestCase { } public void testGetFeatureTypeById() { - catalog.add( ft ); + addFeatureType(); FeatureTypeInfo ft2 = catalog.getFeatureType(ft.getId()); assertNotNull(ft2); @@ -703,7 +761,7 @@ public class CatalogImplTest extends TestCase { } public void testGetFeatureTypeByName() { - catalog.add( ft ); + addFeatureType(); FeatureTypeInfo ft2 = catalog.getFeatureTypeByName(ft.getName()); assertNotNull(ft2); @@ -741,7 +799,7 @@ public class CatalogImplTest extends TestCase { DataStoreInfo ds1 = catalog.getFactory().createDataStore(); ds1.setName( "ds1" ); - catalog.add( ds ); + catalog.add( ds1 ); FeatureTypeInfo ft1 = catalog.getFactory().createFeatureType(); ft1.setName( "ft1" ); @@ -775,7 +833,7 @@ public class CatalogImplTest extends TestCase { } public void testModifyFeatureType() { - catalog.add( ft ); + addFeatureType(); FeatureTypeInfo ft2 = catalog.getFeatureTypeByName(ft.getName()); ft2.setDescription( "ft2Description" ); @@ -796,7 +854,8 @@ public class CatalogImplTest extends TestCase { public void testFeatureTypeEvents() { //set default namespace - catalog.add( ns ); + addNamespace(); + addDataStore(); TestListener l = new TestListener(); catalog.addListener( l ); @@ -830,7 +889,8 @@ public class CatalogImplTest extends TestCase { public void testModifyMetadata() { //set default namespace - catalog.add( ns ); + addNamespace(); + addDataStore(); TestListener l = new TestListener(); catalog.addListener( l ); @@ -859,7 +919,7 @@ public class CatalogImplTest extends TestCase { public void testAddLayer() { assertTrue( catalog.getLayers().isEmpty() ); - catalog.add( l ); + addLayer(); assertEquals( 1, catalog.getLayers().size() ); @@ -891,7 +951,7 @@ public class CatalogImplTest extends TestCase { } public void testGetLayerById() { - catalog.add( l ); + addLayer(); LayerInfo l2 = catalog.getLayer( l.getId() ); assertNotNull(l2); @@ -900,7 +960,7 @@ public class CatalogImplTest extends TestCase { } public void testGetLayerByName() { - catalog.add( l ); + addLayer(); LayerInfo l2 = catalog.getLayerByName( l.getName() ); assertNotNull(l2); @@ -909,7 +969,7 @@ public class CatalogImplTest extends TestCase { } public void testGetLayerByResource() { - catalog.add(l); + addLayer(); List layers = catalog.getLayers(ft); assertEquals( 1, layers.size() ); @@ -920,7 +980,7 @@ public class CatalogImplTest extends TestCase { } public void testRemoveLayer() { - catalog.add(l); + addLayer(); assertEquals( 1, catalog.getLayers().size() ); catalog.remove(l); @@ -928,7 +988,7 @@ public class CatalogImplTest extends TestCase { } public void testModifyLayer() { - catalog.add(l); + addLayer(); LayerInfo l2 = catalog.getLayerByName( l.getName() ); // l2.setName( null ); @@ -960,7 +1020,7 @@ public class CatalogImplTest extends TestCase { } public void testEnableLayer() { - catalog.add(l); + addLayer(); LayerInfo l2 = catalog.getLayerByName(l.getName()); assertTrue(l2.isEnabled()); @@ -977,6 +1037,8 @@ public class CatalogImplTest extends TestCase { } public void testLayerEvents() { + addFeatureType(); + TestListener tl = new TestListener(); catalog.addListener( tl ); @@ -1004,7 +1066,8 @@ public class CatalogImplTest extends TestCase { public void testAddStyle() { assertTrue( catalog.getStyles().isEmpty() ); - catalog.add( s ); + + addStyle(); assertEquals( 1, catalog.getStyles().size() ); StyleInfo s2 = catalog.getFactory().createStyle(); @@ -1033,7 +1096,7 @@ public class CatalogImplTest extends TestCase { } public void testGetStyleById() { - catalog.add( s ); + addStyle(); StyleInfo s2 = catalog.getStyle( s.getId() ); assertNotNull( s2 ); @@ -1042,7 +1105,7 @@ public class CatalogImplTest extends TestCase { } public void testGetStyleByName() { - catalog.add( s ); + addStyle(); StyleInfo s2 = catalog.getStyleByName( s.getName() ); assertNotNull( s2 ); @@ -1051,7 +1114,7 @@ public class CatalogImplTest extends TestCase { } public void testModifyStyle() { - catalog.add(s); + addStyle(); StyleInfo s2 = catalog.getStyleByName( s.getName() ); s2.setName( null ); @@ -1084,7 +1147,7 @@ public class CatalogImplTest extends TestCase { } public void testRemoveStyle() { - catalog.add(s); + addStyle(); assertEquals( 1, catalog.getStyles().size()); catalog.remove(s); @@ -1189,7 +1252,7 @@ public class CatalogImplTest extends TestCase { public void testAddWMSStore() { assertTrue( catalog.getStores(WMSStoreInfo.class).isEmpty() ); - catalog.add( wms ); + addWMSStore(); assertEquals( 1, catalog.getStores(WMSStoreInfo.class).size() ); WMSStoreInfo retrieved = catalog.getStore(wms.getId(), WMSStoreInfo.class); diff --git a/main/src/test/java/org/geoserver/config/GeoServerImplTest.java b/main/src/test/java/org/geoserver/config/GeoServerImplTest.java index e8b7a50..be7cdbf 100644 --- a/main/src/test/java/org/geoserver/config/GeoServerImplTest.java +++ b/main/src/test/java/org/geoserver/config/GeoServerImplTest.java @@ -13,12 +13,16 @@ import org.geoserver.config.impl.ServiceInfoImpl; public class GeoServerImplTest extends TestCase { - GeoServerImpl geoServer; + protected GeoServerImpl geoServer; protected void setUp() throws Exception { super.setUp(); - geoServer = new GeoServerImpl(); + geoServer = createGeoServer(); + } + + protected GeoServerImpl createGeoServer() { + return new GeoServerImpl(); } public void testGlobal() throws Exception { @@ -85,6 +89,7 @@ public class GeoServerImplTest extends TestCase { assertEquals( "bar", s2.getTitle() ); geoServer.save( s1 ); + s2 = geoServer.getServiceByName( "foo", ServiceInfo.class ); assertEquals( "changed", s2.getTitle() ); } diff --git a/main/src/test/java/org/geoserver/test/GeoServerAbstractTestSupport.java b/main/src/test/java/org/geoserver/test/GeoServerAbstractTestSupport.java index faae45a..9168f23 100644 --- a/main/src/test/java/org/geoserver/test/GeoServerAbstractTestSupport.java +++ b/main/src/test/java/org/geoserver/test/GeoServerAbstractTestSupport.java @@ -52,6 +52,7 @@ import org.geoserver.catalog.NamespaceInfo; import org.geoserver.config.GeoServer; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.config.GeoServerLoader; +import org.geoserver.config.GeoServerLoaderProxy; import org.geoserver.data.test.TestData; import org.geoserver.logging.LoggingUtils; import org.geoserver.ows.util.KvpUtils; @@ -280,7 +281,7 @@ public abstract class GeoServerAbstractTestSupport extends OneTimeSetupTest { *

*/ protected void reloadCatalogAndConfiguration() throws Exception { - GeoServerLoader loader = GeoServerExtensions.bean( GeoServerLoader.class , applicationContext ); + GeoServerLoaderProxy loader = GeoServerExtensions.bean( GeoServerLoaderProxy.class , applicationContext ); loader.reload(); } diff --git a/main/src/test/java/org/geoserver/test/GeoServerTestApplicationContext.java b/main/src/test/java/org/geoserver/test/GeoServerTestApplicationContext.java index e8aae8a..cc19bd1 100644 --- a/main/src/test/java/org/geoserver/test/GeoServerTestApplicationContext.java +++ b/main/src/test/java/org/geoserver/test/GeoServerTestApplicationContext.java @@ -64,7 +64,7 @@ public class GeoServerTestApplicationContext extends ClassPathXmlApplicationCont super.loadBeanDefinitions(reader); BeanDefinition def = reader.getBeanFactory().getBeanDefinition("geoServerLoader"); - def.setBeanClassName( "org.geoserver.config.LegacyGeoServerLoader"); + def.setBeanClassName( "org.geoserver.test.TestGeoServerLoaderProxy"); try { def = reader.getBeanFactory().getBeanDefinition("wcsLoader"); diff --git a/main/src/test/java/org/geoserver/test/TestGeoServerLoaderProxy.java b/main/src/test/java/org/geoserver/test/TestGeoServerLoaderProxy.java new file mode 100644 index 0000000..edf268d --- /dev/null +++ b/main/src/test/java/org/geoserver/test/TestGeoServerLoaderProxy.java @@ -0,0 +1,20 @@ +package org.geoserver.test; + +import org.geoserver.config.GeoServerLoader; +import org.geoserver.config.GeoServerLoaderProxy; +import org.geoserver.config.LegacyGeoServerLoader; +import org.geoserver.platform.GeoServerResourceLoader; +import org.springframework.context.ApplicationContext; + +public class TestGeoServerLoaderProxy extends GeoServerLoaderProxy { + + public TestGeoServerLoaderProxy(GeoServerResourceLoader resourceLoader) { + super(resourceLoader); + } + + @Override + protected GeoServerLoader lookupGeoServerLoader(ApplicationContext appContext) { + return new LegacyGeoServerLoader(resourceLoader); + } + +} diff --git a/ows/src/main/java/org/geoserver/ows/util/OwsUtils.java b/ows/src/main/java/org/geoserver/ows/util/OwsUtils.java index a757cf9..913c618 100644 --- a/ows/src/main/java/org/geoserver/ows/util/OwsUtils.java +++ b/ows/src/main/java/org/geoserver/ows/util/OwsUtils.java @@ -5,6 +5,7 @@ package org.geoserver.ows.util; import java.lang.reflect.Method; +import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -90,6 +91,18 @@ public class OwsUtils { } /** + * Reflectively determines if an object has a specified property. + + * @param object The target object. + * @param property The property to lookup. + * + * @return True if the property exists, otherwise false. + */ + public static boolean has(Object object, String property) { + return getter(object.getClass(), property, null) != null; + } + + /** * Reflectively gets a property from an object. *

* This method uses {@link #getter(Class, String, Class)} to locate the getter @@ -227,4 +240,72 @@ public class OwsUtils { ex = cause; } while(true); } + + /** + * Copies properties from one object to another. + * + * @param source The source object. + * @param target The target object. + * @param clazz The class of source and target. + */ + public static void copy(T source, T target, Class clazz) { + ClassProperties properties = getClassProperties(clazz); + for (String p : properties.properties()) { + Method getter = properties.getter(p, null); + if (getter == null) { + continue; // should not really happen + } + + Class type = getter.getReturnType(); + Method setter = properties.setter(p, type); + + // do a check for read only before calling the getter to avoid an uneccesary call + if (setter == null + && !(Collection.class.isAssignableFrom(type) || Map.class + .isAssignableFrom(type))) { + // read only + continue; + } + + try { + Object newValue = getter.invoke(source, null); + if (newValue == null) { + continue; + // TODO: make this a flag whether to overwrite with null values + } + if (setter == null) { + if (Collection.class.isAssignableFrom(type)) { + updateCollectionProperty(target, (Collection) newValue, getter); + } else if (Map.class.isAssignableFrom(type)) { + updateMapProperty(target, (Map) newValue, getter); + } + continue; + } + + setter.invoke(target, newValue); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * Helper method for updating a collection based property. + */ + static void updateCollectionProperty(Object object, Collection newValue, Method getter) + throws Exception { + Collection oldValue = (Collection) getter.invoke(object, null); + oldValue.clear(); + oldValue.addAll(newValue); + } + + /** + * Helper method for updating a map based property. + */ + static void updateMapProperty(Object object, Map newValue, Method getter) throws Exception { + Map oldValue = (Map) getter.invoke(object, null); + oldValue.clear(); + oldValue.putAll(newValue); + } + } diff --git a/platform/src/main/java/org/geoserver/platform/GeoServerExtensions.java b/platform/src/main/java/org/geoserver/platform/GeoServerExtensions.java index ba5c2d7..590bc56 100644 --- a/platform/src/main/java/org/geoserver/platform/GeoServerExtensions.java +++ b/platform/src/main/java/org/geoserver/platform/GeoServerExtensions.java @@ -230,6 +230,14 @@ public class GeoServerExtensions implements ApplicationContextAware, Application * @return */ public static final Object bean(String name) { + return bean(name, context); + } + + /** + * Returns a specific bean given its name with a specified application context. + * + */ + public static final Object bean(String name, ApplicationContext context) { checkContext(context); return context != null ? context.getBean(name) : null; } diff --git a/web/core/src/main/java/org/geoserver/web/data/resource/LayerModel.java b/web/core/src/main/java/org/geoserver/web/data/resource/LayerModel.java index b4ac465..cd8081c 100644 --- a/web/core/src/main/java/org/geoserver/web/data/resource/LayerModel.java +++ b/web/core/src/main/java/org/geoserver/web/data/resource/LayerModel.java @@ -16,7 +16,7 @@ public class LayerModel implements IModel { LayerInfo layerInfo; public LayerModel(LayerInfo layerInfo) { - this.layerInfo = layerInfo; + setObject(layerInfo); } public Object getObject() { @@ -26,7 +26,9 @@ public class LayerModel implements IModel { } public void setObject(Object object) { - this.layerInfo = (LayerInfo) object; + //workaround for dbconfig, by "dettaching" we force hibernate to reload the object + // fully initialized with no lazy lists or proxies + this.layerInfo = GeoServerApplication.get().getCatalog().detach((LayerInfo) object); } public void detach() { diff --git a/wfs/src/main/java/org/geoserver/wfs/GMLInfo.java b/wfs/src/main/java/org/geoserver/wfs/GMLInfo.java index 921b652..4ab00af 100644 --- a/wfs/src/main/java/org/geoserver/wfs/GMLInfo.java +++ b/wfs/src/main/java/org/geoserver/wfs/GMLInfo.java @@ -1,12 +1,14 @@ package org.geoserver.wfs; +import java.io.Serializable; + /** * Configuration for gml encoding. * * @author Justin Deoliveira, The Open Planning Project * */ -public interface GMLInfo { +public interface GMLInfo extends Serializable { /** * Enumeration for srsName style.