/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.remote; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.platform.resource.Resource; import org.geoserver.security.PropertyFileWatcher; import org.geotools.util.Utilities; import org.geotools.util.logging.Logging; /** * Basic property file based {@link RemoteProcessFactoryConfigurationGenerator} implementation with ability to reload config when the file changes. If * property file is not present, a new one will be created. * * @author Alessio Fabiani, GeoSolutions * */ public class RemoteProcessFactoryConfigurationWatcher extends TimerTask implements RemoteProcessFactoryConfigurationGenerator { public static final String REMOTE_PROCESS_DIR = "remote-process"; public static final String PROPERTYFILENAME = "remoteProcess.properties"; public final static String DEFAULT_PROPERTY_PATH = REMOTE_PROCESS_DIR + File.separator + PROPERTYFILENAME; /** The LOGGER */ public final static Logger LOGGER = Logging .getLogger(RemoteProcessFactoryConfigurationWatcher.class); /** * {@link PropertyFileWatcher} used for loading the property file. */ private PropertyFileWatcher watcher; /** * time in seconds between successive task executions */ private long period = 60 * 2; /** * delay in seconds before task is to be executed */ private long delay = 60 * 2; /** * The new {@link RemoteProcessFactoryConfiguration} object containing the properties load from the properties file. */ private RemoteProcessFactoryConfiguration configuration; /** * {@link Timer} object used for periodically watching the properties file */ private Timer timer; /** Default watches remoteProcess.properties */ public RemoteProcessFactoryConfigurationWatcher() { // Get the Resource loader GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class); // Check if the property file is present if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Checking properties file"); } File properties = null; try { properties = loader.find(PROPERTYFILENAME); } catch (IOException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, e.getMessage(), e); } } // Properties file not found. A new one is copied into the GeoServer data directory if (properties == null || !properties.exists()) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Properties file not found"); } try { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Copying the default properties file inside the data directory"); } // Copy the default property file into the data directory URL url = RemoteProcessFactoryConfigurationWatcher.class .getResource(DEFAULT_PROPERTY_PATH); if (url != null) { properties = loader.createFile(PROPERTYFILENAME); loader.copyFromClassPath(DEFAULT_PROPERTY_PATH, properties, RemoteProcessFactoryConfigurationWatcher.class); } } catch (IOException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, e.getMessage(), e); } } } else { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Properties file found"); } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Loading configuration"); } // Get the Property file as a Resource Resource remoteProcessFactoryProperties = loader.get(PROPERTYFILENAME); init(new PropertyFileWatcher(remoteProcessFactoryProperties)); } /** * Initialization method for loading the {@link RemoteProcessFactoryConfiguration}. * * @param propertyFileWatcher Watcher of the property file */ private void init(PropertyFileWatcher propertyFileWatcher) { Utilities.ensureNonNull("propertyFileWatcher", propertyFileWatcher); // Loading configuration from the file this.watcher = propertyFileWatcher; RemoteProcessFactoryConfiguration newConfiguration = loadConfiguration(); if (newConfiguration != null) { configuration = newConfiguration; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("New configuration loaded:\n" + configuration); } } // start background checks timer = new Timer(true); timer.scheduleAtFixedRate(this, delay * 1000, period * 1000); } /** * Loads the configuration from disk. * * @return an instance of {@link RemoteProcessFactoryConfiguration}. */ private RemoteProcessFactoryConfiguration loadConfiguration() { // load remote process factory Properties final File file = watcher.getFile(); RemoteProcessFactoryConfiguration newConfiguration = null; try { if (file.exists() && file.canRead()) { // load contents Properties properties = watcher.getProperties(); // parse contents newConfiguration = parseConfigurationValues(properties); } else { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Unable to read confguration file for remote process factory: " + file.getAbsolutePath() + " continuing with default configuration-->\n" + configuration); } } } catch (Exception e) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, e.getLocalizedMessage(), e); } if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Unable to read confguration file for remote process factory: " + file.getAbsolutePath() + " continuing with default configuration-->\n" + configuration); } } // return return newConfiguration; } /** * Parses the properties file for the remote process factory configuration. When it runs into problems it uses default values * * @param remoteProcessFactoryProperties the {@link Properties} file to parse. Cannot be null. * @return an instance of {@link RemoteProcessFactoryConfiguration}. */ private RemoteProcessFactoryConfiguration parseConfigurationValues( Properties remoteProcessFactoryProperties) { Utilities.ensureNonNull("remoteProcessFactoryProperties", remoteProcessFactoryProperties); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Parsing the properties file"); } // Initialize the configuration fields with default values long remoteProcessStubCycleSleepTime = RemoteProcessFactoryConfiguration.DEFAULT_SLEEP_TIME; Map<String, String> configKvPs = new HashMap<String, String>(); // Extract the keyset from the property files Set<Object> properties = remoteProcessFactoryProperties.keySet(); // Iterates on the various keys in order to search for the various properies for (Object property : properties) { String prop = (String) property; // remote process sleep time if (prop.equalsIgnoreCase(RemoteProcessFactoryConfiguration.DEFAULT_SLEEP_TIME_NAME)) { // get value String value = (String) remoteProcessFactoryProperties .get(RemoteProcessFactoryConfiguration.DEFAULT_SLEEP_TIME_NAME); // check and assign try { final long parseLong = Long.parseLong(value); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("maxFeatures parsed to " + parseLong); } if (parseLong > 0) { remoteProcessStubCycleSleepTime = parseLong; } } catch (NumberFormatException e) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, e.getLocalizedMessage(), e); } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("remoteProcessStubCycleSleepTime assigned to " + remoteProcessStubCycleSleepTime); } } else { configKvPs.put(prop, remoteProcessFactoryProperties.getProperty(prop)); } } // create the configuration object return new RemoteProcessFactoryConfiguration(remoteProcessStubCycleSleepTime, configKvPs); } @Override public void run() { if (watcher.isStale()) { // reload RemoteProcessFactoryConfiguration newConfiguration = loadConfiguration(); if (newConfiguration != null) { synchronized (newConfiguration) { configuration = newConfiguration; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("New configuration loaded:\n" + configuration); } } } } } /** * Stop the configuration watcher. */ public void stop() { try { timer.cancel(); } catch (Throwable t) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, t.getLocalizedMessage(), t); } } } @Override public RemoteProcessFactoryConfiguration getConfiguration() { return configuration; } }