package org.geoserver.jdbcloader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletContext; import org.geoserver.config.GeoServerPluginConfigurator; import org.geoserver.data.util.IOUtils; import org.geoserver.jdbcconfig.JDBCGeoServerLoader; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.platform.resource.Paths; import org.geoserver.platform.resource.Resource; import org.geoserver.platform.resource.ResourceStore; import org.geoserver.platform.resource.Resources; import org.geotools.data.DataUtilities; import org.geotools.util.logging.Logging; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.web.context.ServletContextAware; public abstract class JDBCLoaderPropertiesFactoryBean extends PropertiesFactoryBean implements GeoServerPluginConfigurator, ServletContextAware { private static final Logger LOGGER = Logging.getLogger(JDBCGeoServerLoader.class); protected static final String CONFIG_FILE = "${prefix}.properties"; protected static final String CONFIG_SYSPROP = "${prefix}.properties"; protected static final String JDBCURL_SYSPROP = "${prefix}.jdbcurl"; protected static final String INITDB_SYSPROP = "${prefix}.initdb"; protected static final String IMPORT_SYSPROP = "${prefix}.import"; protected ResourceStore resourceStore; protected String prefix; protected String dataDirectory; public JDBCLoaderPropertiesFactoryBean(ResourceStore resourceStore, String prefix) { this.resourceStore = resourceStore; this.prefix = prefix; } protected abstract JDBCLoaderProperties createConfig() throws IOException; protected abstract String[] getScripts(); protected abstract String[] getSampleConfigurations(); protected String replacePrefix(String s) { return s.replace("${prefix}", prefix); } @Override public Properties createProperties() throws IOException { JDBCLoaderProperties config = loadConfig(); if (!config.isEnabled()) { LOGGER.info("jdbcloader is disabled"); return config; } return config; } private JDBCLoaderProperties loadConfig() throws IOException { //copy over sample scripts JDBCLoaderProperties config = loadDefaultConfig(); /* * Find configuration, lookup heuristic is as follows. * 1. check system property "jdbcconfig.properties" for path/url to properties file * 2. check system properties jdbconfig.jdbcurl, jdbconfig.initdb, jdbcimport.import * 3. look for <GEOSERVER_DATA_DIR>/jdbcconfig/jdbcconfig.properties * 4. use built in defaults */ if (loadConfigFromURL(config)) { return config; } if (loadConfigFromSysProps(config)) { return config; } if (loadConfigFromDataDir(config)) { return config; } LOGGER.info("Configuring jdbcloader from defaults"); //copy over default config to data dir saveConfig(config, "Default GeoServer JDBC loader driver and connection pool options." + " Edit as appropriate."); copySampleConfigsToDataDir(); copyScriptsToDataDir(); return config; } protected JDBCLoaderProperties loadDefaultConfig() throws IOException { JDBCLoaderProperties config = createConfig(); config.load(getClass().getResourceAsStream("/" + replacePrefix(CONFIG_FILE))); return config; } protected boolean loadConfigFromSysProps(JDBCLoaderProperties config) throws IOException { String jdbcUrl = System.getProperty(replacePrefix(JDBCURL_SYSPROP)); if (jdbcUrl != null) { config.setJdbcUrl(jdbcUrl); config.setInitDb(Boolean.getBoolean(replacePrefix(INITDB_SYSPROP))); config.setImport(Boolean.getBoolean(replacePrefix(IMPORT_SYSPROP))); if (LOGGER.isLoggable(Level.INFO)) { StringBuilder msg = new StringBuilder("Configuring jdbcloader from system properties:\n"); msg.append(" ").append(replacePrefix(JDBCURL_SYSPROP)).append("=").append(jdbcUrl).append("\n"); msg.append(" ").append(replacePrefix(INITDB_SYSPROP)).append("=").append(config.isInitDb()).append("\n"); msg.append(" ").append(replacePrefix(IMPORT_SYSPROP)).append("=").append(config.isImport()).append("\n"); LOGGER.info(msg.toString()); } return true; } return false; } private boolean loadConfigFromURL(JDBCLoaderProperties config) throws IOException { String propUrl = System.getProperty(replacePrefix(CONFIG_SYSPROP)); if (propUrl == null) { return false; } URL url = null; try { //try to parse directly as url try { url = new URL(propUrl); } catch(MalformedURLException e) { //failed, try as a file path File f = new File(propUrl); if (f.canRead() && f.exists()) { url = DataUtilities.fileToURL(f); } } } catch(Exception e) { LOGGER.log(Level.WARNING, "Error trying to read " + propUrl, e); } if (url != null) { LOGGER.info("Configuring jdbcloader from " + url.toString()); InputStream in = url.openStream(); try { config.load(in); } finally { in.close(); } return true; } LOGGER.severe("System property " + replacePrefix(CONFIG_SYSPROP) + " specified " + propUrl + " but could not be read, ignoring."); return false; } private boolean loadConfigFromDataDir(JDBCLoaderProperties config) throws IOException { Resource propFile = getBaseDir().get(replacePrefix(CONFIG_FILE)); if (Resources.exists(propFile)) { LOGGER.info("Loading jdbcloader properties from " + propFile.path()); InputStream stream = propFile.in(); try { config.load(stream); return true; } finally { stream.close(); } } return false; } void saveConfig(JDBCLoaderProperties config) throws IOException { saveConfig(config, ""); } private void saveConfig(JDBCLoaderProperties config, String comment) throws IOException { Resource propFile = getBaseDir().get(replacePrefix(CONFIG_FILE)); try { OutputStream out = propFile.out(); try { config.store(out, comment); } finally { out.close(); } } catch (Exception e) { LOGGER.log(Level.WARNING, "Error saving jdbc loader properties to file " + propFile.path(), e); propFile.delete(); } } private void copyScriptsToDataDir() throws IOException { final Resource scriptsDir = getScriptDir(); Class<?> scope = getClass(); for (String scriptName : getScripts()) { Resource target = scriptsDir.get(scriptName); if (!Resources.exists(target)) { IOUtils.copy(scope.getResourceAsStream(scriptName), target.out()); } } } private void copySampleConfigsToDataDir() throws IOException { final Resource baseDirectory = getBaseDir(); for (String sampleConfig : getSampleConfigurations()) { Resource target = baseDirectory.get(sampleConfig); if (!Resources.exists(target)) { IOUtils.copy(Thread.currentThread().getContextClassLoader().getResourceAsStream(sampleConfig), target.out()); } } } protected String getDataDirStr() { if (dataDirectory == null) { if (resourceStore instanceof GeoServerResourceLoader) { dataDirectory = ((GeoServerResourceLoader) resourceStore).getBaseDirectory().getAbsolutePath(); } else { throw new IllegalStateException("Data directory could not be determined."); } } return dataDirectory; } protected Resource getDataDir() { return resourceStore.get(""); } protected Resource getBaseDir() { return resourceStore.get(prefix); } protected Resource getScriptDir() { return resourceStore.get(Paths.path(prefix, "scripts")); } @Override public void setServletContext(ServletContext servletContext) { dataDirectory = GeoServerResourceLoader.lookupGeoServerDataDirectory(servletContext); } }