com.freedomotic.app.Freedomotic Maven / Gradle / Ivy
/**
*
* Copyright (c) 2009-2014 Freedomotic team http://freedomotic.com
*
* This file is part of Freedomotic
*
* 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 2, or (at your option) 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
* Freedomotic; see the file COPYING. If not, see
* .
*/
package com.freedomotic.app;
import com.freedomotic.api.API;
import com.freedomotic.api.Client;
import com.freedomotic.api.EventTemplate;
import com.freedomotic.bus.BootStatus;
import com.freedomotic.bus.BusConsumer;
import com.freedomotic.bus.BusMessagesListener;
import com.freedomotic.bus.BusService;
import com.freedomotic.core.SynchManager;
import com.freedomotic.core.TopologyManager;
import com.freedomotic.environment.EnvironmentLogic;
import com.freedomotic.environment.EnvironmentRepository;
import com.freedomotic.events.PluginHasChanged;
import com.freedomotic.events.PluginHasChanged.PluginActions;
import com.freedomotic.exceptions.RepositoryException;
import com.freedomotic.exceptions.FreedomoticException;
import com.freedomotic.exceptions.PluginLoadingException;
import com.freedomotic.marketplace.ClassPathUpdater;
import com.freedomotic.marketplace.IPluginCategory;
import com.freedomotic.marketplace.MarketPlaceService;
import com.freedomotic.things.EnvObjectLogic;
import com.freedomotic.things.ThingRepository;
import com.freedomotic.plugins.ClientStorage;
import com.freedomotic.plugins.PluginsManager;
import com.freedomotic.reactions.Command;
import com.freedomotic.reactions.CommandPersistence;
import com.freedomotic.reactions.ReactionPersistence;
import com.freedomotic.reactions.TriggerPersistence;
import com.freedomotic.security.Auth;
import com.freedomotic.security.UserRealm;
import com.freedomotic.util.Info;
import com.freedomotic.util.LogFormatter;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.ThreadState;
import org.slf4j.bridge.SLF4JBridgeHandler;
/**
* This is the starting class of the project
*
* @author Enrico Nicoletti
*/
public class Freedomotic implements BusConsumer {
private static final Logger LOG = Logger.getLogger(Freedomotic.class.getName());
public static String INSTANCE_ID;
public static ArrayList onlinePluginCategories;
/**
* Should NOT be used. Reserved for una tantum internal freedomotic core use
* only!!
*/
@Deprecated
public static Injector INJECTOR;
//dependencies
private final EnvironmentRepository environmentRepository;
private final ThingRepository thingsRepository;
private final TopologyManager topologyManager;
private final SynchManager synchManager;
private final ClientStorage clientStorage;
private final PluginsManager pluginsManager;
private AppConfig config;
private final Auth auth;
private final API api;
private BusMessagesListener listener;
// TODO remove static modifier once static methods sendEvent & sendCommand are erased.
private static BusService busService;
/**
*
* @param pluginsLoader
* @param environmentRepository
* @param thingsRepository
* @param clientStorage
* @param config
* @param api
* @param busService
* @param topologyManager
* @param synchManager
*/
@Inject
public Freedomotic(
PluginsManager pluginsLoader,
EnvironmentRepository environmentRepository,
ThingRepository thingsRepository,
ClientStorage clientStorage,
AppConfig config,
API api,
BusService busService,
TopologyManager topologyManager,
SynchManager synchManager) {
this.pluginsManager = pluginsLoader;
this.environmentRepository = environmentRepository;
this.thingsRepository = thingsRepository;
this.busService = busService;
this.topologyManager = topologyManager;
this.synchManager = synchManager;
this.clientStorage = clientStorage;
this.config = config;
this.api = api;
this.auth = api.getAuth();
}
/**
*
* @throws FreedomoticException
*/
public void start() throws FreedomoticException {
// Relocate base data folder according to configuration (if specified in the config file)
// Do not move it in AppConfigImpl otherwise unit tests will become dependent to the presence of the data folder
String defaultPath = Info.PATHS.PATH_DATA_FOLDER.getAbsolutePath();
Info.relocateDataPath(new File(config.getStringProperty("KEY_DATA_PATH", defaultPath)));
// init localization
api.getI18n().setDefaultLocale(config.getStringProperty("KEY_ENABLE_I18N", "no"));
// init auth* framework
auth.initBaseRealm();
auth.load();
if (auth.isInited()) {
PrincipalCollection principals = new SimplePrincipalCollection("system", UserRealm.USER_REALM_NAME);
Subject SysSubject = new Subject.Builder().principals(principals).buildSubject();
SysSubject.getSession().setTimeout(-1);
ThreadState threadState = new SubjectThreadState(SysSubject);
threadState.bind();
LOG.info("Booting as user:" + auth.getSubject().getPrincipal() + ". Session will last:" + auth.getSubject().getSession().getTimeout());
}
String resourcesPath
= new File(Info.PATHS.PATH_WORKDIR
+ config.getStringProperty("KEY_RESOURCES_PATH", "/build/classes/it/freedom/resources/")).getPath();
LOG.info("\nOS: " + System.getProperty("os.name") + "\n" + api.getI18n().msg("architecture") + ": "
+ System.getProperty("os.arch") + "\n" + "OS Version: " + System.getProperty("os.version")
+ "\n" + api.getI18n().msg("user") + ": " + System.getProperty("user.name") + "\n" + "Java Home: "
+ System.getProperty("java.home") + "\n" + "Java Library Path: {"
+ System.getProperty("java.library.path") + "}\n" + "Program path: "
+ System.getProperty("user.dir") + "\n" + "Java Version: " + System.getProperty("java.version")
+ "\n" + "Resources Path: " + resourcesPath);
//check if topology manager is initiated
if (topologyManager == null) {
throw new IllegalStateException("Topology manager has not started");
}
if (synchManager == null) {
throw new IllegalStateException("Synch manager has not started");
}
// register listener
this.listener = new BusMessagesListener(this, busService);
// this class is a BusConsumer too
// listen for exit signal (an event) and call onExit method if received
listener.consumeEventFrom("app.event.system.exit");
// Stop on initialization error.
final BootStatus currentStatus = BootStatus.getCurrentStatus();
if (!BootStatus.STARTED.equals(currentStatus)) {
kill(currentStatus.getCode());
}
/**
* ******************************************************************
* Starting the logger and popup it in the browser
* *****************************************************************
*/
if (config.getBooleanProperty("KEY_SAVE_LOG_TO_FILE", false)) {
try {
File logdir = new File(Info.PATHS.PATH_WORKDIR + "/log/");
logdir.mkdir();
File logfile = new File(logdir + "/freedomotic.html");
logfile.createNewFile();
FileHandler handler = new FileHandler(logfile.getAbsolutePath(),
false);
handler.setFormatter(new LogFormatter());
LOG.setLevel(Level.ALL);
LOG.addHandler(handler);
LOG.config(api.getI18n().msg("INIT_MESSAGE"));
if ((config.getBooleanProperty("KEY_LOGGER_POPUP", true) == true)
&& (java.awt.Desktop.getDesktop().isSupported(Desktop.Action.BROWSE))) {
java.awt.Desktop.getDesktop()
.browse(new File(Info.PATHS.PATH_WORKDIR + "/log/freedomotic.html").toURI());
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
/**
* ******************************************************************
* Create data backup folder (FEATURE DISABLED!!!)
* *****************************************************************
*/
// if (getConfig().getBooleanProperty("KEY_BACKUP_DATA_BEFORE_START", true) == true) {
// try {
// CopyFile.copy(new File(Info.getDatafilePath()), new File(Info.getApplicationPath() + "/backup"));
// } catch (Exception ex) {
// logger.warning("unable to saveAll a backup copy of application data " + getStackTraceInfo(ex));
// }
// }
/**
* ******************************************************************
* Shows the freedomotic website if stated in the config file
* *****************************************************************
*/
if (config.getBooleanProperty("KEY_SHOW_WEBSITE_ON_STARTUP", false)) {
try {
java.awt.Desktop.getDesktop().browse(new URI("www.freedomotic.com"));
} catch (URISyntaxException ex) {
LOG.log(Level.SEVERE, null, ex);
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
/**
* ******************************************************************
* Dynamically load all plugins
* *****************************************************************
*/
try {
pluginsManager.loadAllPlugins();
} catch (PluginLoadingException ex) {
LOG.log(Level.WARNING,"Error while loading all plugins. Impossible to load " + ex.getPluginName(), ex);
}
/**
* ******************************************************************
* Dynamically load jar files in /plugin/providers folder for plugins
* *****************************************************************
*/
try {
ClassPathUpdater.add(Info.PATHS.PATH_PROVIDERS_FOLDER);
} catch (IOException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
LOG.log(Level.SEVERE, null, ex);
}
/**
* ******************************************************************
* Cache online plugins
* *****************************************************************
*/
if (config.getBooleanProperty("CACHE_MARKETPLACE_ON_STARTUP", false)) {
try {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new Thread(new Runnable() {
@Override
public void run() {
LOG.info("Starting marketplace service");
MarketPlaceService mps = MarketPlaceService.getInstance();
onlinePluginCategories = mps.getCategoryList();
}
}).start();
}
});
} catch (Exception e) {
LOG.warning("Unable to cache plugins package from marketplace");
}
}
// Bootstrap Things in the environments
// This should be done after loading all Things plugins otherwise
// its java class will not be recognized by the system
environmentRepository.init();
for (EnvironmentLogic env : environmentRepository.findAll()) {
// Load all the Things in this environment
File thingsFolder = env.getObjectFolder();
List loadedThings = thingsRepository.loadAll(thingsFolder);
for (EnvObjectLogic thing: loadedThings) {
thing.setEnvironment(env);
// Actvates the Thing. Important, otherwise it will be not visible in the environment
thingsRepository.create(thing);
}
}
// Loads the entire Reactions system (Trigger + Commands + Reactions)
TriggerPersistence.loadTriggers(new File(Info.PATHS.PATH_DATA_FOLDER + "/trg/"));
CommandPersistence.loadCommands(new File(Info.PATHS.PATH_DATA_FOLDER + "/cmd/"));
ReactionPersistence.loadReactions(new File(Info.PATHS.PATH_DATA_FOLDER + "/rea/"));
// Starting plugins
for (Client plugin : clientStorage.getClients()) {
String startupTime = plugin.getConfiguration().getStringProperty("startup-time", "undefined");
if (startupTime.equalsIgnoreCase("on load")) {
plugin.start();
PluginHasChanged event = new PluginHasChanged(this,
plugin.getName(),
PluginActions.DESCRIPTION);
busService.send(event);
}
}
double MB = 1024 * 1024;
Runtime runtime = Runtime.getRuntime();
LOG.config("Used Memory:" + ((runtime.totalMemory() - runtime.freeMemory()) / MB));
LOG.info("Freedomotic startup completed");
}
/**
*
* @return
*/
public static String getInstanceID() {
return INSTANCE_ID;
}
// FIXME This shouldn't be done through this method
/**
*
* @param event
*/
@Deprecated
public static void sendEvent(EventTemplate event) {
busService.send(event);
}
// FIXME This shouldn't be done through this method
/**
*
* @param command
* @return
*/
@Deprecated
public static Command sendCommand(final Command command) {
return busService.send(command);
}
/**
* Main entry point of freedomotic. All starts from here.
*
* @param args
*/
public static void main(String[] args) {
configureLogging();
try {
INSTANCE_ID = args[0];
} catch (Exception e) {
INSTANCE_ID = "";
}
if ((INSTANCE_ID == null) || (INSTANCE_ID.isEmpty())) {
INSTANCE_ID = UUID.randomUUID().toString();
}
LOG.info("Freedomotic instance ID: " + INSTANCE_ID);
try {
INJECTOR = Guice.createInjector(new FreedomoticInjector());
Freedomotic freedomotic = INJECTOR.getInstance(Freedomotic.class);
//start freedomotic
freedomotic.start();
} catch (FreedomoticException ex) {
LOG.severe(ex.getMessage());
System.exit(1);
}
}
/**
* Configures java.util.logging (hereafter JUL)
*
* While Freedomotic is still using JUL, here is configured SLF4J Brigde.
*
* Thereby now all logging (including third party packages) is done through
* SLF4J.
*/
private static void configureLogging() {
// Remove all handlers in jul (java.util.logging) root logger
SLF4JBridgeHandler.removeHandlersForRootLogger();
// Register slf4j handler to jul root logger
SLF4JBridgeHandler.install();
// Set jul root log level to ALL, because default slf4jbridge handler is INFO.
LogManager.getLogManager().getLogger("").setLevel(Level.ALL);
}
@Override
public void onMessage(ObjectMessage message) {
Object payload = null;
try {
payload = message.getObject();
if (payload instanceof EventTemplate) {
final EventTemplate event = (EventTemplate) payload;
onExit(event);
}
} catch (JMSException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
/**
*
* @param event
*/
public void onExit(EventTemplate event) {
LOG.info("Received exit signal...");
//stop all plugins
for (Client plugin : clientStorage.getClients()) {
plugin.stop();
}
BootStatus.setCurrentStatus(BootStatus.STOPPING);
busService.destroy();
config.save();
auth.save();
String savedDataRoot;
if (config.getBooleanProperty("KEY_OVERRIDE_REACTIONS_ON_EXIT", false) == true) {
savedDataRoot = Info.PATHS.PATH_DATA_FOLDER.getAbsolutePath();
} else {
savedDataRoot = Info.PATHS.PATH_WORKDIR + "/testSave/data";
}
TriggerPersistence.saveTriggers(new File(savedDataRoot + "/trg"));
CommandPersistence.saveCommands(new File(savedDataRoot + "/cmd"));
ReactionPersistence.saveReactions(new File(savedDataRoot + "/rea"));
//save the environment
String environmentFilePath = Info.PATHS.PATH_DATA_FOLDER + "/furn/" + config.getProperty("KEY_ROOM_XML_PATH");
File folder = null;
try {
folder = new File(environmentFilePath).getParentFile();
environmentRepository.saveEnvironmentsToFolder(folder);
if (config.getBooleanProperty("KEY_OVERRIDE_OBJECTS_ON_EXIT", false) == true) {
File saveDir = null;
try {
saveDir = new File(folder + "/data/obj");
thingsRepository.saveAll(saveDir);
} catch (RepositoryException ex) {
LOG.log(Level.SEVERE, "Cannot save objects in {0}", saveDir.getAbsolutePath());
}
}
} catch (RepositoryException ex) {
LOG.log(Level.SEVERE, "Cannot save environment to folder {0} due to {1}", new Object[]{folder, ex.getCause()});
}
System.exit(0);
}
/**
*
*/
public static void kill() {
kill(0);
}
/**
*
* @param status
*/
public static void kill(int status) {
System.exit(status);
}
/**
*
* @param t
* @return
*/
public static String getStackTraceInfo(Throwable t) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy