/*
* ManagerApp.java
*/
package modmanager.app;
import com.thoughtworks.xstream.io.StreamException;
import org.jdesktop.application.Application;
import org.jdesktop.application.Task;
import org.jdesktop.application.SingleFrameApplication;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import modmanager.business.ManagerOptions;
import modmanager.controller.Manager;
import modmanager.gui.ManagerCtrl;
import modmanager.gui.ManagerGUI;
import modmanager.gui.l10n.L10n;
import modmanager.utility.FileUtils;
import modmanager.utility.OS;
import modmanager.utility.SplashScreenMain;
import modmanager.utility.update.UpdateManager;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import java.util.concurrent.ExecutionException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.io.File;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.lang.instrument.Instrumentation;
import java.net.URISyntaxException;
import java.nio.channels.FileLock;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @author Shirkit
* The main class of the application.
*/
public class ManagerApp extends SingleFrameApplication {
Logger logger;
ManagerCtrl ctrl; // Controller
// File with log4j configuration
private static final String LOGGER_PROPS = "modmanager/utility/log4j.properties";
/**
* At startup create and show the main frame of the application. This is where
* logging system and L10n framework is initialized.
*/
@Override
protected void startup() {
// Check for lock file.
if (!lockInstance(FileUtils.getManagerTempFolder() + File.separator + "Manager.lock")) {
JOptionPane.showMessageDialog(null, "Another instance of the Manager is already running.", "Error", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
// Checking java version
if (System.getProperty("java.version").startsWith("1.5") || System.getProperty("java.version").startsWith("1.4")) {
JOptionPane.showMessageDialog(null, "Please update your JRE environment to the latest version.", "Error", JOptionPane.ERROR_MESSAGE);
}
System.setProperty("http.agent", "All-In HoN ModManager");
// A separated thread to run the SplashScreen
Task<Void, Void> task = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("SplashScreen");
SplashScreenMain splashScreen = new SplashScreenMain(new ImageIcon(getClass().getResource("/modmanager/gui/resources/splash.jpg")));
return null;
}
};
task.execute();
// And a another thread to continue loading the manager
Task<Void, Void> task2 = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("ManagerLaunch");
// Look for Manager update
final ExecutorService pool = Executors.newCachedThreadPool();
final Future<Boolean> hasUpdate = pool.submit(new UpdateManager());
// Initiate log4j logger and the fatal logger
ClassLoader cl = this.getClass().getClassLoader();
InputStream is = cl.getResourceAsStream(LOGGER_PROPS);
Properties props = new Properties();
try {
props.load(is);
// Change path but don't change the filename
props.setProperty("log4j.appender.file.File", FileUtils.getManagerPerpetualFolder().getAbsolutePath() + File.separator + props.getProperty("log4j.appender.file.File"));
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Cannot initialize logging system", "Error", JOptionPane.ERROR_MESSAGE);
}
PropertyConfigurator.configure(props);
//StdOutErrLog.tieSystemErrToLog();
// Log procedure operations
logger = Logger.getLogger(this.getClass().getPackage().getName());
logger.info("------------------------------------------------------------------------------------------------------------------------");
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss");
Date date = new Date();
logger.info("HonMod manager is starting.");
logger.info("Local time: " + dateFormat.format(date));
logger.info("Java version: " + System.getProperty("java.version"));
logger.info("HonMod manager version: " + ManagerOptions.getInstance().getVersion());
logger.info("Running on: " + System.getProperty("os.name") + "|" + System.getProperty("os.version") + "|" + System.getProperty("os.arch"));
try {
// Try load options. They must be loaded now for language loading
try {
Manager.getInstance().loadOptions();
} catch (StreamException e) {
logger.error("StreamException from loadOptions(), couldn't load options file.", e);
// Mod options is invalid, just ignore and it will be deleted.
} catch (Exception e) {
System.out.println(e);
}
// Load language, if any
if (ManagerOptions.getInstance().getLanguage() != null && !ManagerOptions.getInstance().getLanguage().isEmpty()) {
L10n.load(ManagerOptions.getInstance().getLanguage());
} else {
L10n.load();
}
} catch (IOException ex) {
}
logger.info("------------------------------------------------------------------------------------------------------------------------");
// Load the interface
try {
ctrl = new ManagerCtrl();
} catch (Exception e) {
logger.error("Error while starting the manager. " + e.getClass() + " | " + e.getCause() + " | " + e.getMessage(), e);
JOptionPane.showMessageDialog(null, " Critical error while starting the manager. " + e.getClass() + " | " + e.getCause() + " | " + e.getMessage(), "Critical Error", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
// Firing a new thread so the Manager can load faster
Task<Void, Void> t = new Task(Application.getInstance()) {
@Override
protected Object doInBackground() throws Exception {
Thread.currentThread().setName("UpdateManager");
// Look for Updater.jar file, and try to delete it. If this file is found, it means the last run was an update. We could do some update actions here.
File updaterJar = new File(System.getProperty("user.dir") + File.separator + "Updater.jar");
if (updaterJar.exists()) {
if (!updaterJar.delete()) {
updaterJar.deleteOnExit();
}
}
// Wait until the searching for the update is done. This can delay the start of the manager, this should be changed to fire a thread.
while (!hasUpdate.isDone()) {
}
try {
if (hasUpdate.get().booleanValue()) {
// If there is a new update
if (ManagerOptions.getInstance().isAutoUpdate() || JOptionPane.showConfirmDialog(ManagerGUI.getInstance(), L10n.getString("message.updateavaliabe"), L10n.getString("message.updateavaliabe.title"), JOptionPane.YES_NO_OPTION) == 0) {
// And user accepts it
try {
// Extract the Updater jar file
InputStream in = getClass().getResourceAsStream("/modmanager/resources/Updater");
FileOutputStream fos = new FileOutputStream(ManagerOptions.MANAGER_FOLDER + File.separator + "Updater.jar");
FileUtils.copyInputStream(in, fos);
in.close();
fos.close();
String currentJar = "";
// Get current jar path. Since user may rename this file, we need to do this way
try {
currentJar = (ManagerApp.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
if ((OS.isWindows() && currentJar.startsWith("/")) || (currentJar.startsWith("//") && !OS.isWindows())) {
currentJar = currentJar.replaceFirst("/", "");
}
} catch (URISyntaxException ex) {
}
String updaterPath = System.getProperty("user.dir") + File.separator + "Updater.jar";
// Run with an String array to avoid errors with blank spaces and uncommon characters
// Gcommer: TODO: Not all users will have "java" in their PATH.
String[] cmd = {"java", "-jar", updaterPath, currentJar, ManagerOptions.getInstance().getVersion(), ManagerOptions.MANAGER_CHECK_UPDATE_VERSIONS, ManagerOptions.MANAGER_CHECK_UPDATE_ROOT_FOLDER, FileUtils.generateTempFolder(false).getAbsolutePath()};
String s = "";
for (int i = 0; i < cmd.length; i++) {
s += " " + cmd[i];
}
logger.info("Updating manager." + s);
Runtime.getRuntime().exec(cmd);
shutdown();
} catch (IOException ex) {
// Failed to launch process
logger.fatal(ex);
}
}
}
} catch (InterruptedException ex) {
// Job is never stopped
} catch (ExecutionException ex) {
// Exceptions are never thrown
}
pool.shutdown();
return null;
}
};
t.execute();
return null;
}
};
task2.execute();
}
/**
* This method is to initialize the specified window by injecting resources.
* Windows shown in our application come fully initialized from the GUI
* builder, so this additional configuration is not needed.
*/
@Override
protected void configureWindow(java.awt.Window root) {
}
@Override
protected void shutdown() {
logger.info("Shutting down!");
FileUtils.deleteTemporaryFolders(); // This can slow down closing speed if the user applied the mods lots and lots of times
System.exit(0);
}
/**
* A convenient static getter for the application instance.
* @return the instance of ManagerApp
*/
public static ManagerApp getApplication() {
return Application.getInstance(ManagerApp.class);
}
public static void requestShutdown() {
getApplication().shutdown();
}
/**
* Main method launching the application.
*/
public static void main(String[] args) throws FileNotFoundException, IOException {
launch(ManagerApp.class, args);
}
/**
* Method to prevent multiple instances of the Manager running.
* @param lockFile path a file. This should be constant.
* @return true is there is no other instance running, false otehrwise.
*/
private boolean lockInstance(final String lockFile) {
try {
final File file = new File(lockFile);
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
if (fileLock != null) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
fileLock.release();
randomAccessFile.close();
file.delete();
} catch (Exception e) {
logger.error("Unable to remove lock file: " + lockFile, e);
}
}
});
return true;
}
} catch (Exception e) {
logger.error("Unable to create and/or lock file: " + lockFile, e);
}
return false;
}
}