package org.peerbox;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import javafx.application.Application;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.Stage;
import org.peerbox.app.AppContext;
import org.peerbox.app.ClientContextFactory;
import org.peerbox.app.Constants;
import org.peerbox.app.config.AppConfig;
import org.peerbox.events.InformationMessage;
import org.peerbox.guice.ApiServerModule;
import org.peerbox.guice.AppConfigModule;
import org.peerbox.guice.AppModule;
import org.peerbox.notifications.InformationNotification;
import org.peerbox.presenter.tray.TrayException;
import org.peerbox.server.IServer;
import org.peerbox.utils.DialogUtils;
import org.peerbox.utils.AppData;
import org.peerbox.utils.IconUtils;
import org.peerbox.view.tray.AbstractSystemTray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* This class is responsible for the initialization of the application.
* This covers everything until the first window is loaded.
*
* @author albrecht
*
*/
public class App extends Application
{
private static final Logger logger = LoggerFactory.getLogger(App.class);
/* location of the logging configuration to load at runtime */
private static final String LOG_CONFIGURATION = "logback.xml";
/* commandline argument -- directory for storing application data. */
private static final String PARAM_APP_DIR = "appdir";
/* Guice dependency injection - root injector */
private Injector injector;
/* primary stage as provided by the JavaFX framework */
private Stage primaryStage;
/* application context: application-wide instances and dependencies */
private AppContext appContext;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
primaryStage = stage;
initializeAppFolder();
initializeLogging();
logger.info("{} starting.", Constants.APP_NAME);
logger.info("AppData Folder: {}", AppData.getDataFolder());
initializeGuice();
initializeAppContext();
loadAppConfig();
startServer();
initializeFonts();
initializeSysTray();
launchInForeground();
appContext.getMessageBus().publish(new InformationMessage("PeerWasp started", "Hello!"));
appContext.getMessageBus().publish(new InformationNotification("PeerWasp started", "Hello!"));
}
/**
* Initializes the folder for application data (appdata folder).
* It is possible to specify the folder using a command line parameter.
*
* Example: --appdir=/home/user/PeerWasp/AppData
*/
private void initializeAppFolder() {
// check arguments passed via command line
if (getParameters().getNamed().containsKey(PARAM_APP_DIR)) {
Path appDir = Paths.get(getParameters().getNamed().get(PARAM_APP_DIR));
AppData.setDataFolder(appDir);
}
boolean success = false;
try {
// create folders and check write access
AppData.createFolders();
AppData.checkAccess();
success = true;
} catch (IOException ioex) {
logger.warn("Could not initialize application folders: {}.", ioex.getMessage(), ioex);
}
if (!success) {
fatalExit(1);
}
}
/**
* Initializes the logging framework.
* The automatic configuration is reset and the logger is configured dynamically:
* - The log folder is set
* - The log configuration is loaded (not from resources, but from the working directory).
*
* This allows switching the folder and the configuration during development and at deployment.
*/
private void initializeLogging() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
// override default configuration
context.reset();
// inject the location of the appdata log folder as "LOG_FOLDER"
// property of the LoggerContext
context.putProperty("LOG_FOLDER", AppData.getLogFolder().toString());
jc.doConfigure(LOG_CONFIGURATION);
} catch (JoranException je) {
// status printer will handle printing of error
}
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
logger.debug("Initialized logging (LOG_FOLDER={})", context.getProperty("LOG_FOLDER"));
}
private void loadAppConfig() {
AppConfig appConfig = appContext.getAppConfig();
try {
appConfig.load();
} catch (IOException ioex) {
logger.warn("Could not load app properties: {}", appConfig.getConfigFile(), ioex);
// error alert
String title = "Could not load application properties.";
String message = String.format("File: %s\nError: %s",
appConfig.getConfigFile(), ioex.getMessage());
showErrorAlert(title, message);
}
}
/**
* Initialization of Guice dependency injection. This is the root injector, mainly used for
* application-wide instances.
*
* A child injector is created for a specific user during the login procedure
* (see {@link ClientContextFactory}.
*
* Add additional modules here.
*/
private void initializeGuice() {
injector = Guice.createInjector(
new AppModule(primaryStage),
new AppConfigModule(),
new ApiServerModule()
);
}
private void initializeAppContext() {
appContext = injector.getInstance(AppContext.class);
}
/**
* Starts the HTTP server for the context menu.
*/
private void startServer() {
IServer server = appContext.getServer();
boolean success = server.start();
if (success) {
try {
appContext.getAppConfig().setApiServerPort(server.getPort());
} catch (IOException e) {
logger.warn("Could not set and save server port.", e);
success = false;
}
} else {
logger.warn("Could not start server.");
}
}
/**
* Font awesome is an icon font. The font is included in the resources. However, the font
* needs to be registered in the glyph font registry of the controlfx library.
*/
private void initializeFonts() {
// just static initialization of the font awesome provider lib
IconUtils.initFontAwesomeOffline();
}
/**
* Adds the tray icon to the system tray
*/
private void initializeSysTray() {
try {
AbstractSystemTray systemTray = appContext.getUiContext().getSystemTray();
systemTray.show();
systemTray.showDefaultIcon();
} catch (TrayException e) {
logger.error("Could not initialize systray");
}
}
/**
* Launches the initial setup window
*/
private void launchInForeground() {
logger.info("Launch startup stage.");
StartupStage startup = injector.getInstance(StartupStage.class);
startup.show();
}
/**
* Force quit application with exit code.
*
* @param exitCode indicating status.
*/
private void fatalExit(int exitCode) {
logger.warn("Exiting... (ExitCode: {})", exitCode);
System.exit(exitCode);
}
/**
* Shows an alert dialog with an error message.
* Blocks until dialog closed.
*
* @param title of dialog
* @param message of error
*/
private void showErrorAlert(String title, String message) {
Alert dlg = DialogUtils.createAlert(AlertType.ERROR);
dlg.setTitle("Error - Initialization");
dlg.setHeaderText(title);
dlg.setContentText(message);
dlg.showAndWait();
}
}