/* Copyright (c) 2001 - 2008 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.config; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogException; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WMSLayerInfo; import org.geoserver.catalog.WMSStoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.event.CatalogAddEvent; import org.geoserver.catalog.event.CatalogListener; import org.geoserver.catalog.event.CatalogModifyEvent; import org.geoserver.catalog.event.CatalogPostModifyEvent; import org.geoserver.catalog.event.CatalogRemoveEvent; import org.geoserver.config.util.XStreamPersister; import org.geoserver.platform.GeoServerResourceLoader; import org.geotools.util.logging.Logging; public class GeoServerPersister implements CatalogListener, ConfigurationListener { /** * logging instance */ static Logger LOGGER = Logging.getLogger( "org.geoserver.config"); GeoServerResourceLoader rl; XStreamPersister xp; public GeoServerPersister(GeoServerResourceLoader rl, XStreamPersister xp) { this.rl = rl; this.xp = xp; } public void handleAddEvent(CatalogAddEvent event) { Object source = event.getSource(); try { if ( source instanceof WorkspaceInfo ) { addWorkspace( (WorkspaceInfo) source ); } else if ( source instanceof NamespaceInfo ) { addNamespace( (NamespaceInfo) source ); } else if ( source instanceof DataStoreInfo ) { addDataStore( (DataStoreInfo) source ); } else if ( source instanceof WMSStoreInfo ) { addWMSStore( (WMSStoreInfo) source ); } else if ( source instanceof FeatureTypeInfo ) { addFeatureType( (FeatureTypeInfo) source ); } else if ( source instanceof CoverageStoreInfo ) { addCoverageStore( (CoverageStoreInfo) source ); } else if ( source instanceof CoverageInfo ) { addCoverage( (CoverageInfo) source ); } else if ( source instanceof WMSLayerInfo ) { addWMSLayer( (WMSLayerInfo) source ); } else if ( source instanceof LayerInfo ) { addLayer( (LayerInfo) source ); } else if ( source instanceof StyleInfo ) { addStyle( (StyleInfo) source ); } else if ( source instanceof LayerGroupInfo ) { addLayerGroup( (LayerGroupInfo) source ); } } catch( IOException e ) { throw new RuntimeException( e ); } } public void handleModifyEvent(CatalogModifyEvent event) { Object source = event.getSource(); try { //here we handle name changes int i = event.getPropertyNames().indexOf( "name" ); if ( i > -1 ) { String newName = (String) event.getNewValues().get( i ); if ( source instanceof WorkspaceInfo ) { renameWorkspace( (WorkspaceInfo) source, newName ); } else if ( source instanceof StoreInfo ) { renameStore( (StoreInfo) source, newName ); } else if ( source instanceof ResourceInfo ) { renameResource( (ResourceInfo) source, newName ); } else if ( source instanceof StyleInfo ) { renameStyle( (StyleInfo) source, newName ); } else if ( source instanceof LayerGroupInfo ) { renameLayerGroup( (LayerGroupInfo) source, newName ); } } //handle the case of a store changing workspace if ( source instanceof StoreInfo ) { i = event.getPropertyNames().indexOf( "workspace"); if ( i > -1 ) { WorkspaceInfo newWorkspace = (WorkspaceInfo) event.getNewValues().get( i ); File oldDir = dir( (StoreInfo) source ); oldDir.renameTo( new File( dir( newWorkspace ), oldDir.getName() ) ); } } //handle the case of a feature type changing store if ( source instanceof FeatureTypeInfo ) { i = event.getPropertyNames().indexOf( "store"); if ( i > -1 ) { StoreInfo newStore = (StoreInfo) event.getNewValues().get( i ); File oldDir = dir( (FeatureTypeInfo) source ); oldDir.renameTo( new File( dir( newStore ), oldDir.getName() ) ); } } //handle default workspace if ( source instanceof Catalog ) { i = event.getPropertyNames().indexOf("defaultWorkspace"); if ( i > -1 ) { WorkspaceInfo defWorkspace = (WorkspaceInfo) event.getNewValues().get( i ); // SG don't bother with a default workspace if we do not have one if (defWorkspace != null) { File d = rl.createDirectory("workspaces"); persist(defWorkspace, new File(d, "default.xml")); } } } } catch (IOException e) { throw new RuntimeException( e ); } } public void handlePostModifyEvent(CatalogPostModifyEvent event) { Object source = event.getSource(); try { if ( source instanceof WorkspaceInfo ) { modifyWorkspace( (WorkspaceInfo) source); } else if ( source instanceof DataStoreInfo ) { modifyDataStore( (DataStoreInfo) source ); } else if ( source instanceof WMSStoreInfo ) { modifyWMSStore( (WMSStoreInfo) source ); } else if ( source instanceof NamespaceInfo ) { modifyNamespace( (NamespaceInfo) source ); } else if ( source instanceof FeatureTypeInfo ) { modifyFeatureType( (FeatureTypeInfo) source ); } else if ( source instanceof CoverageStoreInfo ) { modifyCoverageStore( (CoverageStoreInfo) source ); } else if ( source instanceof CoverageInfo ) { modifyCoverage( (CoverageInfo) source ); } else if ( source instanceof WMSLayerInfo ) { modifyWMSLayer( (WMSLayerInfo) source ); } else if ( source instanceof LayerInfo ) { modifyLayer( (LayerInfo) source ); } else if ( source instanceof StyleInfo ) { modifyStyle( (StyleInfo) source ); } else if ( source instanceof LayerGroupInfo ) { modifyLayerGroup( (LayerGroupInfo) source ); } } catch( IOException e ) { throw new RuntimeException( e ); } } public void handleRemoveEvent(CatalogRemoveEvent event) { Object source = event.getSource(); try { if ( source instanceof WorkspaceInfo ) { removeWorkspace( (WorkspaceInfo) source ); } else if ( source instanceof NamespaceInfo ) { removeNamespace( (NamespaceInfo) source ); } else if ( source instanceof DataStoreInfo ) { removeDataStore( (DataStoreInfo) source ); } else if ( source instanceof FeatureTypeInfo ) { removeFeatureType( (FeatureTypeInfo) source ); } else if ( source instanceof CoverageStoreInfo ) { removeCoverageStore( (CoverageStoreInfo) source ); } else if ( source instanceof CoverageInfo ) { removeCoverage( (CoverageInfo) source ); } else if ( source instanceof WMSStoreInfo ) { removeWMSStore( (WMSStoreInfo) source ); } else if ( source instanceof WMSLayerInfo ) { removeWMSLayer( (WMSLayerInfo) source ); } else if ( source instanceof LayerInfo ) { removeLayer( (LayerInfo) source ); } else if ( source instanceof StyleInfo ) { removeStyle( (StyleInfo) source ); } else if ( source instanceof LayerGroupInfo ) { removeLayerGroup( (LayerGroupInfo) source ); } } catch( IOException e ) { throw new RuntimeException( e ); } } public void handleGlobalChange(GeoServerInfo global, List<String> propertyNames, List<Object> oldValues, List<Object> newValues) { } public void handlePostGlobalChange(GeoServerInfo global) { try { persist( global, new File( rl.getBaseDirectory(), "global.xml") ); } catch (IOException e) { throw new RuntimeException( e ); } } public void handleLoggingChange(LoggingInfo logging, List<String> propertyNames, List<Object> oldValues, List<Object> newValues) { } public void handlePostLoggingChange(LoggingInfo logging) { try { persist( logging, new File( rl.getBaseDirectory(), "logging.xml") ); } catch (IOException e) { throw new RuntimeException( e ); } } public void handleServiceAdded(ServiceInfo service) { } public void handleServiceChange(ServiceInfo service, List<String> propertyNames, List<Object> oldValues, List<Object> newValues) { } public void handlePostServiceChange(ServiceInfo service) { } public void handleServiceRemove(ServiceInfo service) { } public void reloaded() { } //workspaces void addWorkspace( WorkspaceInfo ws ) throws IOException { LOGGER.fine( "Persisting workspace " + ws.getName() ); File dir = dir( ws, true ); dir.mkdirs(); persist( ws, file( ws ) ); } void renameWorkspace( WorkspaceInfo ws, String newName ) throws IOException { LOGGER.fine( "Renaming workspace " + ws.getName() + "to " + newName ); rename( dir( ws ), newName ); } void modifyWorkspace( WorkspaceInfo ws ) throws IOException { LOGGER.fine( "Persisting workspace " + ws.getName() ); persist( ws, file( ws ) ); } void removeWorkspace( WorkspaceInfo ws ) throws IOException { LOGGER.fine( "Removing workspace " + ws.getName() ); File dir = dir( ws ); FileUtils.deleteDirectory( dir ); } File dir( WorkspaceInfo ws ) throws IOException { return dir( ws, false ); } File dir( WorkspaceInfo ws, boolean create ) throws IOException { File d = rl.find( "workspaces", ws.getName() ); if ( d == null && create ) { d = rl.createDirectory( "workspaces", ws.getName() ); } return d; } File file( WorkspaceInfo ws ) throws IOException { return new File( dir( ws ), "workspace.xml" ); } //namespaces void addNamespace( NamespaceInfo ns ) throws IOException { LOGGER.fine( "Persisting namespace " + ns.getPrefix() ); File dir = dir( ns, true ); dir.mkdirs(); persist( ns, file(ns) ); } void modifyNamespace( NamespaceInfo ns) throws IOException { LOGGER.fine( "Persisting namespace " + ns.getPrefix() ); persist( ns, file(ns) ); } void removeNamespace( NamespaceInfo ns ) throws IOException { LOGGER.fine( "Removing namespace " + ns.getPrefix() ); file( ns ).delete(); } File dir( NamespaceInfo ns ) throws IOException { return dir( ns, false ); } File dir( NamespaceInfo ns, boolean create ) throws IOException { File d = rl.find( "workspaces", ns.getPrefix() ); if ( d == null && create ) { d = rl.createDirectory( "workspaces", ns.getPrefix() ); } return d; } File file( NamespaceInfo ns ) throws IOException { return new File( dir( ns ), "namespace.xml"); } //datastores void addDataStore( DataStoreInfo ds ) throws IOException { LOGGER.fine( "Persisting datastore " + ds.getName() ); File dir = dir( ds ); dir.mkdir(); persist( ds, file( ds ) ); } void renameStore( StoreInfo s, String newName ) throws IOException { LOGGER.fine( "Renaming store " + s.getName() + "to " + newName ); rename( dir( s ), newName ); } void modifyDataStore( DataStoreInfo ds ) throws IOException { LOGGER.fine( "Persisting datastore " + ds.getName() ); persist( ds, file( ds ) ); } void removeDataStore( DataStoreInfo ds ) throws IOException { LOGGER.fine( "Removing datastore " + ds.getName() ); File dir = dir( ds ); FileUtils.deleteDirectory( dir ); } File dir( StoreInfo s ) throws IOException { return new File( dir( s.getWorkspace() ), s.getName() ); } File file( DataStoreInfo ds ) throws IOException { return new File( dir( ds ), "datastore.xml" ); } //feature types void addFeatureType( FeatureTypeInfo ft ) throws IOException { LOGGER.fine( "Persisting feature type " + ft.getName() ); File dir = dir( ft ); dir.mkdir(); persist( ft, file( ft ) ); } void renameResource( ResourceInfo r, String newName ) throws IOException { LOGGER.fine( "Renaming resource " + r.getName() + " to " + newName ); rename( dir( r ), newName ); } void modifyFeatureType( FeatureTypeInfo ft ) throws IOException { LOGGER.fine( "Persisting feature type " + ft.getName() ); persist( ft, file( ft ) ); } void removeFeatureType( FeatureTypeInfo ft ) throws IOException { LOGGER.fine( "Removing feature type " + ft.getName() ); File dir = dir( ft ); FileUtils.deleteDirectory( dir ); } File dir( ResourceInfo r ) throws IOException { return new File( dir( r.getStore() ), r.getName() ); } File file( FeatureTypeInfo ft ) throws IOException { return new File( dir( ft ), "featuretype.xml"); } //coverage stores void addCoverageStore( CoverageStoreInfo cs ) throws IOException { LOGGER.fine( "Persisting coverage store " + cs.getName() ); File dir = dir( cs ); dir.mkdir(); persist( cs, file( cs ) ); } void modifyCoverageStore( CoverageStoreInfo cs ) throws IOException { LOGGER.fine( "Persisting coverage store " + cs.getName() ); persist( cs, file( cs ) ); } void removeCoverageStore( CoverageStoreInfo cs ) throws IOException { LOGGER.fine( "Removing coverage store " + cs.getName() ); File dir = dir( cs ); FileUtils.deleteDirectory( dir ); } File file( CoverageStoreInfo cs ) throws IOException { return new File( dir( cs ), "coveragestore.xml"); } //coverages void addCoverage( CoverageInfo c ) throws IOException { LOGGER.fine( "Persisting coverage " + c.getName() ); File dir = dir( c ); dir.mkdir(); persist( c, dir, "coverage.xml" ); } void modifyCoverage( CoverageInfo c ) throws IOException { LOGGER.fine( "Persisting coverage " + c.getName() ); File dir = dir( c ); persist( c, dir, "coverage.xml"); } void removeCoverage( CoverageInfo c ) throws IOException { LOGGER.fine( "Removing coverage " + c.getName() ); File dir = dir( c ); FileUtils.deleteDirectory( dir ); } //wms stores void addWMSStore( WMSStoreInfo wms ) throws IOException { LOGGER.fine( "Persisting wms store " + wms.getName() ); File dir = dir( wms ); dir.mkdir(); persist( wms, file( wms ) ); } void modifyWMSStore( WMSStoreInfo ds ) throws IOException { LOGGER.fine( "Persisting wms store " + ds.getName() ); persist( ds, file( ds ) ); } void removeWMSStore( WMSStoreInfo ds ) throws IOException { LOGGER.fine( "Removing datastore " + ds.getName() ); File dir = dir( ds ); FileUtils.deleteDirectory( dir ); } File file( WMSStoreInfo ds ) throws IOException { return new File( dir( ds ), "wmsstore.xml" ); } //wms layers void addWMSLayer( WMSLayerInfo wms ) throws IOException { LOGGER.fine( "Persisting wms layer " + wms.getName() ); File dir = dir( wms ); dir.mkdir(); persist( wms, dir, "wmslayer.xml" ); } void modifyWMSLayer( WMSLayerInfo wms ) throws IOException { LOGGER.fine( "Persisting wms layer" + wms.getName() ); File dir = dir( wms ); persist( wms, dir, "wmslayer.xml"); } void removeWMSLayer( WMSLayerInfo c ) throws IOException { LOGGER.fine( "Removing wms layer " + c.getName() ); File dir = dir( c ); FileUtils.deleteDirectory( dir ); } //layers void addLayer( LayerInfo l ) throws IOException { LOGGER.fine( "Persisting layer " + l.getName() ); File dir = dir( l ); dir.mkdir(); persist( l, file( l ) ); } void modifyLayer( LayerInfo l ) throws IOException { LOGGER.fine( "Persisting layer " + l.getName() ); persist( l, file( l ) ); } void removeLayer( LayerInfo l ) throws IOException { LOGGER.fine( "Removing layer " + l.getName() ); File dir = dir( l ); FileUtils.deleteDirectory( dir ); } File dir( LayerInfo l ) throws IOException { if ( l.getResource() instanceof FeatureTypeInfo) { return dir( (FeatureTypeInfo) l.getResource() ); } else if ( l.getResource() instanceof CoverageInfo ) { return dir( (CoverageInfo) l.getResource() ); } else if ( l.getResource() instanceof WMSLayerInfo ) { return dir( (WMSLayerInfo) l.getResource() ); } return null; } File file( LayerInfo l ) throws IOException { return new File( dir( l ), "layer.xml" ); } //styles void addStyle( StyleInfo s ) throws IOException { LOGGER.fine( "Persisting style " + s.getName() ); dir( s, true ); persist( s, file( s ) ); } void renameStyle( StyleInfo s, String newName ) throws IOException { LOGGER.fine( "Renameing style " + s.getName() + " to " + newName ); rename( file( s ), newName+".xml" ); } void modifyStyle( StyleInfo s ) throws IOException { LOGGER.fine( "Persisting style " + s.getName() ); persist( s, file( s ) ); /* //save out sld File f = file(s); BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( f ) ); SLDTransformer tx = new SLDTransformer(); try { tx.transform( s.getSLD(),out ); out.flush(); } catch (TransformerException e) { throw (IOException) new IOException().initCause( e ); } finally { out.close(); } */ } void removeStyle( StyleInfo s ) throws IOException { LOGGER.fine( "Removing style " + s.getName() ); file( s ).delete(); } File dir( StyleInfo s ) throws IOException { return dir( s, false ); } File dir( StyleInfo s, boolean create ) throws IOException { File d = rl.find( "styles" ); if ( d == null && create ) { d = rl.createDirectory( "styles" ); } return d; } File file( StyleInfo s ) throws IOException { //special case for styles, if the file name (minus the suffix) matches the id of the style // and the suffix is xml (rather than sld) we need to avoid overwritting the actual // style file if (s.getFilename() != null && s.getFilename().endsWith(".xml") && s.getFilename().startsWith(s.getName()+".")) { //append a second .xml suffix return new File( dir( s ), s.getName() + ".xml.xml"); } else { return new File( dir( s ), s.getName() + ".xml"); } } //layer groups void addLayerGroup( LayerGroupInfo lg ) throws IOException { LOGGER.fine( "Persisting layer group " + lg.getName() ); dir( lg, true ); persist( lg, file( lg ) ); } void renameLayerGroup( LayerGroupInfo lg, String newName ) throws IOException { LOGGER.fine( "Renaming layer group " + lg.getName() + " to " + newName ); rename( file( lg ), newName+".xml" ); } void modifyLayerGroup( LayerGroupInfo lg ) throws IOException { LOGGER.fine( "Persisting layer group " + lg.getName() ); persist( lg, file( lg ) ); } void removeLayerGroup( LayerGroupInfo lg ) throws IOException { LOGGER.fine( "Removing layer group " + lg.getName() ); file( lg ).delete(); } File dir( LayerGroupInfo lg ) throws IOException { return dir( lg, false ); } File dir( LayerGroupInfo lg, boolean create ) throws IOException { File d = rl.find( "layergroups" ); if ( d == null && create ) { d = rl.createDirectory( "layergroups"); } return d; } File file( LayerGroupInfo lg ) throws IOException { return new File( dir( lg ), lg.getName() + ".xml" ); } //helpers void backupDirectory(File dir) throws IOException { File bak = new File( dir.getCanonicalPath() + ".bak"); if ( bak.exists() ) { FileUtils.deleteDirectory( bak ); } dir.renameTo( bak ); } void rename(File f, String newName) throws IOException { rename( f, new File( f.getParentFile(), newName ) ); } void rename( File source, File dest ) throws IOException { // same path? Do nothing if (source.getCanonicalPath().equalsIgnoreCase(dest.getCanonicalPath())) return; // different path boolean win = System.getProperty("os.name").startsWith("Windows"); if ( win && dest.exists() ) { //windows does not do atomic renames, and can not rename a file if the dest file // exists if (!dest.delete()) { throw new IOException("Could not delete: " + dest.getCanonicalPath()); } source.renameTo(dest); } else { source.renameTo(dest); } } void persist( Object o, File dir, String filename ) throws IOException { persist( o, new File( dir, filename ) ); } void persist( Object o, File f ) throws IOException { try { synchronized ( xp ) { //first save to a temp file File temp = new File(f.getParentFile(),f.getName()+".tmp"); if ( temp.exists() ) { temp.delete(); } BufferedOutputStream out = null; try{ out=new BufferedOutputStream( new FileOutputStream( temp ) ); xp.save( o, out ); out.flush(); } finally { if (out != null) org.apache.commons.io.IOUtils.closeQuietly(out); } //no errors, overwrite the original file rename(temp,f); } LOGGER.fine("Persisted " + o.getClass().getName() + " to " + f.getAbsolutePath() ); } catch( Exception e ) { //catch any exceptions and send them back as CatalogExeptions String msg = "Error persisting " + o + " to " + f.getCanonicalPath(); throw new CatalogException(msg, e); } } }