/*
* $Id$
*
* Copyright (c) 2000-2009 by Rodney Kinney, Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.launch;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.Socket;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import VASSAL.Info;
import VASSAL.build.GameModule;
import VASSAL.build.module.ExtensionsLoader;
import VASSAL.i18n.Resources;
import VASSAL.tools.ErrorDialog;
import VASSAL.tools.ThrowableUtils;
import VASSAL.tools.concurrent.listener.EventListener;
import VASSAL.tools.ipc.IPCMessenger;
import VASSAL.tools.ipc.SimpleIPCMessage;
import VASSAL.tools.logging.LoggedOutputStream;
import VASSAL.tools.menu.MenuManager;
/**
* @author Joel Uckelman
* @since 3.1.0
*/
public abstract class Launcher {
private static final Logger logger = LoggerFactory.getLogger(Launcher.class);
protected IPCMessenger ipc = null;
protected final LaunchRequest lr;
private static Launcher instance = null;
public static Launcher getInstance() {
return instance;
}
protected Launcher(String[] args) {
if (instance != null) throw new IllegalStateException();
instance = this;
LaunchRequest lreq = null;
try {
lreq = LaunchRequest.parseArgs(args);
}
catch (LaunchRequestException e) {
System.err.println("VASSAL: " + e.getMessage());
System.exit(1);
}
lr = lreq;
// Note: We could do more sanity checking of the launch request
// in standalone mode, but we don't bother because this is meant
// only for debugging, not for normal use. If you pass nonsense
// arguments (e.g., '-e' to the Player), don't expect it to work.
final boolean standalone = lr.standalone;
/*
// parse the command line args now if we're standalone, since they
// could be messed up and so we'll bail before setup
LaunchRequest lr = null;
if (standalone) {
// Note: We could do more sanity checking of the launch request
// in standalone mode, but we don't bother because this is meant
// only for debugging, not for normal use. If you pass nonsense
// arguments (e.g., '-e' to the Player), don't expect it to work.
try {
lr = LaunchRequest.parseArgs(args);
}
catch (LaunchRequestException e) {
System.err.println("VASSAL: " + e.getMessage());
System.exit(1);
}
}
*/
// start the error log and setup system properties
final StartUp start = SystemUtils.IS_OS_MAC_OSX ?
new MacOSXStartUp() : new StartUp();
start.startErrorLog();
// log everything which comes across our stderr
System.setErr(new PrintStream(new LoggedOutputStream(), true));
logger.info(getClass().getSimpleName());
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
start.initSystemProperties();
// if we're not standalone, contact the module manager for instructions
if (!standalone) {
try {
final int port = Integer.parseInt(System.getProperty("VASSAL.port"));
final InetAddress lo = InetAddress.getByName(null);
final Socket cs = new Socket(lo, port);
ipc = new IPCMessenger(cs);
ipc.addEventListener(CloseRequest.class, new CloseRequestListener());
ipc.start();
ipc.send(new StartedNotice(Info.getInstanceID()));
}
catch (IOException e) {
// What we've got here is failure to communicate.
ErrorDialog.show(
e,
"Error.communication_error",
Resources.getString(getClass().getSimpleName() + ".app_name")
);
System.exit(1);
}
}
createMenuManager();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
launch();
}
catch (ExtensionsLoader.LoadExtensionException e2) {
warn(e2);
}
catch (IOException e1) {
warn(e1);
}
}
private void warn (Exception e1) {
if (ipc == null) {
// we are standalone, so warn the user directly
ErrorDialog.showDetails(
e1,
ThrowableUtils.getStackTrace(e1),
"Error.module_load_failed",
e1.getMessage()
);
}
else {
// we have a manager, so pass the load failure back to it
try {
ipc.send(new AbstractLaunchAction.NotifyOpenModuleFailed(lr, e1));
}
catch (IOException e2) {
// warn the user directly as a last resort
ErrorDialog.showDetails(
e1,
ThrowableUtils.getStackTrace(e1),
"Error.module_load_failed",
e1.getMessage()
);
ErrorDialog.show(
e2,
"Error.communication_error",
Resources.getString(getClass().getSimpleName() + ".app_name")
);
}
}
System.exit(1);
}
});
}
protected abstract void launch() throws IOException;
protected abstract MenuManager createMenuManager();
public static class CloseRequest extends SimpleIPCMessage {
private static final long serialVersionUID = 1L;
}
public static class CloseAccept extends SimpleIPCMessage {
private static final long serialVersionUID = 1L;
public final long pid;
public CloseAccept(long pid) {
this.pid = pid;
}
}
public static class CloseReject extends SimpleIPCMessage {
private static final long serialVersionUID = 1L;
public final long pid;
public CloseReject(long pid) {
this.pid = pid;
}
}
public static class StartedNotice extends SimpleIPCMessage {
private static final long serialVersionUID = 1L;
public final long pid;
public StartedNotice(long pid) {
this.pid = pid;
}
}
protected class CloseRequestListener implements EventListener<CloseRequest> {
private boolean shutdown;
public void receive(Object src, CloseRequest msg) {
final GameModule module = GameModule.getGameModule();
if (module != null) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
module.getFrame().toFront();
shutdown = module.shutDown();
}
});
}
catch (InterruptedException e) {
logger.error("", e);
shutdown = false;
}
catch (InvocationTargetException e) {
ErrorDialog.bug(e);
shutdown = false;
}
}
if (shutdown) {
if (ipc != null) {
try {
ipc.send(new CloseAccept(-msg.getId()));
}
catch (IOException e) {
e.printStackTrace();
}
}
System.exit(0);
}
else {
if (ipc != null) {
try {
ipc.send(new CloseReject(-msg.getId()));
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public void sendSaveCmd(File f) {
if (ipc != null) {
try {
ipc.send(new AbstractLaunchAction.NotifySaveFileOk(f));
}
// FIXME: review error message
catch (IOException e) {
}
}
}
}