Java tutorial
/* * Lilith - a log event viewer. * Copyright (C) 2007-2017 Joern Huxhorn * * 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package de.huxhorn.lilith; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.gaffer.GafferConfigurator; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.status.ErrorStatus; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.status.StatusUtil; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterException; import de.huxhorn.lilith.appender.InternalLilithAppender; import de.huxhorn.lilith.cli.Cat; import de.huxhorn.lilith.cli.CommandLineArgs; import de.huxhorn.lilith.cli.Filter; import de.huxhorn.lilith.cli.Help; import de.huxhorn.lilith.cli.Index; import de.huxhorn.lilith.cli.Md5; import de.huxhorn.lilith.cli.Tail; import de.huxhorn.lilith.handler.Slf4JHandler; import de.huxhorn.lilith.logback.tools.ContextHelper; import de.huxhorn.lilith.swing.ApplicationPreferences; import de.huxhorn.lilith.swing.LicenseAgreementDialog; import de.huxhorn.lilith.swing.MainFrame; import de.huxhorn.lilith.swing.SplashScreen; import de.huxhorn.lilith.tools.CatCommand; import de.huxhorn.lilith.tools.CreateMd5Command; import de.huxhorn.lilith.tools.FilterCommand; import de.huxhorn.lilith.tools.ImportExportCommand; import de.huxhorn.lilith.tools.IndexCommand; import de.huxhorn.lilith.tools.TailCommand; import de.huxhorn.sulky.formatting.SafeString; import de.huxhorn.sulky.io.IOUtilities; import de.huxhorn.sulky.sounds.jlayer.JLayerSounds; import de.huxhorn.sulky.swing.Windows; import it.sauronsoftware.junique.AlreadyLockedException; import it.sauronsoftware.junique.JUnique; import java.awt.EventQueue; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Handler; import javax.swing.UIManager; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.simplericity.macify.eawt.Application; import org.simplericity.macify.eawt.DefaultApplication; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Lilith { /** * Application name */ public static final String APP_NAME; /** * Version string *including* a -SNAPSHOT, if available */ public static final String APP_VERSION; /** * Version string *excluding* the -SNAPSHOT */ public static final String APP_PLAIN_VERSION; /** * true if APP_VERSION ends in -SNAPSHOT, false otherwise. */ public static final boolean APP_SNAPSHOT; /** * The git revision of this version */ public static final String APP_REVISION; /** * Long containing the timestamp of the build. */ public static final long APP_TIMESTAMP; /** * The timestamp of the build formatted as a date. */ public static final String APP_TIMESTAMP_DATE; public static final VersionBundle APP_VERSION_BUNDLE; private static final String SNAPSHOT_POSTFIX = "-SNAPSHOT"; private static final String JUNIQUE_MSG_SHOW = "Show"; private static final String JUNIQUE_REPLY_OK = "OK"; private static final String JUNIQUE_REPLY_UNKNOWN = "Unknown"; private static final String APPLE_SCREEN_MENU_BAR_SYSTEM_PROPERTY = "apple.laf.useScreenMenuBar"; private static final String GROOVY_EXTENSION = ".groovy"; private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler; private static MainFrame mainFrame; private static class UncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private final Logger logger = LoggerFactory.getLogger(UncaughtExceptionHandler.class); @Override public void uncaughtException(Thread t, Throwable e) { if (logger.isErrorEnabled()) logger.error("Caught an uncaught exception from thread {}!", t, e); System.err.println("\n-----\nThread " + t.getName() + " threw an exception!"); e.printStackTrace(); } } static { // I access InternalLilithAppender *before* any Logger is used. // Otherwise an obscure ClassNotFoundException is thrown in MainFrame. InternalLilithAppender.getSourceIdentifier(); final Logger logger = LoggerFactory.getLogger(Lilith.class); InputStream is = Lilith.class.getResourceAsStream("/app.properties"); Properties p = new Properties(); try { p.load(is); } catch (IOException ex) { if (logger.isErrorEnabled()) logger.error("Couldn't find app info resource!", ex); //ex.printStackTrace(); } finally { IOUtilities.closeQuietly(is); } APP_NAME = p.getProperty("application.name"); APP_VERSION = p.getProperty("application.version"); boolean snapshot = false; String plainVersion = APP_VERSION; if (plainVersion != null && plainVersion.endsWith(SNAPSHOT_POSTFIX)) { snapshot = true; plainVersion = plainVersion.substring(0, plainVersion.length() - SNAPSHOT_POSTFIX.length()); } APP_SNAPSHOT = snapshot; APP_PLAIN_VERSION = plainVersion; APP_REVISION = p.getProperty("application.revision"); String tsStr = p.getProperty("application.timestamp"); long ts = -1; String dateStr = null; if (tsStr != null) { try { ts = Long.parseLong(tsStr); dateStr = SafeString.toString(new Date(ts)); } catch (NumberFormatException ex) { if (logger.isErrorEnabled()) logger.error("Exception while reading timestamp!", ex); } } else { if (logger.isErrorEnabled()) logger.error("Application-timestamp not found!"); } APP_TIMESTAMP = ts; APP_TIMESTAMP_DATE = dateStr; APP_VERSION_BUNDLE = new VersionBundle(APP_PLAIN_VERSION, APP_TIMESTAMP); if (APP_VERSION != null) { System.setProperty("lilith.version", APP_VERSION); System.setProperty("lilith.version.bundle", APP_VERSION_BUNDLE.toString()); } if (APP_TIMESTAMP > -1) { System.setProperty("lilith.timestamp.milliseconds", "" + APP_TIMESTAMP); } if (APP_TIMESTAMP_DATE != null) { System.setProperty("lilith.timestamp", APP_TIMESTAMP_DATE); } if (APP_REVISION != null) { System.setProperty("lilith.revision", APP_REVISION); } } // TODO: - Shortcut in tooltip of toolbars...? // TODO: - check termination of every started thread public static void main(String[] args) { { // initialize java.util.logging to use slf4j... Handler handler = new Slf4JHandler(); java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger(""); rootLogger.addHandler(handler); rootLogger.setLevel(java.util.logging.Level.WARNING); } StringBuilder appTitle = new StringBuilder(); appTitle.append(APP_NAME).append(" V").append(APP_VERSION); if (APP_SNAPSHOT) { // always append timestamp for SNAPSHOT appTitle.append(" (").append(APP_TIMESTAMP_DATE).append(")"); } CommandLineArgs cl = new CommandLineArgs(); JCommander commander = new JCommander(cl); Cat cat = new Cat(); commander.addCommand(Cat.NAME, cat); Tail tail = new Tail(); commander.addCommand(Tail.NAME, tail); Filter filter = new Filter(); commander.addCommand(Filter.NAME, filter); Index index = new Index(); commander.addCommand(Index.NAME, index); Md5 md5 = new Md5(); commander.addCommand(Md5.NAME, md5); Help help = new Help(); commander.addCommand(Help.NAME, help); try { commander.parse(args); } catch (ParameterException ex) { printAppInfo(appTitle.toString(), false); System.out.println(ex.getMessage() + "\n"); printHelp(commander); System.exit(-1); } if (cl.verbose) { if (!APP_SNAPSHOT) { // timestamp is always appended for SNAPSHOT // don't append it twice appTitle.append(" (").append(APP_TIMESTAMP_DATE).append(")"); } appTitle.append(" - ").append(APP_REVISION); } String appTitleString = appTitle.toString(); if (cl.showHelp) { printAppInfo(appTitleString, false); printHelp(commander); System.exit(0); } String command = commander.getParsedCommand(); if (!Tail.NAME.equals(command) && !Cat.NAME.equals(command) && !Filter.NAME.equals(command)) // don't print info in case of cat, tail or filter { printAppInfo(appTitleString, true); } if (cl.logbackConfig != null) { File logbackFile = new File(cl.logbackConfig); if (!logbackFile.isFile()) { System.out.println(logbackFile.getAbsolutePath() + " is not a valid file."); System.exit(-1); } try { initLogbackConfig(logbackFile.toURI().toURL()); } catch (MalformedURLException e) { System.out.println("Failed to convert " + logbackFile.getAbsolutePath() + " to URL. " + e); System.exit(-1); } } else if (cl.verbose) { initVerboseLogging(); } if (cl.printBuildTimestamp) { System.out.println("Build-Date : " + APP_TIMESTAMP_DATE); System.out.println("Build-Revision : " + APP_REVISION); System.out.println("Build-Timestamp: " + APP_TIMESTAMP); System.exit(0); } if (Help.NAME.equals(command)) { commander.usage(); if (help.commands == null || help.commands.size() == 0) { commander.usage(Help.NAME); } else { Map<String, JCommander> commands = commander.getCommands(); for (String current : help.commands) { if (commands.containsKey(current)) { commander.usage(current); } else { System.out.println("Unknown command '" + current + "'!"); } } } System.exit(0); } if (Md5.NAME.equals(command)) { List<String> files = md5.files; if (files == null || files.isEmpty()) { printHelp(commander); System.out.println("Missing files!"); System.exit(-1); } boolean error = false; for (String current : files) { if (!CreateMd5Command.createMd5(new File(current))) { error = true; } } if (error) { System.exit(-1); } System.exit(0); } if (Index.NAME.equals(command)) { if (!cl.verbose && cl.logbackConfig == null) { initCLILogging(); } List<String> files = index.files; if (files == null || files.size() == 0) { printHelp(commander); System.exit(-1); } boolean error = false; for (String current : files) { if (!IndexCommand.indexLogFile(new File(current))) { error = true; } } if (error) { System.exit(-1); } System.exit(0); } if (Cat.NAME.equals(command)) { if (!cl.verbose && cl.logbackConfig == null) { initCLILogging(); } List<String> files = cat.files; if (files == null || files.size() != 1) { printHelp(commander); System.exit(-1); } if (CatCommand.catFile(new File(files.get(0)), cat.pattern, cat.numberOfLines)) { System.exit(0); } System.exit(-1); } if (Tail.NAME.equals(command)) { if (!cl.verbose && cl.logbackConfig == null) { initCLILogging(); } List<String> files = tail.files; if (files == null || files.size() != 1) { printHelp(commander); System.exit(-1); } if (TailCommand.tailFile(new File(files.get(0)), tail.pattern, tail.numberOfLines, tail.keepRunning)) { System.exit(0); } System.exit(-1); } if (Filter.NAME.equals(command)) { if (!cl.verbose && cl.logbackConfig == null) { initCLILogging(); } if (FilterCommand.filterFile(new File(filter.input), new File(filter.output), new File(filter.condition), filter.searchString, filter.pattern, filter.overwrite, filter.keepRunning, filter.exclusive)) { System.exit(0); } System.exit(-1); } if (cl.flushPreferences) { flushPreferences(); } if (cl.exportPreferencesFile != null) { exportPreferences(cl.exportPreferencesFile); } if (cl.importPreferencesFile != null) { importPreferences(cl.importPreferencesFile); } if (cl.exportPreferencesFile != null || cl.importPreferencesFile != null) { System.exit(0); } if (cl.flushLicensed) { flushLicensed(); } startLilith(appTitleString); } private static void printHelp(JCommander commander) { commander.usage(); String command = commander.getParsedCommand(); if (command != null) { commander.usage(command); } } private static void printAppInfo(String appTitle, boolean printHelpInfo) { System.out.println(" _ _ _ _ _ _ \n" + "| | (_) (_) |_| |__ \n" + "| | | | | | __| '_ \\ \n" + "| |___| | | | |_| | | |\n" + "|_____|_|_|_|\\__|_| |_|"); System.out.println(appTitle); System.out.println("http://lilithapp.com"); System.out.println("\nCopyright (C) 2007-2016 Joern Huxhorn\n\n" + "This program comes with ABSOLUTELY NO WARRANTY!\n\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions.\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"); if (printHelpInfo) { System.out.println("Use commandline option -h to view help.\n"); } } private static void importPreferences(String file) { ImportExportCommand.importPreferences(new File(file)); } private static void exportPreferences(String file) { ImportExportCommand.exportPreferences(new File(file)); } private static void startLilith(String appTitle) { final Logger logger = LoggerFactory.getLogger(Lilith.class); uncaughtExceptionHandler = new UncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler); // preventing duplicate instances... try { JUnique.acquireLock(Lilith.class.getName(), Lilith::handleJUniqueMessage); } catch (AlreadyLockedException e) { if (logger.isInfoEnabled()) logger.info("Detected running instance, quitting."); String result = JUnique.sendMessage(Lilith.class.getName(), "Show"); if (logger.isDebugEnabled()) logger.debug("JUnique result: {}", result); return; } // ok, we are the first instance this user has started... // install uncaught exception handler on event thread. EventQueue.invokeLater(() -> Thread.currentThread().setUncaughtExceptionHandler(uncaughtExceptionHandler)); startUI(appTitle); } private static void initCLILogging() { initLogbackConfig(Lilith.class.getResource("/logbackCLI.groovy")); } private static void initVerboseLogging() { initLogbackConfig(Lilith.class.getResource("/logbackVerbose.groovy")); } private static void initLogbackConfig(URL configUrl) { ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); if (loggerFactory instanceof LoggerContext) { LoggerContext loggerContext = (LoggerContext) loggerFactory; StatusManager sm = loggerContext.getStatusManager(); sm.clear(); // reset previous configuration initially loaded from logback.groovy loggerContext.reset(); if (configUrl.toString().endsWith(GROOVY_EXTENSION)) { // http://jira.qos.ch/browse/LOGBACK-1079 GafferConfigurator configurator = new GafferConfigurator(loggerContext); try { configurator.run(configUrl); final Logger logger = LoggerFactory.getLogger(Lilith.class); if (logger.isDebugEnabled()) logger.debug("Configured logging with {}.", configUrl); } catch (RuntimeException ex) { sm.add(new ErrorStatus("Exception while configuring Logback!", configUrl, ex)); } } else { JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(loggerContext); try { configurator.doConfigure(configUrl); final Logger logger = LoggerFactory.getLogger(Lilith.class); if (logger.isDebugEnabled()) logger.debug("Configured logging with {}.", configUrl); } catch (JoranException ex) { sm.add(new ErrorStatus("Exception while configuring Logback!", configUrl, ex)); } } int level = ContextHelper.getHighestLevel(loggerContext); long lastReset = ContextHelper.getTimeOfLastReset(loggerContext); if (level > Status.INFO) { List<Status> statusList = StatusUtil.filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), lastReset); if (statusList != null) { System.err.println("############################################################"); System.err.println("## Logback Status ##"); System.err.println("############################################################"); StringBuilder statusBuilder = new StringBuilder(); for (Status current : statusList) { appendStatus(statusBuilder, current, 0); } System.err.println(statusBuilder.toString()); System.err.println("############################################################"); } } } } private static final String[] STATUS_TEXT = { "INFO : ", "WARN : ", "ERROR: ", }; @SuppressWarnings({ "ThrowableResultOfMethodCallIgnored" }) private static void appendStatus(StringBuilder builder, Status status, int indent) { int levelCode = status.getLevel(); appendIndent(builder, indent); if (levelCode >= 0 && levelCode < STATUS_TEXT.length) { builder.append(STATUS_TEXT[levelCode]); } builder.append(status.getMessage()).append('\n'); Throwable t = status.getThrowable(); while (t != null) { appendIndent(builder, indent + 1); builder.append(t.getClass().getName()); String message = t.getMessage(); if (message != null) { builder.append(": ").append(message); } builder.append('\n'); // probably check for causes, too t = t.getCause(); } if (status.hasChildren()) { Iterator<Status> children = status.iterator(); while (children.hasNext()) { appendStatus(builder, children.next(), indent + 1); } } } private static void appendIndent(StringBuilder builder, int indent) { for (int i = 0; i < indent; i++) { builder.append('\t'); } } private static void flushLicensed() { final Logger logger = LoggerFactory.getLogger(Lilith.class); ApplicationPreferences prefs = new ApplicationPreferences(); prefs.setLicensed(false); if (logger.isInfoEnabled()) logger.info("Flushed licensed..."); System.exit(0); } private static void flushPreferences() { final Logger logger = LoggerFactory.getLogger(Lilith.class); ApplicationPreferences prefs = new ApplicationPreferences(); prefs.reset(); prefs.setLicensed(false); if (logger.isInfoEnabled()) logger.info("Flushed preferences..."); System.exit(0); } private static String handleJUniqueMessage(String msg) { if (JUNIQUE_MSG_SHOW.equals(msg)) { showMainFrame(); return JUNIQUE_REPLY_OK; } return JUNIQUE_REPLY_UNKNOWN; } private static void showMainFrame() { if (mainFrame != null) { final MainFrame frame = mainFrame; EventQueue.invokeLater(() -> { if (frame.isVisible()) { frame.setVisible(false); } Windows.showWindow(frame, null, false); frame.toFront(); }); } } private static void updateSplashStatus(final SplashScreen splashScreen, final String status) { if (splashScreen != null) { EventQueue.invokeLater(() -> { if (!splashScreen.isVisible()) { Windows.showWindow(splashScreen, null, true); } splashScreen.toFront(); splashScreen.setStatusText(status); }); } } private static void hideSplashScreen(final SplashScreen splashScreen) throws InvocationTargetException, InterruptedException { if (splashScreen != null) { EventQueue.invokeAndWait(() -> splashScreen.setVisible(false)); } } public static void startUI(final String appTitle) { final Logger logger = LoggerFactory.getLogger(Lilith.class); UIManager.installLookAndFeel("JGoodies Windows", "com.jgoodies.looks.windows.WindowsLookAndFeel"); UIManager.installLookAndFeel("JGoodies Plastic", "com.jgoodies.looks.plastic.PlasticLookAndFeel"); UIManager.installLookAndFeel("JGoodies Plastic 3D", "com.jgoodies.looks.plastic.Plastic3DLookAndFeel"); UIManager.installLookAndFeel("JGoodies Plastic XP", "com.jgoodies.looks.plastic.PlasticXPLookAndFeel"); // Substance requires 1.6 UIManager.installLookAndFeel("Substance Dark - Twilight", "org.pushingpixels.substance.api.skin.SubstanceTwilightLookAndFeel"); UIManager.installLookAndFeel("Substance Light - Business", "org.pushingpixels.substance.api.skin.SubstanceBusinessLookAndFeel"); //UIManager.installLookAndFeel("Napkin", "net.sourceforge.napkinlaf.NapkinLookAndFeel"); // look & feels must be installed before creation of ApplicationPreferences. ApplicationPreferences applicationPreferences = new ApplicationPreferences(); // init look & feel String lookAndFeelName = applicationPreferences.getLookAndFeel(); String systemLookAndFeelClassName = UIManager.getSystemLookAndFeelClassName(); String lookAndFeelClassName = systemLookAndFeelClassName; if (lookAndFeelName != null) { for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if (lookAndFeelName.equals(info.getName())) { lookAndFeelClassName = info.getClassName(); break; } } } try { UIManager.setLookAndFeel(lookAndFeelClassName); } catch (Throwable e) { if (logger.isWarnEnabled()) logger.warn("Failed to set look&feel to '{}'.", lookAndFeelClassName, e); if (!lookAndFeelClassName.equals(systemLookAndFeelClassName)) { try { UIManager.setLookAndFeel(systemLookAndFeelClassName); lookAndFeelClassName = systemLookAndFeelClassName; } catch (Throwable e2) { if (logger.isWarnEnabled()) logger.warn("Failed to set look&feel to '{}'.", systemLookAndFeelClassName, e); lookAndFeelClassName = null; } } } boolean screenMenuBar = false; if (systemLookAndFeelClassName.equals(lookAndFeelClassName)) { // This instance of application is only used to query some info. The real one is in MainFrame. Application application = new DefaultApplication(); if (application.isMac()) { // Use Apple Aqua L&F screen menu bar if available; set property before any frames created try { System.setProperty(APPLE_SCREEN_MENU_BAR_SYSTEM_PROPERTY, "true"); screenMenuBar = true; } catch (Throwable e) { try { screenMenuBar = Boolean .parseBoolean(System.getProperty(APPLE_SCREEN_MENU_BAR_SYSTEM_PROPERTY, "false")); } catch (Throwable e2) { // ignore } } } } applicationPreferences.setUsingScreenMenuBar(screenMenuBar); boolean splashScreenDisabled = applicationPreferences.isSplashScreenDisabled(); try { SplashScreen splashScreen = null; if (!splashScreenDisabled) { CreateSplashRunnable createRunnable = new CreateSplashRunnable(appTitle); EventQueue.invokeAndWait(createRunnable); splashScreen = createRunnable.getSplashScreen(); Thread.sleep(500); // so the splash gets the chance to get displayed :( updateSplashStatus(splashScreen, "Initialized application preferences..."); } File startupApplicationPath = applicationPreferences.getStartupApplicationPath(); if (startupApplicationPath.mkdirs()) { if (logger.isDebugEnabled()) logger.debug("Created '{}'.", startupApplicationPath.getAbsolutePath()); } // System.err redirection { File errorLog = new File(startupApplicationPath, "errors.log"); boolean freshFile = false; if (!errorLog.isFile()) { freshFile = true; } try { FileOutputStream fos = new FileOutputStream(errorLog, true); PrintStream ps = new PrintStream(fos, true, StandardCharsets.UTF_8.name()); if (!freshFile) { ps.println("----------------------------------------"); } String currentDateTime = DateTimeFormatters.DATETIME_IN_SYSTEM_ZONE_SPACE.format(Instant.now()); ps.println("Started " + APP_NAME + " V" + APP_VERSION + " at " + currentDateTime); System.setErr(ps); if (logger.isInfoEnabled()) logger.info("Writing System.err to '{}'.", errorLog.getAbsolutePath()); } catch (FileNotFoundException | UnsupportedEncodingException e) { e.printStackTrace(); } } File prevPathFile = new File(startupApplicationPath, ApplicationPreferences.PREVIOUS_APPLICATION_PATH_FILENAME); if (prevPathFile.isFile()) { updateSplashStatus(splashScreen, "Moving application path content..."); moveApplicationPathContent(prevPathFile, startupApplicationPath); } if (!applicationPreferences.isLicensed()) { hideSplashScreen(splashScreen); LicenseAgreementDialog licenseDialog = new LicenseAgreementDialog(); licenseDialog.setAlwaysOnTop(true); licenseDialog.setAutoRequestFocus(true); Windows.showWindow(licenseDialog, null, true); if (licenseDialog.isLicenseAgreed()) { applicationPreferences.setLicensed(true); } else { if (logger.isWarnEnabled()) logger.warn("Didn't accept license! Exiting..."); System.exit(-1); } } updateSplashStatus(splashScreen, "Creating main window..."); CreateMainFrameRunnable createMain = new CreateMainFrameRunnable(applicationPreferences, splashScreen, appTitle); EventQueue.invokeAndWait(createMain); final MainFrame frame = createMain.getMainFrame(); if (logger.isDebugEnabled()) logger.debug("After show..."); updateSplashStatus(splashScreen, "Initializing application..."); EventQueue.invokeAndWait(frame::startUp); hideSplashScreen(splashScreen); mainFrame = frame; } catch (InterruptedException ex) { if (logger.isInfoEnabled()) logger.info("Interrupted...", ex); IOUtilities.interruptIfNecessary(ex); } catch (InvocationTargetException ex) { if (logger.isWarnEnabled()) logger.warn("InvocationTargetException...", ex); if (logger.isWarnEnabled()) logger.warn("Target-Exception: ", ex.getTargetException()); } } static class CreateSplashRunnable implements Runnable { private SplashScreen splashScreen; private String appTitle; CreateSplashRunnable(String appTitle) { this.appTitle = appTitle; } public void run() { splashScreen = new SplashScreen(appTitle); Windows.showWindow(splashScreen, null, true); } SplashScreen getSplashScreen() { return splashScreen; } } static class CreateMainFrameRunnable implements Runnable { private SplashScreen splashScreen; private MainFrame mainFrame; private ApplicationPreferences applicationPreferences; private String appTitle; CreateMainFrameRunnable(ApplicationPreferences applicationPreferences, SplashScreen splashScreen, String appTitle) { this.splashScreen = splashScreen; this.appTitle = appTitle; this.applicationPreferences = applicationPreferences; } public void run() { mainFrame = new MainFrame(applicationPreferences, splashScreen, appTitle); mainFrame.setSounds(new JLayerSounds()); mainFrame.setSize(1024, 768); Windows.showWindow(mainFrame, null, false); } public MainFrame getMainFrame() { return mainFrame; } } /** * @param prevPathFile the file that contains (!!!) the previous application path - not the previous application path itself! * @param startupApplicationPath the current application path, i.e. the destination path. */ private static void moveApplicationPathContent(File prevPathFile, File startupApplicationPath) { final Logger logger = LoggerFactory.getLogger(Lilith.class); InputStream is = null; String prevPathStr = null; try { is = new FileInputStream(prevPathFile); prevPathStr = IOUtils.toString(is, StandardCharsets.UTF_8); } catch (IOException ex) { if (logger.isWarnEnabled()) logger.warn("Exception while reading previous application path!", ex); } finally { IOUtilities.closeQuietly(is); } if (prevPathStr != null) { File prevPath = new File(prevPathStr); try { FileUtils.copyDirectory(prevPath, startupApplicationPath); FileUtils.deleteDirectory(prevPath); } catch (IOException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception while moving content of previous application path '" + prevPath.getAbsolutePath() + "' to new one '" + startupApplicationPath.getAbsolutePath() + "'!", ex); } } if (logger.isInfoEnabled()) { logger.info("Moved content from previous application path '{}' to new application path '{}'.", prevPath.getAbsolutePath(), startupApplicationPath.getAbsolutePath()); } } if (prevPathFile.delete()) { if (logger.isDebugEnabled()) logger.debug("Deleted {}.", prevPathFile.getAbsolutePath()); } } }