aboutsummaryrefslogtreecommitdiffstats
path: root/netx/jogamp/plugin
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-01-30 10:39:16 +0100
committerSven Gothel <[email protected]>2014-01-30 10:39:16 +0100
commit64e7dcc21339ae56841f10131a4f8a462454dec4 (patch)
treea62d2fbafc6fbe412d2d5ad599cd992bdf97ddbe /netx/jogamp/plugin
parent98c9d6e1ea22db18913b531b8056fbdc5465eb18 (diff)
Experimental Applet without AWT (Applet3)
DISCLAIMER: - Only new Applet3 applets are supported under X11 for now - AWT Applet are disabled - Namespace com.jogamp.* and jogamp.* is only chosen to indicate new AWT-less code - Applet3 code path does not invoke any AWT function - JNLP code path still utilizes AWT/Swing (UIs, ..) TODO: - Refactor AWT dependencies properly via UI interfaces ? - Decide whether we shall merge netx and plugin namespace ? IMHO the right thing to do, jumping hoops due to separation. - Add support for Windows, OSX, Wayland, .. Applet3: - New AWT-less Applet3 interfaces are: - com.jogamp.plugin.applet.Applet3 - User implements - com.jogamp.plugin.applet.Applet3Context - Plugin implements - com.jogamp.plugin.ui.NativeWindowUpstream - Plugin window, aka browser parent of Applet3 - com.jogamp.plugin.ui.NativeWindowDownstream - Applet3 user window - User interfaces are exported as: - plugin3-public.jar - plugin3-public-src.zip
Diffstat (limited to 'netx/jogamp/plugin')
-rw-r--r--netx/jogamp/plugin/jnlp/App3Launcher.java882
-rw-r--r--netx/jogamp/plugin/jnlp/NetxApplet3Panel.java230
-rw-r--r--netx/jogamp/plugin/jnlp/runtime/Applet3Environment.java273
-rw-r--r--netx/jogamp/plugin/jnlp/runtime/Applet3Instance.java134
-rw-r--r--netx/jogamp/plugin/jnlp/runtime/JNLP3SecurityManager.java388
5 files changed, 1907 insertions, 0 deletions
diff --git a/netx/jogamp/plugin/jnlp/App3Launcher.java b/netx/jogamp/plugin/jnlp/App3Launcher.java
new file mode 100644
index 0000000..1d0b9c6
--- /dev/null
+++ b/netx/jogamp/plugin/jnlp/App3Launcher.java
@@ -0,0 +1,882 @@
+// 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.<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 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<String, String[]> extra = null;
+
+ /**
+ * Create a launcher with the runtime's default update policy
+ * and launch handler.
+ */
+ public App3Launcher() {
+ this(null, null);
+
+ if (handler == null) {
+ handler = JNLPRuntime.getDefaultLaunchHandler();
+ }
+ }
+
+ /**
+ * Create a launcher with the runtime's default update policy
+ * and launch handler.
+ *
+ * @param exitOnFailure Exit if there is an error (usually default, but false when being used from the plugin)
+ */
+ public App3Launcher(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 App3Launcher(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 App3Launcher(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;
+ }
+
+ /**
+ * Set the parser settings to use when the Launcher initiates parsing of
+ * a JNLP file.
+ * @param settings
+ */
+ public void setParserSettings(ParserSettings settings) {
+ parserSettings = settings;
+ }
+
+ /**
+ * Set a map to use when trying to extract extra information, including
+ * arguments, properties and parameters, to be merged into the main JNLP
+ * @param input a map containing extra information to add to the main JNLP.
+ * the values for keys "arguments", "parameters", and "properties" are
+ * used.
+ */
+ public void setInformationToMerge(Map<String, String[]> input) {
+ this.extra = input;
+ }
+
+ /**
+ * 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 {
+ TgThread tg;
+
+ mergeExtraInformation(file, extra);
+
+ JNLPRuntime.markNetxRunning();
+
+ //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) {
+ OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "File cannot be launched because offline-allowed tag not specified and system currently offline.");
+ return null;
+ } catch (Exception e) {
+ OutputController.getLogger().log(e);
+ }
+ }
+
+ if (file instanceof PluginBridge) {
+ tg = new TgThread(file, true);
+ }
+ else {
+ tg = new TgThread(file);
+ }
+
+ 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) {
+ // FIXME 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
+ * location to get the pristine version
+ * @throws LaunchException if there was an exception
+ * @return the application instance
+ */
+ public ApplicationInstance launch(URL location) throws LaunchException {
+ return launch(fromUrl(location));
+ }
+
+ /**
+ * Merges extra information into the jnlp file
+ *
+ * @param file the JNLPFile
+ * @param extra extra information to merge into the JNLP file
+ * @throws LaunchException if an exception occurs while extracting
+ * extra information
+ */
+ private void mergeExtraInformation(JNLPFile file, Map<String, String[]> extra) throws LaunchException {
+ if (extra == null) {
+ return;
+ }
+
+ String[] properties = extra.get("properties");
+ if (properties != null) {
+ addProperties(file, properties);
+ }
+
+ String[] arguments = extra.get("arguments");
+ if (arguments != null && file.isApplication()) {
+ addArguments(file, arguments);
+ }
+
+ String[] parameters = extra.get("parameters");
+ if (parameters != null && file.isApplet()) {
+ addParameters(file, parameters);
+ }
+ }
+
+ /**
+ * Add the properties to the JNLP file.
+ * @throws LaunchException if an exception occurs while extracting
+ * extra information
+ */
+ private void addProperties(JNLPFile file, String[] props) throws LaunchException {
+ ResourcesDesc resources = file.getResources();
+
+ for (int i = 0; i < props.length; i++) {
+ // allows empty property, not sure about validity of that.
+ int equals = props[i].indexOf("=");
+ if (equals == -1) {
+ throw launchError(new LaunchException(R("BBadProp", props[i])));
+ }
+
+ String key = props[i].substring(0, equals);
+ String value = props[i].substring(equals + 1, props[i].length());
+
+ resources.addResource(new PropertyDesc(key, value));
+ }
+ }
+
+ /**
+ * Add the params to the JNLP file; only call if file is
+ * actually an applet file.
+ * @throws LaunchException if an exception occurs while extracting
+ * extra information
+ */
+ private void addParameters(JNLPFile file, String[] params) throws LaunchException {
+ AppletDesc applet = file.getApplet();
+
+ for (int i = 0; i < params.length; i++) {
+ // allows empty param, not sure about validity of that.
+ int equals = params[i].indexOf("=");
+ if (equals == -1) {
+ throw launchError(new LaunchException(R("BBadParam", params[i])));
+ }
+
+ String name = params[i].substring(0, equals);
+ String value = params[i].substring(equals + 1, params[i].length());
+
+ applet.addParameter(name, value);
+ }
+ }
+
+ /**
+ * Add the arguments to the JNLP file; only call if file is
+ * actually an application (not installer).
+ */
+ private void addArguments(JNLPFile file, String[] args) {
+ ApplicationDesc app = file.getApplication();
+
+ for (int i = 0; i < args.length; i++) {
+ app.addArgument(args[i]);
+ }
+ }
+
+
+ /**
+ * 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>();
+
+ // this property is set by the javaws launcher to point to the javaws binary
+ String pathToWebstartBinary = System.getProperty("icedtea-web.bin.location");
+ 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 fromUrl(URL location) throws LaunchException {
+ try {
+ JNLPFile file = new JNLPFile(location, parserSettings);
+
+ boolean isLocal = false;
+ boolean haveHref = false;
+ if ("file".equalsIgnoreCase(location.getProtocol()) && new File(location.getFile()).exists()) {
+ isLocal = true;
+ }
+ if (file.getSourceLocation() != null) {
+ haveHref = true;
+ }
+
+ if (isLocal && haveHref) {
+ file = new JNLPFile(file.getSourceLocation(), parserSettings);
+ }
+ 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")));
+ }
+
+ try {
+
+ try {
+ ServiceUtil.checkExistingSingleInstance(file);
+ } catch (InstanceExistsException e) {
+ OutputController.getLogger().log("Single instance application is already running.");
+ 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;
+ }
+
+ handler.launchInitialized(file);
+
+ 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) {
+ final JarFile mainJar = new JarFile(f);
+ try {
+ mainName = mainJar.getManifest().getMainAttributes().getValue("Main-Class");
+ } finally {
+ mainJar.close();
+ }
+ }
+ }
+
+ 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();
+
+ setContextClassLoaderForAllThreads(app.getThreadGroup(), app.getClassLoader());
+
+ handler.launchStarting(app);
+
+ main.setAccessible(true);
+
+ OutputController.getLogger().log("Invoking main() with args: " + Arrays.toString(args));
+ 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) {
+ OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "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) 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;
+ }
+
+ };
+
+
+
+}
diff --git a/netx/jogamp/plugin/jnlp/NetxApplet3Panel.java b/netx/jogamp/plugin/jnlp/NetxApplet3Panel.java
new file mode 100644
index 0000000..875dc5a
--- /dev/null
+++ b/netx/jogamp/plugin/jnlp/NetxApplet3Panel.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ * This file is part of IcedTea, http://icedtea.classpath.org
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package jogamp.plugin.jnlp;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import jogamp.applet.App3Context;
+import jogamp.applet.Applet3Panel;
+import jogamp.plugin.jnlp.runtime.Applet3Instance;
+import net.sourceforge.jnlp.Launcher;
+import net.sourceforge.jnlp.PluginBridge;
+import net.sourceforge.jnlp.PluginParameters;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.splashscreen.SplashController;
+import net.sourceforge.jnlp.splashscreen.SplashPanel;
+import net.sourceforge.jnlp.splashscreen.SplashUtils;
+import net.sourceforge.jnlp.util.logging.OutputController;
+
+import com.jogamp.plugin.applet.Applet3Context;
+
+/**
+ * This panel calls into netx to run an applet, and pipes the display
+ * into a panel from the icedtea-web browser plugin.
+ *
+ * @author Francis Kung &lt;[email protected]&gt;
+ */
+public class NetxApplet3Panel extends Applet3Panel implements SplashController {
+ private final PluginParameters pluginParameters;
+ private PluginBridge bridge = null;
+ private Applet3Instance appInst = null;
+ private SplashController splashController;
+ /** The plugin .. */
+ private Applet3Context upstreamContext = null;
+ private volatile boolean initialized;
+
+ // We use this so that we can create exactly one thread group
+ // for all panels with the same uKey.
+ private static final Map<String, ThreadGroup> uKeyToTG =
+ new HashMap<String, ThreadGroup>();
+ private static final Object TGMapMutex = new Object();
+
+ // This map is actually a set (unfortunately there is no ConcurrentSet
+ // in java.util.concurrent). If KEY is in this map, then we know that
+ // an app context has been created for the panel that has uKey.equals(KEY),
+ // so we avoid creating it a second time for panels with the same uKey.
+ // Because it's a set, only the keys matter. However, we can't insert
+ // null values in because if we did, we couldn't use null checks to see
+ // if a key was absent before a putIfAbsent.
+ private static final ConcurrentMap<String, Boolean> appContextCreated =
+ new ConcurrentHashMap<String, Boolean>();
+
+ public NetxApplet3Panel(long nativeWindowHandle, int width, int height, URL documentURL, PluginParameters params) {
+ super(nativeWindowHandle, width, height, documentURL, params.getUnderlyingHashtable());
+
+ this.pluginParameters = params;
+ this.initialized = false;
+
+ String uniqueKey = params.getUniqueKey(getCodeBase());
+ synchronized(TGMapMutex) {
+ if (!uKeyToTG.containsKey(uniqueKey)) {
+ ThreadGroup tg = new ThreadGroup(Launcher.getMainGroup(), this.documentURL.toString());
+ uKeyToTG.put(uniqueKey, tg);
+ }
+ }
+ }
+
+ @Override
+ protected void showAppletException(Throwable t) {
+ /*
+ * Log any exceptions thrown while loading, initializing, starting,
+ * and stopping the applet.
+ */
+ OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, t); //new logger
+ super.showAppletException(t);
+ }
+
+ //Overriding to use Netx classloader. You might need to relax visibility
+ //in sun.applet.AppletPanel for runLoader().
+ @Override
+ protected void runLoader() {
+
+ try {
+ bridge = new PluginBridge(baseURL,
+ getDocumentBase(),
+ getJarFiles(),
+ getCode(),
+ getWidth(),
+ getHeight(),
+ pluginParameters);
+
+ doInit = true;
+ dispatchAppletEvent(APPLET_LOADING, null);
+ status = APPLET_LOAD;
+
+ App3Launcher l = new App3Launcher(false);
+
+ // May throw LaunchException:
+ appInst = (Applet3Instance) l.launch(bridge);
+ applet = appInst.getApplet();
+
+ if (applet != null) {
+ // Stick it in the frame
+ showAppletStatus("loaded");
+ }
+ } catch (Exception e) {
+ status = APPLET_ERROR;
+ OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
+ replaceSplash(SplashUtils.getErrorSplashScreen(getWidth(), getHeight(), e));
+ } finally {
+ // PR1157: This needs to occur even in the case of an exception
+ // so that the applet's event listeners are signaled.
+ // Once PluginAppletViewer.AppletEventListener is signaled PluginAppletViewer can properly stop waiting
+ // in PluginAppletViewer.waitForAppletInit
+ this.initialized = true;
+ dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
+ }
+ }
+
+ /**
+ * Creates a new Thread (in a new applet-specific ThreadGroup) for running
+ * the applet
+ */
+ // Reminder: Relax visibility in sun.applet.AppletPanel
+ @Override
+ public synchronized void createAppletThread() {
+ // initialize JNLPRuntime in the main threadgroup
+ synchronized (JNLPRuntime.initMutex) {
+ //The custom NetX Policy and SecurityManager are set here.
+ if (!JNLPRuntime.isInitialized()) {
+ OutputController.getLogger().log("initializing JNLPRuntime...");
+
+ JNLPRuntime.initialize(false);
+ } else {
+ OutputController.getLogger().log("JNLPRuntime already initialized");
+ }
+ }
+
+ handler = new Thread(getThreadGroup(), this, "NetxPanelThread@" + this.documentURL);
+ handler.start();
+ }
+
+ public final void setAppletContext(Applet3Context ctx) {
+ upstreamContext = ctx;
+ }
+
+ @Override
+ protected final Applet3Context getAppletContext() {
+ if( null != upstreamContext ) {
+ return upstreamContext;
+ } else {
+ return appInst.getAppletEnvironment();
+ }
+ }
+
+ @Override
+ public ClassLoader getAppletClassLoader() {
+ return appInst.getClassLoader();
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public ThreadGroup getThreadGroup() {
+ synchronized(TGMapMutex) {
+ return uKeyToTG.get(pluginParameters.getUniqueKey(getCodeBase()));
+ }
+ }
+
+ public void createNewAppContext() {
+ if (Thread.currentThread().getThreadGroup() != getThreadGroup()) {
+ throw new RuntimeException("createNewAppContext called from the wrong thread.");
+ }
+ // only create a new context if one hasn't already been created for the
+ // applets with this unique key.
+ if (null == appContextCreated.putIfAbsent(pluginParameters.getUniqueKey(getCodeBase()), Boolean.TRUE)) {
+ App3Context.createAppContext(); // FIXME: Really ?
+ // SunToolkit.createNewAppContext();
+ }
+ }
+
+ public void setAppletViewerFrame(SplashController framePanel) {
+ splashController=framePanel;
+ }
+
+ @Override
+ public void removeSplash() {
+ splashController.removeSplash();
+ }
+
+ @Override
+ public void replaceSplash(SplashPanel r) {
+ splashController.replaceSplash(r);
+ }
+
+ @Override
+ public int getSplashWidth() {
+ return splashController.getSplashWidth();
+ }
+
+ @Override
+ public int getSplashHeigth() {
+ return splashController.getSplashHeigth();
+ }
+
+}
diff --git a/netx/jogamp/plugin/jnlp/runtime/Applet3Environment.java b/netx/jogamp/plugin/jnlp/runtime/Applet3Environment.java
new file mode 100644
index 0000000..66df33a
--- /dev/null
+++ b/netx/jogamp/plugin/jnlp/runtime/Applet3Environment.java
@@ -0,0 +1,273 @@
+// 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.runtime;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.splashscreen.SplashController;
+import net.sourceforge.jnlp.util.logging.OutputController;
+
+import com.jogamp.plugin.applet.Applet3;
+import com.jogamp.plugin.applet.Applet3Context;
+
+/**
+ * The applet environment including stub, context, and frame. The
+ * default environment puts the applet in a non-resiable frame;
+ * this can be changed by obtaining the frame and setting it
+ * resizable.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.12 $
+ */
+public class Applet3Environment implements Applet3Context {
+
+ /** the JNLP file */
+ private JNLPFile file;
+
+ /** the applet */
+ private Applet3 applet;
+
+ /** the parameters */
+ private Map<String, String> parameters;
+
+ /** whether the applet has been started / displayed */
+ private boolean appletStarted = false;
+
+ /** whether the applet has been destroyed */
+ private boolean destroyed = false;
+
+ /**
+ * Create a new applet environment for the applet specified by
+ * the JNLP file.
+ */
+ public Applet3Environment(JNLPFile file, final Applet3Instance appletInstance) {
+ this.file = file;
+ this.applet = appletInstance.getApplet();
+
+ parameters = file.getApplet().getParameters();
+ }
+
+ /**
+ * Checks whether the applet has been destroyed, and throws an
+ * IllegalStateException if the applet has been destroyed of.
+ *
+ * @throws IllegalStateException
+ */
+ private void checkDestroyed() {
+ if (destroyed) {
+ throw new IllegalStateException("Illegal applet stub/context access: applet destroyed.");
+ }
+ }
+
+ /**
+ * Disposes the applet's resources and disables the applet
+ * environment from further use; after calling this method the
+ * applet stub and context methods throw IllegalStateExceptions.
+ */
+ public void destroy() {
+ destroyed = true;
+ }
+
+ /**
+ * FIXME: Currently null
+ */
+ public SplashController getSplashControler() {
+ return null;
+ }
+
+ /**
+ * Initialize, start, and show the applet.
+ */
+ public void startApplet() {
+ checkDestroyed();
+
+ if (appletStarted) {
+ return;
+ }
+
+ appletStarted = true;
+
+ try {
+ // This is only needed if the applet is in its own frame.
+ applet.init(Applet3Environment.this);
+ applet.start();
+ } catch (Exception ex) {
+ OutputController.getLogger().log(ex);
+
+ // should also kill the applet?
+ }
+ }
+
+ // applet context methods
+
+ /**
+ * Returns the applet if the applet's name is specified,
+ * otherwise return null.
+ */
+ @Override
+ public Applet3Context getAppletContext(String name) {
+ checkDestroyed();
+
+ if (name != null && name.equals(file.getApplet().getName())) {
+ return this;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the applet of this environment; can only be called once.
+ */
+ public void setApplet(Applet3 applet) {
+ if (this.applet != null) {
+ OutputController.getLogger().log(new IllegalStateException("Applet can only be set once."));
+ return;
+ }
+ this.applet = applet;
+ }
+
+ @Override
+ public Enumeration<Applet3Context> getAllAppletContexts() {
+ checkDestroyed();
+
+ return Collections.enumeration(Arrays.asList(new Applet3Context[] { this}));
+ }
+
+ /**
+ * Not implemented yet.
+ */
+ @Override
+ public void showDocument(java.net.URL uRL) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Not implemented yet.
+ */
+ @Override
+ public void showDocument(java.net.URL uRL, java.lang.String str) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Not implemented yet.
+ */
+ @Override
+ public void showStatus(java.lang.String str) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Required for JRE1.4, but not implemented yet.
+ */
+ @Override
+ public void setStream(String key, InputStream stream) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Required for JRE1.4, but not implemented yet.
+ */
+ @Override
+ public InputStream getStream(String key) {
+ checkDestroyed();
+
+ return null;
+ }
+
+ /**
+ * Required for JRE1.4, but not implemented yet.
+ */
+ @Override
+ public Iterator<String> getStreamKeys() {
+ checkDestroyed();
+
+ return null;
+ }
+
+ // stub methods
+
+ @Override
+ public void resize(int width, int height) {
+ appletResize(width, height);
+ }
+ public void appletResize(int width, int height) {
+ checkDestroyed();
+ // FIXME: ??
+ }
+
+ @Override
+ public Applet3 getApplet() {
+ checkDestroyed();
+
+ return applet;
+ }
+
+ @Override
+ public URL getCodeBase() {
+ checkDestroyed();
+
+ return file.getCodeBase();
+ }
+
+ @Override
+ public URL getDocumentBase() {
+ checkDestroyed();
+
+ return file.getApplet().getDocumentBase();
+ }
+
+ // FIXME: Sun's applet code forces all parameters to lower case.
+ // Does Netx's JNLP code do the same, so we can remove the first lookup?
+ @Override
+ public String getParameter(String name) {
+ checkDestroyed();
+
+ String s = parameters.get(name);
+ if (s != null) {
+ return s;
+ }
+
+ return parameters.get(name.toLowerCase());
+ }
+
+ @Override
+ public boolean isActive() {
+ checkDestroyed();
+
+ // it won't be started or stopped, so if it can call it's running
+ return true;
+ }
+
+ @Override
+ public String getAppletName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/netx/jogamp/plugin/jnlp/runtime/Applet3Instance.java b/netx/jogamp/plugin/jnlp/runtime/Applet3Instance.java
new file mode 100644
index 0000000..7293936
--- /dev/null
+++ b/netx/jogamp/plugin/jnlp/runtime/Applet3Instance.java
@@ -0,0 +1,134 @@
+// 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.runtime;
+
+import jogamp.applet.App3Context;
+
+import com.jogamp.plugin.applet.Applet3;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.ApplicationInstance;
+import net.sourceforge.jnlp.util.logging.OutputController;
+
+/**
+ * Represents a launched application instance created from a JNLP
+ * file. This class does not control the operation of the applet,
+ * use the AppletEnvironment class to start and stop the applet.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.9 $
+ */
+public class Applet3Instance extends ApplicationInstance {
+
+ /** whether the applet's stop and destroy methods have been called */
+ private boolean appletStopped = false;
+
+ /** the applet */
+ private Applet3 applet;
+
+ /** the applet environment */
+ private Applet3Environment environment;
+
+ /**
+ * Create a New Task based on the Specified URL
+ */
+ public Applet3Instance(JNLPFile file, ThreadGroup group, ClassLoader loader, Applet3 applet) {
+ super(file, group, loader, App3Context.getAppContext());
+
+ this.applet = applet;
+
+ this.environment = new Applet3Environment(file, this);
+ }
+
+ /**
+ * Set the applet of this launched application; can only be called once.
+ */
+ public void setApplet(Applet3 applet) {
+ if (this.applet != null) {
+ OutputController.getLogger().log(new IllegalStateException("Applet can only be set once."));
+ return;
+ }
+ this.applet = applet;
+ }
+
+ /**
+ * Sets whether the applet is resizable or not. Applets default
+ * to being not resizable.
+ */
+ public void setResizable(boolean resizable) {
+ /** FIXME
+ Container c = environment.getAppletFrame();
+ if (c instanceof Frame)
+ ((Frame) c).setResizable(resizable); */
+ }
+
+ /**
+ * Returns whether the applet is resizable.
+ */
+ public boolean isResizable() {
+ /** FIXME
+ Container c = environment.getAppletFrame();
+ if (c instanceof Frame)
+ return ((Frame) c).isResizable();
+
+ return false; */
+ return false;
+ }
+
+ /**
+ * Returns the application title.
+ */
+ public String getTitle() {
+ return getJNLPFile().getApplet().getName();
+ }
+
+ /**
+ * Returns the applet environment.
+ */
+ public Applet3Environment getAppletEnvironment() {
+ return environment;
+ }
+
+ /**
+ * Returns the applet.
+ */
+ public Applet3 getApplet() {
+ return applet;
+ }
+
+ /**
+ * Stop the application and destroy its resources.
+ */
+ public void destroy() {
+ if (appletStopped)
+ return;
+
+ appletStopped = true;
+
+ try {
+ applet.stop();
+ applet.destroy();
+ } catch (Exception ex) {
+ OutputController.getLogger().log(ex);
+ }
+
+ environment.destroy();
+
+ super.destroy();
+ }
+
+}
diff --git a/netx/jogamp/plugin/jnlp/runtime/JNLP3SecurityManager.java b/netx/jogamp/plugin/jnlp/runtime/JNLP3SecurityManager.java
new file mode 100644
index 0000000..0804bdc
--- /dev/null
+++ b/netx/jogamp/plugin/jnlp/runtime/JNLP3SecurityManager.java
@@ -0,0 +1,388 @@
+// 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.runtime;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.security.AccessControlException;
+import java.security.Permission;
+
+import jogamp.applet.App3Context;
+import net.sourceforge.jnlp.runtime.ApplicationInstance;
+import net.sourceforge.jnlp.runtime.JNLPClassLoader;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.logging.OutputController;
+import net.sourceforge.jnlp.util.WeakList;
+
+/**
+ * Security manager for JNLP environment. This security manager
+ * cannot be replaced as it always denies attempts to replace the
+ * security manager or policy.<p>
+ *
+ * The JNLP security manager tracks windows created by an
+ * application, allowing those windows to be disposed when the
+ * application exits but the JVM does not. If security is not
+ * enabled then the first application to call System.exit will
+ * halt the JVM.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.17 $
+ */
+public class JNLP3SecurityManager extends SecurityManager {
+
+ // todo: some apps like JDiskReport can close the VM even when
+ // an exit class is set - fix!
+
+ // todo: create an event dispatch thread for each application,
+ // so that the context classloader doesn't have to be switched
+ // to the foreground application (the currently the approach
+ // since some apps need their classloader as event dispatch
+ // thread's context classloader).
+
+ // todo: use a custom Permission object to identify the current
+ // application in an AccessControlContext by setting a side
+ // effect in its implies method. Use a custom
+ // AllPermissions-like permission to do this for apps granted
+ // all permissions (but investigate whether this will nuke
+ // the all-permission optimizations in the JRE).
+
+ // todo: does not exit app if close button pressed on JFrame
+ // with CLOSE_ON_EXIT (or whatever) set; if doesn't exit, use an
+ // WindowListener to catch WindowClosing event, then if exit is
+ // called immediately afterwards from AWT thread.
+
+ // todo: deny all permissions to applications that should have
+ // already been 'shut down' by closing their resources and
+ // interrupt the threads if operating in a shared-VM (exit class
+ // set). Deny will probably will slow checks down a lot though.
+
+ // todo: weak remember last getProperty application and
+ // re-install properties if another application calls, or find
+ // another way for different apps to have different properties
+ // in java.lang.Sytem with the same names.
+
+ /** only class that can exit the JVM, if set */
+ private Object exitClass = null;
+
+ /** this exception prevents exiting the JVM */
+ private SecurityException closeAppEx = // making here prevents huge stack traces
+ new SecurityException(R("RShutdown"));
+
+ /** weak list of windows created */
+ private WeakList<Object> weakWindows = new WeakList<Object>();
+
+ /** weak list of applications corresponding to window list */
+ private WeakList<ApplicationInstance> weakApplications =
+ new WeakList<ApplicationInstance>();
+
+ /** Sets whether or not exit is allowed (in the context of the plugin, this is always false) */
+ private boolean exitAllowed = true;
+
+ /**
+ * Creates a JNLP SecurityManager.
+ */
+ public JNLP3SecurityManager() {
+ }
+
+ /**
+ * Returns whether the exit class is present on the stack, or
+ * true if no exit class is set.
+ */
+ public boolean isExitClass() {
+ return isExitClass(getClassContext());
+ }
+
+ /**
+ * Returns whether the exit class is present on the stack, or
+ * true if no exit class is set.
+ */
+ private boolean isExitClass(Class<?> stack[]) {
+ if (exitClass == null) {
+ return true;
+ }
+
+ for (int i = 0; i < stack.length; i++) {
+ if (stack[i] == exitClass) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Set the exit class, which is the only class that can exit the
+ * JVM; if not set then any class can exit the JVM.
+ *
+ * @param exitClass the exit class
+ * @throws IllegalStateException if the exit class is already set
+ */
+ public void setExitClass(Class<?> exitClass) throws IllegalStateException {
+ if (this.exitClass != null) {
+ throw new IllegalStateException(R("RExitTaken"));
+ }
+
+ this.exitClass = exitClass;
+ }
+
+ /**
+ * Return the current Application, or null if none can be
+ * determined.
+ */
+ public ApplicationInstance getApplication() {
+ return getApplication(Thread.currentThread(), getClassContext(), 0);
+ }
+
+ /**
+ * Return the application the opened the specified window (only
+ * call from event dispatch thread).
+ */
+ protected ApplicationInstance getApplication(Object window) {
+ for (int i = weakWindows.size(); i-- > 0;) {
+ Object w = weakWindows.get(i);
+ if (w == null) {
+ weakWindows.remove(i);
+ weakApplications.remove(i);
+ }
+
+ if (w == window) {
+ return weakApplications.get(i);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the current Application, or null.
+ */
+ protected ApplicationInstance getApplication(Thread thread, Class<?> stack[], int maxDepth) {
+ ClassLoader cl;
+ JNLPClassLoader jnlpCl;
+
+ cl = thread.getContextClassLoader();
+ while (cl != null) {
+ jnlpCl = getJnlpClassLoader(cl);
+ if (jnlpCl != null && jnlpCl.getApplication() != null) {
+ return jnlpCl.getApplication();
+ }
+ cl = cl.getParent();
+ }
+
+ if (maxDepth <= 0) {
+ maxDepth = stack.length;
+ }
+
+ // this needs to be tightened up
+ for (int i = 0; i < stack.length && i < maxDepth; i++) {
+ cl = stack[i].getClassLoader();
+ while (cl != null) {
+ jnlpCl = getJnlpClassLoader(cl);
+ if (jnlpCl != null && jnlpCl.getApplication() != null) {
+ return jnlpCl.getApplication();
+ }
+ cl = cl.getParent();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the JNLPClassLoader associated with the given ClassLoader, or
+ * null.
+ * @param cl a ClassLoader
+ * @return JNLPClassLoader or null
+ */
+ private JNLPClassLoader getJnlpClassLoader(ClassLoader cl) {
+ // Since we want to deal with JNLPClassLoader, extract it if this
+ // is a codebase loader
+ if (cl instanceof JNLPClassLoader.CodeBaseClassLoader) {
+ cl = ((JNLPClassLoader.CodeBaseClassLoader) cl).getParentJNLPClassLoader();
+ }
+
+ if (cl instanceof JNLPClassLoader) {
+ JNLPClassLoader loader = (JNLPClassLoader) cl;
+ return loader;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the application's thread group if the application can
+ * be determined; otherwise returns super.getThreadGroup()
+ */
+ @Override
+ public ThreadGroup getThreadGroup() {
+ ApplicationInstance app = getApplication();
+ if (app == null) {
+ return super.getThreadGroup();
+ }
+
+ return app.getThreadGroup();
+ }
+
+ /**
+ * Throws a SecurityException if the permission is denied,
+ * otherwise return normally. This method always denies
+ * permission to change the security manager or policy.
+ */
+ @Override
+ public void checkPermission(Permission perm) {
+ String name = perm.getName();
+
+ // Enable this manually -- it'll produce too much output for -verbose
+ // otherwise.
+ // if (true)
+ // OutputController.getLogger().log("Checking permission: " + perm.toString());
+
+ if (!JNLPRuntime.isWebstartApplication() &&
+ ("setPolicy".equals(name) || "setSecurityManager".equals(name))) {
+ throw new SecurityException(R("RCantReplaceSM"));
+ }
+
+ try {
+ // deny all permissions to stopped applications
+ // The call to getApplication() below might not work if an
+ // application hasn't been fully initialized yet.
+ // if (JNLPRuntime.isDebug()) {
+ // if (!"getClassLoader".equals(name)) {
+ // ApplicationInstance app = getApplication();
+ // if (app != null && !app.isRunning())
+ // throw new SecurityException(R("RDenyStopped"));
+ // }
+ // }
+
+ super.checkPermission(perm);
+ } catch (SecurityException ex) {
+ OutputController.getLogger().log("Denying permission: " + perm);
+ throw ex;
+ }
+ }
+
+ /**
+ * Checks whether the window can be displayed without an applet
+ * warning banner, and adds the window to the list of windows to
+ * be disposed when the calling application exits.
+ */
+ @SuppressWarnings("deprecation")
+ @Override
+ public boolean checkTopLevelWindow(Object window) {
+ ApplicationInstance app = getApplication();
+
+ // remember window -> application mapping for focus, close on exit
+ if (app != null ) {
+ OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "SM: app: " + app.getTitle() + " is adding a window: " + window + " with appContext " + App3Context.getAppContext());
+
+ weakWindows.add(window); // for mapping window -> app
+ weakApplications.add(app);
+
+ app.addWindow(window);
+ }
+
+ // todo: set awt.appletWarning to custom message
+ // todo: logo on with glass pane on JFrame/JWindow?
+
+ return super.checkTopLevelWindow(window);
+ }
+
+ /**
+ * Checks whether the caller can exit the system. This method
+ * identifies whether the caller is a real call to Runtime.exec
+ * and has special behavior when returning from this method
+ * would exit the JVM and an exit class is set: if the caller is
+ * not the exit class then the calling application will be
+ * stopped and its resources destroyed (when possible), and an
+ * exception will be thrown to prevent the JVM from shutting
+ * down.<p>
+ *
+ * Calls not from Runtime.exit or with no exit class set will
+ * behave normally, and the exit class can always exit the JVM.
+ */
+ @Override
+ public void checkExit(int status) {
+
+ // applets are not allowed to exit, but the plugin main class (primordial loader) is
+ Class<?> stack[] = getClassContext();
+ if (!exitAllowed) {
+ for (int i = 0; i < stack.length; i++) {
+ if (stack[i].getClassLoader() != null) {
+ throw new AccessControlException("Applets may not call System.exit()");
+ }
+ }
+ }
+
+ super.checkExit(status);
+
+ boolean realCall = (stack[1] == Runtime.class);
+
+ if (isExitClass(stack)) {
+ return;
+ } // to Runtime.exit or fake call to see if app has permission
+
+ // not called from Runtime.exit()
+ if (!realCall) {
+ // apps that can't exit should think they can exit normally
+ super.checkExit(status);
+ return;
+ }
+
+ // but when they really call, stop only the app instead of the JVM
+ ApplicationInstance app = getApplication(Thread.currentThread(), stack, 0);
+ if (app == null) {
+ throw new SecurityException(R("RExitNoApp"));
+ }
+
+ app.destroy();
+
+ throw closeAppEx;
+ }
+
+ public void disableExit() {
+ exitAllowed = false;
+ }
+
+ /**
+ * Tests if a client can get access to the AWT event queue. This version allows
+ * complete access to the EventQueue for its own AppContext-specific EventQueue.
+ *
+ * FIXME there are probably huge security implications for this. Eg:
+ * http://hg.openjdk.java.net/jdk7/awt/jdk/rev/8022709a306d
+ *
+ * @exception SecurityException if the caller does not have
+ * permission to accesss the AWT event queue.
+ */
+ @SuppressWarnings("deprecation")
+ @Override
+ public void checkAwtEventQueueAccess() {
+ /*
+ * this is the templace of the code that should allow applets access to
+ * eventqueues
+ */
+
+ // AppContext appContext = AppContext.getAppContext();
+ // ApplicationInstance instance = getApplication();
+
+ // if ((appContext == mainAppContext) && (instance != null)) {
+ // If we're about to allow access to the main EventQueue,
+ // and anything untrusted is on the class context stack,
+ // disallow access.
+ super.checkAwtEventQueueAccess();
+ // }
+ }
+
+}