import com.gmail.dpierron.calibre.configuration.ConfigurationManager; import com.gmail.dpierron.calibre.datamodel.test.TestDataModel; import com.gmail.dpierron.calibre.gui.Mainframe; import com.gmail.dpierron.calibre.opds.Catalog; import com.gmail.dpierron.calibre.opds.Constants; import com.gmail.dpierron.calibre.opds.Log4jCatalogCallback; import com.gmail.dpierron.tools.Helper; import com.gmail.dpierron.tools.i18n.Localization; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.status.StatusLogger; import javax.swing.*; import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Vector; public class Runner { private static Logger logger = null; private static boolean introDone = false; private static boolean testMode = false; // Set this to true to generate a test datamodel private static boolean guiMode; /** * Constructor * @param startGui * @throws IOException */ static void run(boolean startGui) throws IOException { intro(); if (testMode) { new TestDataModel().testDataModel(); } else if (startGui) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new Mainframe().setVisible(true); } }); } else { Log4jCatalogCallback log4jCatalogCallback = new Log4jCatalogCallback(); log4jCatalogCallback.setStartGui(false); new Catalog(log4jCatalogCallback).createMainCatalog(); } } /** * Constructor * Start of run initialisation * @param args * @param startGui */ public static void run(String[] args, boolean startGui) { if (logger == null) initLog4J(); assert configurationFolder != null; ConfigurationManager.setConfigurationDirectory(configurationFolder); ConfigurationManager.initialiseListOfSupportedEbookFormats(); Locale lc = Locale.getDefault(); Localization.Main.reloadLocalizations(Locale.ENGLISH); // Initalize Localization object to English Vector<Locale> avail = Localization.Main.getAvailableLocalizationsAsLocales(); Localization.Enum.reloadLocalizations(avail.contains(lc) ? lc : Locale.ENGLISH); Localization.Main.reloadLocalizations(avail.contains(lc) ? lc : Locale.ENGLISH); guiMode = startGui; logger.info(""); logger.info("--------------------------------------------"); logger.info(Constants.PROGTITLE + Constants.BZR_VERSION); logger.info("**** " + (startGui?"GUI":"BATCH") + " MODE ****"); logger.info(""); logger.info("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " (" + System.getProperty( "os.arch") + ")"); logger.info("LANG: " + lc.getLanguage() + " (" + lc.getDisplayLanguage() + ")"); logger.info("JAVA: " + System.getProperty("java.specification.version") + " (" + System.getProperty("java.version") + ")"); logger.info(""); String levelText = ""; if (logger.isTraceEnabled()) { levelText += "TRACE + "; } if (logger.isDebugEnabled()) { levelText += "DEBUG + "; } if (logger.isInfoEnabled()) { levelText += "INFO + "; } levelText += "WARN + ERROR + FATAL"; logger.info(""); logger.info ("LOG LEVELS: " + levelText); logger.info(""); try { String currentProfileName = ConfigurationManager.getCurrentProfileName(); logger.info(Localization.Main.getText("startup.profiledefault", currentProfileName)); switch (args.length) { case 0: // This is the normal default where we use the last profile used in the GUI. break; default: // We seem to have more parameters than expected. // Log the details and then assume that the first may be a profile logger.warn("startup.extraParameters"); String argstring = ""; for (String s : args) argstring += s + " "; logger.warn(" " + argstring ); // FALLTHRU case 1: // It appears that a profile has been supplied String profileName = args[0]; if (ConfigurationManager.isExistingConfiguration(profileName) == null) { logger.error(Localization.Main.getText("startup.profilemissing", profileName)); if (!startGui) System.exit(-3); } else { logger.info(Localization.Main.getText("Switching to profile: {0}", ConfigurationManager.isExistingConfiguration(profileName))); ConfigurationManager.changeProfile(ConfigurationManager.isExistingConfiguration(profileName), startGui); } break; } // runner.run(startGui); run(startGui); } catch (IOException e) { logger.info(Localization.Main.getText("error.generic", Constants.AUTHOREMAIL)); e.printStackTrace(); } } /** * Display project team information * (protected so only displayed once if called recursively) */ private static void intro() { if (!introDone) { logger.info(""); logger.info(Localization.Main.getText("intro.goal")); logger.info(Localization.Main.getText("intro.wiki.title") + Localization.Main.getText("intro.wiki.url")); logger.info(""); // TODO: ITIMPI: List of members revised to reflect active developers! logger.info(Localization.Main.getText("intro.team.title")); logger.info(" * " + Localization.Main.getText("intro.team.list1")); logger.info(""); logger.info(Localization.Main.getText("intro.team.title2")); logger.info(" * " + Localization.Main.getText("intro.team.list2")); logger.info(" * " + Localization.Main.getText("intro.team.list3")); logger.info(" * " + Localization.Main.getText("intro.team.list4")); logger.info(" * " + Localization.Main.getText("intro.team.list5")); logger.info(""); logger.info(Localization.Main.getText("intro.thanks.1")); logger.info(Localization.Main.getText("intro.thanks.2")); logger.info(""); logger.info(Localization.Main.getText("usage.intro", ConfigurationManager.getCurrentProfile().getPropertiesFile().getPath())); logger.info(""); introDone = true; } } /** * log4j initialisation * * * NOTE: As we are going to specify the log configuration file * programatically we must not use any classes that try * and instantiate a logger berfore this has happened or * we will find there is an issue with getting logger * started as expected. */ private static void initLog4J() { System.setProperty("org.apache.logging.log4j.level", "INFO"); // Set default logging level for no configuration file System.setProperty("org.apache.logging.simplelogj.level", "INFO"); System.setProperty("Log4jDefaultStatusLevel", "INFO"); String[] levels = new String[]{".info", ".debug", ".trace", ".trace.FileCachingSystem", "STANDARD"}; String defaultLevel = ".info"; configurationFolder = getDefaultConfigurationDirectory(); if (configurationFolder == null) { addStartupLogMessage("ERROR: Failed to initialize logging - could not find suitable log folder"); System.exit(-5); } // Create the standard default list of log4j onfiguration files if they do not already exist System.setProperty("calibre2opds.home", configurationFolder.getAbsolutePath()); addStartupLogMessage("calibre2opds.home=" + configurationFolder.getAbsolutePath()); String defaultOutFileName = "log/log4j2"; File log4jConfig = null; for (String level : levels) { String outFileName; String inFileName = "log4j2"; if (level.equals("STANDARD")) { outFileName = defaultOutFileName; inFileName += defaultLevel; } else { outFileName = defaultOutFileName + level; inFileName += level; } inFileName += Constants.XML_EXTENSION; outFileName += Constants.XML_EXTENSION; log4jConfig = new File(configurationFolder, outFileName); // If the target does not already exist thenwe copy the // default file of this type from from the resources if (!log4jConfig.exists()) { // do not overwrite try { new File(log4jConfig.getParent()).mkdirs(); InputStream is = null; FileOutputStream os = null; try { is = Runner.class.getResourceAsStream(inFileName); if (is == null) { addStartupLogMessage("Cannot find " + inFileName + " in the resources"); } os = new FileOutputStream(log4jConfig); byte buffer[] = new byte[1024]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } finally { if (os != null) { os.flush(); os.close(); } if (is != null) { is.close(); } } } catch (Exception e) { e.printStackTrace(System.out); } } } addStartupLogMessage("Log4j2 configuration file is " + log4jConfig.getAbsolutePath()); // Configure and watch System.setProperty("log4j.configurationFile", log4jConfig.getAbsolutePath()); try { ConfigurationSource source = new ConfigurationSource(new FileInputStream(log4jConfig)); Configurator.initialize(null, source); logger = LogManager.getLogger(Runner.class.getName()); addStartupLogMessage("Using log4j2 configuration file " + log4jConfig); } catch (java.io.FileNotFoundException e) { // Ignore? } catch (java.io.IOException f) { // Ignore? } // Now get saved messages into the log for ( String s : getStartupLogMessages()) { logger.debug(s); } clearStartupLogMessages(); } private static List<String> startupLogMessages; /** * Add a log message to the array of those to be kept for replaying to * the log after log4j has been initialised. The message is also * output to the console so that we can get some basic diagnsotics * even if the program fails to start properly. * * @param message */ public static void addStartupLogMessage (String message) { System.out.println(message); if (startupLogMessages == null) { startupLogMessages = new ArrayList<String>(); } startupLogMessages.add(message); } /** * Get the list of startup messages that have been built up * @return */ public static List<String> getStartupLogMessages() { return startupLogMessages; } /** * Clear down the list of startup messages. * (Just saves a little RAM if there were a lot?) */ public static void clearStartupLogMessages() { startupLogMessages = null; } /** * Get the startup messsages * * @return */ private static String startupMessagesForDisplay() { StringBuffer s = new StringBuffer("\n\nLOG:"); for (String m : startupLogMessages) { s.append(m + "\n"); } return s.toString(); } private static File configurationFolder = null; private final static String CONFIGURATION_FOLDER = ".calibre2opds"; /** * Check for redirection (if any) * * @param redirectToNewHome Folder to check * @return Final result (same a redirectToNewHome if redirect not active. */ private static File configurationRedirect(File redirectToNewHome) { assert redirectToNewHome != null; assert redirectToNewHome.exists() == true; File redirectConfigurationFile = new File(redirectToNewHome, ".redirect"); if (! redirectConfigurationFile.exists()) { addStartupLogMessage("Using configuration folder: " + redirectToNewHome); return redirectToNewHome; } // Attempt to follow a redirect String message = ".redirect file found in " + redirectToNewHome.getPath(); addStartupLogMessage(message); try { BufferedReader fr = null; try { fr = new BufferedReader(new FileReader(redirectConfigurationFile)); String newHomeFileName = fr.readLine(); File newHome = new File(newHomeFileName); if (newHome.exists()) { addStartupLogMessage("redirecting home folder to " + newHome.getAbsolutePath()); // Allow for recursion return configurationRedirect(newHome); } else { String message2 = "... unable to find redirect folder " + newHome.getPath(); String message3 = "... so redirect abandoned"; if (guiMode) { JOptionPane.showMessageDialog(null, message + "\n" + message2 + "\n" + message3 + startupMessagesForDisplay(), Constants.PROGNAME, JOptionPane.ERROR_MESSAGE); } addStartupLogMessage(Helper.getTextFromPseudoHtmlText(message3)); addStartupLogMessage(Helper.getTextFromPseudoHtmlText(message2)); System.exit(-1); } } finally { if (fr != null) fr.close(); } } catch (IOException e) { String message2 = "... failure reading .redirect file"; String message3 = "... so redirect abandoned"; if (guiMode) { JOptionPane.showMessageDialog(null, message + "\n" + message2 + "\n" + message3 + startupMessagesForDisplay(), Constants.PROGNAME, JOptionPane.ERROR_MESSAGE); } addStartupLogMessage(Helper.getTextFromPseudoHtmlText(message2)); addStartupLogMessage(Helper.getTextFromPseudoHtmlText(message3)); addStartupLogMessage("Exit(-2)"); System.exit(-2); } // Do not think we can actually get here! // However if we do assume no redirect found addStartupLogMessage("Using configuration folder: " + redirectToNewHome); return redirectToNewHome; } /** * Work out where the configuration folder is located. * Note that at this poin t log4j will not have been initiaised * so send any messages to system.out. * @return Folder to be used for configuration purposes */ public static File getDefaultConfigurationDirectory() { // Check for redirect // Note that redirect's can be chained. // If we got here then configuration folder exists, and no redirect is active if (configurationFolder != null) { addStartupLogMessage("Using configuration folder: " + configurationFolder); return configurationFolder; } assert configurationFolder == null; // Now all the standard locations if we do not already have an answer // try the CALIBRE2OPDS_CONFIG environment variable String configDirectory = System.getenv("CALIBRE2OPDS_CONFIG"); if (configDirectory != null) { File envConfigurationFolder = new File(configDirectory); addStartupLogMessage("CALIBRE2OPDS_CONFIG=" + envConfigurationFolder); if (envConfigurationFolder.exists()) { // Allow for redirect return configurationRedirect(envConfigurationFolder); } else { addStartupLogMessage("... but specified folder does not exist"); configurationFolder = null; } } assert configurationFolder == null; File configurationFolderParent = null; // Set to the first potential parent for configuration folder. // try with user.home (normal default) String userHomePath = System.getProperty("user.home"); if (userHomePath != null) { File homeParent = new File(userHomePath); addStartupLogMessage("Try configuration folder in user home folder: " + homeParent); if (homeParent.exists()) { File homeConfigurationFolder = new File(homeParent, CONFIGURATION_FOLDER); if (homeConfigurationFolder.exists()) { // Allow for redirection addStartupLogMessage("Try configuration folder in user home folder: " + homeConfigurationFolder); return configurationRedirect(homeConfigurationFolder); } configurationFolderParent = homeParent; // Set as potential home for configuration folder } else { addStartupLogMessage("... but specified folder does not exist"); } } assert configurationFolder == null; // try with tilde (fallback default on linux/mac) File tildeParent = new File("~"); addStartupLogMessage("Try configuration folder from tilde folder: " + configurationFolderParent); if (tildeParent.exists()) { File tildeConfigurationFolder = new File(tildeParent, CONFIGURATION_FOLDER); if (tildeConfigurationFolder.exists()) { return configurationRedirect(tildeConfigurationFolder); } else { if (configurationFolderParent == null) configurationFolderParent = tildeConfigurationFolder; } } else { addStartupLogMessage("... but specified folder does not exist"); } assert configurationFolder == null; // Last ditch effort - try the install folder File installConfigurationFolderParent = Helper.getInstallDirectory(); addStartupLogMessage("Try configuration folder from .jar location: " + installConfigurationFolderParent); if (installConfigurationFolderParent.exists()) { File installConfigurationFolder = new File(installConfigurationFolderParent, CONFIGURATION_FOLDER); if (installConfigurationFolder.exists()) { // Allow for redirect return configurationRedirect(installConfigurationFolder); } else { if (configurationFolderParent == null) configurationFolderParent = installConfigurationFolderParent; } } else { // ITIMPI: Is this condition really possible as surely the InstallDirectory exists!! addStartupLogMessage("... but specified folder does not exist"); } assert configurationFolder == null; // No suitable location found (is this actually possible! if (configurationFolderParent == null) { String message = "No suitable configuration folder found"; if (guiMode) { JOptionPane.showMessageDialog(null, message + startupMessagesForDisplay(), Constants.PROGNAME, JOptionPane.ERROR_MESSAGE); } addStartupLogMessage(message); addStartupLogMessage("Exit(-1)"); System.exit(-3); } assert configurationFolderParent != null && configurationFolder == null && configurationFolderParent.exists(); // OK - configuration folder does not exist so we need to create it File newConfigurationFolder = new File (configurationFolderParent,CONFIGURATION_FOLDER); assert !newConfigurationFolder.exists(); if (! newConfigurationFolder.mkdirs()) { String message = Localization.Main.getText("startup.foldernotcreatefailed", newConfigurationFolder); if (guiMode) { JOptionPane.showMessageDialog(null, message + startupMessagesForDisplay(), Constants.PROGNAME, JOptionPane.ERROR_MESSAGE); } addStartupLogMessage(message); addStartupLogMessage("Exit(-1)"); System.exit(-4); } configurationFolder = newConfigurationFolder; addStartupLogMessage("Using configuration folder: " + configurationFolder); return configurationFolder; } }