/**
* Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* - Apache License, version 2.0
* - Apache Software License, version 1.0
* - GNU Lesser General Public License, version 3
* - Mozilla Public License, versions 1.0, 1.1 and 2.0
* - Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* icense version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
package org.n52.ses.util.common;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.muse.core.Environment;
import org.apache.muse.ws.addressing.EndpointReference;
import org.n52.oxf.xmlbeans.parser.GMLAbstractFeatureCase;
import org.n52.oxf.xmlbeans.parser.XMLBeansParser;
import org.n52.ses.api.IUnitConverter;
import org.n52.ses.api.IFilterEngine;
import org.n52.ses.api.ISESFilePersistence;
import org.n52.ses.api.common.FreeResourceListener;
import org.n52.ses.api.common.GlobalConstants;
import org.n52.ses.api.ws.IPublisherEndpoint;
import org.n52.ses.api.ws.IRegisterPublisher;
import org.n52.ses.api.ws.ISubscriptionManager;
import org.n52.ses.util.concurrent.IConcurrentNotificationHandler;
import org.n52.ses.util.concurrent.ITimeoutEstimation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Use this class to get global functions and parameters.
*
* Please use a public static final String for each key.
*
* If there is a default value for a key add it to the SESProperties class.
*
* If you want that your key to be easily visible, also add it in the
* ses_config.properties file (src/main/resources/sesconfig/).
*
* @author Matthes Rieke <m.rieke@uni-muenster.de>
*
*/
public class ConfigurationRegistry {
/**
* Key for the postgres port
*/
public static final String POSTGRES_PORT_KEY = "POSTGRES_PORT";
/**
* Key for the postgres user
*/
public static final String POSTGRES_USER_KEY = "POSTGRES_USER";
/**
* Key for the postgres password
*/
public static final String POSTGRES_PWD_KEY = "POSTGRES_PWD";
/**
* the host of the database
*/
public static final String POSTGRES_HOST_KEY = "POSTGRES_HOST";
/**
* Key for the postgres database
*/
public static final String POSTGRES_DATABASE = "POSTGRES_DATABASE";
/**
* Key for the filter engine in use
*/
public static final String USED_FILTER_ENGINE = "USED_FILTER_ENGINE";
/**
* Key for the re-subscribe setting
*
* Allowed values are 'yes' and 'no'
*/
public static final String RESUBSCRIBE_ON_STARTUP = "RESUBSCRIBE_ON_STARTUP";
/**
* Key for the path to the SES instance
*/
public static final String SES_INSTANCE = "SES_INSTANCE";
/**
* Key for the indication if the instance is used for GENESIS.
*
* This has implications on the encoding of the output.
*
* Allowed values are <code>true</code> and <code>false</code>
*/
public static final String USE_FOR_GENESIS = "GENESIS";
/**
* The namespace used for Genesis Topics
*/
public static final String GENESIS_NAMESPACE = "GENESIS_NAMESPACE";
/**
* the concrete topic expression used for Genesis topics
* (must include the prefix)
*/
public static final String GENESIS_TOPIC = "GENESIS_TOPIC";
/**
* milliseconds to wait before performing the startup procedures
*/
public static final String TIME_TO_WAKEUP = "TIME_TO_WAKEUP";
/**
* Indicates which parser to use for incoming notifications
*
* Allowed values are "basic"(default) and "generic"
*/
public static final String PARSER = "PARSER";
/**
* Instructs the SES to preserve the geometry information
* when selecting only a property and not a complete
* event. This will only be done for select functions
* that act only on one event (e.g. SelectProperty but
* not SelectSum). Will also only be done on simple patterns.
*
* ATTENTION: this may cause errors as some events might
* not contain a geometry!
*
* Allowed values are <code>false</code> (default) and <code>true</code>.
*/
public static final String PRESERVE_GEOMETRY = "PRESERVE_GEOMETRY";
/**
* Key for the indication if the instance is used for BAW.
*
* This has implications on the encoding of some select functions.
*
* Allowed values are <code>true</code> and <code>false</code>
*/
public static final String USE_FOR_BAW = "BAW";
/**
* the property for the enrichment flag.
*/
public static final String USE_ENRICHMENT = "USE_ENRICHMENT";
/**
* property for using the external request logging webapp
*/
public static final String USE_REQUEST_LOGGER = "USE_REQUEST_LOGGER";
/**
* the url for the logging webapp
*/
public static final String REQUEST_LOGGER_URL = "REQUEST_LOGGER_URL";
private static final Logger logger = LoggerFactory.getLogger(ConfigurationRegistry.class);
/**
* the key for the EsperController fqcn
*/
public static final String EML_CONTROLLER = "EML_CONTROLLER";
public static final String EML_001_IMPL = "org.n52.ses.eml.v001.filterlogic.esper.EsperController";
public static final String EML_002_IMPL = "org.n52.ses.eml.v002.filterlogic.esper.EsperController";
/**
* the key for maximum number of threads per threadpool
*/
public static final String MAX_THREADS = "MAX_THREADS";
/**
* th key for the {@link IConcurrentNotificationHandler} impl
*/
public static final String CONCURRENT_WORKER = "CONCURRENT_WORKER";
/**
* the initial timeout at setup time used in the {@link IConcurrentNotificationHandler}
* for waiting for processing of elements has been finished.
*/
public static final String CONCURRENT_MAXIMUM_TIMEOUT = "CONCURRENT_MAXIMUM_TIMEOUT";
/**
* the timeout which will never be undercutted in the {@link IConcurrentNotificationHandler}
* for waiting for processing of elements has been finished.
*/
public static final String CONCURRENT_MINIMUM_TIMEOUT = "CONCURRENT_MINIMUM_TIMEOUT";
/**
* the key for enabling the concurrent fifo worker queue. this should be activated
* in environments where order of messages is a requirment for filter matching (e.g. EML
* causel patterns)
*/
public static final String USE_CONCURRENT_ORDERED_HANDLING = "USE_CONCURRENT_ORDERED_HANDLING";
/**
* key for enabling the use of intelligent timeout estimation
*/
public static final String CONCURRENT_INTELLIGENT_TIMEOUT = "CONCURRENT_INTELLIGENT_TIMEOUT";
/**
* the key for the {@link ITimeoutEstimation} implementation to be used.
*/
public static final String TIMEOUT_ESTIMATION = "TIMEOUT_ESTIMATION";
/**
* key for enalbing xml validation
*/
public static final String VALIDATE_XML = "VALIDATE_XML";
/**
* key for the timeout when sending notification to consumers
*/
public static final String NOTIFY_TIMEOUT = "NOTIFY_TIMEOUT";
/**
* the location of the config file (relative to WEB-INF/classes)
*/
public static final String CONFIG_FILE = "sesconfig/ses_config.xml";
/**
* use gzip compression for outgoing requests?
*/
public static final String USE_GZIP = "USE_GZIP";
/**
* minimum size in bytes to enable gzip
*/
public static final String MINIMUM_GZIP_SIZE = "MINIMUM_GZIP_SIZE";
/**
* used if the service instance is secured with HTTP basic authentication
*/
public static final String BASIC_AUTH_USER = "BASIC_AUTH_USER";
public static final String BASIC_AUTH_PASSWORD = "BASIC_AUTH_PASSWORD";
/**
* the number of persisted events. if > 0, persistence of events will be activated
*/
public static final String MAX_PERSISTED_EVENTS = "MAX_PERSISTED_EVENTS";
private static ConfigurationRegistry _instance;
private SESProperties parameters;
private EndpointReference sesPortTypeEPR;
private IUnitConverter unitConverter;
private List<ISubscriptionManager> reresubs;
private List<IPublisherEndpoint> rerepubs;
private ISESFilePersistence filePersistence;
private IFilterEngine filterEngine;
private IRegisterPublisher registerPublisher;
private Environment environment;
private String subMgrWsdl;
private boolean persistencyEnabled;
private List<FreeResourceListener> freeResourceListeners = new ArrayList<FreeResourceListener>();
private File configFile;
/**
* @param config InputStream of config file.
* @param defaultURI the default URI of the service
* @param unitConverter
*/
private ConfigurationRegistry(InputStream config, String defaultURI,
IUnitConverter unitConverter) {
this.unitConverter = unitConverter;
try {
this.sesPortTypeEPR = new EndpointReference(new URI("http://localhost/URIfailure"));
if (defaultURI != null) {
this.sesPortTypeEPR = new EndpointReference(new URI(defaultURI));
}
} catch (URISyntaxException e1) {
logger.warn(e1.getMessage(), e1);
}
this.parameters = new SESProperties();
try {
this.parameters.load(config);
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
/*
* register lax validation cases
*/
XMLBeansParser.registerLaxValidationCase(GMLAbstractFeatureCase.getInstance());
/*
* Check if we have globally deactivated validation.
* This could be useful in predictable environments where
* every request is sort of the same.
*/
XMLBeansParser.setValidationGloballyEnabled(Boolean.parseBoolean(
this.parameters.getProperty(VALIDATE_XML)));
this.reresubs = new ArrayList<ISubscriptionManager>();
this.rerepubs = new ArrayList<IPublisherEndpoint>();
this.persistencyEnabled = Boolean.parseBoolean(this.parameters.getProperty(RESUBSCRIBE_ON_STARTUP));
}
/**
* @return The singleton instance of the {@link ConfigurationRegistry}
*/
public static synchronized ConfigurationRegistry getInstance() {
while (_instance == null) {
try {
logger.info("Thread {} is waiting for the instance...", Thread.currentThread().getName());
ConfigurationRegistry.class.wait(5000);
} catch (InterruptedException e) {
logger.warn(e.getMessage(), e);
}
}
return _instance;
}
/**
* Initialize the ConfigurationRegistry (with properties file, Logging
* and default environment-URI (http://xyz:123/ses-webapp/Broker)
*
* @param config InputStream for properties file
* @param env the muse environment
* @param defaultURI Default muse-environment URI
* @param unitConverter converter for units of measurement
*/
public static synchronized void init(InputStream config, Environment env,
IUnitConverter unitConverter) {
if (_instance == null) {
logger.info("Thread {} is initializing the instance...", Thread.currentThread().getName());
_instance = new ConfigurationRegistry(config, env == null ? "" : env.getDefaultURI(), unitConverter);
_instance.setEnvironment(env);
ConfigurationRegistry.class.notifyAll();
}
}
/**
* @param env the muse environment
*/
private void setEnvironment(Environment env) {
this.environment = env;
}
/**
* @return the muse environment
*/
public Environment getEnvironment() {
return this.environment;
}
/**
* Returns a configuration value.
*
* @param key the parameter
* @return the corresponding value
*/
public String getPropertyForKey(String key) {
if (this.parameters != null) {
return this.parameters.getProperty(key);
}
return null;
}
/**
* @return the default service endpoint.
*/
public EndpointReference getSesPortTypeEPR() {
return this.sesPortTypeEPR;
}
/**
* @return the global UnitConverter
*/
public IUnitConverter getUnitConverter() {
return this.unitConverter;
}
/**
* adds a registered subscription manager
* @param sesSubscriptionManager the subscription manager
*/
public synchronized void addReregisteredSubMgr(ISubscriptionManager sesSubscriptionManager) {
synchronized (this.reresubs) {
this.reresubs.add(sesSubscriptionManager);
this.reresubs.notifyAll();
}
}
/**
* adds a registered publisher endpoint
* @param repub the publishers
*/
public void addReregisteredPublisher(IPublisherEndpoint repub) {
synchronized (this.rerepubs) {
this.rerepubs.add(repub);
this.rerepubs.notifyAll();
}
}
/**
*
* @return the registered subscription managers
*/
public synchronized List<ISubscriptionManager> getReresubs() {
return this.reresubs;
}
/**
* @return the registered publisher endpoints
*/
public List<IPublisherEndpoint> getRerepubs() {
return rerepubs;
}
/**
* This methods blocks the calling thread until
* all persistent Publisher instances are active.
*/
public void waitForAllPersistentPublishers() {
synchronized (this.rerepubs) {
while (this.filePersistence == null || this.rerepubs.size() != this.filePersistence.getPersistentPublisherCount()) {
try {
this.rerepubs.wait();
} catch (InterruptedException e) {
logger.warn(e.getMessage(), e);
}
}
}
}
public void setFilePersistence(ISESFilePersistence fp) {
this.filePersistence = fp;
synchronized (this.rerepubs) {
this.rerepubs.notifyAll();
}
}
public ISESFilePersistence getFilePersistence() {
return filePersistence;
}
/**
* Registers the {@link IFilterEngine}
*
* @param filterEngine instance of implementing class
*/
public void setFilterEngine(IFilterEngine filterEngine) {
this.filterEngine = filterEngine;
}
/**
* @return the instance of the {@link IFilterEngine}
*/
public IFilterEngine getFilterEngine() {
return this.filterEngine;
}
/**
* @param registerpublisher the global one
*/
public void setGlobalRegisterPublisher(IRegisterPublisher registerpublisher) {
this.registerPublisher = registerpublisher;
}
/**
* @return the global one
*/
public IRegisterPublisher getGlobalRegisterPublisher() {
return this.registerPublisher;
}
/**
* Method for initiating graceful shutdown of the SES
*/
public void shutdown() {
this.filterEngine.shutdown();
for (FreeResourceListener frl : this.freeResourceListeners) {
frl.freeResources();
}
}
/**
* @param string the global URL for the submanager wsdl
*/
public void setSubscriptionManagerWsdl(String string) {
this.subMgrWsdl = string;
}
/**
* @return the global URL for the submanager wsdl
*/
public String getSubMgrWsdl() {
if (this.subMgrWsdl == null) {
String tmp = getEnvironment().getDefaultURI().substring(0,
getEnvironment().getDefaultURI().lastIndexOf("/services"));
String subMgrUrl = tmp + "/services/" + GlobalConstants.SUBSCRIPTION_MANAGER_CONTEXT_PATH + "?wsdl";
return subMgrUrl;
}
return this.subMgrWsdl;
}
public boolean persistencyEnabled() {
return this.persistencyEnabled;
}
/**
* @param frl the listener to add. freeResource() gets called during shutdown.
*/
public void registerFreeResourceListener(
FreeResourceListener frl) {
this.freeResourceListeners.add(frl);
}
public SESProperties getProperties() {
return this.parameters;
}
public void setPropertyForKey(String key, String value) {
this.parameters.put(key, value);
}
public void saveConfiguration() throws IOException {
FileWriter fw = new FileWriter(this.configFile);
this.parameters.store(fw, null);
}
public List<String> getRegisteredParserClasses() {
return this.parameters.getRegisteredParsers();
}
public static boolean isAvailable() {
return _instance != null;
}
/**
* @param key the property key
* @return the integer value or null if not a well-formed integer number
*/
public Integer getIntegerProperty(String key) {
String v = getPropertyForKey(key);
try {
return Integer.parseInt(v);
}
catch (NumberFormatException e) {
logger.warn("Could not parse integer property: "+v);
}
return null;
}
}