/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, availible at the root * application directory. */ package org.vfny.geoserver.global; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.NoSuchElementException; import java.util.logging.Logger; import javax.servlet.ServletContext; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.GeoServerResourceLoader; import org.geotools.data.DataUtilities; import org.opengis.feature.simple.SimpleFeatureType; import org.springframework.context.ApplicationContext; import org.springframework.web.context.WebApplicationContext; /** * This class allows for abstracting the location of the Geoserver Data directory. Some people call this "GEOSERVER_HOME". * * Inside this directory should be two more directories: a. "WEB-INF/" Inside this is a catalog.xml b. "data/" Inside this is a set of other * directories. * * For the exact content of these directories, see any existing geoserver install's server/geoserver directory. * * In order to find the geoserver data directory the following steps take place: * * 1. search for the "GEOSERVER_DATA_DIR" system property. this will most likely have come from "java -DGEOSERVER_DATA_DIR=..." or from you * web container 2. search for a "GEOSERVER_DATA_DIR" in the web.xml document <context-param> <param-name>GEOSERVER_DATA_DIR</param-name> * <param-value>...</param-value> </context-param> 3. It defaults to the old behavior - ie. the application root - usually * "server/geoserver" in your .WAR. * * * NOTE: a set method is currently undefined because you should either modify you web.xml or set the environment variable and re-start * geoserver. * * @author dblasby * */ public class GeoserverDataDirectory { // caches the dataDir public static GeoServerResourceLoader loader; private static Catalog catalog; private static ApplicationContext appContext; private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.global"); /** * See the class documentation for more details. 1. search for the "GEOSERVER_DATA_DIR" system property. 2. search for a * "GEOSERVER_DATA_DIR" in the web.xml document 3. It defaults to the old behavior - ie. the application root - usually * "server/geoserver" in your .WAR. * * @return location of the geoserver data dir */ static public File getGeoserverDataDirectory() { if (loader != null) { return loader.getBaseDirectory(); } else { return null; } } /** * Locate feature type directory name using the FeatureType as a key into the catalog * @see Data#getFeatureTypeInfo(String) * @param name * String The FeatureTypeInfo Name * @return the feature type dir name, or null if not found (either the feature type or the directory) * * @throws NoSuchElementException */ static public String findFeatureTypeDirName(SimpleFeatureType featureType) { String name = featureType.getTypeName(); String namespace = featureType.getName().getNamespaceURI(); FeatureTypeInfo ftInfo = null; Catalog data = getCatalog(); if(namespace != null) { NamespaceInfo nsInfo = data.getNamespaceByURI(namespace); if(nsInfo != null) ftInfo = data.getFeatureTypeByName( nsInfo.getPrefix(), name); } if(ftInfo == null) ftInfo = data.getFeatureTypeByName(name); if(ftInfo == null) return null; String dirName = ftInfo.getMetadata().get("dirName",String.class); if ( dirName == null ) { dirName = ftInfo.getNamespace().getPrefix() + "_" + ftInfo.getName(); } return dirName; } /** * Locate coverage type directory name using the coverage name as a key into the catalog * @see Data#getCoverageInfo(String) * @param coverageName * String The FeatureTypeInfo Name * @return the feature type dir name, or null if not found (either the feature type or the directory) * * @throws NoSuchElementException */ public static String findCoverageDirName(String coverageName) { Catalog data = getCatalog(); CoverageInfo coverageInfo = data.getCoverageByName(coverageName); return coverageInfo.getMetadata().get( "dirName", String.class ); } /** * Utility method to find the approriate sub-data dir config. This is a helper for the fact that we're transitioning away from the * WEB-INF type of hacky storage, but during the transition things can be in both places. So this method takes the root file, the * dataDir, and a name of a directory that is stored in the data dir, and checks for it in the data/ dir (the old way), and directly in * the dir (the new way) * * @param root * Generally the Data Directory, the directory to try to find the config file in. * @param dirName * The name of the directory to find in the data Dir. * @return The proper config directory. * @throws ConfigurationException * if the directory could not be found at all. */ public static File findConfigDir(File root, String dirName) throws ConfigurationException { File configDir; try { configDir = loader.find(dirName); } catch (IOException e) { throw new ConfigurationException(e); } return configDir; } /** * Same as {@link #findConfigDir(File, String), but it will create the configuration directory * if missing (as a top level directory inside the Geoserver data directory) * @param dirName * @return * @throws ConfigurationException */ public static File findCreateConfigDir(String dirName) throws ConfigurationException { File configDir = findConfigDir(getGeoserverDataDirectory(), dirName); if ((configDir == null) || !configDir.exists()) { configDir = new File(getGeoserverDataDirectory(), dirName); configDir.mkdir(); if (configDir.exists()) { return configDir; } } return configDir; } /** * Given a url, tries to interpret it as a file into the data directory, or as an absolute * location, and returns the actual absolute location of the File * @param path * @return */ public static File findDataFile(URL url) { return findDataFile(url.getFile()); } /** * Looks up a file under the "styles" directory. * * @param fileName The name of the file. * * @return The style file, or null if it does not exist. */ public static File findStyleFile(String fileName) { return findStyleFile( fileName, false ); } /** * Looks up a file under the "styles" directory. * * @param fileName The name of the file. * @param resolve If set to true a non-null file handle will be returned even * when the file does not exist. * * @return The style file, or null if it does not exist and resolve == false. */ public static File findStyleFile(String fileName, boolean resolve) { File baseDir = GeoserverDataDirectory.getGeoserverDataDirectory(); File styleFile = new File( new File( baseDir, "styles" ), fileName ); if (resolve || styleFile.exists() ) { return styleFile; } return null; } /** * Given a path, tries to interpret it as a file into the data directory, or as an absolute * location, and returns the actual absolute location of the File * @param path * @return */ public static File findDataFile(String path) { File baseDir = GeoserverDataDirectory.getGeoserverDataDirectory(); // if path looks like an absolute file: URL, try standard conversion if (path.startsWith("file:/")) { try { return DataUtilities.urlToFile(new URL(path)); } catch (Exception e) { // failure, so fall through } } // do we ever have something that is not a file system reference? if (path.startsWith("file:")) { path = path.substring(5); // remove 'file:' prefix File f = new File(path); // if it's an absolute path, use it as such, // otherwise try to map it inside the data dir if (f.isAbsolute() || f.exists()) { return f; } else { return new File(baseDir, path); } } else { return new File(path); } } /** * Utility method fofinding a config file under the data directory. * * @param file * Path to file, absolute or relative to data dir. * * @return The file handle, or null. */ public static File findConfigFile(String file) throws ConfigurationException { try { return loader.find(file); } catch (IOException e) { throw new ConfigurationException(e); } } /** * Initializes the data directory lookup service. * * @param servContext */ public static void init(WebApplicationContext context) { ServletContext servContext = context.getServletContext(); // Oh, this is really sad. We need a reference to Data in order to // resolve feature type dirs, but gathering it here triggers the loading // of Geoserver (on whose Catalog depends on), which depends on having // DataDirectory and Config initialized, but this is not possible // here... // So we keep a reference to context in order to resolve Data later appContext = context; // This was once in the GetGeoserverDataDirectory method, I've moved // here so that servlet // context is not needed as a parameter anymore. // caching this, so we're not looking up everytime, and more // importantly, so we can actually look up this stuff without // having to pass in a ServletContext. This should be fine, since we // don't allow a set method, as we recommend restarting GeoServer, // so it should always get a ServletContext in the startup routine. // If this assumption can't be made, then we can't allow data_dir // _and_ webapp options with relative data/ links -ch if (loader == null) { // get the loader from the context loader = (GeoServerResourceLoader) context.getBean("resourceLoader"); File dataDir = null; String dataDirStr = findGeoServerDataDir(servContext); dataDir = new File(dataDirStr); LOGGER .severe("\n----------------------------------\n- GEOSERVER_DATA_DIR: " + dataDir.getAbsolutePath() + "\n----------------------------------"); return; } } /** * Loops over a list of variables that can represent the path to the * GeoServer data directory and attempts to resolve the value by looking at * 1) Java environment variable * 2) Servlet context variable * 3) System variable * * For each of these, the methods checks that * 1) The path exists * 2) Is a directory * 3) Is writable * * @param servContext * @return String representation of path, null otherwise * @deprecated use {@link GeoServerResourceLoader#lookupGeoServerDataDirectory(ServletContext)} */ public static String findGeoServerDataDir(ServletContext servContext) { return GeoServerResourceLoader.lookupGeoServerDataDirectory(servContext); } /** * Signals the data directory to throw away all global state. * <p> * This code should *not* be called by any non-test GeoServer code. * </p> */ public static void destroy() { loader = null; catalog = null; } private static Catalog getCatalog() { if(catalog == null) { catalog = (Catalog) GeoServerExtensions.bean( "catalog"); } return catalog; } /** * Helper method to help client code migrade from using this class to using * {@link org.geoserver.config.GeoserverDataDirectory}. */ public static org.geoserver.config.GeoServerDataDirectory accessor() { return new org.geoserver.config.GeoServerDataDirectory(loader); } /** * Sets the resource loader. This method is only used for testing. */ public static void setResourceLoader(GeoServerResourceLoader loader) { GeoserverDataDirectory.loader = loader; } }