/**
* File ./src/main/java/de/lemo/dms/core/config/ServerConfiguration.java Lemo-Data-Management-Server for learning
* analytics. Copyright (C) 2013 Leonard Kappe, Andreas Pursian, Sebastian Schwarzrock, Boris Wenzlaff
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
**/
/**
* File ./main/java/de/lemo/dms/core/config/ServerConfiguration.java Date 2013-01-24 Project Lemo Learning Analytics
*/
package de.lemo.dms.core.config;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.apache.log4j.Logger;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import de.lemo.dms.connectors.ConnectorManager;
import de.lemo.dms.connectors.ESourcePlatform;
import de.lemo.dms.connectors.IConnector;
import de.lemo.dms.db.DBConfigObject;
import de.lemo.dms.db.IDBHandler;
import de.lemo.dms.db.hibernate.HibernateDBHandler;
import de.lemo.dms.db.hibernate.MiningHibernateUtil;
/**
* Manages and loads all configuration data for the server and all connectors.
*
* @author Leonard Kappe
*/
public enum ServerConfiguration {
/**
* Singleton instance.
*/
INSTANCE;
private final Logger logger = Logger.getLogger(this.getClass());
private IDBHandler miningDbHandler;
private long startTime;
private String serverName;
private Integer pathAnalysisTimeout;
/**
* Gets the instance of the server's configuration.
*
* @return the singleton instance
*/
public static ServerConfiguration getInstance() {
return INSTANCE;
}
/**
* The context path is used to determine the configuration file's name. The first char (a slash by convention) will be
* ignored. Some context paths aren't valid file names (like the root context or those with sub-paths), therefore
* tomcat's naming conventions for .war files will be used. Examples:
*
* <pre>
* context -> file name
* -------------------------
* / -> ROOT.xml
* /lemo -> lemo.xml
* /lemo/foo -> lemo#foo.xml
* </pre>
*
* @param contextPath
* the application's context path (URL relative to context root)
*/
public void loadConfig(final String contextPath) {
final LemoConfig lemoConfig = this.readConfigFiles(contextPath);
this.serverName = lemoConfig.dataManagementServer.name;
if(lemoConfig.dataManagementServer.pathAnalysisTimeout > 0) {
this.pathAnalysisTimeout = lemoConfig.dataManagementServer.pathAnalysisTimeout;
}
this.logger.info("Inititalizing mining database");
final DBConfigObject miningDBConfig = this.createDBConfig(lemoConfig.dataManagementServer.databaseProperties);
MiningHibernateUtil.initSessionFactory(miningDBConfig);
this.miningDbHandler = new HibernateDBHandler();
final List<IConnector> connectors = this.createConnectors(lemoConfig.dataManagementServer.connectors);
final ConnectorManager connectorManager = ConnectorManager.getInstance();
for(final IConnector connector : connectors) {
connectorManager.addConnector(connector);
}
}
private LemoConfig readConfigFiles(String contextPath) {
String contextPathTMP = contextPath;
if(contextPathTMP.isEmpty()) {
// empty means root context, Tomcat convention for root context war files is 'ROOT.war'
contextPathTMP = "/ROOT";
}
// remove leading slash, replace any slashes with hashes, like Tomcat does with the .war files
final String warName = contextPathTMP.substring(1).replace('/', '#');
final Set<String> fileNames = new LinkedHashSet<String>();
// default, based on war name
fileNames.add(warName + ".xml");
int lastHash;
String warPath = warName;
while((lastHash = warPath.lastIndexOf('#')) > 0) {
// Try to read more generic name by removing sub-paths,
// e.g. `/analytics/lemo/dms` -> `/analytics/lemo` -> `/analytics`.
// This is useful to read a single file in a shared directory that contains both server's configurations.
warPath = warPath.substring(0, lastHash);
fileNames.add(warPath + ".xml");
}
// eventually try generic lemo.xml for use in local development
fileNames.add("lemo.xml");
LemoConfig lemoConfig = null;
try {
final Unmarshaller jaxbUnmarshaller = JAXBContext.newInstance(
LemoConfig.class).createUnmarshaller();
for(final String fileName : fileNames) {
final URL resource = this.getClass().getResource("/" + fileName);
InputStream in = null;
try {
in = resource.openStream();
} catch (Exception e) {
this.logger.info("Looking for config file .... " + fileName + " not found!");
}
if(in != null) {
this.logger.info("Using config file: " + fileName);
lemoConfig = (LemoConfig) jaxbUnmarshaller.unmarshal(in);
}
}
} catch (final JAXBException e) {
// no way to recover, re-throw at runtime
throw new RuntimeException(e);
} catch (final Exception e) {
logger.error(e.getMessage(), e);
}
if(lemoConfig == null) {
final String files = fileNames.toString();
throw new RuntimeException(
"No config file found. Tried to read files in following order but none were found in the classpath: "
+ files.substring(1, files.length() - 1));
}
this.logger.info("Config loaded for '" + lemoConfig.dataManagementServer.name + "'");
return lemoConfig;
}
private List<IConnector> createConnectors(final List<Connector> connectorConfigurations) {
List<IConnector> result = Lists.newArrayList();
for(Connector connectorConfig : connectorConfigurations) {
this.logger.info("Inititalizing connector: " + connectorConfig);
ESourcePlatform platform = ESourcePlatform.valueOf(connectorConfig.platformType);
DBConfigObject config = this.createDBConfig(connectorConfig.properties);
result.add(platform.newConnector(connectorConfig.platformId, connectorConfig.name, config,
connectorConfig.courseIdFilter, connectorConfig.courseLoginFilter));
}
return result;
}
private DBConfigObject createDBConfig(final List<PropertyConfig> properties) {
final HashMap<String, String> propertyMap = Maps.newHashMap();
this.logger.debug("Properties: " + propertyMap.size());
for(final PropertyConfig property : properties) {
this.logger.debug(" " + property.key + ":\t" + property.value);
propertyMap.put(property.key, property.value);
}
return new DBConfigObject(propertyMap);
}
/**
* Gets the server start time or last context reload.
*
* @return timestamp of server start
*/
public long getStartTime() {
return this.startTime;
}
/**
* Sets the server start time.
*
* @param startTime
* timestamp of server start
*/
public void setStartTime(final long startTime) {
this.startTime = startTime;
}
/**
* Gets the mining database's handler.
*
* @return a mining database handler
*/
public IDBHandler getMiningDbHandler() {
return this.miningDbHandler;
}
/**
* Gets the applications name.
*
* @return the servers/applications name
*/
public String getName() {
return this.serverName;
}
/**
* The timeout or null if no timeout should be used.
*
* @return timeout in seconds
*/
public Integer getPathAnalysisTimeout() {
return pathAnalysisTimeout;
}
}