org.geoserver.data.test.SystemTestData.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.data.test.SystemTestData.java

Source

/* Copyright (c) 2001 - 2013 OpenPlans - 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.data.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.namespace.QName;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Level;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.Keyword;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.ProjectionPolicy;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.CatalogImpl;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.config.GeoServerPersister;
import org.geoserver.config.LoggingInfo;
import org.geoserver.config.ServiceInfo;
import org.geoserver.config.ServiceLoader;
import org.geoserver.config.SettingsInfo;
import org.geoserver.config.impl.GeoServerImpl;
import org.geoserver.config.impl.ServiceInfoImpl;
import org.geoserver.config.util.XStreamPersister;
import org.geoserver.config.util.XStreamPersisterFactory;
import org.geoserver.config.util.XStreamServiceLoader;
import org.geoserver.data.util.IOUtils;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.data.DataUtilities;
import org.geotools.data.property.PropertyDataStore;
import org.geotools.data.property.PropertyDataStoreFactory;
import org.geotools.feature.NameImpl;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.util.logging.Logging;
import org.springframework.context.ApplicationContext;

/**
 * Test setup uses for GeoServer system tests.
 * <p>
 * This is the default test setup used by {@link GeoServerSystemTestSupport}. During setup this 
 * class creates a full GeoServer data directory configuration on disk. 
 * </p>
 * <p>
 * Customizing the setup can be done in two ways. Customizations that occur pre system startup and
 * those that happen after. Methods that may be called pre system start with the prefix "setUp". 
 * Methods that may be called after system startup are prefixed with "add".  
 * </p>
 *
 * @author Justin Deoliveira, OpenGeo
 *
 */
public class SystemTestData extends CiteTestData {

    /** Multiband tiff */
    private static final QName MULTIBAND = new QName(WCS_URI, "multiband", WCS_PREFIX);

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

    /**
     * Keys for overriding default layer properties
     */
    public static class LayerProperty<T> {

        T get(Map<LayerProperty, Object> map, T def) {
            return map != null && map.containsKey(this) ? (T) map.get(this) : def;
        }

        public static LayerProperty<String> NAME = new LayerProperty<String>();
        public static LayerProperty<ProjectionPolicy> PROJECTION_POLICY = new LayerProperty<ProjectionPolicy>();
        public static LayerProperty<String> STYLE = new LayerProperty<String>();
        public static LayerProperty<ReferencedEnvelope> ENVELOPE = new LayerProperty<ReferencedEnvelope>();
        public static LayerProperty<ReferencedEnvelope> LATLON_ENVELOPE = new LayerProperty<ReferencedEnvelope>();
        public static LayerProperty<Integer> SRS = new LayerProperty<Integer>();
    }

    /** data directory root */
    protected File data;

    /** internal catalog, used for setup before the real catalog available */
    Catalog catalog;

    public SystemTestData() throws IOException {
        // setup the root
        data = IOUtils.createRandomDirectory("./target", "default", "data");
        data.delete();
        data.mkdir();
    }

    public SystemTestData(File data) {
        this.data = data;
    }

    @Override
    public void setUp() throws Exception {
        createCatalog();
        createConfig();
    }

    public void setUpDefault() throws Exception {
        setUpDefaultLayers();
        setUpSecurity();
    }

    /**
     * Sets up the default set of layers, which is all the vector layers whose names are included
     * in the {@link CiteTestData#TYPENAMES} array.
     */
    public void setUpDefaultLayers() throws IOException {
        for (QName layerName : TYPENAMES) {
            addVectorLayer(layerName, catalog);
        }
    }

    /**
     * Sets up the default set of raster layers. 
     * <p>
     * Layer names included in this set include:
     * <ul>
     *  <li>{@link CiteTestData#TASMANIA_BM}
     *  <li>{@link CiteTestData#TASMANIA_DEM}
     *  <li>{@link CiteTestData#ROTATED_CAD}
     *  <li>{@link CiteTestData#WORLD}
     * </ul>
     * </p>
     */
    public void setUpDefaultRasterLayers() throws IOException {
        addWorkspace(WCS_PREFIX, WCS_URI, catalog);
        addDefaultRasterLayer(TASMANIA_DEM, catalog);
        addDefaultRasterLayer(TASMANIA_BM, catalog);
        addDefaultRasterLayer(ROTATED_CAD, catalog);
        addDefaultRasterLayer(WORLD, catalog);
        addDefaultRasterLayer(MULTIBAND, catalog);
    }

    public void setUpWcs10RasterLayers() throws IOException {
        addRasterLayer(USA_WORLDIMG, "usa.zip", PNG, catalog);
    }

    /**
     * Sets up the WCS 11 raster layers.
     * <p>
     * This method is a synonym for {@link #setUpDefaultLayers()}
     * </p>
     */
    public void setUpWcs11RasterLayers() throws IOException {
        setUpDefaultRasterLayers();
    }

    /**
     * Adds a vector layer to the setup with no custom properties.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addVectorLayer(QName, Catalog)}
     *  
     */
    public void setUpVectorLayer(QName layerName) throws IOException {
        addVectorLayer(layerName, catalog);
    }

    /**
     * Adds a vector layer to the setup.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addVectorLayer(QName, Map, Catalog)}
     *  
     */
    public void setUpVectorLayer(QName qName, Map<LayerProperty, Object> props) throws IOException {
        addVectorLayer(qName, props, catalog);
    }

    /**
     * Adds a vector layer to the setup.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addVectorLayer(QName, Map, Class, Catalog)}
     *  
     */
    public void setUpVectorLayer(QName qName, Map<LayerProperty, Object> props, Class scope) throws IOException {
        addVectorLayer(qName, props, scope, catalog);
    }

    /**
     * Adds a vector layer to the setup.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addVectorLayer(QName, Map, String, Class, Catalog)}
     *  
     */
    public void setUpVectorLayer(QName qName, Map<LayerProperty, Object> props, String filename, Class scope)
            throws IOException {
        addVectorLayer(qName, props, filename, scope, catalog);
    }

    /**
     * Adds a raster layer to the setup with no custom properties.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addRasterLayer(QName, String, String, Catalog)}
     *  
     */
    public void setUpRasterLayer(QName qName, String filename, String extension) throws IOException {
        addRasterLayer(qName, filename, extension, catalog);
    }

    /**
     * Adds a raster layer to the setup.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addRasterLayer(QName, String, String, Map, Catalog)}
     */
    public void setUpRasterLayer(QName qName, String filename, String extension, Map<LayerProperty, Object> props)
            throws IOException {
        addRasterLayer(qName, filename, extension, props, catalog);
    }

    /**
     * Adds a raster layer to the setup.
     * <p>
     * This method should be called during the pre system setup phase, for example from 
     * {@link GeoServerSystemTestSupport#setUpTestData(SystemTestData)}.  
     * </p>
     *
     * @see {@link #addRasterLayer(QName, String, String, Map, Class, Catalog)}
     */
    public void setUpRasterLayer(QName qName, String filename, String extension, Map<LayerProperty, Object> props,
            Class scope) throws IOException {
        addRasterLayer(qName, filename, extension, props, scope, catalog);
    }

    public void setUpSecurity() throws IOException {
        File secDir = new File(getDataDirectoryRoot(), "security");
        IOUtils.decompress(SystemTestData.class.getResourceAsStream("security.zip"), secDir);
    }

    protected void createCatalog() throws IOException {
        CatalogImpl catalog = new CatalogImpl();
        catalog.setExtendedValidation(false);
        catalog.setResourceLoader(new GeoServerResourceLoader(data));

        catalog.addListener(new GeoServerPersister(catalog.getResourceLoader(), createXStreamPersister()));

        //workspaces
        addWorkspace(DEFAULT_PREFIX, DEFAULT_URI, catalog);
        addWorkspace(SF_PREFIX, SF_URI, catalog);
        addWorkspace(CITE_PREFIX, CITE_URI, catalog);
        addWorkspace(CDF_PREFIX, CDF_URI, catalog);
        addWorkspace(CGF_PREFIX, CGF_URI, catalog);

        //default style
        addStyle(DEFAULT_VECTOR_STYLE, catalog);
        addStyle(DEFAULT_RASTER_STYLE, catalog);

        this.catalog = catalog;
    }

    protected void createConfig() {
        GeoServerImpl geoServer = new GeoServerImpl();
        geoServer.addListener(new GeoServerPersister(new GeoServerResourceLoader(data), createXStreamPersister()));

        GeoServerInfo global = geoServer.getFactory().createGlobal();
        geoServer.setGlobal(global);
        addSettings(null, geoServer);

        LoggingInfo logging = geoServer.getFactory().createLogging();
        geoServer.setLogging(logging);
    }

    XStreamPersister createXStreamPersister() {
        XStreamPersister xp = new XStreamPersisterFactory().createXMLPersister();
        xp.setEncryptPasswordFields(false);
        return xp;
    }

    /**
     * Adds a workspace to the test setup.
     * 
     * @param name The name of the workspace.
     * @param uri The namespace uri associated with the workspace.
     */
    public void addWorkspace(String name, String uri, Catalog catalog) {

        WorkspaceInfo ws = catalog.getWorkspaceByName(name);
        if (ws == null) {
            ws = catalog.getFactory().createWorkspace();
            ws.setName(name);
            catalog.add(ws);
        }

        NamespaceInfo ns = catalog.getNamespaceByPrefix(name);
        if (ns == null) {
            ns = catalog.getFactory().createNamespace();
            ns.setPrefix(name);
            ns.setURI(uri);
            catalog.add(ns);
        } else {
            ns.setURI(uri);
            catalog.save(ns);
        }
    }

    /**
     * Adds a style to the test setup.
     * <p>
     * To set up the style a file named <tt>name</tt>.sld is copied from the classpath relative
     * to this class.
     * </p>
     * @param name The name of the style.
     */
    public void addStyle(String name, Catalog catalog) throws IOException {
        addStyle(name, getClass(), catalog);
    }

    /**
     * Adds a style to the test setup.
     * <p>
     * To set up the style a file named <tt>name</tt>.sld is copied from the classpath relative
     * to the <tt>scope</tt> parameter.
     * </p>
     * @param name The name of the style.
     * @param scope Class from which to load sld resource from.
     */
    public void addStyle(String name, Class scope, Catalog catalog) throws IOException {
        addStyle(name, name + ".sld", scope, catalog);
    }

    /**
     * Adds a style to the test setup.
     * <p>
     * To set up the style a file named <tt>filename</tt> is copied from the classpath relative
     * to the <tt>scope</tt> parameter.
     * </p>
     * @param name The name of the style.
     * @param filename The filename to copy from classpath.
     * @param scope Class from which to load sld resource from.
     */
    public void addStyle(String name, String filename, Class scope, Catalog catalog) throws IOException {
        addStyle((WorkspaceInfo) null, name, filename, scope, catalog);
    }

    /**
     * Adds a style to the test setup.
     * <p>
     * To set up the style a file named <tt>filename</tt> is copied from the classpath relative
     * to the <tt>scope</tt> parameter.
     * </p>
     * @param ws The workspace to include the style in.
     * @param name The name of the style.
     * @param filename The filename to copy from classpath.
     * @param scope Class from which to load sld resource from.
     */
    public void addStyle(WorkspaceInfo ws, String name, String filename, Class scope, Catalog catalog)
            throws IOException {
        File styles = catalog.getResourceLoader().findOrCreateDirectory(data, "styles");

        catalog.getResourceLoader().copyFromClassPath(filename, new File(styles, filename), scope);

        StyleInfo style = catalog.getStyleByName(ws, name);
        if (style == null) {
            style = catalog.getFactory().createStyle();
            style.setName(name);
            style.setWorkspace(ws);
        }
        style.setFilename(filename);
        if (style.getId() == null) {
            catalog.add(style);
        } else {
            catalog.save(style);
        }
    }

    /**
     * Adds a vector layer to the catalog setup.
     * <p>
     * This method calls through to {@link #addVectorLayer(QName, Map, Catalog)} with no custom 
     * properties.
     * </p>
     */
    public void addVectorLayer(QName qName, Catalog catalog) throws IOException {
        addVectorLayer(qName, new HashMap(), catalog);
    }

    /**
     * Adds a vector layer to the catalog setup.
     * <p>
     * This method calls through to {@link #addVectorLayer(QName, Map, Class, Catalog)} passing in
     * this class as the scope.
     * </p> 
     */
    public void addVectorLayer(QName qName, Map<LayerProperty, Object> props, Catalog catalog) throws IOException {
        addVectorLayer(qName, props, getClass(), catalog);
    }

    /**
     * Adds a vector layer to the catalog setup.
     * <p>
     * The layer is created within a store named <code>qName.getPrefix()</code>, creating it 
     * if it does not exist. The resulting store is a {@link PropertyDataStore} that points at the 
     * directory <code>getDataDirectoryRoot()/qName.getPrefix()</code>. Similarily the layer and
     * store are created within a workspace named <code>qName.getPrefix()</code>, which is created
     * if it does not already exist.
     * </p>
     * <p>
     * The properties data for the layer is copied from the classpath, with a file name of 
     * "<code>qName.getLocalPart()</code>.properties". The <tt>scope</tt> parameter is used as the 
     * class from which to load the properties file relative to. 
     * </p>
     * <p>
     * The <tt>props</tt> parameter is used to define custom properties for the layer. See the 
     * {@link LayerProperty} class for supported properties. 
     * </p>
     */
    public void addVectorLayer(QName qName, Map<LayerProperty, Object> props, Class scope, Catalog catalog)
            throws IOException {
        addVectorLayer(qName, props, qName.getLocalPart() + ".properties", scope, catalog);
    }

    /**
     * Adds a vector layer to the catalog setup.
     * <p>
     * The layer is created within a store named <code>qName.getPrefix()</code>, creating it 
     * if it does not exist. The resulting store is a {@link PropertyDataStore} that points at the 
     * directory <code>getDataDirectoryRoot()/qName.getPrefix()</code>. Similarily the layer and
     * store are created within a workspace named <code>qName.getPrefix()</code>, which is created
     * if it does not already exist.
     * </p>
     * <p>
     * The properties data for the layer is copied from the classpath, with a file name of 
     * "<code>filename</code>.properties". The <tt>scope</tt> parameter is used as the 
     * class from which to load the properties file relative to. 
     * </p>
     * <p>
     * The <tt>props</tt> parameter is used to define custom properties for the layer. See the 
     * {@link LayerProperty} class for supported properties. 
     * </p>
     */
    public void addVectorLayer(QName qName, Map<LayerProperty, Object> props, String filename, Class scope,
            Catalog catalog) throws IOException {
        String prefix = qName.getPrefix();
        String name = qName.getLocalPart();
        String uri = qName.getNamespaceURI();

        //configure workspace if it doesn;t already exist
        if (catalog.getWorkspaceByName(prefix) == null) {
            addWorkspace(prefix, uri, catalog);
        }

        //configure store if it doesn't already exist

        File storeDir = catalog.getResourceLoader().findOrCreateDirectory(prefix);

        DataStoreInfo store = catalog.getDataStoreByName(prefix);
        if (store == null) {
            store = catalog.getFactory().createDataStore();
            store.setName(prefix);
            store.setWorkspace(catalog.getWorkspaceByName(prefix));
            store.setEnabled(true);

            store.getConnectionParameters().put(PropertyDataStoreFactory.DIRECTORY.key, storeDir);
            store.getConnectionParameters().put(PropertyDataStoreFactory.NAMESPACE.key, uri);
            catalog.add(store);
        }

        //copy the properties file over

        catalog.getResourceLoader().copyFromClassPath(filename, new File(storeDir, filename), scope);

        //configure feature type
        FeatureTypeInfo featureType = catalog.getFactory().createFeatureType();
        featureType.setStore(store);
        featureType.setNamespace(catalog.getNamespaceByPrefix(prefix));
        featureType.setName(LayerProperty.NAME.get(props, name));
        featureType.setNativeName(FilenameUtils.getBaseName(filename));
        featureType.setTitle(name);
        featureType.setAbstract("abstract about " + name);

        Integer srs = LayerProperty.SRS.get(props, SRS.get(qName));
        if (srs == null) {
            srs = 4326;
        }
        featureType.setSRS("EPSG:" + srs);
        try {
            featureType.setNativeCRS(CRS.decode("EPSG:" + srs));
        } catch (Exception e) {
            LOGGER.warning("Failed to decode EPSG:" + srs + ", setting the native SRS to null");
        }
        featureType.setNumDecimals(8);
        featureType.getKeywords().add(new Keyword(name));
        featureType.setEnabled(true);
        featureType.setProjectionPolicy(LayerProperty.PROJECTION_POLICY.get(props, ProjectionPolicy.NONE));
        featureType.setLatLonBoundingBox(LayerProperty.LATLON_ENVELOPE.get(props, DEFAULT_LATLON_ENVELOPE));
        featureType.setNativeBoundingBox(LayerProperty.ENVELOPE.get(props, null));

        FeatureTypeInfo ft = catalog.getFeatureTypeByDataStore(store, name);
        LayerInfo layer = catalog.getLayerByName(new NameImpl(prefix, name));
        if (ft == null) {
            ft = featureType;
            catalog.add(featureType);
        } else {
            if (layer == null) {
                // handles the case of layer removed, but feature type not
                catalog.remove(ft);
                ft = featureType;
                catalog.add(featureType);
            } else {
                new CatalogBuilder(catalog).updateFeatureType(ft, featureType);
                catalog.save(ft);
            }
        }

        if (layer == null || !layer.getResource().getNamespace().equals(catalog.getNamespaceByPrefix(prefix))) {
            layer = catalog.getFactory().createLayer();
        }

        layer.setResource(ft);

        StyleInfo defaultStyle = null;
        if (LayerProperty.STYLE.get(props, null) != null) {
            defaultStyle = catalog.getStyleByName(LayerProperty.STYLE.get(props, null));
        } else {
            //look for a style matching the layer name
            defaultStyle = catalog.getStyleByName(name);
            if (defaultStyle == null) {
                //see if the resource exists and we just need to create it
                if (getClass().getResource(name + ".sld") != null) {
                    addStyle(name, catalog);
                    defaultStyle = catalog.getStyleByName(name);
                }
            }
        }

        if (defaultStyle == null) {
            defaultStyle = catalog.getStyleByName(DEFAULT_VECTOR_STYLE);
        }

        layer.getStyles().clear();
        layer.setDefaultStyle(defaultStyle);
        layer.setType(LayerInfo.Type.VECTOR);
        layer.setEnabled(true);

        if (layer.getId() == null) {
            catalog.add(layer);
        } else {
            catalog.save(layer);
        }
    }

    /**
     * Adds one of the default raster layers.
     * <p>
     *  The <tt>name</tt> parameter must be one of:
     *  <ul>
     *  <li>{@link CiteTestData#TASMANIA_BM}
     *  <li>{@link CiteTestData#TASMANIA_DEM}
     *  <li>{@link CiteTestData#ROTATED_CAD}
     *  <li>{@link CiteTestData#WORLD}
     * </ul>
     * </p>
     */
    public void addDefaultRasterLayer(QName name, Catalog catalog) throws IOException {
        if (name.equals(TASMANIA_DEM)) {
            addRasterLayer(name, "tazdem.tiff", null, catalog);
        } else if (name.equals(TASMANIA_BM)) {
            addRasterLayer(name, "tazbm.tiff", null, catalog);
        } else if (name.equals(ROTATED_CAD)) {
            addRasterLayer(name, "rotated.tiff", null, catalog);
        } else if (name.equals(WORLD)) {
            addRasterLayer(name, "world.tiff", null, catalog);
        } else if (name.equals(MULTIBAND)) {
            addRasterLayer(name, "multiband.tiff", null, catalog);
        } else {
            throw new IllegalArgumentException("Unknown default raster layer: " + name);
        }
    }

    /**
     * Adds a raster layer to the setup with no custom properties.
     * <p>
     * This method calls through to {@link #addRasterLayer(QName, String, String, Map, Catalog)}
     * </p> 
     */
    public void addRasterLayer(QName qName, String filename, String extension, Catalog catalog) throws IOException {
        addRasterLayer(qName, filename, extension, new HashMap(), catalog);
    }

    /**
     * Adds a raster layer to the setup.
     * <p>
     * The <tt>filename</tt> parameter defines the raster file to be loaded from the classpath.
     * This method assumes the scope of this class and calls through to 
     * {@link #addRasterLayer(QName, String, String, Map, Class, Catalog)}
     * </p>
     */
    public void addRasterLayer(QName qName, String filename, String extension, Map<LayerProperty, Object> props,
            Catalog catalog) throws IOException {
        addRasterLayer(qName, filename, extension, props, getClass(), catalog);
    }

    /**
     * Adds a raster layer to the setup.
     * <p>
     * This method configures a raster layer with the name <code>qName.getLocalPart()</code>. A 
     * coverage store is created (if it doesn't already exist) with the same name. The workspace
     * of the resulting store and layer is determined by <code>qName.getPrefix()</code>.
     * </p>
     * <p>
     * The <tt>filename</tt> parameter defines the raster file to be loaded from the classpath 
     * and copied into the data directory. The <tt>scope</tt> is used as the class from which to 
     * load the file from.
     * </p>
     * <p>
     * In the case of adding a zipped archive that contains multiple file the <tt>filename</tt> 
     * paramter should have a ".zip" extension and the <tt>extension</tt> parameter must define the 
     * extension of the main raster file. The parameter is not necessary and may be null if the 
     * <tt>filename</tt> does not refer to a zip file.
     * </p>
     * <p>
     * The <tt>props</tt> parameter is used to define custom properties for the layer. See the 
     * {@link LayerProperty} class for supported properties. 
     * </p>
     * @param qName The name of the raster layer.
     * @param filename The name of the file containing the raster, to be loaded from the classpath.
     * @param extension The file extension (without a ".") of the main raster file. This parameter
     *   my be <code>null</code> only if <tt>filename</tt> does not refer to a zip file.
     * @param props Custom properties to assign to the created raster layer.
     * @param scope The class from which to load the <tt>filename</tt> resource from. 
     *
     */
    public void addRasterLayer(QName qName, String filename, String extension, Map<LayerProperty, Object> props,
            Class scope, Catalog catalog) throws IOException {

        String prefix = qName.getPrefix();
        String name = qName.getLocalPart();

        //setup the data
        File dir = new File(data, name);
        dir.mkdirs();

        File file = new File(dir, filename);
        catalog.getResourceLoader().copyFromClassPath(filename, file, scope);

        String ext = FilenameUtils.getExtension(filename);
        if ("zip".equalsIgnoreCase(ext)) {

            //unpack the archive
            IOUtils.decompress(file, dir);

            //delete archive
            file.delete();

            if (extension == null) {
                //zip with no extension, we just the directory as the file
                file = dir;
            } else {
                //files may have been top level, or one directory level deep
                file = new File(dir, FilenameUtils.getBaseName(filename) + "." + extension);
                if (!file.exists()) {
                    File file2 = new File(new File(dir, dir.getName()), file.getName());
                    if (file2.exists()) {
                        file = file2;
                    }
                }
            }

            if (!file.exists()) {
                throw new FileNotFoundException(file.getPath());
            }
        }

        //load the format/reader
        AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat(file);
        if (format == null) {
            throw new RuntimeException("No format for " + file.getCanonicalPath());
        }
        GridCoverage2DReader reader = null;
        try {
            reader = (GridCoverage2DReader) format.getReader(file);
            if (reader == null) {
                throw new RuntimeException(
                        "No reader for " + file.getCanonicalPath() + " with format " + format.getName());
            }

            //configure workspace if it doesn;t already exist
            if (catalog.getWorkspaceByName(prefix) == null) {
                addWorkspace(prefix, qName.getNamespaceURI(), catalog);
            }
            //create the store
            CoverageStoreInfo store = catalog.getCoverageStoreByName(prefix, name);
            if (store == null) {
                store = catalog.getFactory().createCoverageStore();
            }

            store.setName(name);
            store.setWorkspace(catalog.getWorkspaceByName(prefix));
            store.setEnabled(true);
            store.setURL(DataUtilities.fileToURL(file).toString());
            store.setType(format.getName());

            if (store.getId() == null) {
                catalog.add(store);
            } else {
                catalog.save(store);
            }

            //create the coverage
            CatalogBuilder builder = new CatalogBuilder(catalog);
            builder.setStore(store);

            CoverageInfo coverage = null;

            try {

                coverage = builder.buildCoverage(reader, null);
                // coverage read params
                if (format instanceof ImageMosaicFormat) {
                    //  make sure we work in immediate mode
                    coverage.getParameters().put(AbstractGridFormat.USE_JAI_IMAGEREAD.getName().getCode(),
                            Boolean.FALSE);
                }
            } catch (Exception e) {
                throw new IOException(e);
            }

            coverage.setName(name);
            coverage.setTitle(name);
            coverage.setDescription(name);
            coverage.setEnabled(true);

            CoverageInfo cov = catalog.getCoverageByCoverageStore(store, name);
            if (cov == null) {
                catalog.add(coverage);
            } else {
                builder.updateCoverage(cov, coverage);
                catalog.save(cov);
                coverage = cov;
            }

            LayerInfo layer = catalog.getLayerByName(new NameImpl(qName));
            if (layer == null) {
                layer = catalog.getFactory().createLayer();
            }
            layer.setResource(coverage);

            layer.setDefaultStyle(catalog.getStyleByName(LayerProperty.STYLE.get(props, DEFAULT_RASTER_STYLE)));
            layer.setType(LayerInfo.Type.RASTER);
            layer.setEnabled(true);

            if (layer.getId() == null) {
                catalog.add(layer);
            } else {
                catalog.save(layer);
            }
        } finally {
            if (reader != null) {
                reader.dispose();
            }
        }
    }

    /**
     * Adds a service configuration to the test setup.
     * </p>
     * If the service object already exists it is simply reverted to its original state.
     * </p>
     * @param serviceClass The class of the service
     * @param workspace The optional workspace for the service, may be <code>null</code>
     * @param geoServer The GeoServer configuration object.
     */
    public <T extends ServiceInfo> void addService(Class<T> serviceClass, String workspace, GeoServer geoServer) {

        Catalog catalog = geoServer.getCatalog();

        List<XStreamServiceLoader> loaders = GeoServerExtensions.extensions(XStreamServiceLoader.class);
        for (XStreamServiceLoader loader : loaders) {
            if (serviceClass.equals(loader.getServiceClass())) {
                //create a new one
                T created = (T) loader.create(geoServer);

                //grab the old one, if it exists
                T old = null;
                WorkspaceInfo ws = null;
                if (workspace != null) {
                    ws = catalog.getWorkspaceByName(workspace);
                    old = geoServer.getService(ws, serviceClass);
                } else {
                    old = geoServer.getService(serviceClass);
                }

                if (old != null) {
                    //update the old copy
                    OwsUtils.copy(created, old, serviceClass);
                    geoServer.save(old);
                } else {
                    //add the new one
                    created.setWorkspace(ws);
                    geoServer.add(created);
                }

                break;
            }
        }
    }

    /**
     * Adds a settings configuration to the test setup.
     * </p>
     * If the settings object already exists it is simply reverted to its original state.
     * </p>
     * @param workspace The optional workspace for the settings, may be <code>null</code>
     * @param geoServer The GeoServer configuration object.
     */
    public void addSettings(String workspace, GeoServer geoServer) {
        WorkspaceInfo ws = workspace != null ? geoServer.getCatalog().getWorkspaceByName(workspace) : null;

        GeoServerInfo global = geoServer.getGlobal();
        SettingsInfo settings = ws != null ? geoServer.getSettings(ws) : global.getSettings();
        if (settings == null) {
            settings = geoServer.getFactory().createSettings();
        }
        settings.setWorkspace(ws);
        settings.getContact().setContactPerson("Andrea Aime");
        settings.setNumDecimals(8);
        settings.setOnlineResource("http://geoserver.org");
        settings.setVerbose(false);
        settings.setVerboseExceptions(false);
        settings.setLocalWorkspaceIncludesPrefix(false);

        if (ws != null) {
            if (settings.getId() != null) {
                geoServer.save(settings);
            } else {
                geoServer.add(settings);
            }
        } else {
            //global
            geoServer.save(global);
        }
    }

    /**
     * Copies some content to a file under the base of the data directory.
     * <p>
     * The <code>location</code> is considred to be a path relative to the
     * data directory root.
     * </p>
     * <p>
     * Note that the resulting file will be deleted when {@link #tearDown()}
     * is called.
     * </p>
     * @param input The content to copy.
     * @param location A relative path
     */
    public void copyTo(InputStream input, String location) throws IOException {
        IOUtils.copy(input, new File(getDataDirectoryRoot(), location));
    }

    @Override
    public void tearDown() throws Exception {
        FileUtils.deleteDirectory(data);
    }

    @Override
    public File getDataDirectoryRoot() {
        return data;
    }

    @Override
    public boolean isTestDataAvailable() {
        return true;
    }
}