/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.monitor; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.config.GeoServerPluginConfigurator; import org.geoserver.data.util.IOUtils; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.platform.resource.Files; import org.geoserver.platform.resource.Paths; import org.geoserver.platform.resource.Resource; import org.geoserver.platform.resource.Resources; import org.geoserver.security.PropertyFileWatcher; import org.geotools.factory.Hints; import org.geotools.referencing.CRS; import org.geotools.util.ConverterFactory; import org.geotools.util.Converters; import org.geotools.util.logging.Logging; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * Configuration object for monitor subsystem. * * @author Justin Deoliveira, OpenGeo * */ public class MonitorConfig implements GeoServerPluginConfigurator, ApplicationContextAware { protected static final String PROPERTYFILENAME = "monitor.properties"; private static final Logger LOGGER = Logging.getLogger(MonitorConfig.class); public static enum Mode { HISTORY, LIVE, @Deprecated // use live HYBRID; } public static enum BboxMode { NONE, NO_WFS, FULL; } protected Properties props; PropertyFileWatcher fw; ApplicationContext context; boolean enabled = true; Exception error; private GeoServerResourceLoader loader; public MonitorConfig() { props = new Properties(); props.put("storage", "memory"); props.put("mode", "history"); props.put("maxBodySize", "1024"); props.put("bboxMode", "no_wfs"); props.put("bboxCrs", "EPSG:4326"); //for backwards compatibility include the hibernate config options props.put("hibernate.sync", "async"); loader = GeoServerExtensions.bean(GeoServerResourceLoader.class); } public MonitorConfig(GeoServerResourceLoader loader) throws IOException { this.loader = loader; Resource f = getConfigurationFile(loader); fw = new PropertyFileWatcher(f); } public String getStorage() { return props().getProperty("storage"); } public Properties getProperties() { return props(); } public Mode getMode() { Mode m = Mode.valueOf(props().getProperty("mode", "history").toUpperCase()); if (m == Mode.HYBRID) { m = Mode.LIVE; } return m; } public long getMaxBodySize() { return Long.parseLong(props().getProperty("maxBodySize", String.valueOf(1024))); } public CoordinateReferenceSystem getBboxCrs() { Properties props = props(); String srs = props.getProperty("bboxCrs"); if (srs == null) { //old property name srs = props.getProperty("bboxLogCrs", "EPSG:4326"); } try { return CRS.decode(srs); } catch (NoSuchAuthorityCodeException e) { LOGGER.log(Level.FINER, e.getMessage(), e); } catch (FactoryException e) { LOGGER.log(Level.FINER, e.getMessage(), e); } return null; } public BboxMode getBboxMode() { Properties props = props(); String mode = props.getProperty("bboxMode"); if (mode == null) { //old property name mode = props.getProperty("bboxLogLevel", "no_wfs"); } if (mode == null) { return null; } return BboxMode.valueOf(mode.toUpperCase()); } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Exception getError() { return error; } public void setError(Exception error) { this.error = error; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } public MonitorDAO createDAO() { MonitorDAO dao = null; String storage = getStorage(); if (storage == null) { //storage key not found, for backward compatibility look up mode Mode mode = getMode(); if (mode == Mode.HISTORY || mode == Mode.HYBRID) { storage = "hibernate"; } } if (storage == null) { storage = MemoryMonitorDAO.NAME; } //match storage to plugin in context for (MonitorDAO d : GeoServerExtensions.extensions(MonitorDAO.class)) { if (storage.equalsIgnoreCase(d.getName())) { dao = d; break; } } if (dao == null) { LOGGER.warning("monitoring storage "+storage+" not found, falling back to '" + MemoryMonitorDAO.NAME +"'"); dao = new MemoryMonitorDAO(); } dao.init(this); return dao; } /** * Allows to retrieve a generic property from the configuration. Extensions and plugins are * supposed to use the plugin.property naming convention, passing both a prefix and a name * * @param prefix namespace prefix * @param name name * @param target Class for conversion * */ public <T> T getProperty(String prefix, String name, Class<T> target) { String key = prefix == null ? name : prefix + "." + name; Object value = props().get(key); if (value != null) { T converted = Converters.convert(value, target, new Hints( ConverterFactory.SAFE_CONVERSION, true)); if (converted == null) { throw new IllegalArgumentException("Object " + value + " could not be converted to the target class " + target); } return converted; } else { return null; } } Properties props() { if (fw != null && fw.isModified()) { synchronized (this) { if (fw.isModified()) { try { props = fw.read(); //backward compatibility hack for sync -> hibernate.sync if (props.getProperty("sync") != null) { props.setProperty("hibernate.sync", props.getProperty("sync")); } } catch (IOException e) { throw new RuntimeException(e); } } } } return props; } @Override public List<Resource> getFileLocations() throws IOException { List<Resource> configurationFiles = new ArrayList<>(); if (loader != null) { Resource f = getConfigurationFile(loader); configurationFiles.add(f); } else if (fw != null && fw.getResource() != null) { configurationFiles.add(fw.getResource()); } return configurationFiles; } /** * @param loader * @return * @throws IOException */ public Resource getConfigurationFile(GeoServerResourceLoader loader) throws IOException { Resource f = loader.get(Paths.path("monitoring", MonitorConfig.PROPERTYFILENAME)); if (!Resources.exists(f)) { IOUtils.copy(MonitorConfig.class.getResourceAsStream(MonitorConfig.PROPERTYFILENAME), f.out()); } return f; } @Override public void saveConfiguration(GeoServerResourceLoader resourceLoader) throws IOException { if (loader != null) { Resource f = getConfigurationFile(loader); Resource targetDir = Files.asResource(resourceLoader.findOrCreateDirectory(Paths.convert(loader.getBaseDirectory(), f.parent().dir()))); Resources.copy(f.file(), targetDir); } else if (fw != null && fw.getResource() != null) { Resources.copy(fw.getFile(), Files.asResource(resourceLoader.getBaseDirectory())); } else if (props != null) { File monitoringConfigurationFile = Resources.file(resourceLoader.get(MonitorConfig.PROPERTYFILENAME), true); OutputStream out = Files.out(monitoringConfigurationFile); try { props.store(out, ""); } finally { out.flush(); out.close(); } } } @Override public void loadConfiguration(GeoServerResourceLoader resourceLoader) throws IOException { synchronized (this) { Resource f = getConfigurationFile(resourceLoader); if (Resources.exists(f)) { fw = new PropertyFileWatcher(f); fw.setKnownLastModified(System.currentTimeMillis()); } } } }