/** * Narvaro: @VERSION@ * Build Date: @DATE@ * Commit Head: @HEAD@ * JDK: @JDK@ * ANT: @ANT@ * */ package edu.csus.ecs.moneybeets.narvaro; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import org.apache.log4j.Logger; import edu.csus.ecs.moneybeets.narvaro.database.DatabaseManager; import edu.csus.ecs.moneybeets.narvaro.model.DataManager; import edu.csus.ecs.moneybeets.narvaro.util.ConfigurationManager; import javafx.application.Application; import javafx.application.Platform; import javafx.event.EventHandler; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.layout.Pane; import javafx.stage.Stage; import javafx.stage.WindowEvent; /** * The main application which loads, initializes, and starts * the GUI and supporting classes. * */ public class Narvaro extends Application { private static final Logger LOG = Logger.getLogger(Narvaro.class.getName()); private static Narvaro instance = null; private Stage stage; private Parent root; private Scene scene; private boolean setupMode = false; // stage for first-time boot setup private Stage subStage; /** * Location of the home directory. All config files should be * located here. */ private Path narvaroHome; /** * True if the application has started. */ private boolean started = false; /** * Creates an instance of Narvaro and starts it. */ public Narvaro() { if (instance != null) { throw new IllegalStateException("Narvaro is already initialized"); } instance = this; } /** * Launches the JavaFX platform * and invokes the start method. */ public static void startup() { Application.launch(); } /** * Returns a singleton instance of Narvaro. * * @return an instance. */ public static Narvaro getInstance() { return instance; } @Override public void start(final Stage stage) throws Exception { // only allow startup once synchronized (Narvaro.class) { if (started == true) { throw new IllegalStateException("Narvaro is already running"); } } try { locateNarvaro(); } catch (Exception e) { // failed to locate home directory // terminate app. LOG.fatal(e.getMessage(), e); return; } if (setupMode) { LOG.info("Setup Mode Activated"); doFirstTimeSetup(); } // startup the database manager by requesting a connection try { Connection con = DatabaseManager.Narvaro.getConnection(); DatabaseManager.Narvaro.closeConnection(con); } catch (Exception e) { LOG.error(e.getMessage(), e); return; } LOG.info("Database Manager Started"); // startup DataManager try { DataManager.Narvaro.start(); } catch (Exception e) { LOG.error(e.getMessage(), e); } LOG.info("Data Manager Started"); this.stage = stage; // locate FXML layout Path fxml = Paths.get(ConfigurationManager.NARVARO.getHomeDirectory() + File.separator + "resources" + File.separator + "Narvaro.fxml"); root = FXMLLoader.load(fxml.toUri().toURL()); scene = new Scene(root); // add CSS here to skin Narvaro Path css = Paths.get(ConfigurationManager.NARVARO.getHomeDirectory() + File.separator + "resources" + File.separator + "Narvaro.css"); root.getStylesheets().add(css.toUri().toURL().toExternalForm()); // add icon Path icon = Paths.get(ConfigurationManager.NARVARO.getHomeDirectory() + File.separator + "resources" + File.separator + "moneybeets_logo.png"); stage.getIcons().add(new Image(icon.toUri().toURL().toExternalForm())); stage.setScene(scene); // used for graceful shutdown when user clicks the X in top right corner stage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(final WindowEvent we) { shutdown(); } }); stage.setResizable(false); stage.show(); started = true; LOG.info("Narvaro Started"); } /** * Locates Narvaro installation. * * @throws FileNotFoundException If the home directory could not be discovered. */ private void locateNarvaro() throws FileNotFoundException { String narvaroDefaultConfigName = "conf" + File.separator + "default_narvaro.properties"; String narvaroConfigName = "conf" + File.separator + "narvaro.properties"; if (narvaroHome == null) { String homeProperty = System.getProperty("narvaroHome"); try { if (homeProperty != null && !"".equals(homeProperty)) { narvaroHome = verifyHome(homeProperty, narvaroDefaultConfigName); } } catch (FileNotFoundException e) { // ignore } // if we still haven't found home directory, try checking the local working direcotry String wd = System.getProperty("user.dir"); try { if (wd != null && !"".equals(wd)) { narvaroHome = verifyHome(wd, narvaroDefaultConfigName); } } catch (FileNotFoundException e) { // ingore } // if still nothing, try one directory higher try { String wdParent = Paths.get(wd).getParent().toString(); if (wdParent != null && !"".equals(wdParent)) { narvaroHome = verifyHome(wdParent, narvaroDefaultConfigName); } } catch (FileNotFoundException e) { // if still nothing, give up throw new FileNotFoundException("Failed to locate Narvaro home"); } } ConfigurationManager.NARVARO.setHomeDirectory(narvaroHome.toString()); if (isFirstLaunch(narvaroHome.toString() + File.separator + narvaroConfigName)) { setupMode = true; LOG.info("First launch - Setup Mode Activated"); try { Files.copy( Paths.get(narvaroHome.toString() + File.separator + narvaroDefaultConfigName), Paths.get(narvaroHome.toString() + File.separator + narvaroConfigName)); } catch (IOException e) { LOG.error("Failed to create runtime configuration file", e); } } ConfigurationManager.NARVARO.setConfigName(narvaroConfigName); } /** * Verifies that the home directory guess is valid. * * We verify validity by checking if the config file is present in the config directory. * * @param homeGuess * @param configName * @return * @throws FileNotFoundException */ private Path verifyHome(final String homeGuess, final String configName) throws FileNotFoundException { Path home = Paths.get(homeGuess); Path configFile = home.resolve(configName); if (!Files.exists(configFile)) { throw new FileNotFoundException(); } else { try { return home.toAbsolutePath(); } catch (Exception e) { throw new FileNotFoundException(); } } } /** * Determines if this program launch is considered * a first-time launch. We consider a first-time launch * anytime the runtime narvaro.properties config file * is not present. * * @param config The path + name of the narvaro.properties file. * @return True if it is a first-time launch. */ private boolean isFirstLaunch(final String config) { Path path = Paths.get(config); LOG.debug("Config path is: " + path.toString()); if (Files.exists(path)) { LOG.debug("Config file found"); return false; } LOG.debug("Config file not found"); return true; } /** * Starts the Setup menu and waits for the menu to exit. */ private void doFirstTimeSetup() { subStage = new Stage(); Pane subRoot = null; try { // locate FXML layout Path fxml = Paths.get(ConfigurationManager.NARVARO.getHomeDirectory() + File.separator + "resources" + File.separator + "FirstBoot.fxml"); subRoot = (Pane)FXMLLoader.load(fxml.toUri().toURL()); } catch (Exception e) { LOG.error("Failed to load first-time boot setup"); } Scene subScene = new Scene(subRoot); subStage.setScene(subScene); try { // add icon Path icon = Paths.get(ConfigurationManager.NARVARO.getHomeDirectory() + File.separator + "resources" + File.separator + "moneybeets_logo.png"); subStage.getIcons().add(new Image(icon.toUri().toURL().toExternalForm())); } catch (Exception e) { LOG.warn(e.getMessage(), e); } subStage.setResizable(false); subStage.showAndWait(); } /** * @return the stage */ public Stage getStage() { return stage; } /** * @return the root */ public Parent getRoot() { return root; } /** * @return the scene */ public Scene getScene() { return scene; } /** * Terminates the application * * Should handle a graceful shutdown such as * closing any open connections, file handles, etc... */ private void shutdown() { LOG.info("Narvaro Shutting down"); // shutdown the data manager LOG.info("Shutting down Data Manager"); DataManager.Narvaro.shutdown(); // shutdown the database manager LOG.info("Shutting down Database Manager"); DatabaseManager.Narvaro.shutdown(); LOG.info("Narvaro halted"); Platform.exit(); System.exit(0); } }