// Copyright (C) 2001-2003 Jon A. Maxwell (JAM) // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package jogamp.plugin.jnlp; import static net.sourceforge.jnlp.runtime.Translator.R; import java.io.File; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import jogamp.applet.App3Context; import jogamp.plugin.jnlp.runtime.Applet3Instance; import com.jogamp.plugin.applet.Applet3; import net.sourceforge.jnlp.AppletDesc; import net.sourceforge.jnlp.ApplicationDesc; import net.sourceforge.jnlp.JARDesc; import net.sourceforge.jnlp.JNLPFile; import net.sourceforge.jnlp.LaunchException; import net.sourceforge.jnlp.LaunchHandler; import net.sourceforge.jnlp.ParserSettings; import net.sourceforge.jnlp.PluginBridge; import net.sourceforge.jnlp.PropertyDesc; import net.sourceforge.jnlp.ResourcesDesc; import net.sourceforge.jnlp.StreamEater; import net.sourceforge.jnlp.util.JarFile; import net.sourceforge.jnlp.cache.CacheUtil; import net.sourceforge.jnlp.cache.UpdatePolicy; import net.sourceforge.jnlp.runtime.ApplicationInstance; import net.sourceforge.jnlp.runtime.JNLPRuntime; import net.sourceforge.jnlp.runtime.JNLPClassLoader; import net.sourceforge.jnlp.services.InstanceExistsException; import net.sourceforge.jnlp.services.ServiceUtil; import net.sourceforge.jnlp.splashscreen.SplashUtils; import net.sourceforge.jnlp.util.logging.OutputController; /** * Launches JNLPFiles either in the foreground or background.
* * An optional LaunchHandler can be specified that is notified of * warning and error condition while launching and that indicates * whether a launch may proceed after a warning has occurred. If * specified, the LaunchHandler is notified regardless of whether * the file is launched in the foreground or background.
*
* @author Jon A. Maxwell (JAM) - initial author
* @version $Revision: 1.22 $
*/
public class App3Launcher {
// defines class Launcher.BgRunner, Launcher.TgThread
/** shared thread group */
/*package*/static final ThreadGroup mainGroup = new ThreadGroup(R("LAllThreadGroup"));
public static ThreadGroup getMainGroup() { return mainGroup; }
/** the handler */
private LaunchHandler handler = null;
/** the update policy */
private UpdatePolicy updatePolicy = JNLPRuntime.getDefaultUpdatePolicy();
/** whether to create an AppContext (if possible) */
private boolean context = true;
/** If the application should call JNLPRuntime.exit on fatal errors */
private boolean exitOnFailure = true;
private ParserSettings parserSettings = new ParserSettings();
private Map
*
* The enableCodeBase parameter adds the applet's codebase to
* the locations searched for resources and classes. This can
* slow down the applet loading but allows browser-style applets
* that don't use JAR files exclusively to be run from a applet
* JNLP file. If the applet JNLP file does not specify any
* resources then the code base will be enabled regardless of
* the specified value.
*
* @param file the JNLP file
* @param enableCodeBase whether to add the codebase URL to the classloader
*/
protected ApplicationInstance launchApplet(JNLPFile file, boolean enableCodeBase) throws LaunchException {
if (!file.isApplet()) {
throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplet"), R("LNotAppletInfo")));
}
if (handler != null) {
handler.launchInitialized(file);
}
Applet3Instance applet = null;
try {
ServiceUtil.checkExistingSingleInstance(file);
applet = createApplet(file, enableCodeBase);
applet.initialize();
applet.getAppletEnvironment().startApplet(); // this should be a direct call to applet instance
return applet;
} catch (InstanceExistsException ieex) {
OutputController.getLogger().log("Single instance applet is already running.");
throw launchError(new LaunchException(file, ieex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LSingleInstanceExists")), applet);
} catch (LaunchException lex) {
throw launchError(lex, applet);
} catch (Exception ex) {
throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo")), applet);
}finally{
if (handler != null) {
handler.launchStarting(applet);
}
}
}
/**
* Gets an ApplicationInstance, but does not launch the applet.
*/
protected ApplicationInstance getApplet(JNLPFile file, boolean enableCodeBase) throws LaunchException {
if (!file.isApplet()) {
throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplet"), R("LNotAppletInfo")));
}
Applet3Instance applet = null;
try {
ServiceUtil.checkExistingSingleInstance(file);
applet = createApplet(file, enableCodeBase);
applet.initialize();
return applet;
} catch (InstanceExistsException ieex) {
OutputController.getLogger().log("Single instance applet is already running.");
throw launchError(new LaunchException(file, ieex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LSingleInstanceExists")), applet);
} catch (LaunchException lex) {
throw launchError(lex, applet);
} catch (Exception ex) {
throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo")), applet);
}
}
/**
* Launches a JNLP installer. This method should be called from
* a thread in the application's thread group.
*/
protected ApplicationInstance launchInstaller(JNLPFile file) throws LaunchException {
// TODO Check for an existing single instance once implemented.
// ServiceUtil.checkExistingSingleInstance(file);
throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCNotSupported"), R("LNoInstallers"), R("LNoInstallersInfo")));
}
/**
* Create an AppletInstance.
*
* @param enableCodeBase whether to add the code base URL to the classloader
*/
//FIXME - when multiple applets are on one page, this method is visited simultaneously
//and then appelts creates in little bit strange manner. This issue is visible with
//randomly showing/notshowing spalshscreens.
//See also PluginAppletViewer.framePanel
protected Applet3Instance createApplet(JNLPFile file, boolean enableCodeBase) throws LaunchException {
Applet3Instance appletInstance = null;
try {
JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
if (enableCodeBase) {
loader.enableCodeBase();
} else if (file.getResources().getJARs().length == 0) {
throw new ClassNotFoundException("Can't do a codebase look up and there are no jars. Failing sooner rather than later");
}
ThreadGroup group = Thread.currentThread().getThreadGroup();
// appletInstance is needed by ServiceManager when looking up
// services. This could potentially be done in applet constructor
// so initialize appletInstance before creating applet.
appletInstance = new Applet3Instance(file, group, loader, null);
loader.setApplication(appletInstance);
// Initialize applet now that ServiceManager has access to its
// appletInstance.
String appletName = file.getApplet().getMainClass();
Class> appletClass = loader.loadClass(appletName);
Applet3 applet = (Applet3) appletClass.newInstance();
// Finish setting up appletInstance.
appletInstance.setApplet(applet);
appletInstance.getAppletEnvironment().setApplet(applet);
setContextClassLoaderForAllThreads(appletInstance.getThreadGroup(), appletInstance.getClassLoader());
return appletInstance;
} catch (Exception ex) {
throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplet"), R("LInitAppletInfo")), appletInstance);
}
}
/**
* Creates an Applet object from a JNLPFile. This is mainly to be used with
* gcjwebplugin.
* @param file the PluginBridge to be used.
* @param enableCodeBase whether to add the code base URL to the classloader.
*/
protected Applet3 createAppletObject(JNLPFile file, boolean enableCodeBase) throws LaunchException {
try {
JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
if (enableCodeBase) {
loader.enableCodeBase();
} else if (file.getResources().getJARs().length == 0) {
throw new ClassNotFoundException("Can't do a codebase look up and there are no jars. Failing sooner rather than later");
}
String appletName = file.getApplet().getMainClass();
Class> appletClass = loader.loadClass(appletName);
Applet3 applet = (Applet3) appletClass.newInstance();
return applet;
} catch (Exception ex) {
throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplet"), R("LInitAppletInfo")));
}
}
/**
* Creates an Application.
*/
protected ApplicationInstance createApplication(JNLPFile file) throws LaunchException {
try {
JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
ThreadGroup group = Thread.currentThread().getThreadGroup();
ApplicationInstance app = new ApplicationInstance(file, group, loader, App3Context.getAppContext());
loader.setApplication(app);
return app;
} catch (Exception ex) {
throw new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplication"), R("LInitApplicationInfo"));
}
}
/**
* Create a thread group for the JNLP file.
*
* Note: if the JNLPFile is an applet (ie it is a subclass of PluginBridge)
* then this method simply returns the existing ThreadGroup. The applet
* ThreadGroup has to be created at an earlier point in the applet code.
*/
protected ThreadGroup createThreadGroup(JNLPFile file) {
final ThreadGroup tg;
if (file instanceof PluginBridge) {
tg = Thread.currentThread().getThreadGroup();
} else {
tg = new ThreadGroup(mainGroup, file.getTitle());
}
return tg;
}
/**
* Send n launch error to the handler, if set, and also to the
* caller.
*/
private LaunchException launchError(LaunchException ex) {
return launchError(ex, null);
}
private LaunchException launchError(LaunchException ex, Applet3Instance applet) {
if (applet != null) {
SplashUtils.showErrorCaught(ex, applet);
}
if (handler != null) {
handler.launchError(ex);
}
return ex;
}
/**
* Send a launch error to the handler, if set, and to the
* caller only if the handler indicated that the launch should
* continue despite the warning.
*
* @return an exception to throw if the launch should be aborted, or null otherwise
*/
private LaunchException launchWarning(LaunchException ex) {
if (handler != null) {
if (!handler.launchWarning(ex))
// no need to destroy the app b/c it hasn't started
return ex;
} // chose to abort
return null; // chose to continue, or no handler
}
/**
* This runnable is used to call the appropriate launch method
* for the application, applet, or installer in its thread group.
*/
private class TgThread extends Thread { // ThreadGroupThread
private JNLPFile file;
private ApplicationInstance application;
private LaunchException exception;
private boolean isPlugin = false;
TgThread(JNLPFile file) {
super(createThreadGroup(file), file.getTitle());
this.file = file;
}
TgThread(JNLPFile file, boolean isPlugin) {
super(createThreadGroup(file), file.getTitle());
this.file = file;
this.isPlugin = isPlugin;
}
@Override
public void run() {
try {
// Do not create new AppContext if we're using NetX and icedteaplugin.
// The plugin needs an AppContext too, but it has to be created earlier.
/** FIXME: Non plugin context
if (context && !isPlugin) {
SunToolkit.createNewAppContext();
}
doPerApplicationAppContextHacks();
*/
if (isPlugin) {
// Do not display download indicators if we're using gcjwebplugin.
JNLPRuntime.setDefaultDownloadIndicator(null);
application = getApplet(file, ((PluginBridge)file).codeBaseLookup());
} else {
if (file.isApplication()) {
application = launchApplication(file);
}
else if (file.isApplet()) {
application = launchApplet(file, true);
} // enable applet code base
else if (file.isInstaller()) {
application = launchInstaller(file);
}
else {
throw launchError(new LaunchException(file, null,
R("LSFatal"), R("LCClient"), R("LNotLaunchable"),
R("LNotLaunchableInfo")));
}
}
} catch (LaunchException ex) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ex);
exception = ex;
// Exit if we can't launch the application.
if (exitOnFailure) {
JNLPRuntime.exit(1);
}
} catch (Throwable ex) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ex);
throw new RuntimeException(ex);
}
}
public LaunchException getException() {
return exception;
}
public ApplicationInstance getApplication() {
return application;
}
};
}