/**
* Global Sensor Networks (GSN) Source Code
* Copyright (c) 2006-2016, Ecole Polytechnique Federale de Lausanne (EPFL)
*
* This file is part of GSN.
*
* GSN 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
* (at your option) any later version.
*
* GSN 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 GSN. If not, see <http://www.gnu.org/licenses/>.
*
* File: src/ch/epfl/gsn/Main.java
*
* @author parobert
* @author cl3m
* @author Jerome Rousselot
* @author gsn_devs
* @author Mehdi Riahi
* @author Ali Salehi
* @author Behnaz Bostanipour
* @author Timotee Maret
* @author Julien Eberle
*
*/
package ch.epfl.gsn;
import ch.epfl.gsn.config.GsnConf;
import ch.epfl.gsn.config.VsConf;
import ch.epfl.gsn.data.DataStore;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.SplashScreen;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.zeromq.ZContext;
import ch.epfl.gsn.ContainerImpl;
import ch.epfl.gsn.DataDistributer;
import ch.epfl.gsn.Main;
import ch.epfl.gsn.Mappings;
import ch.epfl.gsn.VSensorLoader;
import ch.epfl.gsn.beans.BeansInitializer;
import ch.epfl.gsn.beans.ContainerConfig;
import ch.epfl.gsn.beans.StorageConfig;
import ch.epfl.gsn.beans.VSensorConfig;
import ch.epfl.gsn.delivery.LocalDeliveryWrapper;
import ch.epfl.gsn.monitoring.MemoryMonitor;
import ch.epfl.gsn.monitoring.Monitorable;
import ch.epfl.gsn.monitoring.MonitoringServer;
import ch.epfl.gsn.networking.zeromq.ZeroMQDeliveryAsync;
import ch.epfl.gsn.networking.zeromq.ZeroMQDeliverySync;
import ch.epfl.gsn.networking.zeromq.ZeroMQProxy;
import ch.epfl.gsn.storage.SQLValidator;
import ch.epfl.gsn.storage.StorageManager;
import ch.epfl.gsn.storage.StorageManagerFactory;
import ch.epfl.gsn.storage.hibernate.DBConnectionInfo;
import ch.epfl.gsn.utils.ValidityTools;
import ch.epfl.gsn.vsensor.SQLValidatorIntegration;
import ch.epfl.gsn.wrappers.WrappersUtil;
public final class Main {
public static final int DEFAULT_MAX_DB_CONNECTIONS = 8;
public static final String DEFAULT_GSN_CONF_FOLDER = "../conf";
public static final String DEFAULT_VIRTUAL_SENSOR_FOLDER = "../virtual-sensors";
public static transient Logger logger = LoggerFactory.getLogger ( Main.class );
/**
* Mapping between the wrapper name (used in addressing of stream source)
* into the class implementing DataSource.
*/
private static Properties wrappers ;
private static Main singleton ;
public static String gsnConfFolder = DEFAULT_GSN_CONF_FOLDER;
public static String virtualSensorDirectory = DEFAULT_VIRTUAL_SENSOR_FOLDER;
private static ZeroMQProxy zmqproxy;
private static StorageManager mainStorage;
private static StorageManager windowStorage;
private static StorageManager validationStorage;
private static ZContext zmqContext = new ZContext();
private static HashMap<Integer, StorageManager> storages = new HashMap<Integer, StorageManager>();
private static HashMap<VSensorConfig, StorageManager> storagesConfigs = new HashMap<VSensorConfig, StorageManager>();
private ContainerConfig containerConfig;
private MonitoringServer monitoringServer;
private static VSensorLoader vsLoader;
private static GsnConf gsnConf;
private static Map <String,VsConf> vsConf = new HashMap<String,VsConf>();
private static ArrayList<Monitorable> toMonitor = new ArrayList<Monitorable>();
/*
* Retrieving ThreadMXBean instance of JVM
* It would be used for monitoring CPU time of each virtual sensor
*/
private static ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
private Main() throws Exception {
ValidityTools.checkAccessibilityOfFiles ( WrappersUtil.DEFAULT_WRAPPER_PROPERTIES_FILE , gsnConfFolder + "/gsn.xml");
ValidityTools.checkAccessibilityOfDirs ( virtualSensorDirectory );
containerConfig = loadContainerConfiguration();
updateSplashIfNeeded(new String[] {"GSN is starting...", "All GSN logs are available at: logs/ch.epfl.gsn.log"});
System.out.println("Global Sensor Networks (GSN) is starting...");
int maxDBConnections = System.getProperty("maxDBConnections") == null ? DEFAULT_MAX_DB_CONNECTIONS : Integer.parseInt(System.getProperty("maxDBConnections"));
int maxSlidingDBConnections = System.getProperty("maxSlidingDBConnections") == null ? DEFAULT_MAX_DB_CONNECTIONS : Integer.parseInt(System.getProperty("maxSlidingDBConnections"));
DataStore ds = new DataStore(gsnConf);
mainStorage = StorageManagerFactory.getInstance(containerConfig.getStorage().getJdbcDriver ( ) , containerConfig.getStorage().getJdbcUsername ( ) , containerConfig.getStorage().getJdbcPassword ( ) , containerConfig.getStorage().getJdbcURL ( ) , maxDBConnections);
StorageConfig sc = containerConfig.getSliding() != null ? containerConfig.getSliding().getStorage() : containerConfig.getStorage() ;
windowStorage = StorageManagerFactory.getInstance(sc.getJdbcDriver ( ) , sc.getJdbcUsername ( ) , sc.getJdbcPassword ( ) , sc.getJdbcURL ( ), maxSlidingDBConnections);
validationStorage = StorageManagerFactory.getInstance("org.h2.Driver", "sa", "", "jdbc:h2:mem:validator", Main.DEFAULT_MAX_DB_CONNECTIONS);
logger.trace ( "The Container Configuration file loaded successfully." );
// starting the monitoring socket
toMonitor.add(new MemoryMonitor());
monitoringServer = new MonitoringServer(containerConfig.getMonitorPort());
monitoringServer.start();
if (containerConfig.isZMQEnabled()){
//start the 0MQ proxy
zmqproxy = new ZeroMQProxy(containerConfig.getZMQProxyPort(),containerConfig.getZMQMetaPort());
}
VSensorLoader vsloader = VSensorLoader.getInstance ( virtualSensorDirectory );
File vsDir=new File(virtualSensorDirectory);
for (File f:vsDir.listFiles()){
if (f.getName().endsWith(".xml")){
VsConf vs= VsConf.load(f.getPath());
vsConf.put(vs.name(), vs);
}
}
Main.vsLoader = vsloader;
vsloader.addVSensorStateChangeListener(new SQLValidatorIntegration(SQLValidator.getInstance()));
vsloader.addVSensorStateChangeListener(DataDistributer.getInstance(LocalDeliveryWrapper.class));
if (containerConfig.isZMQEnabled())
vsloader.addVSensorStateChangeListener(DataDistributer.getInstance(ZeroMQDeliverySync.class));
vsloader.addVSensorStateChangeListener(DataDistributer.getInstance(ZeroMQDeliveryAsync.class));
ContainerImpl.getInstance().addVSensorDataListener(DataDistributer.getInstance(LocalDeliveryWrapper.class));
ContainerImpl.getInstance().addVSensorDataListener(DataDistributer.getInstance(ZeroMQDeliverySync.class));
ContainerImpl.getInstance().addVSensorDataListener(DataDistributer.getInstance(ZeroMQDeliveryAsync.class));
vsloader.startLoading();
}
private static void closeSplashIfneeded() {
if (isHeadless())
return;
SplashScreen splash = SplashScreen.getSplashScreen();
//Check if we have specified any splash screen
if (splash == null) {
return;
}
if (splash.isVisible())
splash.close();
}
private static void updateSplashIfNeeded(String message[]) {
boolean headless_check = isHeadless();
if (!headless_check) {
SplashScreen splash = SplashScreen.getSplashScreen();
if (splash == null)
return;
if (splash.isVisible()) {
//Get a graphics overlay for the splash screen
Graphics2D g = splash.createGraphics();
//Do some drawing on the graphics object
//Now update to the splash screen
g.setComposite(AlphaComposite.Clear);
g.fillRect(0,0,400,70);
g.setPaintMode();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.setFont(new Font("Arial",Font.BOLD,11));
for (int i=0;i<message.length;i++)
g.drawString(message[i], 13, 16*i+10);
splash.update();
}
}
}
private static boolean isHeadless() {
return GraphicsEnvironment.isHeadless();
}
public synchronized static Main getInstance() {
if (singleton==null)
try {
singleton=new Main();
} catch (Exception e) {
logger.error(e.getMessage(),e);
throw new RuntimeException(e);
}
return singleton;
}
public static void main(String[] args) {
if (args.length > 0) {
Main.gsnConfFolder = args[0];
}
if (args.length > 1) {
Main.virtualSensorDirectory = args[1];
}
updateSplashIfNeeded(new String[] {"GSN is trying to start.","All GSN logs are available at: logs/gsn.log"});
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
System.out.println("GSN is stopping...");
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}finally {
logger.warn("Forced exit...");
System.out.println("GSN is stopped (forced).");
Runtime.getRuntime().halt(1);
}
}}).start();
try {
logger.info("Shutting down GSN...");
if (vsLoader != null) {
vsLoader.stopLoading();
logger.info("All virtual sensors have been stopped, shutting down virtual machine.");
} else {
logger.warn("Could not shut down virtual sensors properly. We are probably exiting GSN before it has been completely initialized.");
}
} catch (Exception e) {
logger.warn("Error while reading from or writing to control connection: " + e.getMessage(), e);
}finally {
System.out.println("GSN is stopped.");
}
}
});
try {
Main.getInstance();
}catch (Exception e) {
logger.error(e.getMessage(), e);
updateSplashIfNeeded(new String[] {"Starting GSN failed! Look at logs/gsn.log for more information."});
try {
Thread.sleep(4000);
} catch (InterruptedException e1) {}
}
closeSplashIfneeded();
}
public static ContainerConfig loadContainerConfiguration() {
ValidityTools.checkAccessibilityOfFiles (WrappersUtil.DEFAULT_WRAPPER_PROPERTIES_FILE , gsnConfFolder + "/gsn.xml");
ValidityTools.checkAccessibilityOfDirs (virtualSensorDirectory);
ContainerConfig toReturn = null;
try {
toReturn = loadContainerConfig (gsnConfFolder + "/gsn.xml");
logger.info ( "Loading wrappers.properties at : " + WrappersUtil.DEFAULT_WRAPPER_PROPERTIES_FILE);
wrappers = WrappersUtil.loadWrappers(new HashMap<String, Class<?>>());
logger.info ( "Wrappers initialization ..." );
} catch (ClassNotFoundException e) {
logger.error ("The file wrapper.properties refers to one or more classes which don't exist in the classpath"+ e.getMessage());
System.exit (1);
}
return toReturn;
}
public static ContainerConfig loadContainerConfig (String gsnXMLpath) throws ClassNotFoundException {
if (!new File(gsnXMLpath).isFile()) {
logger.error("Couldn't find the gsn.xml file @: "+(new File(gsnXMLpath).getAbsolutePath()));
System.exit(1);
}
GsnConf gsn = GsnConf.load(gsnXMLpath);
gsnConf = gsn;
ContainerConfig conf=BeansInitializer.container(gsn);
Class.forName(conf.getStorage().getJdbcDriver());
conf.setContainerConfigurationFileName ( gsnXMLpath );
return conf;
}
public static Properties getWrappers() {
if (singleton==null )
return WrappersUtil.loadWrappers(new HashMap<String, Class<?>>());
return Main.wrappers;
}
public static Class < ? > getWrapperClass ( String id ) {
try {
String className = getWrappers().getProperty(id);
if (className ==null) {
logger.error("The requested wrapper: "+id+" doesn't exist in the wrappers.properties file.");
return null;
}
return Class.forName(className);
} catch (ClassNotFoundException e) {
logger.error(e.getMessage(),e);
}
return null;
}
/**
* Get's the GSN configuration without starting GSN.
* @return
* @throws Exception
*/
public static ContainerConfig getContainerConfig() {
if (singleton == null)
try {
return loadContainerConfig(Main.gsnConfFolder + "/gsn.xml");
} catch (Exception e) {
return null;
}
else
return singleton.containerConfig;
}
public static StorageManager getValidationStorage() {
return validationStorage;
}
public static StorageManager getStorage(VSensorConfig config) {
StorageManager sm = storagesConfigs.get(config == null ? null : config);
if (sm != null)
return sm;
DBConnectionInfo dci = null;
if (config == null || config.getStorage() == null || !config.getStorage().isDefined()) {
sm = mainStorage;
} else {
if (config.getStorage().isIdentifierDefined()) {
throw new IllegalArgumentException("Identifiers for storage is not supported yet.");
} else {
dci = new DBConnectionInfo(config.getStorage().getJdbcDriver(),
config.getStorage().getJdbcURL(),
config.getStorage().getJdbcUsername(),
config.getStorage().getJdbcPassword());
}
sm = storages.get(dci.hashCode());
if (sm == null) {
sm = StorageManagerFactory.getInstance(config.getStorage().getJdbcDriver(), config.getStorage().getJdbcUsername(), config.getStorage().getJdbcPassword(), config.getStorage().getJdbcURL(), DEFAULT_MAX_DB_CONNECTIONS);
storages.put(dci.hashCode(), sm);
storagesConfigs.put(config, sm);
}
}
return sm;
}
public static StorageManager getStorage(String vsName) {
return getStorage(Mappings.getVSensorConfig(vsName));
}
public static StorageManager getDefaultStorage() {
return getStorage((VSensorConfig)null);
}
public static StorageManager getWindowStorage() {
return windowStorage;
}
public static ZContext getZmqContext(){
return zmqContext;
}
public static ZeroMQProxy getZmqProxy(){
return zmqproxy;
}
public GsnConf getGsnConf(){
return gsnConf;
}
public Map<String,VsConf> getVsConf(){
return vsConf;
}
public ArrayList<Monitorable> getToMonitor(){
return toMonitor;
}
public static ThreadMXBean getThreadMXBean() {
return threadBean;
}
}