/*
* 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.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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Instant;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import javax.swing.UIManager;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
public class Lilith
{
static
{
SLF4JBridgeHandler.removeHandlersForRootLogger(); // (since SLF4J 1.6.5)
SLF4JBridgeHandler.install();
}
// uncomment the code below to enable flyingsaucer logging......
/*
static
{
System.getProperties().setProperty("xr.util-logging.loggingEnabled", "true");
XRLog.setLoggingEnabled(true);
}
*/
/**
* 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)
{
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-2017 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("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))
{
if(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
{
OutputStream fos = Files.newOutputStream(errorLog.toPath(), CREATE, APPEND);
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(IOException 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);
String prevPathStr = null;
try(InputStream is = Files.newInputStream(prevPathFile.toPath()))
{
prevPathStr = IOUtils.toString(is, StandardCharsets.UTF_8);
}
catch(IOException ex)
{
if(logger.isWarnEnabled()) logger.warn("Exception while reading previous application path!", ex);
}
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());
}
}
private static boolean isMac()
{
String osName = System.getProperty("os.name").toLowerCase(Locale.US);
return osName.startsWith("mac");
}
}