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;
}
}