diff options
Diffstat (limited to 'netx/net/sourceforge/jnlp/Launcher.java')
-rw-r--r-- | netx/net/sourceforge/jnlp/Launcher.java | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/netx/net/sourceforge/jnlp/Launcher.java b/netx/net/sourceforge/jnlp/Launcher.java new file mode 100644 index 0000000..fbd8a60 --- /dev/null +++ b/netx/net/sourceforge/jnlp/Launcher.java @@ -0,0 +1,883 @@ +// 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 net.sourceforge.jnlp; + +import java.applet.Applet; +import java.awt.Container; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.URL; +import java.net.UnknownHostException; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.util.LinkedList; +import java.util.List; +import java.util.jar.JarFile; + +import net.sourceforge.jnlp.cache.CacheUtil; +import net.sourceforge.jnlp.cache.ResourceTracker; +import net.sourceforge.jnlp.cache.UpdatePolicy; +import net.sourceforge.jnlp.runtime.AppThreadGroup; +import net.sourceforge.jnlp.runtime.AppletInstance; +import net.sourceforge.jnlp.runtime.ApplicationInstance; +import net.sourceforge.jnlp.runtime.JNLPClassLoader; +import net.sourceforge.jnlp.runtime.JNLPRuntime; +import net.sourceforge.jnlp.services.InstanceExistsException; +import net.sourceforge.jnlp.services.ServiceUtil; +import net.sourceforge.jnlp.util.Reflect; + +import javax.swing.SwingUtilities; + +import sun.awt.SunToolkit; + +/** + * Launches JNLPFiles either in the foreground or background.<p> + * + * 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.<p> + * + * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author + * @version $Revision: 1.22 $ + */ +public class Launcher { + + // defines class Launcher.BgRunner, Launcher.TgThread + + /** shortcut for resources */ + private static String R(String key) { return JNLPRuntime.getMessage(key); } + + /** shared thread group */ + /*package*/ static final ThreadGroup mainGroup = new ThreadGroup(R("LAllThreadGroup")); + + /** 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 System.exit on fatal errors */ + private boolean exitOnFailure = true; + + /** a lock which is held to indicate that an instance of netx is running */ + private FileLock fileLock; + + /** + * Create a launcher with the runtime's default update policy + * and launch handler. + */ + public Launcher() { + this(null, null); + + if (handler == null) + handler = JNLPRuntime.getDefaultLaunchHandler(); + } + + /** + * Create a launcher with the runtime's default update policy + * and launch handler. + * + * @param exitOnError Exit if there is an error (usually default, but false when being used from the plugin) + */ + public Launcher(boolean exitOnFailure) { + this(null, null); + + if (handler == null) + handler = JNLPRuntime.getDefaultLaunchHandler(); + + this.exitOnFailure = exitOnFailure; + } + + /** + * Create a launcher with the specified handler and the + * runtime's default update policy. + * + * @param handler the handler to use or null for no handler. + */ + public Launcher(LaunchHandler handler) { + this(handler, null); + } + + /** + * Create a launcher with an optional handler using the + * specified update policy and launch handler. + * + * @param handler the handler to use or null for no handler. + * @param policy the update policy to use or null for default policy. + */ + public Launcher(LaunchHandler handler, UpdatePolicy policy) { + if (policy == null) + policy = JNLPRuntime.getDefaultUpdatePolicy(); + + this.handler = handler; + this.updatePolicy = policy; + + } + + /** + * Sets the update policy used by launched applications. + */ + public void setUpdatePolicy(UpdatePolicy policy) { + if (policy == null) + throw new IllegalArgumentException(R("LNullUpdatePolicy")); + + this.updatePolicy = policy; + } + + /** + * Returns the update policy used when launching applications. + */ + public UpdatePolicy getUpdatePolicy() { + return updatePolicy; + } + + /** + * Sets whether to launch the application in a new AppContext + * (a separate event queue, look and feel, etc). If the + * sun.awt.SunToolkit class is not present then this method + * has no effect. The default value is true. + */ + public void setCreateAppContext(boolean context) { + this.context = context; + } + + /** + * Returns whether applications are launched in their own + * AppContext. + */ + public boolean isCreateAppContext() { + return this.context; + } + + /** + * Launches a JNLP file by calling the launch method for the + * appropriate file type. The application will be started in + * a new window. + * + * @param file the JNLP file to launch + * @return the application instance + * @throws LaunchException if an error occurred while launching (also sent to handler) + */ + public ApplicationInstance launch(JNLPFile file) throws LaunchException { + return launch(file, null); + } + + /** + * Launches a JNLP file inside the given container if it is an applet. Specifying a + * container has no effect for Applcations and Installers. + * + * @param file the JNLP file to launch + * @param cont the container in which to place the application, if it is an applet + * @return the application instance + * @throws LaunchException if an error occurred while launching (also sent to handler) + */ + public ApplicationInstance launch(JNLPFile file, Container cont) throws LaunchException { + TgThread tg; + + //First checks whether offline-allowed tag is specified inside the jnlp + //file. + if (!file.getInformation().isOfflineAllowed()) { + try { + //Checks the offline/online status of the system. + //If system is offline do not launch. + InetAddress.getByName(file.getSourceLocation().getHost()); + + } catch (UnknownHostException ue) { + System.err.println("File cannot be launched because offline-allowed tag not specified and system currently offline."); + return null; + } catch (Exception e) { + System.err.println(e); + } + } + + if (file instanceof PluginBridge && cont != null) + tg = new TgThread(file, cont, true); + else if (cont == null) + tg = new TgThread(file); + else + tg = new TgThread(file, cont); + + tg.start(); + + try { + tg.join(); + } + catch (InterruptedException ex) { + //By default, null is thrown here, and the message dialog is shown. + throw launchWarning(new LaunchException(file, ex, R("LSMinor"), R("LCSystem"), R("LThreadInterrupted"), R("LThreadInterruptedInfo"))); + } + + if (tg.getException() != null) + throw tg.getException(); // passed to handler when first created + + if (handler != null) + handler.launchCompleted(tg.getApplication()); + + return tg.getApplication(); + } + + /** + * Launches a JNLP file by calling the launch method for the + * appropriate file type. + * + * @param location the URL of the JNLP file to launch + * @throws LaunchException if there was an exception + * @return the application instance + */ + public ApplicationInstance launch(URL location) throws LaunchException { + return launch(toFile(location)); + } + + /** + * Launches a JNLP file by calling the launch method for the + * appropriate file type in a different thread. + * + * @param file the JNLP file to launch + */ + public void launchBackground(JNLPFile file) { + BgRunner runner = new BgRunner(file, null); + new Thread(runner).start(); + } + + /** + * Launches the JNLP file at the specified location in the + * background by calling the launch method for its file type. + * + * @param location the location of the JNLP file + */ + public void launchBackground(URL location) { + BgRunner runner = new BgRunner(null, location); + new Thread(runner).start(); + } + + /** + * Launches the JNLP file in a new JVM instance. The launched + * application's output is sent to the system out and it's + * standard input channel is closed. + * + * @param vmArgs the arguments to pass to the new JVM. Can be empty but + * must not be null. + * @param file the JNLP file to launch + * @param javawsArgs the arguments to pass to the javaws command. Can be + * an empty list but must not be null. + * @throws LaunchException if there was an exception + */ + public void launchExternal(List<String> vmArgs, JNLPFile file, List<String> javawsArgs) throws LaunchException { + List<String> updatedArgs = new LinkedList<String>(javawsArgs); + + if (file.getFileLocation() != null) + updatedArgs.add(file.getFileLocation().toString()); + else if (file.getSourceLocation() != null) + updatedArgs.add(file.getFileLocation().toString()); + else + launchError(new LaunchException(file, null, R("LSFatal"), R("LCExternalLaunch"), R("LNullLocation"), R("LNullLocationInfo"))); + + launchExternal(vmArgs, updatedArgs); + + } + + /** + * Launches the JNLP file in a new JVM instance. The launched + * application's output is sent to the system out and it's + * standard input channel is closed. + * + * @param url the URL of the JNLP file to launch + * @throws LaunchException if there was an exception + */ + public void launchExternal(URL url) throws LaunchException { + List<String> javawsArgs = new LinkedList<String>(); + javawsArgs.add(url.toString()); + launchExternal(new LinkedList<String>(), javawsArgs); + } + + /** + * Launches the JNLP file at the specified location in a new JVM + * instance. The launched application's output is sent to the + * system out and it's standard input channel is closed. + * @param vmArgs the arguments to pass to the jvm + * @param javawsArgs the arguments to pass to javaws (aka Netx) + * @throws LaunchException if there was an exception + */ + public void launchExternal(List<String> vmArgs, List<String> javawsArgs) throws LaunchException { + try { + + List<String> commands = new LinkedList<String>(); + + String pathToWebstartBinary = System.getProperty("java.home") + + File.separatorChar + + "bin" + + File.separatorChar + + "javaws"; + commands.add(pathToWebstartBinary); + // use -Jargument format to pass arguments to the JVM through the launcher + for (String arg: vmArgs) { + commands.add("-J" + arg); + } + commands.addAll(javawsArgs); + + String[] command = commands.toArray(new String[] {}); + + Process p = Runtime.getRuntime().exec(command); + new StreamEater(p.getErrorStream()).start(); + new StreamEater(p.getInputStream()).start(); + p.getOutputStream().close(); + + } + catch (NullPointerException ex) { + throw launchError(new LaunchException(null, null, R("LSFatal"), R("LCExternalLaunch"), R("LNetxJarMissing"), R("LNetxJarMissingInfo"))); + } + catch (Exception ex) { + throw launchError(new LaunchException(null, ex, R("LSFatal"), R("LCExternalLaunch"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo"))); + } + } + + /** + * Returns the JNLPFile for the URL, with error handling. + */ + private JNLPFile toFile(URL location) throws LaunchException { + try { + JNLPFile file = null; + + try { + file = new JNLPFile(location, (Version) null, true, updatePolicy); // strict + } + catch (ParseException ex) { + file = new JNLPFile(location, (Version) null, false, updatePolicy); + + // only here if strict failed but lax did not fail + LaunchException lex = + launchWarning(new LaunchException(file, ex, R("LSMinor"), R("LCFileFormat"), R("LNotToSpec"), R("LNotToSpecInfo"))); + + if (lex != null) + throw lex; + } + + return file; + } + catch (Exception ex) { + if (ex instanceof LaunchException) + throw (LaunchException) ex; // already sent to handler when first thrown + else // IO and Parse + throw launchError(new LaunchException(null, ex, R("LSFatal"), R("LCReadError"), R("LCantRead"), R("LCantReadInfo"))); + } + } + + /** + * Launches a JNLP application. This method should be called + * from a thread in the application's thread group. + */ + protected ApplicationInstance launchApplication(JNLPFile file) throws LaunchException { + if (!file.isApplication()) + throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplication"), R("LNotApplicationInfo"))); + + markNetxRunning(); + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { markNetxStopped(); } + }); + + try { + + try { + ServiceUtil.checkExistingSingleInstance(file); + } catch (InstanceExistsException e) { + return null; + } + + if (JNLPRuntime.getForksAllowed() && file.needsNewVM()) { + List<String> netxArguments = new LinkedList<String>(); + netxArguments.add("-Xnofork"); + netxArguments.addAll(JNLPRuntime.getInitialArguments()); + launchExternal(file.getNewVMArgs(), netxArguments); + return null; + } + + final int preferredWidth = 500; + final int preferredHeight = 400; + JNLPSplashScreen splashScreen = null; + URL splashImageURL = file.getInformation().getIconLocation( + IconDesc.SPLASH, preferredWidth, preferredHeight); + if (splashImageURL != null) { + ResourceTracker resourceTracker = new ResourceTracker(true); + resourceTracker.addResource(splashImageURL, file.getFileVersion(), updatePolicy); + splashScreen = new JNLPSplashScreen(resourceTracker, null, null); + splashScreen.setSplashImageURL(splashImageURL); + if (splashScreen.isSplashScreenValid()) { + splashScreen.setVisible(true); + } + } + + + ApplicationInstance app = createApplication(file); + app.initialize(); + + String mainName = file.getApplication().getMainClass(); + + // When the application-desc field is empty, we should take a + // look at the main jar for the main class. + if (mainName == null) { + JARDesc mainJarDesc = file.getResources().getMainJAR(); + File f = CacheUtil.getCacheFile(mainJarDesc.getLocation(), null); + if (f != null) { + JarFile mainJar = new JarFile(f); + mainName = mainJar.getManifest(). + getMainAttributes().getValue("Main-Class"); + } + } + + if (mainName == null) + throw launchError(new LaunchException(file, null, + R("LSFatal"), R("LCClient"), R("LCantDetermineMainClass") , + R("LCantDetermineMainClassInfo"))); + + Class mainClass = app.getClassLoader().loadClass(mainName); + + Method main = mainClass.getMethod("main", new Class[] {String[].class} ); + String args[] = file.getApplication().getArguments(); + + SwingUtilities.invokeAndWait(new Runnable() { + // dummy method to force Event Dispatch Thread creation + public void run(){} + }); + + setContextClassLoaderForAllThreads(app.getThreadGroup(), app.getClassLoader()); + + if (splashScreen != null) { + if (splashScreen.isSplashScreenValid()) { + splashScreen.setVisible(false); + } + splashScreen.dispose(); + } + + main.invoke(null, new Object[] { args } ); + + return app; + } + catch (LaunchException lex) { + throw launchError(lex); + } + catch (Exception ex) { + throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo"))); + } + } + + /** + * Set the classloader as the context classloader for all threads in + * the given threadgroup. This is required to make some applications + * work. For example, an application that provides a custom Swing LnF + * may ask the swing thread to load resources from their JNLP, which + * would only work if the Swing thread knows about the JNLPClassLoader. + * + * @param tg The threadgroup for which the context classloader should be set + * @param classLoader the classloader to set as the context classloader + */ + private void setContextClassLoaderForAllThreads(ThreadGroup tg, ClassLoader classLoader) { + + /* be prepared for change in thread size */ + int threadCountGuess = tg.activeCount(); + Thread[] threads; + do { + threadCountGuess = threadCountGuess * 2; + threads = new Thread[threadCountGuess]; + tg.enumerate(threads, true); + } while (threads[threadCountGuess-1] != null); + + + for (Thread thread: threads) { + if (thread != null) { + if (JNLPRuntime.isDebug()) { + System.err.println("Setting " + classLoader + " as the classloader for thread " + thread.getName()); + } + thread.setContextClassLoader(classLoader); + } + } + + } + + /** + * Launches a JNLP applet. This method should be called from a + * thread in the application's thread group.<p> + * + * 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.<p> + * + * @param file the JNLP file + * @param enableCodeBase whether to add the codebase URL to the classloader + */ + protected ApplicationInstance launchApplet(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException { + if (!file.isApplet()) + throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplet"), R("LNotAppletInfo"))); + + try { + AppletInstance applet = createApplet(file, enableCodeBase, cont); + applet.initialize(); + + applet.getAppletEnvironment().startApplet(); // this should be a direct call to applet instance + return applet; + } + catch (LaunchException lex) { + throw launchError(lex); + } + catch (Exception ex) { + throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo"))); + } + } + + /** + * Gets an ApplicationInstance, but does not launch the applet. + */ + protected ApplicationInstance getApplet(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException { + if (!file.isApplet()) + throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplet"), R("LNotAppletInfo"))); + + try { + AppletInstance applet = createApplet(file, enableCodeBase, cont); + applet.initialize(); + return applet; + } + catch (LaunchException lex) { + throw launchError(lex); + } + catch (Exception ex) { + throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo"))); + } + } + + /** + * 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 { + 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 + */ + protected AppletInstance createApplet(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException { + try { + JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy); + + if (enableCodeBase || file.getResources().getJARs().length == 0) + loader.enableCodeBase(); + + AppThreadGroup group = (AppThreadGroup) Thread.currentThread().getThreadGroup(); + + String appletName = file.getApplet().getMainClass(); + + //Classloader chokes if there's '/' in the path to the main class. + //Must replace with '.' instead. + appletName = appletName.replace('/', '.'); + Class appletClass = loader.loadClass(appletName); + Applet applet = (Applet) appletClass.newInstance(); + + AppletInstance appletInstance; + if (cont == null) + appletInstance = new AppletInstance(file, group, loader, applet); + else + appletInstance = new AppletInstance(file, group, loader, applet, cont); + + group.setApplication(appletInstance); + loader.setApplication(appletInstance); + + setContextClassLoaderForAllThreads(appletInstance.getThreadGroup(), appletInstance.getClassLoader()); + + return appletInstance; + } + catch (Exception ex) { + throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplet"), R("LInitAppletInfo"))); + } + } + + /** + * 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 Applet createAppletObject(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException { + try { + JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy); + + if (enableCodeBase || file.getResources().getJARs().length == 0) + loader.enableCodeBase(); + + String appletName = file.getApplet().getMainClass(); + + //Classloader chokes if there's '/' in the path to the main class. + //Must replace with '.' instead. + appletName = appletName.replace('/', '.'); + Class appletClass = loader.loadClass(appletName); + Applet applet = (Applet) 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); + AppThreadGroup group = (AppThreadGroup) Thread.currentThread().getThreadGroup(); + + ApplicationInstance app = new ApplicationInstance(file, group, loader); + group.setApplication(app); + 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 AppThreadGroup createThreadGroup(JNLPFile file) { + AppThreadGroup appThreadGroup = null; + + if (file instanceof PluginBridge) { + appThreadGroup = (AppThreadGroup) Thread.currentThread().getThreadGroup(); + } else { + appThreadGroup = new AppThreadGroup(mainGroup, file.getTitle()); + } + + return appThreadGroup; + } + + /** + * Send n launch error to the handler, if set, and also to the + * caller. + */ + private LaunchException launchError(LaunchException ex) { + 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 + } + + /** + * Indicate that netx is running by creating the {@link JNLPRuntime#INSTANCE_FILE} and + * acquiring a shared lock on it + */ + private void markNetxRunning() { + try { + String message = "This file is used to check if netx is running"; + + File netxRunningFile = new File(JNLPRuntime.NETX_RUNNING_FILE); + netxRunningFile.getParentFile().mkdirs(); + if (netxRunningFile.createNewFile()) { + FileOutputStream fos = new FileOutputStream(netxRunningFile); + try { + fos.write(message.getBytes()); + } finally { + fos.close(); + } + } + + if (!netxRunningFile.isFile()) { + if (JNLPRuntime.isDebug()) { + System.err.println("Unable to create instance file"); + } + fileLock = null; + return; + } + + FileInputStream is = new FileInputStream(netxRunningFile); + FileChannel channel = is.getChannel(); + fileLock = channel.tryLock(0, Long.MAX_VALUE, true); + if (fileLock != null && fileLock.isShared()) { + if (JNLPRuntime.isDebug()) { + System.out.println("Acquired shared lock on " + + JNLPRuntime.NETX_RUNNING_FILE + " to indicate javaws is running"); + } + } else { + fileLock = null; + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + + /** + * Indicate that netx is stopped by releasing the shared lock on + * {@link JNLPRuntime#INSTANCE_FILE}. + */ + private void markNetxStopped() { + if (fileLock == null) { + return; + } + try { + fileLock.release(); + fileLock.channel().close(); + fileLock = null; + if (JNLPRuntime.isDebug()) { + System.out.println("Release shared lock on " + JNLPRuntime.NETX_RUNNING_FILE); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /** + * 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 Container cont; + private boolean isPlugin = false; + + TgThread(JNLPFile file) { + this(file, null); + } + + TgThread(JNLPFile file, Container cont) { + super(createThreadGroup(file), file.getTitle()); + + this.file = file; + this.cont = cont; + } + + TgThread(JNLPFile file, Container cont, boolean isPlugin) { + super(createThreadGroup(file), file.getTitle()); + this.file = file; + this.cont = cont; + this.isPlugin = isPlugin; + } + + 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. + if (context && !isPlugin) + SunToolkit.createNewAppContext(); + + if (isPlugin) { + // Do not display download indicators if we're using gcjwebplugin. + JNLPRuntime.setDefaultDownloadIndicator(null); + application = getApplet(file, true, cont); + } else { + if (file.isApplication()) + application = launchApplication(file); + else if (file.isApplet()) + application = launchApplet(file, true, cont); // 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) { + ex.printStackTrace(); + exception = ex; + // Exit if we can't launch the application. + if (exitOnFailure) + System.exit(0); + } + } + + public LaunchException getException() { + return exception; + } + + public ApplicationInstance getApplication() { + return application; + } + + }; + + + /** + * This runnable is used by the <code>launchBackground</code> + * methods to launch a JNLP file from a separate thread. + */ + private class BgRunner implements Runnable { + private JNLPFile file; + private URL location; + + BgRunner(JNLPFile file, URL location) { + this.file = file; + this.location = location; + } + + public void run() { + try { + if (file != null) + launch(file); + if (location != null) + launch(location); + } + catch (LaunchException ex) { + // launch method communicates error conditions to the + // handler if it exists, otherwise we don't care because + // there's nothing that can be done about the exception. + } + } + }; + +} |