/** * Copyright (C) 2006 - 2016 52°North Initiative for Geospatial Open Source * Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.n52.wps.commons; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletConfig; import org.apache.xmlbeans.XmlException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.n52.wps.FormatDocument.Format; import org.n52.wps.GeneratorDocument.Generator; import org.n52.wps.ParserDocument.Parser; import org.n52.wps.PropertyDocument.Property; import org.n52.wps.RepositoryDocument.Repository; import org.n52.wps.ServerDocument; import org.n52.wps.WPSConfigurationDocument; import org.n52.wps.impl.WPSConfigurationDocumentImpl.WPSConfigurationImpl; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; public class WPSConfig implements Serializable { /** * */ private static final long serialVersionUID = 3198223084611936675L; private static transient WPSConfig wpsConfig; private static transient WPSConfigurationImpl wpsConfigXMLBeans; private static final Logger LOGGER = LoggerFactory.getLogger(WPSConfig.class); // constants for the Property change event names public static final String WPSCONFIG_PROPERTY_EVENT_NAME = "WPSConfigUpdate"; public static final String WPSCAPABILITIES_SKELETON_PROPERTY_EVENT_NAME = "WPSCapabilitiesUpdate"; public static final String CONFIG_FILE_PROPERTY = "wps.config.file"; public static final String CONFIG_FILE_NAME = "wps_config.xml"; private static final String CONFIG_FILE_DIR = "config"; private static final String URL_DECODE_ENCODING = "UTF-8"; // FvK: added Property Change support protected final PropertyChangeSupport propertyChangeSupport; private static String configPath; private WPSConfig(String wpsConfigPath) throws XmlException, IOException { configPath = wpsConfigPath; wpsConfigXMLBeans = (WPSConfigurationImpl) WPSConfigurationDocument.Factory.parse(new File(wpsConfigPath)).getWPSConfiguration(); // FvK: added Property Change support this.propertyChangeSupport = new PropertyChangeSupport(this); } private WPSConfig(InputStream resourceAsStream) throws XmlException, IOException { wpsConfigXMLBeans = (WPSConfigurationImpl) WPSConfigurationDocument.Factory.parse(resourceAsStream).getWPSConfiguration(); // FvK: added Property Change support this.propertyChangeSupport = new PropertyChangeSupport(this); } /** * Add an Listener to the wpsConfig * * @param propertyName * @param listener */ public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener) { this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener); } /** * remove a listener from the wpsConfig * * @param propertyName * @param listener */ public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener) { this.propertyChangeSupport.removePropertyChangeListener(propertyName, listener); } // For Testing purpose only public void notifyListeners() { this.propertyChangeSupport.firePropertyChange(WPSCONFIG_PROPERTY_EVENT_NAME, null, null); } public void firePropertyChange(String event) { propertyChangeSupport.firePropertyChange(event, null, null); } // private synchronized static void writeObject(java.io.ObjectOutputStream oos) throws IOException { // oos.writeObject(wpsConfigXMLBeans.xmlText()); // } // // private synchronized static void readObject(java.io.ObjectInputStream oos) throws IOException, // ClassNotFoundException { // try { // String wpsConfigXMLBeansAsXml = (String) oos.readObject(); // XmlObject configXmlObject = XmlObject.Factory.parse(wpsConfigXMLBeansAsXml); // WPSConfigurationDocument configurationDocument = WPSConfigurationDocument.Factory.newInstance(); // configurationDocument.addNewWPSConfiguration().set(configXmlObject); // wpsConfig = new WPSConfig(new ByteArrayInputStream(configurationDocument.xmlText().getBytes())); // } // catch (XmlException e) { // LOGGER.error(e.getMessage()); // throw new IOException(e.getMessage()); // } // } /** * WPSConfig is a singleton. If there is a need for reinitialization, use this path. * * @param configPath path to the wps_config.xml * @throws XmlException * @throws IOException */ public static void forceInitialization(String configPath) throws XmlException, IOException { // temporary save all registered listeners PropertyChangeListener[] listeners = {}; if (wpsConfig != null) { listeners = wpsConfig.propertyChangeSupport.getPropertyChangeListeners(); } wpsConfig = new WPSConfig(configPath); // register all saved listeners to new wpsConfig Instance // reversed order to keep original order of the registration!!! for (int i = listeners.length - 1; i >= 0; i--) { wpsConfig.propertyChangeSupport.addPropertyChangeListener(listeners[i]); } // fire event wpsConfig.propertyChangeSupport.firePropertyChange(WPSCONFIG_PROPERTY_EVENT_NAME, null, wpsConfig); LOGGER.info("Configuration Reloaded, Listeners informed"); } /** * WPSConfig is a singleton. If there is a need for reinitialization, use this path. * * @param stream * stream containing the wps_config.xml * @throws XmlException * @throws IOException */ public static void forceInitialization(InputStream stream) throws XmlException, IOException { // temporary save all registered listeners PropertyChangeListener[] listeners = {}; if (wpsConfig != null) { listeners = wpsConfig.propertyChangeSupport.getPropertyChangeListeners(); } wpsConfig = new WPSConfig(stream); // register all saved listeners to new wpsConfig Instance // reversed order to keep original order of the registration!!! for (int i = listeners.length - 1; i >= 0; i--) { wpsConfig.propertyChangeSupport.addPropertyChangeListener(listeners[i]); } // fire event wpsConfig.propertyChangeSupport.firePropertyChange(WPSCONFIG_PROPERTY_EVENT_NAME, null, wpsConfig); LOGGER.info("Configuration Reloaded, Listeners informed"); } /** * returns an instance of the WPSConfig class. WPSConfig is a single. If there is need for * reinstantitation, use forceInitialization(). * * @return WPSConfig object representing the wps_config.xml from the classpath or webapps folder */ public static WPSConfig getInstance() { // if (LOGGER.isDebugEnabled()) // LOGGER.debug("Getting WPSConfig instance... without input."); if (wpsConfig == null) { String path = getConfigPath(); WPSConfig config = getInstance(path); wpsConfig = config; } return wpsConfig; } /** * returns an instance of the WPSConfig class. WPSCofnig is a single. If there is need for * reinstantitation, use forceInitialization(). * * @param path * path to the wps_config.xml * @return WPSConfig object representing the wps_config.xml from the given path */ public static WPSConfig getInstance(String path) { LOGGER.debug("Getting WPSConfig instance... from path: {}", path); if (wpsConfig == null) { try { wpsConfig = new WPSConfig(path); configPath = path; } catch (XmlException e) { LOGGER.error("Failed to initialize WPS. Reason: " + e.getMessage()); throw new RuntimeException("Failed to initialize WPS. Reason: " + e.getMessage()); } catch (IOException e) { LOGGER.error("Failed to initialize WPS. Reason: " + e.getMessage()); throw new RuntimeException("Failed to initialize WPS. Reason: " + e.getMessage()); } } return wpsConfig; } public static WPSConfig getInstance(ServletConfig config) { LOGGER.debug("Getting WPSConfig instance... with ServletConfig: {}", config.toString()); String path = getConfigPath(config); LOGGER.debug("Found config file under " + path); return getInstance(path); } public static String getConfigPath(ServletConfig config) { Optional<ServletConfig> servletConfig = Optional.fromNullable(config); for (WPSConfigFileStrategy strategy : getWPSConfigFileStrategies()) { Optional<File> file = strategy.find(servletConfig); if (file.isPresent()) { return file.get().getAbsolutePath(); } } throw new RuntimeException("Could not find and load wps_config.xml"); } private static List<WPSConfigFileStrategy> getWPSConfigFileStrategies() { return ImmutableList.of(new SystemPropertyStrategy(), new JNDIContextStrategy(), new HomeFolderStrategy(), new InitParameterStrategy(), new RelativeInitParameterStrategy(), new DefaultPathStrategy(), new ClassPathStrategy(), new WebAppTargetStrategy(), new WebAppSourceStrategy(), new WebAppPathStrategy(), new LastResortStrategy()); } /** * This method retrieves the full path for the file (wps_config.xml), searching in WEB-INF/config. This is * only applicable for webapp applications. To customize this, please use directly * {@link WPSConfig#forceInitialization(String)} and then getInstance(). * * @return */ public static String getConfigPath() { if (configPath == null) { return getConfigPath(null); } return configPath; } public WPSConfigurationImpl getWPSConfig() { return wpsConfigXMLBeans; } public Parser[] getRegisteredParser() { return wpsConfigXMLBeans.getDatahandlers().getParserList().getParserArray(); } public Parser[] getActiveRegisteredParser() { Parser[] parsers = getRegisteredParser(); ArrayList<Parser> activeParsers = new ArrayList<Parser>(parsers.length); for (Parser parser : parsers) { if (parser.getActive()) { activeParsers.add(parser); } } Parser[] parArr = {}; return activeParsers.toArray(parArr); } public Generator[] getRegisteredGenerator() { return wpsConfigXMLBeans.getDatahandlers().getGeneratorList().getGeneratorArray(); } public Generator[] getActiveRegisteredGenerator() { Generator[] generators = getRegisteredGenerator(); ArrayList<Generator> activeGenerators = new ArrayList<Generator>(generators.length); for (Generator generator : generators) { if (generator.getActive()) { activeGenerators.add(generator); } } Generator[] genArr = {}; return activeGenerators.toArray(genArr); } public Repository[] getRegisterdAlgorithmRepositories() { return wpsConfigXMLBeans.getAlgorithmRepositoryList().getRepositoryArray(); } public Property[] getPropertiesForGeneratorClass(String className) { Generator[] generators = wpsConfigXMLBeans.getDatahandlers().getGeneratorList().getGeneratorArray(); for (Generator generator : generators) { if (generator.getClassName().equals(className)) { return generator.getPropertyArray(); } } return (Property[]) Array.newInstance(Property.class, 0); } public Format[] getFormatsForGeneratorClass(String className) { Generator[] generators = wpsConfigXMLBeans.getDatahandlers().getGeneratorList().getGeneratorArray(); for (Generator generator : generators) { if (generator.getClassName().equals(className)) { return generator.getFormatArray(); } } return (Format[]) Array.newInstance(Format.class, 0); } public Property[] getPropertiesForParserClass(String className) { Parser[] parsers = wpsConfigXMLBeans.getDatahandlers().getParserList().getParserArray(); for (Parser parser : parsers) { if (parser.getClassName().equals(className)) { return parser.getPropertyArray(); } } return (Property[]) Array.newInstance(Property.class, 0); } public Property[] getPropertiesForServer() { return getWPSConfig().getServer().getPropertyArray(); } public Format[] getFormatsForParserClass(String className) { Parser[] parsers = wpsConfigXMLBeans.getDatahandlers().getParserList().getParserArray(); for (Parser parser : parsers) { if (parser.getClassName().equals(className)) { return parser.getFormatArray(); } } return (Format[]) Array.newInstance(Format.class, 0); } public boolean isParserActive(String className) { Parser[] activeParser = getActiveRegisteredParser(); for (Parser parser : activeParser) { if (parser.getClassName().equals(className)) { return parser.getActive(); } } return false; } public boolean isGeneratorActive(String className) { Generator[] generators = getActiveRegisteredGenerator(); for (Generator generator : generators) { if (generator.getClassName().equals(className)) { return generator.getActive(); } } return false; } public boolean isRepositoryActive(String className) { Repository[] repositories = getRegisterdAlgorithmRepositories(); for (Repository repository : repositories) { if (repository.getClassName().equals(className)) { return repository.getActive(); } } return false; } public Property[] getPropertiesForRepositoryClass(String className) { Repository[] repositories = getRegisterdAlgorithmRepositories(); for (Repository repository : repositories) { if (repository.getClassName().equals(className)) { return repository.getPropertyArray(); } } return (Property[]) Array.newInstance(Property.class, 0); } public Property getPropertyForKey(Property[] properties, String key) { for (Property property : properties) { if (property.getName().equalsIgnoreCase(key)) { return property; } } return null; } /** * * @return directory of the configuration folder */ public static String getConfigDir() { String dir = getConfigPath(); return dir.substring(0, dir.lastIndexOf(CONFIG_FILE_NAME)); } public static String getServerBaseURL(){ ServerDocument.Server server = WPSConfig.getInstance().getWPSConfig().getServer(); String hostName = server.getHostname(); String hostPort = server.getHostport(); String webAppPath = server.getWebappPath(); hostPort = (hostPort != null && !hostPort.isEmpty()) ? ":" + hostPort : ""; webAppPath = (webAppPath != null && !webAppPath.isEmpty()) ? "/" + webAppPath : ""; return String.format(server.getProtocol() + "://%s%s%s", hostName, hostPort, webAppPath); } public static abstract class WPSConfigFileStrategy { public Optional<File> find(Optional<ServletConfig> servletConfig) { return checkPath(getPath(servletConfig)); } private Optional<File> checkPath(String path) { if (path != null && !path.isEmpty()) { LOGGER.debug("Checking {} for WPS config", path); File file = new File(path); if (!file.exists()) { LOGGER.debug("{} does not exist", path); } else if (!file.isFile()) { LOGGER.debug("{} is not a file", path); } else if (!file.canRead()) { LOGGER.debug("{} is not readable", path); } else { return Optional.of(file); } } return Optional.absent(); } protected abstract String getPath(Optional<ServletConfig> servletConfig); } private static class SystemPropertyStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { return System.getProperty(CONFIG_FILE_PROPERTY); } } private static class JNDIContextStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { try { Context ctx = (Context) new InitialContext().lookup("java:comp/env"); if (ctx == null) { return null; } return (String) ctx.lookup(CONFIG_FILE_PROPERTY); } catch (NamingException ex) { LOGGER.info("Can not get java:comp/env context", ex); return null; } } } private static class InitParameterStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { return servletConfig.isPresent() ? servletConfig.get().getInitParameter(CONFIG_FILE_PROPERTY) : null; } } private static class RelativeInitParameterStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { if (servletConfig.isPresent()) { String path = servletConfig.get().getInitParameter(CONFIG_FILE_PROPERTY); if (path != null) { return servletConfig.get().getServletContext().getRealPath(path); } } return null; } } private static class DefaultPathStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { return servletConfig.isPresent()? servletConfig.get().getServletContext() .getRealPath(CONFIG_FILE_DIR + File.separator + CONFIG_FILE_NAME) : null; } } private static class ClassPathStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { URL configPathURL = WPSConfig.class.getClassLoader().getResource(CONFIG_FILE_NAME); if (configPathURL != null) { String config = configPathURL.getFile(); try { config = URLDecoder.decode(config, URL_DECODE_ENCODING); } catch (UnsupportedEncodingException e) { LOGGER.error("Could not devode URL to get config from class path.", e); return null; } return config; } return null; } } private static class WebAppTargetStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { String domain = WPSConfig.class.getProtectionDomain().getCodeSource().getLocation().getFile(); int index1 = domain.indexOf("52n-wps-parent"); if (index1 > 0) { // try to load from classpath String ds = domain.substring(0, index1 + 14); String path; try { path = URLDecoder.decode(ds, URL_DECODE_ENCODING); } catch (UnsupportedEncodingException e) { LOGGER.error("could not decode URL", e); return null; } path = path + File.separator + "52n-wps-webapp" + File.separator + "target"; File f = new File(path); String[] dirs = f.getAbsoluteFile().list(); if (dirs != null) { for (String dir : dirs) { if (dir.startsWith("52n-wps-webapp") && !dir.endsWith(".war")) { path = path + File.separator + dir + File.separator + CONFIG_FILE_DIR + "/" + CONFIG_FILE_NAME; } } return path; } } return null; } } private static class WebAppSourceStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { String domain = WPSConfig.class.getProtectionDomain().getCodeSource().getLocation().getFile(); int index1 = domain.indexOf("52n-wps-parent"); if (index1 > 0) { // try to load from classpath String ds = domain.substring(0, index1 + 14); String path; try { path = URLDecoder.decode(ds, URL_DECODE_ENCODING); } catch (UnsupportedEncodingException e) { LOGGER.error("could not decode URL", e); return null; } path = path + File.separator + "52n-wps-webapp"; File f = new File(path); String[] dirs = f.getAbsoluteFile().list(); if (dirs != null) { for (String dir : dirs) { if (dir.equals("src")) { path = path + File.separator + dir + File.separator + "main" + File.separator + "webapp" + File.separator + CONFIG_FILE_DIR + File.separator + CONFIG_FILE_NAME; } } if ( ! (new File(path)).exists()) { return null; } return path; } } return null; } } private static class WebAppPathStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { //XXX: any objectctions against using getResource("/") instead? // String domain = WPSConfig.class.getProtectionDomain().getCodeSource().getLocation().getFile(); String domain; try { domain = new File(WPSConfig.class.getResource("/").toURI()).toString(); } catch (URISyntaxException e) { throw new RuntimeException(e); } int index = domain.indexOf("WEB-INF"); if (index > 0) { String substring = domain.substring(0, index); // if ( !substring.endsWith("/")) { // substring = substring + "/"; // } // substring = substring + CONFIG_FILE_DIR + File.separator + CONFIG_FILE_NAME; File configDir = new File(new File(substring), CONFIG_FILE_DIR); if (configDir.exists() && configDir.isDirectory()) { return new File(configDir, CONFIG_FILE_NAME).getAbsolutePath(); } } return null; } } private static class LastResortStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { String domain = WPSConfig.class.getProtectionDomain().getCodeSource().getLocation().getFile(); try { domain = URLDecoder.decode(domain, "UTF-8"); } catch (UnsupportedEncodingException e) { LOGGER.warn("Could not decode URL of WPSConfig class, continuing."); } /* * domain should always be 52n-wps-commons/target/classes so we just go three directories up */ File classDir = new File(domain); File projectRoot = classDir.getParentFile().getParentFile().getParentFile(); String path = projectRoot.getAbsolutePath(); String[] dirs = projectRoot.getAbsoluteFile().list(); for (String dir : dirs) { if (dir.startsWith("52n-wps-webapp") && !dir.endsWith(".war")) { path = path + File.separator + dir + File.separator + "src" + File.separator + "main" + File.separator + "webapp" + File.separator + CONFIG_FILE_DIR + File.separator + CONFIG_FILE_NAME; } } LOGGER.info(path); return path; } } private static class HomeFolderStrategy extends WPSConfigFileStrategy { @Override protected String getPath(Optional<ServletConfig> servletConfig) { return System.getProperty("user.home") + File.separator + CONFIG_FILE_NAME; } } }