/**
* 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;
}
}
}