aboutsummaryrefslogtreecommitdiffstats
path: root/netx
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
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')
-rw-r--r--netx/com/jogamp/plugin/applet/Applet3.java150
-rw-r--r--netx/com/jogamp/plugin/applet/Applet3Context.java87
-rw-r--r--netx/com/jogamp/plugin/ui/NativeWindowDownstream.java81
-rw-r--r--netx/com/jogamp/plugin/ui/NativeWindowUpstream.java75
-rw-r--r--netx/jogamp/applet/App3Context.java803
-rw-r--r--netx/jogamp/applet/Applet3ClassLoader.java883
-rw-r--r--netx/jogamp/applet/Applet3ObjectInputStream.java120
-rw-r--r--netx/jogamp/applet/Applet3Panel.java1536
-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
-rw-r--r--netx/net/sourceforge/jnlp/Launcher.java23
-rw-r--r--netx/net/sourceforge/jnlp/PluginParameters.java6
-rw-r--r--netx/net/sourceforge/jnlp/runtime/AppletInstance.java5
-rw-r--r--netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java68
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java43
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java3
-rw-r--r--netx/net/sourceforge/jnlp/splashscreen/SplashUtils.java30
-rw-r--r--netx/sun/applet/Applet3MessageHandler.java21
21 files changed, 5788 insertions, 53 deletions
diff --git a/netx/com/jogamp/plugin/applet/Applet3.java b/netx/com/jogamp/plugin/applet/Applet3.java
new file mode 100644
index 0000000..b285852
--- /dev/null
+++ b/netx/com/jogamp/plugin/applet/Applet3.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.plugin.applet;
+
+import java.util.Locale;
+
+import com.jogamp.plugin.ui.NativeWindowDownstream;
+import com.jogamp.plugin.ui.NativeWindowUpstream;
+
+/**
+ * Implemented by user.
+ * <a name="lifecycle"><h5>Applet3 Lifecycle</h5></a>
+ * <p>
+ * <ul>
+ * <li>{@link #createNativeWindow(Applet3Context, NativeWindowUpstream)}</li>
+ * <li>{@link #init(Applet3Context)}</li>
+ * <li>{@link #start()}</li>
+ * <li>{@link #stop()}</li>
+ * <li>{@link #destroy()}</li>
+ * </ul>
+ * </p>
+ */
+public interface Applet3 {
+
+ /**
+ * Returns applet information or <code>null</code>.
+ * <p>
+ * Implementation may utilize this method to
+ * return information about the author, version, and copyright of the applet.
+ * </p>
+ */
+ String getAppletInfo();
+
+ /**
+ * Returns a custom locale of the applet or <code>null</code>.
+ */
+ Locale getLocale();
+
+ /**
+ * Returns a description of parameters as used by this applet, or <code>null</code>.
+ * <p>
+ * The returned string array is arranged to
+ * contain the <code>name</code>, the <code>type</code>, and a <code>description</code>.
+ * Example:
+ * <pre>
+ * String pinfo[][] = {
+ * {"gl_profile", "GLProfile String", "The GLProfile to be used, i.e. GL2ES2"},
+ * {"gl_swap_interval", "0-4", "The swap interval for vertical sync, i.e. 0 or 1"},
+ * {"gl_debug", "boolean", "Enable JOGL debugging"}
+ * };
+ * </pre>
+ */
+ String[][] getParameterInfo();
+
+ /**
+ * Implementation creates a native child window, allowing to be controlled by the plugin.
+ * <p>
+ * The applet's child window is destroyed by the plugin after it has called {@link #destroy()}.
+ * </p>
+ * <p>
+ * Note that the returned {@link NativeWindowDownstream} instance's {@link NativeWindowDownstream#getParent()}
+ * must match the given <code>parent</code> instance, otherwise the applet will be aborted.
+ * </p>
+ * <p>
+ * See <a href="#lifecycle">Applet Lifecycle</a>.
+ * </p>
+ * @param context the {@link Applet3Context}
+ * @param parent the parent {@link NativeWindowUpstream}, reflecting the plugin's native applet window.
+ * @return {@link NativeWindowDownstream} users native child window.
+ */
+ NativeWindowDownstream createNativeWindow(Applet3Context context, NativeWindowUpstream parent);
+
+ /**
+ * Initialize the applet. Implementation may allocate resources.
+ * <p>
+ * See <a href="#lifecycle">Applet Lifecycle</a>.
+ * </p>
+ *
+ * @see #createNativeWindow(Applet3Context, NativeWindowUpstream)
+ * @see #destroy()
+ * @see #start()
+ * @see #stop()
+ */
+ void init(Applet3Context context);
+
+ /**
+ * Start the applet.
+ * <p>
+ * See <a href="#lifecycle">Applet Lifecycle</a>.
+ * </p>
+ * @see #createNativeWindow(Applet3Context, NativeWindowUpstream)
+ * @see #destroy()
+ * @see #start()
+ * @see #stop()
+ */
+ void start();
+
+ /**
+ * Stop the applet.
+ * <p>
+ * See <a href="#lifecycle">Applet Lifecycle</a>.
+ * </p>
+ * @see #createNativeWindow(Applet3Context, NativeWindowUpstream)
+ * @see #destroy()
+ * @see #start()
+ * @see #stop()
+ */
+ void stop();
+
+ /**
+ * Destroy the applet and all it's resources. Implementation shall release resources as allocated via {@link #init(Applet3Context)}.
+ * <p>
+ * Note that the applet's child window {@link #createNativeWindow(Applet3Context, NativeWindowUpstream) created by this implementation}
+ * is still valid and may be destroyed here.
+ * </p>
+ * <p>
+ * See <a href="#lifecycle">Applet Lifecycle</a>.
+ * </p>
+ * @see #createNativeWindow(Applet3Context, NativeWindowUpstream)
+ * @see #destroy()
+ * @see #start()
+ * @see #stop()
+ */
+ void destroy();
+}
diff --git a/netx/com/jogamp/plugin/applet/Applet3Context.java b/netx/com/jogamp/plugin/applet/Applet3Context.java
new file mode 100644
index 0000000..04528e6
--- /dev/null
+++ b/netx/com/jogamp/plugin/applet/Applet3Context.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.plugin.applet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * Provided by Plugin implementation.
+ */
+public interface Applet3Context {
+ /**
+ * Returns the {@link Applet3} bound to this context
+ */
+ Applet3 getApplet();
+
+ String getAppletName();
+
+ boolean isActive();
+ String getParameter(String name);
+ URL getDocumentBase();
+ URL getCodeBase();
+
+ /**
+ * Requests that this applet be resized.
+ *
+ * @param width the new requested width for the applet.
+ * @param height the new requested height for the applet.
+ */
+ void resize(int width, int height);
+
+ /**
+ * Returns the {@link Applet3Context} within this context domain,
+ * referenced by the document, codebase and the given <code>name</code>.
+ * <p>
+ * The <code>name</code> can be set in the HTML tag by setting the <code>name</code> attribute.
+ * </p>
+ */
+ Applet3Context getAppletContext(String name);
+
+ /**
+ * Returns all {@link Applet3Context} within this context domain,
+ * referenced by the document and codebase.
+ */
+ Enumeration<Applet3Context> getAllAppletContexts();
+
+ void showDocument(URL url);
+
+ void showDocument(URL url, String target);
+
+ void showStatus(String status);
+
+ void setStream(String key, InputStream stream)throws IOException;
+
+ InputStream getStream(String key);
+
+ Iterator<String> getStreamKeys();
+
+}
diff --git a/netx/com/jogamp/plugin/ui/NativeWindowDownstream.java b/netx/com/jogamp/plugin/ui/NativeWindowDownstream.java
new file mode 100644
index 0000000..0846505
--- /dev/null
+++ b/netx/com/jogamp/plugin/ui/NativeWindowDownstream.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.plugin.ui;
+
+/**
+ * Implemented by user.
+ * <p>
+ * Representing the user applet child window,
+ * which is controlled by the plugin.
+ * </p>
+ */
+public interface NativeWindowDownstream {
+ /**
+ * Destroys this window incl. releasing all related resources.
+ */
+ public void destroy();
+
+ /**
+ * @return The parent NativeWindow, or null if this NativeWindow is top level.
+ */
+ public NativeWindowUpstream getParent();
+
+ /**
+ * Returns the window handle for this applet. <P>
+ *
+ * The window handle shall reflect the platform one
+ * for all window related operations, e.g. open, close, resize. <P>
+ *
+ * On X11 this returns an entity of type Window. <BR>
+ * On Microsoft Windows this returns an entity of type HWND.
+ */
+ public long getWindowHandle();
+
+ /**
+ * Set size.
+ */
+ public void setSize(int width, int height);
+
+ /**
+ * Request focus.
+ */
+ public void requestFocus();
+
+ /**
+ * Set this NativeWindow visible state.
+ */
+ public void setVisible(boolean v);
+
+ /**
+ * Trigger asynchronous rendering of this display's content.
+ * <p>
+ * Method shall return immediately and not wait for result.
+ * </p>
+ */
+ public void display();
+}
diff --git a/netx/com/jogamp/plugin/ui/NativeWindowUpstream.java b/netx/com/jogamp/plugin/ui/NativeWindowUpstream.java
new file mode 100644
index 0000000..ccd42e0
--- /dev/null
+++ b/netx/com/jogamp/plugin/ui/NativeWindowUpstream.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.plugin.ui;
+
+/**
+ * Representing the plugin window, i.e. the user applet's parent window.
+ */
+public interface NativeWindowUpstream {
+ /**
+ * Returns the display connection for this NativeWindow,
+ * maybe <code>null</code> for default.
+ * <p>
+ * On X11 this returns the X11 display connection string, e.g. <code>:0.0</code>.<BR>
+ * </p>
+ */
+ public String getDisplayConnection();
+
+ /**
+ * Returns the screen index for this NativeWindow.
+ */
+ public int getScreenIndex();
+
+ /**
+ * Returns the window handle for this NativeWindow. <P>
+ *
+ * The window handle shall reflect the platform one
+ * for all window related operations, e.g. open, close, resize. <P>
+ *
+ * On X11 this returns an entity of type Window. <BR>
+ * On Microsoft Windows this returns an entity of type HWND.
+ */
+ public long getWindowHandle();
+
+ /**
+ * Returns the width of the client area excluding insets (window decorations).
+ * @return width of the client area
+ */
+ public int getWidth();
+
+ /**
+ * Returns the height of the client area excluding insets (window decorations).
+ * @return height of the client area
+ */
+ public int getHeight();
+
+ /**
+ * Notify plugin that the applet's window has been <i>updated</i>, e.g. rendered and swapped.
+ */
+ public void notifySurfaceUpdated(NativeWindowDownstream swappedWin);
+}
diff --git a/netx/jogamp/applet/App3Context.java b/netx/jogamp/applet/App3Context.java
new file mode 100644
index 0000000..3205a16
--- /dev/null
+++ b/netx/jogamp/applet/App3Context.java
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * <p>
+ * Copyright (c) 2014, JogAmp Community. All rights reserved.
+ *
+ * JogAmp changes are compatible w/ GPLv2,
+ * and also available under the the New BSD 2-clause license.
+ *
+ * Please visit the JogAmp Community via http://jogamp.org/
+ * and find appropriate channels (forum, email, irc) to contact us
+ * if you need additional information or have any
+ * questions.
+ * </p>
+ */
+package jogamp.applet;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import sun.util.logging.PlatformLogger;
+
+import com.jogamp.plugin.ui.NativeWindowDownstream;
+
+/**
+ * The AppContext is a table referenced by ThreadGroup which stores
+ * application service instances. (If you are not writing an application
+ * service, or don't know what one is, please do not use this class.)
+ * The AppContext allows applet access to what would otherwise be
+ * potentially dangerous services, such as the ability to peek at
+ * EventQueues or change the look-and-feel of a Swing application.<p>
+ *
+ * Most application services use a singleton object to provide their
+ * services, either as a default (such as getSystemEventQueue or
+ * getDefaultToolkit) or as static methods with class data (System).
+ * The AppContext works with the former method by extending the concept
+ * of "default" to be ThreadGroup-specific. Application services
+ * lookup their singleton in the AppContext.<p>
+ *
+ * For example, here we have a Foo service, with its pre-AppContext
+ * code:<p>
+ * <code><pre>
+ * public class Foo {
+ * private static Foo defaultFoo = new Foo();
+ *
+ * public static Foo getDefaultFoo() {
+ * return defaultFoo;
+ * }
+ *
+ * ... Foo service methods
+ * }</pre></code><p>
+ *
+ * The problem with the above is that the Foo service is global in scope,
+ * so that applets and other untrusted code can execute methods on the
+ * single, shared Foo instance. The Foo service therefore either needs
+ * to block its use by untrusted code using a SecurityManager test, or
+ * restrict its capabilities so that it doesn't matter if untrusted code
+ * executes it.<p>
+ *
+ * Here's the Foo class written to use the AppContext:<p>
+ * <code><pre>
+ * public class Foo {
+ * public static Foo getDefaultFoo() {
+ * Foo foo = (Foo)AppContext.getAppContext().get(Foo.class);
+ * if (foo == null) {
+ * foo = new Foo();
+ * getAppContext().put(Foo.class, foo);
+ * }
+ * return foo;
+ * }
+ *
+ * ... Foo service methods
+ * }</pre></code><p>
+ *
+ * Since a separate AppContext can exist for each ThreadGroup, trusted
+ * and untrusted code have access to different Foo instances. This allows
+ * untrusted code access to "system-wide" services -- the service remains
+ * within the AppContext "sandbox". For example, say a malicious applet
+ * wants to peek all of the key events on the EventQueue to listen for
+ * passwords; if separate EventQueues are used for each ThreadGroup
+ * using AppContexts, the only key events that applet will be able to
+ * listen to are its own. A more reasonable applet request would be to
+ * change the Swing default look-and-feel; with that default stored in
+ * an AppContext, the applet's look-and-feel will change without
+ * disrupting other applets or potentially the browser itself.<p>
+ *
+ * Because the AppContext is a facility for safely extending application
+ * service support to applets, none of its methods may be blocked by a
+ * a SecurityManager check in a valid Java implementation. Applets may
+ * therefore safely invoke any of its methods without worry of being
+ * blocked.
+ *
+ * Note: If a SecurityManager is installed which derives from
+ * sun.awt.AWTSecurityManager, it may override the
+ * AWTSecurityManager.getAppContext() method to return the proper
+ * AppContext based on the execution context, in the case where
+ * the default ThreadGroup-based AppContext indexing would return
+ * the main "system" AppContext. For example, in an applet situation,
+ * if a system thread calls into an applet, rather than returning the
+ * main "system" AppContext (the one corresponding to the system thread),
+ * an installed AWTSecurityManager may return the applet's AppContext
+ * based on the execution context.
+ *
+ * @author Thomas Ball
+ * @author Fred Ecks
+ */
+public final class App3Context {
+ private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.AppContext");
+
+ /* Since the contents of an AppContext are unique to each Java
+ * session, this class should never be serialized. */
+
+ /*
+ * The key to put()/get() the Java EventQueue into/from the AppContext.
+ */
+ public static final Object EVENT_QUEUE_KEY = new StringBuffer("EventQueue");
+
+ /*
+ * The keys to store EventQueue push/pop lock and condition.
+ */
+ public final static Object EVENT_QUEUE_LOCK_KEY = new StringBuilder("EventQueue.Lock");
+ public final static Object EVENT_QUEUE_COND_KEY = new StringBuilder("EventQueue.Condition");
+
+ /* A map of AppContexts, referenced by ThreadGroup.
+ */
+ private static final Map<ThreadGroup, App3Context> threadGroup2appContext =
+ Collections.synchronizedMap(new IdentityHashMap<ThreadGroup, App3Context>());
+
+ private final Set<NativeWindowDownstream> registeredWindows = new HashSet<NativeWindowDownstream>();
+
+ private static final Map<NativeWindowDownstream, App3Context> appletWindow2AppContext =
+ Collections.synchronizedMap(new WeakHashMap<NativeWindowDownstream, App3Context>());
+
+ /**
+ * Returns a set containing all <code>AppContext</code>s.
+ */
+ public static Set<App3Context> getAppContexts() {
+ synchronized (threadGroup2appContext) {
+ return new HashSet<App3Context>(threadGroup2appContext.values());
+ }
+ }
+
+ /* The main "system" AppContext, used by everything not otherwise
+ contained in another AppContext. It is implicitly created for
+ standalone apps only (i.e. not applets)
+ */
+ private static App3Context mainAppContext;
+
+ /*
+ * The hash map associated with this AppContext. A private delegate
+ * is used instead of subclassing HashMap so as to avoid all of
+ * HashMap's potentially risky methods, such as clear(), elements(),
+ * putAll(), etc.
+ */
+ private final Map<Object, Object> table = new HashMap<Object, Object>();
+
+ private final ThreadGroup threadGroup;
+
+ /**
+ * If any <code>PropertyChangeListeners</code> have been registered,
+ * the <code>changeSupport</code> field describes them.
+ *
+ * @see #addPropertyChangeListener
+ * @see #removePropertyChangeListener
+ * @see #firePropertyChange
+ */
+ private PropertyChangeSupport changeSupport = null;
+
+ public static final String DISPOSED_PROPERTY_NAME = "disposed";
+ public static final String GUI_DISPOSED = "guidisposed";
+
+ private enum State {
+ VALID,
+ BEING_DISPOSED,
+ DISPOSED
+ };
+
+ private volatile State state = State.VALID;
+
+ public boolean isDisposed() {
+ return state == State.DISPOSED;
+ }
+
+ /*
+ * The total number of AppContexts, system-wide. This number is
+ * incremented at the beginning of the constructor, and decremented
+ * at the end of dispose(). getAppContext() checks to see if this
+ * number is 1. If so, it returns the sole AppContext without
+ * checking Thread.currentThread().
+ */
+ private static final AtomicInteger numAppContexts = new AtomicInteger(0);
+
+
+ /*
+ * The context ClassLoader that was used to create this AppContext.
+ */
+ private final ClassLoader contextClassLoader;
+
+ /**
+ * Constructor for AppContext. This method is <i>not</i> public,
+ * nor should it ever be used as such. The proper way to construct
+ * an AppContext is through the use of SunToolkit.createNewAppContext.
+ * A ThreadGroup is created for the new AppContext, a Thread is
+ * created within that ThreadGroup, and that Thread calls
+ * SunToolkit.createNewAppContext before calling anything else.
+ * That creates both the new AppContext and its EventQueue.
+ *
+ * @param threadGroup The ThreadGroup for the new AppContext
+ * @see sun.awt.SunToolkit
+ * @since 1.2
+ */
+ App3Context(ThreadGroup threadGroup) {
+ numAppContexts.incrementAndGet();
+
+ this.threadGroup = threadGroup;
+ threadGroup2appContext.put(threadGroup, this);
+
+ this.contextClassLoader =
+ AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ @Override
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ });
+
+ // Initialize push/pop lock and its condition to be used by all the
+ // EventQueues within this AppContext
+ Lock eventQueuePushPopLock = new ReentrantLock();
+ put(EVENT_QUEUE_LOCK_KEY, eventQueuePushPopLock);
+ Condition eventQueuePushPopCond = eventQueuePushPopLock.newCondition();
+ put(EVENT_QUEUE_COND_KEY, eventQueuePushPopCond);
+ }
+
+ private static final ThreadLocal<App3Context> threadAppContext =
+ new ThreadLocal<App3Context>();
+
+ static {
+ // On the main Thread, we get the ThreadGroup, make a corresponding
+ // AppContext, and instantiate the Java EventQueue. This way, legacy
+ // code is unaffected by the move to multiple AppContext ability.
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ ThreadGroup currentThreadGroup =
+ Thread.currentThread().getThreadGroup();
+ ThreadGroup parentThreadGroup = currentThreadGroup.getParent();
+ while (parentThreadGroup != null) {
+ // Find the root ThreadGroup to construct our main AppContext
+ currentThreadGroup = parentThreadGroup;
+ parentThreadGroup = currentThreadGroup.getParent();
+ }
+
+ mainAppContext = new App3Context(currentThreadGroup);
+ return null;
+ }
+ });
+ }
+
+ public static final App3Context createAppContext() {
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ return new App3Context(threadGroup);
+ }
+
+ public final static App3Context getAppContext(NativeWindowDownstream nw) {
+ return appletWindow2AppContext.get(nw);
+ }
+
+ /**
+ * Returns the appropriate AppContext for the caller,
+ * as determined by its ThreadGroup. If the main "system" AppContext
+ * would be returned and there's an AWTSecurityManager installed, it
+ * is called to get the proper AppContext based on the execution
+ * context.
+ *
+ * @return the AppContext for the caller.
+ * @see java.lang.ThreadGroup
+ * @since 1.2
+ */
+ public final static App3Context getAppContext() {
+ // we are standalone app, return the main app context
+ if (numAppContexts.get() == 1 && mainAppContext != null) {
+ return mainAppContext;
+ }
+
+ App3Context appContext = threadAppContext.get();
+
+ if (null == appContext) {
+ appContext = AccessController.doPrivileged(new PrivilegedAction<App3Context>()
+ {
+ @Override
+ public App3Context run() {
+ // Get the current ThreadGroup, and look for it and its
+ // parents in the hash from ThreadGroup to AppContext --
+ // it should be found, because we use createNewContext()
+ // when new AppContext objects are created.
+ ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
+ ThreadGroup threadGroup = currentThreadGroup;
+
+ App3Context context = threadGroup2appContext.get(threadGroup);
+ while (context == null) {
+ threadGroup = threadGroup.getParent();
+ if (threadGroup == null) {
+ return null;
+ }
+ context = threadGroup2appContext.get(threadGroup);
+ }
+
+ // In case we did anything in the above while loop, we add
+ // all the intermediate ThreadGroups to threadGroup2appContext
+ // so we won't spin again.
+ for (ThreadGroup tg = currentThreadGroup; tg != threadGroup; tg = tg.getParent()) {
+ threadGroup2appContext.put(tg, context);
+ }
+
+ // Now we're done, so we cache the latest key/value pair.
+ threadAppContext.set(context);
+
+ return context;
+ }
+ });
+ }
+
+ return appContext;
+ }
+
+ /**
+ * Returns true if the specified AppContext is the main AppContext.
+ *
+ * @param ctx the context to compare with the main context
+ * @return true if the specified AppContext is the main AppContext.
+ * @since 1.8
+ */
+ public final static boolean isMainContext(App3Context ctx) {
+ return (ctx != null && ctx == mainAppContext);
+ }
+
+ private final long DISPOSAL_TIMEOUT = 5000; // Default to 5-second timeout
+ // for disposal of all Frames
+ // (we wait for this time twice,
+ // once for dispose(), and once
+ // to clear the EventQueue).
+
+ private final long THREAD_INTERRUPT_TIMEOUT = 1000;
+ // Default to 1-second timeout for all
+ // interrupted Threads to exit, and another
+ // 1 second for all stopped Threads to die.
+
+ public void registerAppletWindow(NativeWindowDownstream nw) {
+ if(null==nw) return;
+ synchronized(registeredWindows) {
+ registeredWindows.add(nw);
+ appletWindow2AppContext.put(nw, this);
+ }
+ }
+
+ public void unregisterAppletWindow(NativeWindowDownstream nw) {
+ if(null==nw) return;
+ synchronized(registeredWindows) {
+ appletWindow2AppContext.remove(nw);
+ registeredWindows.remove(nw);
+ }
+ }
+
+ /**
+ * Disposes of this AppContext, all of its top-level Frames, and
+ * all Threads and ThreadGroups contained within it.
+ *
+ * This method must be called from a Thread which is not contained
+ * within this AppContext.
+ *
+ * @exception IllegalThreadStateException if the current thread is
+ * contained within this AppContext
+ * @since 1.2
+ */
+ @SuppressWarnings("deprecation")
+ public void dispose() throws IllegalThreadStateException {
+ // Check to be sure that the current Thread isn't in this AppContext
+ if (this.threadGroup.parentOf(Thread.currentThread().getThreadGroup())) {
+ throw new IllegalThreadStateException(
+ "Current Thread is contained within AppContext to be disposed."
+ );
+ }
+
+ synchronized(this) {
+ if (this.state != State.VALID) {
+ return; // If already disposed or being disposed, bail.
+ }
+
+ this.state = State.BEING_DISPOSED;
+ }
+
+ final PropertyChangeSupport changeSupport = this.changeSupport;
+ if (changeSupport != null) {
+ changeSupport.firePropertyChange(DISPOSED_PROPERTY_NAME, false, true);
+ }
+
+ // First, we post an InvocationEvent to be run on the
+ // EventDispatchThread which disposes of all top-level Frames and TrayIcons
+
+ log.info("AppContect.dispose() : Closing "+registeredWindows.size()+" remaining windows");
+ final Object notificationLock = new Object();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized(registeredWindows) {
+ for (Iterator<NativeWindowDownstream> iter=registeredWindows.iterator(); iter.hasNext(); ) {
+ final NativeWindowDownstream w = iter.next();
+ appletWindow2AppContext.remove(w);
+ try {
+ log.info("AppContect2.dispose() : "+w);
+ w.destroy();
+ } catch (Throwable t) {
+ log.warning("exception occured while disposing app context", t);
+ }
+ iter.remove();
+ }
+ registeredWindows.clear();
+ }
+ // Alert PropertyChangeListeners that the GUI has been disposed.
+ if (changeSupport != null) {
+ changeSupport.firePropertyChange(GUI_DISPOSED, false, true);
+ }
+ synchronized(notificationLock) {
+ notificationLock.notifyAll(); // Notify caller that we're done
+ }
+ }
+ };
+ synchronized(notificationLock) {
+ Thread t = new Thread(runnable);
+ t.start();
+ try {
+ notificationLock.wait(DISPOSAL_TIMEOUT);
+ } catch (InterruptedException e) { }
+ }
+
+ // Next, we post another InvocationEvent to the end of the
+ // EventQueue. When it's executed, we know we've executed all
+ // events in the queue.
+ runnable = new Runnable() { @Override
+ public void run() {
+ synchronized(notificationLock) {
+ notificationLock.notifyAll(); // Notify caller that we're done
+ }
+ } };
+ synchronized(notificationLock) {
+ Thread t = new Thread(runnable);
+ t.start();
+ try {
+ notificationLock.wait(DISPOSAL_TIMEOUT);
+ } catch (InterruptedException e) { }
+ }
+
+ // We are done with posting events, so change the state to disposed
+ synchronized(this) {
+ this.state = State.DISPOSED;
+ }
+
+ // Next, we interrupt all Threads in the ThreadGroup
+ this.threadGroup.interrupt();
+ // Note, the EventDispatchThread we've interrupted may dump an
+ // InterruptedException to the console here. This needs to be
+ // fixed in the EventDispatchThread, not here.
+
+ // Next, we sleep 10ms at a time, waiting for all of the active
+ // Threads in the ThreadGroup to exit.
+
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + THREAD_INTERRUPT_TIMEOUT;
+ while ((this.threadGroup.activeCount() > 0) &&
+ (System.currentTimeMillis() < endTime)) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) { }
+ }
+
+ // Then, we stop any remaining Threads
+ this.threadGroup.stop();
+
+ // Next, we sleep 10ms at a time, waiting for all of the active
+ // Threads in the ThreadGroup to die.
+
+ startTime = System.currentTimeMillis();
+ endTime = startTime + THREAD_INTERRUPT_TIMEOUT;
+ while ((this.threadGroup.activeCount() > 0) &&
+ (System.currentTimeMillis() < endTime)) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) { }
+ }
+
+ // Next, we remove this and all subThreadGroups from threadGroup2appContext
+ int numSubGroups = this.threadGroup.activeGroupCount();
+ if (numSubGroups > 0) {
+ ThreadGroup [] subGroups = new ThreadGroup[numSubGroups];
+ numSubGroups = this.threadGroup.enumerate(subGroups);
+ for (int subGroup = 0; subGroup < numSubGroups; subGroup++) {
+ threadGroup2appContext.remove(subGroups[subGroup]);
+ }
+ }
+ threadGroup2appContext.remove(this.threadGroup);
+
+ threadAppContext.set(null);
+
+ // Finally, we destroy the ThreadGroup entirely.
+ try {
+ this.threadGroup.destroy();
+ } catch (IllegalThreadStateException e) {
+ // Fired if not all the Threads died, ignore it and proceed
+ }
+
+ synchronized (table) {
+ this.table.clear(); // Clear out the Hashtable to ease garbage collection
+ }
+
+ numAppContexts.decrementAndGet();
+
+ mostRecentKeyValue = null;
+ }
+
+ static final class CreateThreadAction implements PrivilegedAction<Thread> {
+ private final App3Context appContext;
+ private final Runnable runnable;
+
+ public CreateThreadAction(App3Context ac, Runnable r) {
+ appContext = ac;
+ runnable = r;
+ }
+
+ @Override
+ public Thread run() {
+ Thread t = new Thread(appContext.getThreadGroup(), runnable);
+ t.setContextClassLoader(appContext.getContextClassLoader());
+ t.setPriority(Thread.NORM_PRIORITY + 1);
+ t.setDaemon(true);
+ return t;
+ }
+ }
+
+ private MostRecentKeyValue mostRecentKeyValue = null;
+ private MostRecentKeyValue shadowMostRecentKeyValue = null;
+
+ /**
+ * Returns the value to which the specified key is mapped in this context.
+ *
+ * @param key a key in the AppContext.
+ * @return the value to which the key is mapped in this AppContext;
+ * <code>null</code> if the key is not mapped to any value.
+ * @see #put(Object, Object)
+ * @since 1.2
+ */
+ public Object get(Object key) {
+ /*
+ * The most recent reference should be updated inside a synchronized
+ * block to avoid a race when put() and get() are executed in
+ * parallel on different threads.
+ */
+ synchronized (table) {
+ // Note: this most recent key/value caching is thread-hot.
+ // A simple test using SwingSet found that 72% of lookups
+ // were matched using the most recent key/value. By instantiating
+ // a simple MostRecentKeyValue object on cache misses, the
+ // cache hits can be processed without synchronization.
+
+ MostRecentKeyValue recent = mostRecentKeyValue;
+ if ((recent != null) && (recent.key == key)) {
+ return recent.value;
+ }
+
+ Object value = table.get(key);
+ if(mostRecentKeyValue == null) {
+ mostRecentKeyValue = new MostRecentKeyValue(key, value);
+ shadowMostRecentKeyValue = new MostRecentKeyValue(key, value);
+ } else {
+ MostRecentKeyValue auxKeyValue = mostRecentKeyValue;
+ shadowMostRecentKeyValue.setPair(key, value);
+ mostRecentKeyValue = shadowMostRecentKeyValue;
+ shadowMostRecentKeyValue = auxKeyValue;
+ }
+ return value;
+ }
+ }
+
+ /**
+ * Maps the specified <code>key</code> to the specified
+ * <code>value</code> in this AppContext. Neither the key nor the
+ * value can be <code>null</code>.
+ * <p>
+ * The value can be retrieved by calling the <code>get</code> method
+ * with a key that is equal to the original key.
+ *
+ * @param key the AppContext key.
+ * @param value the value.
+ * @return the previous value of the specified key in this
+ * AppContext, or <code>null</code> if it did not have one.
+ * @exception NullPointerException if the key or value is
+ * <code>null</code>.
+ * @see #get(Object)
+ * @since 1.2
+ */
+ public Object put(Object key, Object value) {
+ synchronized (table) {
+ MostRecentKeyValue recent = mostRecentKeyValue;
+ if ((recent != null) && (recent.key == key))
+ recent.value = value;
+ return table.put(key, value);
+ }
+ }
+
+ /**
+ * Removes the key (and its corresponding value) from this
+ * AppContext. This method does nothing if the key is not in the
+ * AppContext.
+ *
+ * @param key the key that needs to be removed.
+ * @return the value to which the key had been mapped in this AppContext,
+ * or <code>null</code> if the key did not have a mapping.
+ * @since 1.2
+ */
+ public Object remove(Object key) {
+ synchronized (table) {
+ MostRecentKeyValue recent = mostRecentKeyValue;
+ if ((recent != null) && (recent.key == key))
+ recent.value = null;
+ return table.remove(key);
+ }
+ }
+
+ /**
+ * Returns the root ThreadGroup for all Threads contained within
+ * this AppContext.
+ * @since 1.2
+ */
+ public ThreadGroup getThreadGroup() {
+ return threadGroup;
+ }
+
+ /**
+ * Returns the context ClassLoader that was used to create this
+ * AppContext.
+ *
+ * @see java.lang.Thread#getContextClassLoader
+ */
+ public ClassLoader getContextClassLoader() {
+ return contextClassLoader;
+ }
+
+ /**
+ * Returns a string representation of this AppContext.
+ * @since 1.2
+ */
+ @Override
+ public String toString() {
+ return getClass().getName() + "[threadGroup=" + threadGroup.getName() + "]";
+ }
+
+ /**
+ * Returns an array of all the property change listeners
+ * registered on this component.
+ *
+ * @return all of this component's <code>PropertyChangeListener</code>s
+ * or an empty array if no property change
+ * listeners are currently registered
+ *
+ * @see #addPropertyChangeListener
+ * @see #removePropertyChangeListener
+ * @see #getPropertyChangeListeners(java.lang.String)
+ * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners
+ * @since 1.4
+ */
+ public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
+ if (changeSupport == null) {
+ return new PropertyChangeListener[0];
+ }
+ return changeSupport.getPropertyChangeListeners();
+ }
+
+ /**
+ * Adds a PropertyChangeListener to the listener list for a specific
+ * property. The specified property may be one of the following:
+ * <ul>
+ * <li>if this AppContext is disposed ("disposed")</li>
+ * </ul>
+ * <ul>
+ * <li>if this AppContext's unowned Windows have been disposed
+ * ("guidisposed"). Code to cleanup after the GUI is disposed
+ * (such as LookAndFeel.uninitialize()) should execute in response to
+ * this property being fired. Notifications for the "guidisposed"
+ * property are sent on the event dispatch thread.</li>
+ * </ul>
+ * <p>
+ * If listener is null, no exception is thrown and no action is performed.
+ *
+ * @param propertyName one of the property names listed above
+ * @param listener the PropertyChangeListener to be added
+ *
+ * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
+ * @see #getPropertyChangeListeners(java.lang.String)
+ * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
+ */
+ public synchronized void addPropertyChangeListener(
+ String propertyName,
+ PropertyChangeListener listener) {
+ if (listener == null) {
+ return;
+ }
+ if (changeSupport == null) {
+ changeSupport = new PropertyChangeSupport(this);
+ }
+ changeSupport.addPropertyChangeListener(propertyName, listener);
+ }
+
+ /**
+ * Removes a PropertyChangeListener from the listener list for a specific
+ * property. This method should be used to remove PropertyChangeListeners
+ * that were registered for a specific bound property.
+ * <p>
+ * If listener is null, no exception is thrown and no action is performed.
+ *
+ * @param propertyName a valid property name
+ * @param listener the PropertyChangeListener to be removed
+ *
+ * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
+ * @see #getPropertyChangeListeners(java.lang.String)
+ * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
+ */
+ public synchronized void removePropertyChangeListener(
+ String propertyName,
+ PropertyChangeListener listener) {
+ if (listener == null || changeSupport == null) {
+ return;
+ }
+ changeSupport.removePropertyChangeListener(propertyName, listener);
+ }
+
+ /**
+ * Returns an array of all the listeners which have been associated
+ * with the named property.
+ *
+ * @return all of the <code>PropertyChangeListeners</code> associated with
+ * the named property or an empty array if no listeners have
+ * been added
+ *
+ * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
+ * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
+ * @see #getPropertyChangeListeners
+ * @since 1.4
+ */
+ public synchronized PropertyChangeListener[] getPropertyChangeListeners(
+ String propertyName) {
+ if (changeSupport == null) {
+ return new PropertyChangeListener[0];
+ }
+ return changeSupport.getPropertyChangeListeners(propertyName);
+ }
+}
+
+final class MostRecentKeyValue {
+ Object key;
+ Object value;
+ MostRecentKeyValue(Object k, Object v) {
+ key = k;
+ value = v;
+ }
+ void setPair(Object k, Object v) {
+ key = k;
+ value = v;
+ }
+}
diff --git a/netx/jogamp/applet/Applet3ClassLoader.java b/netx/jogamp/applet/Applet3ClassLoader.java
new file mode 100644
index 0000000..9f3cda2
--- /dev/null
+++ b/netx/jogamp/applet/Applet3ClassLoader.java
@@ -0,0 +1,883 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * <p>
+ * Copyright (c) 2014, JogAmp Community. All rights reserved.
+ *
+ * JogAmp changes are compatible w/ GPLv2,
+ * and also available under the the New BSD 2-clause license.
+ *
+ * Please visit the JogAmp Community via http://jogamp.org/
+ * and find appropriate channels (forum, email, irc) to contact us
+ * if you need additional information or have any
+ * questions.
+ * </p>
+ */
+
+package jogamp.applet;
+
+import java.lang.NullPointerException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+
+import sun.applet.AppletThreadGroup;
+import sun.misc.IOUtils;
+import sun.net.www.ParseUtil;
+import sun.security.util.SecurityConstants;
+
+/**
+ * This class defines the class loader for loading applet classes and
+ * resources. It extends URLClassLoader to search the applet code base
+ * for the class or resource after checking any loaded JAR files.
+ */
+public class Applet3ClassLoader extends URLClassLoader {
+ private URL base; /* applet code base URL */
+ private CodeSource codesource; /* codesource for the base URL */
+ private AccessControlContext acc;
+ private boolean exceptionStatus = false;
+
+ private final Object threadGroupSynchronizer = new Object();
+ private final Object grabReleaseSynchronizer = new Object();
+
+ private boolean codebaseLookup = true;
+ private volatile boolean allowRecursiveDirectoryRead = true;
+
+ /*
+ * Creates a new AppletClassLoader for the specified base URL.
+ */
+ protected Applet3ClassLoader(URL base) {
+ super(new URL[0]);
+ this.base = base;
+ this.codesource =
+ new CodeSource(base, (java.security.cert.Certificate[]) null);
+ acc = AccessController.getContext();
+ }
+
+ public void disableRecursiveDirectoryRead() {
+ allowRecursiveDirectoryRead = false;
+ }
+
+
+ /**
+ * Set the codebase lookup flag.
+ */
+ void setCodebaseLookup(boolean codebaseLookup) {
+ this.codebaseLookup = codebaseLookup;
+ }
+
+ /*
+ * Returns the applet code base URL.
+ */
+ URL getBaseURL() {
+ return base;
+ }
+
+ /*
+ * Returns the URLs used for loading classes and resources.
+ */
+ public URL[] getURLs() {
+ URL[] jars = super.getURLs();
+ URL[] urls = new URL[jars.length + 1];
+ System.arraycopy(jars, 0, urls, 0, jars.length);
+ urls[urls.length - 1] = base;
+ return urls;
+ }
+
+ /*
+ * Adds the specified JAR file to the search path of loaded JAR files.
+ * Changed modifier to protected in order to be able to overwrite addJar()
+ * in PluginClassLoader.java
+ */
+ protected void addJar(String name) throws IOException {
+ URL url;
+ try {
+ url = new URL(base, name);
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("name");
+ }
+ addURL(url);
+ // DEBUG
+ //URL[] urls = getURLs();
+ //for (int i = 0; i < urls.length; i++) {
+ // System.out.println("url[" + i + "] = " + urls[i]);
+ //}
+ }
+
+ /*
+ * Override loadClass so that class loading errors can be caught in
+ * order to print better error messages.
+ */
+ public synchronized Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ // First check if we have permission to access the package. This
+ // should go away once we've added support for exported packages.
+ int i = name.lastIndexOf('.');
+ if (i != -1) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPackageAccess(name.substring(0, i));
+ }
+ try {
+ return super.loadClass(name, resolve);
+ } catch (ClassNotFoundException e) {
+ //printError(name, e.getException());
+ throw e;
+ } catch (RuntimeException e) {
+ //printError(name, e);
+ throw e;
+ } catch (Error e) {
+ //printError(name, e);
+ throw e;
+ }
+ }
+
+ /*
+ * Finds the applet class with the specified name. First searches
+ * loaded JAR files then the applet code base for the class.
+ */
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+
+ int index = name.indexOf(";");
+ String cookie = "";
+ if(index != -1) {
+ cookie = name.substring(index, name.length());
+ name = name.substring(0, index);
+ }
+
+ // check loaded JAR files
+ try {
+ return super.findClass(name);
+ } catch (ClassNotFoundException e) {
+ }
+
+ // Otherwise, try loading the class from the code base URL
+
+ // 4668479: Option to turn off codebase lookup in AppletClassLoader
+ // during resource requests. [stanley.ho]
+ if (codebaseLookup == false)
+ throw new ClassNotFoundException(name);
+
+// final String path = name.replace('.', '/').concat(".class").concat(cookie);
+ String encodedName = ParseUtil.encodePath(name.replace('.', '/'), false);
+ final String path = (new StringBuffer(encodedName)).append(".class").append(cookie).toString();
+ try {
+ byte[] b = (byte[]) AccessController.doPrivileged(
+ new PrivilegedExceptionAction<byte[]>() {
+ public byte[] run() throws IOException {
+ try {
+ URL finalURL = new URL(base, path);
+
+ // Make sure the codebase won't be modified
+ if (base.getProtocol().equals(finalURL.getProtocol()) &&
+ base.getHost().equals(finalURL.getHost()) &&
+ base.getPort() == finalURL.getPort()) {
+ return getBytes(finalURL);
+ }
+ else {
+ return null;
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }, acc);
+
+ if (b != null) {
+ return defineClass(name, b, 0, b.length, codesource);
+ } else {
+ throw new ClassNotFoundException(name);
+ }
+ } catch (PrivilegedActionException e) {
+ throw new ClassNotFoundException(name, e.getException());
+ }
+ }
+
+ /**
+ * Returns the permissions for the given codesource object.
+ * The implementation of this method first calls super.getPermissions,
+ * to get the permissions
+ * granted by the super class, and then adds additional permissions
+ * based on the URL of the codesource.
+ * <p>
+ * If the protocol is "file"
+ * and the path specifies a file, permission is granted to read all files
+ * and (recursively) all files and subdirectories contained in
+ * that directory. This is so applets with a codebase of
+ * file:/blah/some.jar can read in file:/blah/, which is needed to
+ * be backward compatible. We also add permission to connect back to
+ * the "localhost".
+ *
+ * @param codesource the codesource
+ * @throws NullPointerException if {@code codesource} is {@code null}.
+ * @return the permissions granted to the codesource
+ */
+ protected PermissionCollection getPermissions(CodeSource codesource)
+ {
+ final PermissionCollection perms = super.getPermissions(codesource);
+
+ URL url = codesource.getLocation();
+
+ String path = null;
+ Permission p;
+
+ try {
+ p = url.openConnection().getPermission();
+ } catch (java.io.IOException ioe) {
+ p = null;
+ }
+
+ if (p instanceof FilePermission) {
+ path = p.getName();
+ } else if ((p == null) && (url.getProtocol().equals("file"))) {
+ path = url.getFile().replace('/', File.separatorChar);
+ path = ParseUtil.decode(path);
+ }
+
+ if (path != null) {
+ final String rawPath = path;
+ if (!path.endsWith(File.separator)) {
+ int endIndex = path.lastIndexOf(File.separatorChar);
+ if (endIndex != -1) {
+ path = path.substring(0, endIndex + 1) + "-";
+ perms.add(new FilePermission(path,
+ SecurityConstants.FILE_READ_ACTION));
+ }
+ }
+ final File f = new File(rawPath);
+ final boolean isDirectory = f.isDirectory();
+ // grant codebase recursive read permission
+ // this should only be granted to non-UNC file URL codebase and
+ // the codesource path must either be a directory, or a file
+ // that ends with .jar or .zip
+ if (allowRecursiveDirectoryRead && (isDirectory ||
+ rawPath.toLowerCase().endsWith(".jar") ||
+ rawPath.toLowerCase().endsWith(".zip"))) {
+
+ Permission bperm;
+ try {
+ bperm = base.openConnection().getPermission();
+ } catch (java.io.IOException ioe) {
+ bperm = null;
+ }
+ if (bperm instanceof FilePermission) {
+ String bpath = bperm.getName();
+ if (bpath.endsWith(File.separator)) {
+ bpath += "-";
+ }
+ perms.add(new FilePermission(bpath,
+ SecurityConstants.FILE_READ_ACTION));
+ } else if ((bperm == null) && (base.getProtocol().equals("file"))) {
+ String bpath = base.getFile().replace('/', File.separatorChar);
+ bpath = ParseUtil.decode(bpath);
+ if (bpath.endsWith(File.separator)) {
+ bpath += "-";
+ }
+ perms.add(new FilePermission(bpath, SecurityConstants.FILE_READ_ACTION));
+ }
+
+ }
+ }
+ return perms;
+ }
+
+ /*
+ * Returns the contents of the specified URL as an array of bytes.
+ */
+ private static byte[] getBytes(URL url) throws IOException {
+ URLConnection uc = url.openConnection();
+ if (uc instanceof java.net.HttpURLConnection) {
+ java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc;
+ int code = huc.getResponseCode();
+ if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) {
+ throw new IOException("open HTTP connection failed.");
+ }
+ }
+ int len = uc.getContentLength();
+
+ // Fixed #4507227: Slow performance to load
+ // class and resources. [stanleyh]
+ //
+ // Use buffered input stream [stanleyh]
+ InputStream in = new BufferedInputStream(uc.getInputStream());
+
+ byte[] b;
+ try {
+ b = IOUtils.readFully(in, len, true);
+ } finally {
+ in.close();
+ }
+ return b;
+ }
+
+ // Object for synchronization around getResourceAsStream()
+ private Object syncResourceAsStream = new Object();
+ private Object syncResourceAsStreamFromJar = new Object();
+
+ // Flag to indicate getResourceAsStream() is in call
+ private boolean resourceAsStreamInCall = false;
+ private boolean resourceAsStreamFromJarInCall = false;
+
+ /**
+ * Returns an input stream for reading the specified resource.
+ *
+ * The search order is described in the documentation for {@link
+ * #getResource(String)}.<p>
+ *
+ * @param name the resource name
+ * @return an input stream for reading the resource, or <code>null</code>
+ * if the resource could not be found
+ * @since JDK1.1
+ */
+ public InputStream getResourceAsStream(String name)
+ {
+
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+
+ try
+ {
+ InputStream is = null;
+
+ // Fixed #4507227: Slow performance to load
+ // class and resources. [stanleyh]
+ //
+ // The following is used to avoid calling
+ // AppletClassLoader.findResource() in
+ // super.getResourceAsStream(). Otherwise,
+ // unnecessary connection will be made.
+ //
+ synchronized(syncResourceAsStream)
+ {
+ resourceAsStreamInCall = true;
+
+ // Call super class
+ is = super.getResourceAsStream(name);
+
+ resourceAsStreamInCall = false;
+ }
+
+ // 4668479: Option to turn off codebase lookup in AppletClassLoader
+ // during resource requests. [stanley.ho]
+ if (codebaseLookup == true && is == null)
+ {
+ // If resource cannot be obtained,
+ // try to download it from codebase
+ URL url = new URL(base, ParseUtil.encodePath(name, false));
+ is = url.openStream();
+ }
+
+ return is;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+
+ /**
+ * Returns an input stream for reading the specified resource from the
+ * the loaded jar files.
+ *
+ * The search order is described in the documentation for {@link
+ * #getResource(String)}.<p>
+ *
+ * @param name the resource name
+ * @return an input stream for reading the resource, or <code>null</code>
+ * if the resource could not be found
+ * @since JDK1.1
+ */
+ public InputStream getResourceAsStreamFromJar(String name) {
+
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+
+ try {
+ InputStream is = null;
+ synchronized(syncResourceAsStreamFromJar) {
+ resourceAsStreamFromJarInCall = true;
+ // Call super class
+ is = super.getResourceAsStream(name);
+ resourceAsStreamFromJarInCall = false;
+ }
+
+ return is;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+
+ /*
+ * Finds the applet resource with the specified name. First checks
+ * loaded JAR files then the applet code base for the resource.
+ */
+ public URL findResource(String name) {
+ // check loaded JAR files
+ URL url = super.findResource(name);
+
+ // 6215746: Disable META-INF/* lookup from codebase in
+ // applet/plugin classloader. [stanley.ho]
+ if (name.startsWith("META-INF/"))
+ return url;
+
+ // 4668479: Option to turn off codebase lookup in AppletClassLoader
+ // during resource requests. [stanley.ho]
+ if (codebaseLookup == false)
+ return url;
+
+ if (url == null)
+ {
+ //#4805170, if it is a call from Applet.getImage()
+ //we should check for the image only in the archives
+ boolean insideGetResourceAsStreamFromJar = false;
+ synchronized(syncResourceAsStreamFromJar) {
+ insideGetResourceAsStreamFromJar = resourceAsStreamFromJarInCall;
+ }
+
+ if (insideGetResourceAsStreamFromJar) {
+ return null;
+ }
+
+ // Fixed #4507227: Slow performance to load
+ // class and resources. [stanleyh]
+ //
+ // Check if getResourceAsStream is called.
+ //
+ boolean insideGetResourceAsStream = false;
+
+ synchronized(syncResourceAsStream)
+ {
+ insideGetResourceAsStream = resourceAsStreamInCall;
+ }
+
+ // If getResourceAsStream is called, don't
+ // trigger the following code. Otherwise,
+ // unnecessary connection will be made.
+ //
+ if (insideGetResourceAsStream == false)
+ {
+ // otherwise, try the code base
+ try {
+ url = new URL(base, ParseUtil.encodePath(name, false));
+ // check if resource exists
+ if(!resourceExists(url))
+ url = null;
+ } catch (Exception e) {
+ // all exceptions, including security exceptions, are caught
+ url = null;
+ }
+ }
+ }
+ return url;
+ }
+
+
+ private boolean resourceExists(URL url) {
+ // Check if the resource exists.
+ // It almost works to just try to do an openConnection() but
+ // HttpURLConnection will return true on HTTP_BAD_REQUEST
+ // when the requested name ends in ".html", ".htm", and ".txt"
+ // and we want to be able to handle these
+ //
+ // Also, cannot just open a connection for things like FileURLConnection,
+ // because they succeed when connecting to a nonexistent file.
+ // So, in those cases we open and close an input stream.
+ boolean ok = true;
+ try {
+ URLConnection conn = url.openConnection();
+ if (conn instanceof java.net.HttpURLConnection) {
+ java.net.HttpURLConnection hconn =
+ (java.net.HttpURLConnection) conn;
+
+ // To reduce overhead, using http HEAD method instead of GET method
+ hconn.setRequestMethod("HEAD");
+
+ int code = hconn.getResponseCode();
+ if (code == java.net.HttpURLConnection.HTTP_OK) {
+ return true;
+ }
+ if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) {
+ return false;
+ }
+ } else {
+ /**
+ * Fix for #4182052 - stanleyh
+ *
+ * The same connection should be reused to avoid multiple
+ * HTTP connections
+ */
+
+ // our best guess for the other cases
+ InputStream is = conn.getInputStream();
+ is.close();
+ }
+ } catch (Exception ex) {
+ ok = false;
+ }
+ return ok;
+ }
+
+ /*
+ * Returns an enumeration of all the applet resources with the specified
+ * name. First checks loaded JAR files then the applet code base for all
+ * available resources.
+ */
+ public Enumeration<URL> findResources(String name) throws IOException {
+
+ final Enumeration<URL> e = super.findResources(name);
+
+ // 6215746: Disable META-INF/* lookup from codebase in
+ // applet/plugin classloader. [stanley.ho]
+ if (name.startsWith("META-INF/"))
+ return e;
+
+ // 4668479: Option to turn off codebase lookup in AppletClassLoader
+ // during resource requests. [stanley.ho]
+ if (codebaseLookup == false)
+ return e;
+
+ URL u = new URL(base, ParseUtil.encodePath(name, false));
+ if (!resourceExists(u)) {
+ u = null;
+ }
+
+ final URL url = u;
+ return new Enumeration<URL>() {
+ private boolean done;
+ public URL nextElement() {
+ if (!done) {
+ if (e.hasMoreElements()) {
+ return e.nextElement();
+ }
+ done = true;
+ if (url != null) {
+ return url;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+ public boolean hasMoreElements() {
+ return !done && (e.hasMoreElements() || url != null);
+ }
+ };
+ }
+
+ /*
+ * Load and resolve the file specified by the applet tag CODE
+ * attribute. The argument can either be the relative path
+ * of the class file itself or just the name of the class.
+ */
+ Class<?> loadCode(String name) throws ClassNotFoundException {
+ // first convert any '/' or native file separator to .
+ name = name.replace('/', '.');
+ name = name.replace(File.separatorChar, '.');
+
+ // deal with URL rewriting
+ String cookie = null;
+ int index = name.indexOf(";");
+ if(index != -1) {
+ cookie = name.substring(index, name.length());
+ name = name.substring(0, index);
+ }
+
+ // save that name for later
+ String fullName = name;
+ // then strip off any suffixes
+ if (name.endsWith(".class") || name.endsWith(".java")) {
+ name = name.substring(0, name.lastIndexOf('.'));
+ }
+ try {
+ if(cookie != null)
+ name = (new StringBuffer(name)).append(cookie).toString();
+ return loadClass(name);
+ } catch (ClassNotFoundException e) {
+ }
+ // then if it didn't end with .java or .class, or in the
+ // really pathological case of a class named class or java
+ if(cookie != null)
+ fullName = (new StringBuffer(fullName)).append(cookie).toString();
+
+ return loadClass(fullName);
+ }
+
+ /*
+ * The threadgroup that the applets loaded by this classloader live
+ * in. In the sun.* implementation of applets, the security manager's
+ * (AppletSecurity) getThreadGroup returns the thread group of the
+ * first applet on the stack, which is the applet's thread group.
+ */
+ private AppletThreadGroup threadGroup;
+ private App3Context appContext;
+
+ public ThreadGroup getThreadGroup() {
+ synchronized (threadGroupSynchronizer) {
+ if (threadGroup == null || threadGroup.isDestroyed()) {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ threadGroup = new AppletThreadGroup(base + "-threadGroup");
+ // threadGroup.setDaemon(true);
+ // threadGroup is now destroyed by AppContext.dispose()
+
+ // Create the new AppContext from within a Thread belonging
+ // to the newly created ThreadGroup, and wait for the
+ // creation to complete before returning from this method.
+ AppContextCreator creatorThread = new AppContextCreator(threadGroup);
+
+ // Since this thread will later be used to launch the
+ // applet's AWT-event dispatch thread and we want the applet
+ // code executing the AWT callbacks to use their own class
+ // loader rather than the system class loader, explicitly
+ // set the context class loader to the AppletClassLoader.
+ creatorThread.setContextClassLoader(Applet3ClassLoader.this);
+
+ creatorThread.start();
+ try {
+ synchronized(creatorThread.syncObject) {
+ while (!creatorThread.created) {
+ creatorThread.syncObject.wait();
+ }
+ }
+ } catch (InterruptedException e) { }
+ appContext = creatorThread.appContext;
+ return null;
+ }
+ });
+ }
+ return threadGroup;
+ }
+ }
+
+ /*
+ * Get the App3Context, if any, corresponding to this AppletClassLoader.
+ */
+ public App3Context getAppContext() {
+ return appContext;
+ }
+
+ int usageCount = 0;
+
+ /**
+ * Grab this AppletClassLoader and its ThreadGroup/AppContext, so they
+ * won't be destroyed.
+ */
+ public void grab() {
+ synchronized(grabReleaseSynchronizer) {
+ usageCount++;
+ }
+ getThreadGroup(); // Make sure ThreadGroup/AppContext exist
+ }
+
+ protected void setExceptionStatus()
+ {
+ exceptionStatus = true;
+ }
+
+ public boolean getExceptionStatus()
+ {
+ return exceptionStatus;
+ }
+
+ /**
+ * Release this AppletClassLoader and its ThreadGroup/AppContext.
+ * If nothing else has grabbed this AppletClassLoader, its ThreadGroup
+ * and AppContext will be destroyed.
+ *
+ * Because this method may destroy the AppletClassLoader's ThreadGroup,
+ * this method should NOT be called from within the AppletClassLoader's
+ * ThreadGroup.
+ *
+ * Changed modifier to protected in order to be able to overwrite this
+ * function in PluginClassLoader.java
+ */
+ protected void release() {
+
+ App3Context tempAppContext = null;
+
+ synchronized(grabReleaseSynchronizer) {
+ if (usageCount > 1) {
+ --usageCount;
+ } else {
+ synchronized(threadGroupSynchronizer) {
+ tempAppContext = resetAppContext();
+ }
+ }
+ }
+
+ // Dispose appContext outside any sync block to
+ // prevent potential deadlock.
+ if (tempAppContext != null) {
+ try {
+ tempAppContext.dispose(); // nuke the world!
+ } catch (IllegalThreadStateException e) { }
+ }
+ }
+
+ /*
+ * reset classloader's AppContext and ThreadGroup
+ * This method is for subclass PluginClassLoader to
+ * reset superclass's AppContext and ThreadGroup but do
+ * not dispose the AppContext. PluginClassLoader does not
+ * use UsageCount to decide whether to dispose AppContext
+ *
+ * @return previous AppContext
+ */
+ protected App3Context resetAppContext() {
+ App3Context tempAppContext = null;
+
+ synchronized(threadGroupSynchronizer) {
+ // Store app context in temp variable
+ tempAppContext = appContext;
+ usageCount = 0;
+ appContext = null;
+ threadGroup = null;
+ }
+ return tempAppContext;
+ }
+
+
+ // Hash map to store applet compatibility info
+ private HashMap<String, Boolean> jdk11AppletInfo = new HashMap<String, Boolean>();
+ private HashMap<String, Boolean> jdk12AppletInfo = new HashMap<String, Boolean>();
+
+ /**
+ * Set applet target level as JDK 1.1.
+ *
+ * @param clazz Applet class.
+ * @param bool true if JDK is targeted for JDK 1.1;
+ * false otherwise.
+ */
+ void setJDK11Target(Class<?> clazz, boolean bool)
+ {
+ jdk11AppletInfo.put(clazz.toString(), Boolean.valueOf(bool));
+ }
+
+ /**
+ * Set applet target level as JDK 1.2.
+ *
+ * @param clazz Applet class.
+ * @param bool true if JDK is targeted for JDK 1.2;
+ * false otherwise.
+ */
+ void setJDK12Target(Class<?> clazz, boolean bool)
+ {
+ jdk12AppletInfo.put(clazz.toString(), Boolean.valueOf(bool));
+ }
+
+ /**
+ * Determine if applet is targeted for JDK 1.1.
+ *
+ * @param applet Applet class.
+ * @return TRUE if applet is targeted for JDK 1.1;
+ * FALSE if applet is not;
+ * null if applet is unknown.
+ */
+ Boolean isJDK11Target(Class<?> clazz)
+ {
+ return (Boolean) jdk11AppletInfo.get(clazz.toString());
+ }
+
+ /**
+ * Determine if applet is targeted for JDK 1.2.
+ *
+ * @param applet Applet class.
+ * @return TRUE if applet is targeted for JDK 1.2;
+ * FALSE if applet is not;
+ * null if applet is unknown.
+ */
+ Boolean isJDK12Target(Class<?> clazz)
+ {
+ return (Boolean) jdk12AppletInfo.get(clazz.toString());
+ }
+
+ /*
+ private static AppletMessageHandler mh =
+ new AppletMessageHandler("appletclassloader");
+
+ * Prints a class loading error message.
+ private static void printError(String name, Throwable e) {
+ String s = null;
+ if (e == null) {
+ s = mh.getMessage("filenotfound", name);
+ } else if (e instanceof IOException) {
+ s = mh.getMessage("fileioexception", name);
+ } else if (e instanceof ClassFormatError) {
+ s = mh.getMessage("fileformat", name);
+ } else if (e instanceof ThreadDeath) {
+ s = mh.getMessage("filedeath", name);
+ } else if (e instanceof Error) {
+ s = mh.getMessage("fileerror", e.toString(), name);
+ }
+ if (s != null) {
+ System.err.println(s);
+ }
+ }
+ */
+}
+
+/*
+ * The AppContextCreator class is used to create an AppContext from within
+ * a Thread belonging to the new AppContext's ThreadGroup. To wait for
+ * this operation to complete before continuing, wait for the notifyAll()
+ * operation on the syncObject to occur.
+ */
+class AppContextCreator extends Thread {
+ Object syncObject = new Object();
+ App3Context appContext = null;
+ volatile boolean created = false;
+
+ AppContextCreator(ThreadGroup group) {
+ super(group, "AppContextCreator");
+ }
+
+ public void run() {
+ appContext = App3Context.createAppContext();
+ created = true;
+ synchronized(syncObject) {
+ syncObject.notifyAll();
+ }
+ } // run()
+
+} // class AppContextCreator
diff --git a/netx/jogamp/applet/Applet3ObjectInputStream.java b/netx/jogamp/applet/Applet3ObjectInputStream.java
new file mode 100644
index 0000000..3cf4b4e
--- /dev/null
+++ b/netx/jogamp/applet/Applet3ObjectInputStream.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved.
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * <p>
+ * Copyright (c) 2014, JogAmp Community. All rights reserved.
+ *
+ * JogAmp changes are compatible w/ GPLv2,
+ * and also available under the the New BSD 2-clause license.
+ *
+ * Please visit the JogAmp Community via http://jogamp.org/
+ * and find appropriate channels (forum, email, irc) to contact us
+ * if you need additional information or have any
+ * questions.
+ * </p>
+ */
+/*
+ * COPYRIGHT goes here
+ */
+
+package jogamp.applet;
+
+import java.io.*;
+import java.lang.reflect.Array;
+
+import sun.applet.AppletIllegalArgumentException;
+
+/**
+ * This subclass of ObjectInputStream delegates loading of classes to
+ * an existing ClassLoader.
+ */
+
+class Applet3ObjectInputStream extends ObjectInputStream
+{
+ private Applet3ClassLoader loader;
+
+ /**
+ * Loader must be non-null;
+ */
+
+ public Applet3ObjectInputStream(InputStream in, Applet3ClassLoader loader)
+ throws IOException, StreamCorruptedException {
+
+ super(in);
+ if (loader == null) {
+ throw new AppletIllegalArgumentException("appletillegalargumentexception.objectinputstream");
+
+ }
+ this.loader = loader;
+ }
+
+ /**
+ * Make a primitive array class
+ */
+
+ private Class<?> primitiveType(char type) {
+ switch (type) {
+ case 'B': return byte.class;
+ case 'C': return char.class;
+ case 'D': return double.class;
+ case 'F': return float.class;
+ case 'I': return int.class;
+ case 'J': return long.class;
+ case 'S': return short.class;
+ case 'Z': return boolean.class;
+ default: return null;
+ }
+ }
+
+ /**
+ * Use the given ClassLoader rather than using the system class
+ */
+ protected Class<?> resolveClass(ObjectStreamClass classDesc)
+ throws IOException, ClassNotFoundException {
+
+ String cname = classDesc.getName();
+ if (cname.startsWith("[")) {
+ // An array
+ Class<?> component; // component class
+ int dcount; // dimension
+ for (dcount=1; cname.charAt(dcount)=='['; dcount++) ;
+ if (cname.charAt(dcount) == 'L') {
+ component = loader.loadClass(cname.substring(dcount+1,
+ cname.length()-1));
+ } else {
+ if (cname.length() != dcount+1) {
+ throw new ClassNotFoundException(cname);// malformed
+ }
+ component = primitiveType(cname.charAt(dcount));
+ }
+ int dim[] = new int[dcount];
+ for (int i=0; i<dcount; i++) {
+ dim[i]=0;
+ }
+ return Array.newInstance(component, dim).getClass();
+ } else {
+ return loader.loadClass(cname);
+ }
+ }
+}
diff --git a/netx/jogamp/applet/Applet3Panel.java b/netx/jogamp/applet/Applet3Panel.java
new file mode 100644
index 0000000..34d8d53
--- /dev/null
+++ b/netx/jogamp/applet/Applet3Panel.java
@@ -0,0 +1,1536 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * <p>
+ * Copyright (c) 2014, JogAmp Community. All rights reserved.
+ *
+ * JogAmp changes are compatible w/ GPLv2,
+ * and also available under the the New BSD 2-clause license.
+ *
+ * Please visit the JogAmp Community via http://jogamp.org/
+ * and find appropriate channels (forum, email, irc) to contact us
+ * if you need additional information or have any
+ * questions.
+ * </p>
+ */
+
+package jogamp.applet;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.lang.ref.WeakReference;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketPermission;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import sun.applet.Applet3MessageHandler;
+import sun.applet.AppletEvent;
+import sun.applet.AppletEventMulticaster;
+import sun.applet.AppletListener;
+import sun.misc.MessageUtils;
+import sun.misc.PerformanceLogger;
+import sun.misc.Queue;
+import sun.security.util.SecurityConstants;
+
+import com.jogamp.plugin.applet.Applet3;
+import com.jogamp.plugin.applet.Applet3Context;
+import com.jogamp.plugin.ui.NativeWindowDownstream;
+import com.jogamp.plugin.ui.NativeWindowUpstream;
+
+/**
+ * Applet panel class. The panel manages and manipulates the
+ * applet as it is being loaded. It forks a separate thread in a new
+ * thread group to call the applet's init(), start(), stop(), and
+ * destroy() methods.
+ *
+ * @author Arthur van Hoff
+ */
+public abstract class Applet3Panel implements Applet3Context, Runnable {
+
+ /**
+ * The applet (if loaded).
+ */
+ protected Applet3 applet;
+ /**
+ * The applet window controller (if initialized).
+ */
+ private NativeWindowDownstream appletWindow;
+
+ private final MyNativeWindow parentWindow;
+
+ /**
+ * Applet will allow initialization. Should be
+ * set to false if loading a serialized applet
+ * that was pickled in the init=true state.
+ */
+ protected boolean doInit = true;
+
+
+ /**
+ * The {@link Applet3ClassLoader} for the applet.
+ * <p>
+ * In case {@link #getClassLoader(URL, String)} produced
+ * an instance of {@link Applet3ClassLoader}.
+ * </p>
+ */
+ protected Applet3ClassLoader applet3Loader;
+
+ /**
+ * The ClassLoader for the applet.
+ */
+ protected ClassLoader loader;
+
+ /* applet event ids */
+ public final static int APPLET_DISPOSE = 0;
+ public final static int APPLET_LOAD = 1;
+ public final static int APPLET_INIT = 2;
+ public final static int APPLET_START = 3;
+ public final static int APPLET_STOP = 4;
+ public final static int APPLET_DESTROY = 5;
+ public final static int APPLET_QUIT = 6;
+ public final static int APPLET_ERROR = 7;
+
+ /* send to the parent to force relayout */
+ public final static int APPLET_RESIZE = 51234;
+
+ /* sent to a (distant) parent to indicate that the applet is being
+ * loaded or as completed loading
+ */
+ public final static int APPLET_LOADING = 51235;
+ public final static int APPLET_LOADING_COMPLETED = 51236;
+
+ /**
+ * The current status. One of:
+ * APPLET_DISPOSE,
+ * APPLET_LOAD,
+ * APPLET_INIT,
+ * APPLET_START,
+ * APPLET_STOP,
+ * APPLET_DESTROY,
+ * APPLET_ERROR.
+ */
+ protected int status;
+
+ /**
+ * The thread for the applet.
+ */
+ protected Thread handler;
+
+
+ /**
+ * The initial applet size.
+ */
+ int[] defaultAppletSize = { 10, 10 };
+
+ /**
+ * The current applet size.
+ */
+ int[] currentAppletSize = { 10, 10 };
+
+ MessageUtils mu = new MessageUtils();
+
+ /**
+ * The thread to use during applet loading
+ */
+
+ Thread loaderThread = null;
+
+ /**
+ * Flag to indicate that a loading has been cancelled
+ */
+ boolean loadAbortRequest = false;
+
+ /* Are we debugging? */
+ static boolean debug = false;
+
+ /**
+ * The document url.
+ */
+ final protected URL documentURL;
+
+ /**
+ * The base url.
+ */
+ final protected URL baseURL;
+
+ /**
+ * The attributes of the applet.
+ */
+ final Hashtable<String, String> parameters;
+
+ public Applet3Panel(long nativeWindowHandle, int width, int height, URL documentURL, Hashtable<String, String> parameters) {
+ this.documentURL = documentURL;
+ this.parameters = parameters;
+ this.updateSizeInParameters(width, height);
+ this.currentAppletSize[0] = width;
+ this.currentAppletSize[1] = height;
+ {
+ URL _baseURL = null;
+ String att = getParameter("codebase");
+ if (att != null) {
+ if (!att.endsWith("/")) {
+ att += "/";
+ }
+ try {
+ _baseURL = new URL(documentURL, att);
+ } catch (MalformedURLException e) {
+ }
+ }
+ if (_baseURL == null) {
+ String file = documentURL.getFile();
+ int i = file.lastIndexOf('/');
+ if (i >= 0 && i < file.length() - 1) {
+ try {
+ _baseURL = new URL(documentURL, file.substring(0, i + 1));
+ } catch (MalformedURLException e) {
+ }
+ }
+ }
+
+ // when all is said & done, baseURL shouldn't be null
+ if (_baseURL == null) {
+ _baseURL = documentURL;
+ }
+ baseURL = _baseURL;
+ }
+ this.parentWindow = new MyNativeWindow(nativeWindowHandle);
+ }
+ class MyNativeWindow implements NativeWindowUpstream {
+ final long handle;
+
+ MyNativeWindow(long nativeWindowHandle) {
+ handle = nativeWindowHandle;
+ }
+
+ @Override
+ public final int getWidth() {
+ return Applet3Panel.this.getWidth();
+ }
+
+ @Override
+ public final int getHeight() {
+ return Applet3Panel.this.getHeight();
+ }
+
+ @Override
+ public final long getWindowHandle() {
+ return this.handle;
+ }
+
+ @Override
+ public final String getDisplayConnection() {
+ return null; // FIXME: Using default for now
+ }
+
+ @Override
+ public final int getScreenIndex() {
+ return 0; // FIXME: Using default for now
+ }
+
+ @Override
+ public final void notifySurfaceUpdated(NativeWindowDownstream swappedWin) {
+ // TODO: May hook for composite extension
+ }
+ };
+
+ public void destroy(boolean notifyApplet, boolean notifyUser) throws java.security.AccessControlException {
+ try {
+ if( notifyApplet && null != applet ) {
+ applet.destroy();
+ }
+ } catch (java.security.AccessControlException e) {
+ setExceptionStatus(e);
+ // rethrow exception to be handled as it normally would be.
+ throw e;
+ } finally {
+ if( null != appletWindow ) {
+ try {
+ appletWindow.destroy();
+ } catch( Throwable t ) {
+ t.printStackTrace();
+ }
+ if( null != applet3Loader ) {
+ applet3Loader.getAppContext().unregisterAppletWindow(appletWindow);
+ }
+ appletWindow = null;
+ }
+ }
+ if( notifyUser ) {
+ showAppletStatus("destroyed");
+ }
+ }
+
+ public NativeWindowDownstream getAppletWindow() {
+ return appletWindow;
+ }
+ public MyNativeWindow getBrowserWindow() {
+ return parentWindow;
+ }
+
+ public int getStatus() { return status; }
+
+ /**
+ * Must be override with upstream plugin implementation, i.e. browser connection.
+ * Known implementations are {@link jogamp.plugin.applet.PluginApplet3Viewer} !
+ */
+ protected abstract Applet3Context getAppletContext();
+
+ //
+ // Applet3Context
+ //
+
+ @Override
+ public final Applet3 getApplet() {
+ return applet;
+ }
+
+ @Override
+ public final String getAppletName() {
+ return getParameter("name");
+ }
+
+ @Override
+ public final boolean isActive() {
+ return status == APPLET_START;
+ }
+
+ @Override
+ public final String getParameter(String name) {
+ return parameters.get(name.toLowerCase());
+ }
+
+ public String getDefaulted(String key, String defaultStr) {
+ final String value = getParameter(key);
+ return (value != null) ? value : defaultStr;
+ }
+
+ @Override
+ public final URL getDocumentBase() { // TODO
+ return documentURL;
+ }
+
+ @Override
+ public final URL getCodeBase() {
+ return baseURL;
+ }
+
+ public void updateSizeInParameters(int width, int height) {
+ parameters.put("width", Integer.toString(width));
+ parameters.put("height", Integer.toString(height));
+ }
+
+ @Override
+ public void resize(int width, int height) {
+ appletResize(width, height);
+ }
+
+ /**
+ * Called when the applet wants to be resized.
+ *
+ * @param width the new requested width for the applet.
+ * @param height the new requested height for the applet.
+ */
+ public void appletResize(int width, int height) {
+ updateSizeInParameters(width, height);
+ currentAppletSize[0] = width;
+ currentAppletSize[1] = height;
+ if( null != appletWindow ) {
+ appletWindow.setSize(width, height);
+ }
+ /** FIXME
+ if(loader != null) {
+ App3Context appCtxt = loader.getAppContext();
+ if(appCtxt != null)
+ appEvtQ = (java.awt.EventQueue)appCtxt.get(App3Context.EVENT_QUEUE_KEY);
+ }
+
+ final Applet3Panel ap = this;
+ if (appEvtQ != null){
+ appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
+ new Runnable(){
+ public void run(){
+ if(ap != null)
+ {
+ ap.dispatchAppletEvent(APPLET_RESIZE, currentSize);
+ }
+ }
+ }));
+ } */
+ }
+
+ @Override
+ public final Applet3Context getAppletContext(String name) {
+ return getAppletContext().getAppletContext(name);
+ }
+
+ @Override
+ public final Enumeration<Applet3Context> getAllAppletContexts() {
+ return getAppletContext().getAllAppletContexts();
+ }
+
+ @Override
+ public final void showDocument(URL url) {
+ getAppletContext().showDocument(url);
+ }
+
+ @Override
+ public final void showDocument(URL url, String target) {
+ getAppletContext().showDocument(url, target);
+ }
+
+ @Override
+ public final void showStatus(String status) {
+ getAppletContext().showStatus(status);
+ }
+
+ @Override
+ public final void setStream(String key, InputStream stream)throws IOException {
+ getAppletContext().setStream(key, stream);
+ }
+
+ @Override
+ public final InputStream getStream(String key) {
+ return getAppletContext().getStream(key);
+ }
+
+ @Override
+ public final Iterator<String> getStreamKeys() {
+ return getAppletContext().getStreamKeys();
+ }
+
+ //
+ // Internal Impl.
+ //
+
+ /**
+ * Get the code parameter
+ */
+ public String getCode() {
+ return getParameter("code");
+ }
+
+ public String getIsApplet3() {
+ return getDefaulted("is_applet3", "false");
+ }
+
+ /**
+ * Return the list of jar files if specified.
+ * Otherwise return null.
+ */
+ public String getJarFiles() {
+ return getParameter("archive");
+ }
+
+ /**
+ * Return the value of the object param
+ */
+ public String getSerializedObject() {
+ return getParameter("object");// another name?
+ }
+
+
+ public static void debug(String s) { // FIXME visibility ?
+ if(debug)
+ System.err.println("AppletViewerPanel:::" + s);
+ }
+
+ static void debug(String s, Throwable t) {
+ if(debug) {
+ t.printStackTrace();
+ debug(s);
+ }
+ }
+
+ /**
+ * Get the width.
+ */
+ public final int getWidth() {
+ return currentAppletSize[0];
+ }
+
+
+ /**
+ * Get the height.
+ */
+ public final int getHeight() {
+ return currentAppletSize[1];
+ }
+
+ /**
+ * Get initial_focus
+ */
+ public boolean hasInitialFocus() {
+
+ // 6234219: Do not set initial focus on an applet
+ // during startup if applet is targeted for
+ // JDK 1.1/1.2. [stanley.ho]
+ if (isJDK11Applet() || isJDK12Applet())
+ return false;
+
+ String initialFocus = getParameter("initial_focus");
+
+ if (initialFocus != null)
+ {
+ if (initialFocus.toLowerCase().equals("false"))
+ return false;
+ }
+
+ return true;
+ }
+
+ protected void setupAppletAppContext() {
+ // do nothing
+ }
+
+ /*
+ * Creates a thread to run the applet. This method is called
+ * each time an applet is loaded and reloaded.
+ */
+ public synchronized void createAppletThread() {
+ // Create a thread group for the applet, and start a new
+ // thread to load the applet.
+ String nm = "applet-" + getCode();
+ loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ applet3Loader.grab(); // Keep this puppy around!
+
+ // 4668479: Option to turn off codebase lookup in AppletClassLoader
+ // during resource requests. [stanley.ho]
+ String param = getParameter("codebase_lookup");
+
+ if (param != null && param.equals("false"))
+ applet3Loader.setCodebaseLookup(false);
+ else
+ applet3Loader.setCodebaseLookup(true);
+
+
+ ThreadGroup appletGroup = applet3Loader.getThreadGroup();
+
+ handler = new Thread(appletGroup, this, "thread " + nm);
+ // set the context class loader for this thread
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ handler.setContextClassLoader(loader);
+ return null;
+ }
+ });
+ handler.start();
+ }
+
+ public void joinAppletThread() throws InterruptedException {
+ if (handler != null) {
+ handler.join();
+ handler = null;
+ }
+ }
+
+ public void release() {
+ if( null != applet3Loader ) {
+ applet3Loader.release();
+ applet3Loader = null;
+ }
+ loader = null;
+ }
+
+ /**
+ * Construct an applet viewer and start the applet.
+ */
+ public void init() {
+ try {
+ // Get the width (if any)
+ final int width = getWidth();
+ defaultAppletSize[0] = width;
+ currentAppletSize[0] = width;
+
+ // Get the height (if any)
+ final int height = getHeight();
+ defaultAppletSize[1] = height;
+ currentAppletSize[1] = height;
+
+ } catch (NumberFormatException e) {
+ // Turn on the error flag and let TagAppletPanel
+ // do the right thing.
+ status = APPLET_ERROR;
+ showAppletStatus("badattribute.exception");
+ showAppletLog("badattribute.exception");
+ showAppletException(e);
+ }
+
+ createAppletThread();
+ }
+
+ private AppletListener listeners;
+
+ /**
+ * AppletEvent Queue
+ */
+ @SuppressWarnings("rawtypes")
+ private Queue queue = null;
+
+
+ synchronized public void addAppletListener(AppletListener l) {
+ listeners = AppletEventMulticaster.add(listeners, l);
+ }
+
+ synchronized public void removeAppletListener(AppletListener l) {
+ listeners = AppletEventMulticaster.remove(listeners, l);
+ }
+
+ /**
+ * Dispatch event to the listeners..
+ */
+ public void dispatchAppletEvent(int id, Object argument) {
+ //System.out.println("SEND= " + id);
+ if (listeners != null) {
+ AppletEvent evt = new AppletEvent(this, id, argument);
+ listeners.appletStateChanged(evt);
+ }
+ }
+
+ /**
+ * Send an event. Queue it for execution by the handler thread.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void sendEvent(int id) {
+ synchronized(this) {
+ if (queue == null) {
+ //System.out.println("SEND0= " + id);
+ queue = new Queue();
+ }
+ Integer eventId = Integer.valueOf(id);
+ queue.enqueue(eventId);
+ notifyAll();
+ }
+ if (id == APPLET_QUIT) {
+ try {
+ joinAppletThread(); // Let the applet event handler exit
+ } catch (InterruptedException e) {
+ }
+
+ // AppletClassLoader.release() must be called by a Thread
+ // not within the applet's ThreadGroup
+ if (loader == null) {
+ // FIXME loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ setClassLoader();
+ }
+ release();
+ }
+ }
+
+ /**
+ * Get an event from the queue.
+ */
+ synchronized AppletEvent getNextEvent() throws InterruptedException {
+ while (queue == null || queue.isEmpty()) {
+ wait();
+ }
+ Integer eventId = (Integer)queue.dequeue();
+ return new AppletEvent(this, eventId.intValue(), null);
+ }
+
+ public boolean emptyEventQueue() {
+ if ((queue == null) || (queue.isEmpty()))
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * This kludge is specific to get over AccessControlException thrown during
+ * Applet.stop() or destroy() when static thread is suspended. Set a flag
+ * in AppletClassLoader to indicate that an
+ * AccessControlException for RuntimePermission "modifyThread" or
+ * "modifyThreadGroup" had occurred.
+ */
+ private void setExceptionStatus(AccessControlException e) {
+ Permission p = e.getPermission();
+ if (p instanceof RuntimePermission) {
+ if (p.getName().startsWith("modifyThread")) {
+ if (loader == null) {
+ // FIXME loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ setClassLoader();
+ }
+ if( null != applet3Loader ) {
+ applet3Loader.setExceptionStatus();
+ }
+ }
+ }
+ }
+
+ /**
+ * Execute applet events.
+ * Here is the state transition diagram
+ *
+ * Note: (XXX) is the action
+ * APPLET_XXX is the state
+ * (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- (
+ * applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet
+ * destroyed called) --> APPLET_DESTROY -->(applet gets disposed) -->
+ * APPLET_DISPOSE -->....
+ *
+ * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays
+ * in the APPLET_START state unless the applet goes away(refresh page or leave the page).
+ * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet
+ * is revisited, it will call applet start method and enter the APPLET_START state and stay there.
+ *
+ * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle
+ * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP
+ * state and then applet destroyed method gets called and enters APPLET_DESTROY state.
+ *
+ * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from
+ * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT .
+ *
+ * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case).
+ * Same as APPLET_LOAD to
+ * APPLET_DISPOSE since all of this are triggered by browser.
+ *
+ *
+ */
+ @Override
+ public void run() {
+
+ Thread curThread = Thread.currentThread();
+ if (curThread == loaderThread) {
+ // if we are in the loader thread, cause
+ // loading to occur. We may exit this with
+ // status being APPLET_DISPOSE, APPLET_ERROR,
+ // or APPLET_LOAD
+ runLoader();
+ return;
+ }
+
+ boolean disposed = false;
+ while (!disposed && !curThread.isInterrupted()) {
+ AppletEvent evt;
+ try {
+ evt = getNextEvent();
+ } catch (InterruptedException e) {
+ showAppletStatus("bail");
+ return;
+ }
+
+ //showAppletStatus("EVENT = " + evt.getID());
+ try {
+ switch (evt.getID()) {
+ case APPLET_LOAD:
+ if (!okToLoad()) {
+ break;
+ }
+ // This complexity allows loading of applets to be
+ // interruptable. The actual thread loading runs
+ // in a separate thread, so it can be interrupted
+ // without harming the applet thread.
+ // So that we don't have to worry about
+ // concurrency issues, the main applet thread waits
+ // until the loader thread terminates.
+ // (one way or another).
+ if (loaderThread == null) {
+ // REMIND: do we want a name?
+ //System.out.println("------------------- loading applet");
+ setLoaderThread(new Thread(this));
+ loaderThread.start();
+ // we get to go to sleep while this runs
+ loaderThread.join();
+ setLoaderThread(null);
+ } else {
+ // REMIND: issue an error -- this case should never
+ // occur.
+ }
+ break;
+
+ case APPLET_INIT:
+ // AppletViewer "Restart" will jump from destroy method to
+ // init, that is why we need to check status w/ APPLET_DESTROY
+ if (status != APPLET_LOAD && status != APPLET_DESTROY) {
+ showAppletStatus("notloaded");
+ break;
+ }
+ if( null == appletWindow ) {
+ appletWindow = applet.createNativeWindow(this, parentWindow);
+ if( parentWindow != appletWindow.getParent() ) {
+ throw new IllegalArgumentException("Applet's parent doesn't match!");
+ }
+ // FIXME loader.getAppContext().registerAppletWindow(appletWindow);
+ } else {
+ appletWindow.setSize(defaultAppletSize[0], defaultAppletSize[1]);
+ }
+ if (doInit) {
+ if (PerformanceLogger.loggingEnabled()) {
+ PerformanceLogger.setTime("Applet Init");
+ PerformanceLogger.outputLog();
+ }
+ applet.init(this);
+ }
+
+ doInit = true; // allow restarts
+
+ status = APPLET_INIT;
+ showAppletStatus("inited");
+ break;
+
+ case APPLET_START:
+ {
+ if (status != APPLET_INIT && status != APPLET_STOP) {
+ showAppletStatus("notinited");
+ break;
+ }
+ appletWindow.setSize(currentAppletSize[0], currentAppletSize[1]);
+ appletWindow.setVisible(true);
+ // Fix for BugTraq ID 4041703.
+ // Set the default focus for an applet.
+ if (hasInitialFocus()) {
+ appletWindow.requestFocus();
+ }
+ applet.start();
+
+ status = APPLET_START;
+ showAppletStatus("started");
+ break;
+ }
+
+ case APPLET_STOP:
+ if (status != APPLET_START) {
+ showAppletStatus("notstarted");
+ break;
+ }
+ status = APPLET_STOP;
+
+ // During Applet.stop(), any AccessControlException on an involved Class remains in
+ // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is
+ // reused, the same exception will occur during class loading. Set the AppletClassLoader's
+ // exceptionStatusSet flag to allow recognition of what had happened
+ // when reusing AppletClassLoader object.
+ try {
+ applet.stop();
+ } catch (java.security.AccessControlException e) {
+ setExceptionStatus(e);
+ // rethrow exception to be handled as it normally would be.
+ throw e;
+ } finally {
+ try {
+ appletWindow.setVisible(false);
+ } catch( Throwable t ) {
+ t.printStackTrace();
+ }
+ }
+ showAppletStatus("stopped");
+ break;
+
+ case APPLET_DESTROY:
+ if (status != APPLET_STOP && status != APPLET_INIT) {
+ showAppletStatus("notstopped");
+ break;
+ }
+ status = APPLET_DESTROY;
+
+ // During Applet.destroy(), any AccessControlException on an involved Class remains in
+ // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is
+ // reused, the same exception will occur during class loading. Set the AppletClassLoader's
+ // exceptionStatusSet flag to allow recognition of what had happened
+ // when reusing AppletClassLoader object.
+ destroy(true, true);
+ break;
+
+ case APPLET_DISPOSE:
+ if (status != APPLET_DESTROY && status != APPLET_LOAD) {
+ showAppletStatus("notdestroyed");
+ break;
+ }
+ status = APPLET_DISPOSE;
+
+ applet = null;
+ showAppletStatus("disposed");
+ disposed = true;
+ break;
+
+ case APPLET_QUIT:
+ return;
+ }
+ } catch (Exception e) {
+ status = APPLET_ERROR;
+ if (e.getMessage() != null) {
+ showAppletStatus("exception2", e.getClass().getName(),
+ e.getMessage());
+ } else {
+ showAppletStatus("exception", e.getClass().getName());
+ }
+ showAppletException(e);
+ } catch (ThreadDeath e) {
+ showAppletStatus("death");
+ return;
+ } catch (Error e) {
+ status = APPLET_ERROR;
+ if (e.getMessage() != null) {
+ showAppletStatus("error2", e.getClass().getName(),
+ e.getMessage());
+ } else {
+ showAppletStatus("error", e.getClass().getName());
+ }
+ showAppletException(e);
+ }
+ clearLoadAbortRequest();
+ }
+ }
+
+ /**
+ * Load the applet into memory.
+ * Runs in a seperate (and interruptible) thread from the rest of the
+ * applet event processing so that it can be gracefully interrupted from
+ * things like HotJava.
+ */
+ protected void runLoader() {
+ if (status != APPLET_DISPOSE) {
+ showAppletStatus("notdisposed");
+ return;
+ }
+
+ dispatchAppletEvent(APPLET_LOADING, null);
+
+ // REMIND -- might be cool to visually indicate loading here --
+ // maybe do animation?
+ status = APPLET_LOAD;
+
+ // Create a class loader
+ // FIXME loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ setClassLoader();
+
+ // Load the archives if present.
+ // REMIND - this probably should be done in a separate thread,
+ // or at least the additional archives (epll).
+
+ String code = getCode();
+
+ // setup applet AppContext
+ // this must be called before loadJarFiles
+ setupAppletAppContext();
+
+ try {
+ loadJarFiles(applet3Loader);
+ applet = createApplet(applet3Loader);
+ } catch (ClassNotFoundException e) {
+ status = APPLET_ERROR;
+ showAppletStatus("notfound", code);
+ showAppletLog("notfound", code);
+ showAppletException(e);
+ return;
+ } catch (InstantiationException e) {
+ status = APPLET_ERROR;
+ showAppletStatus("nocreate", code);
+ showAppletLog("nocreate", code);
+ showAppletException(e);
+ return;
+ } catch (IllegalAccessException e) {
+ status = APPLET_ERROR;
+ showAppletStatus("noconstruct", code);
+ showAppletLog("noconstruct", code);
+ showAppletException(e);
+ // sbb -- I added a return here
+ return;
+ } catch (Exception e) {
+ status = APPLET_ERROR;
+ showAppletStatus("exception", e.getMessage());
+ showAppletException(e);
+ return;
+ } catch (ThreadDeath e) {
+ status = APPLET_ERROR;
+ showAppletStatus("death");
+ return;
+ } catch (Error e) {
+ status = APPLET_ERROR;
+ showAppletStatus("error", e.getMessage());
+ showAppletException(e);
+ return;
+ } finally {
+ // notify that loading is no longer going on
+ dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
+ }
+
+ // Fixed #4508194: NullPointerException thrown during
+ // quick page switch
+ //
+ if (applet != null)
+ {
+ // Stick it in the frame
+ showAppletStatus("loaded");
+ }
+ }
+
+ private Applet3 createApplet(final Applet3ClassLoader loader) throws ClassNotFoundException,
+ IllegalAccessException, IOException, InstantiationException, InterruptedException {
+ final String serName = getSerializedObject();
+ String code = getCode();
+
+ if (code != null && serName != null) {
+ System.err.println(amh.getMessage("runloader.err"));
+// return null;
+ throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both.");
+ }
+ if (code == null && serName == null) {
+ String msg = "nocode";
+ status = APPLET_ERROR;
+ showAppletStatus(msg);
+ showAppletLog(msg);
+ }
+ if (code != null) {
+ applet = (Applet3)loader.loadCode(code).newInstance();
+ doInit = true;
+ } else {
+ // serName is not null;
+ InputStream is = null;
+ ObjectInputStream ois = null;
+ try {
+ is = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+ @Override
+ public InputStream run() {
+ return loader.getResourceAsStream(serName);
+ } } );
+ handler.setContextClassLoader(loader);
+ ois = new Applet3ObjectInputStream(is, loader);
+ applet = (Applet3) ois.readObject();
+ } finally {
+ if(null != ois) {
+ ois.close();
+ }
+ if(null != is) {
+ is.close();
+ }
+ }
+ doInit = false; // skip over the first init
+ /**
+ try (InputStream is = AccessController.doPrivileged(
+ (PrivilegedAction<InputStream>)() -> loader.getResourceAsStream(serName));
+ ObjectInputStream ois = new AppletObjectInputStream(is, loader)) {
+
+ applet = (Applet3) ois.readObject();
+ doInit = false; // skip over the first init
+ } */
+ }
+
+ // Determine the JDK level that the applet targets.
+ // This is critical for enabling certain backward
+ // compatibility switch if an applet is a JDK 1.1
+ // applet. [stanley.ho]
+ findAppletJDKLevel(applet);
+
+ if (Thread.interrupted()) {
+ try {
+ status = APPLET_DISPOSE; // APPLET_ERROR?
+ applet = null;
+ // REMIND: This may not be exactly the right thing: the
+ // status is set by the stop button and not necessarily
+ // here.
+ showAppletStatus("death");
+ } finally {
+ Thread.currentThread().interrupt(); // resignal interrupt
+ }
+ return null;
+ }
+ return applet;
+ }
+
+ protected void loadJarFiles(Applet3ClassLoader loader) throws IOException,
+ InterruptedException {
+ // Load the archives if present.
+ // REMIND - this probably should be done in a separate thread,
+ // or at least the additional archives (epll).
+ String jarFiles = getJarFiles();
+
+ if (jarFiles != null) {
+ StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
+ while(st.hasMoreTokens()) {
+ String tok = st.nextToken().trim();
+ try {
+ loader.addJar(tok);
+ } catch (IllegalArgumentException e) {
+ // bad archive name
+ continue;
+ }
+ }
+ }
+ }
+
+ /**
+ * Request that the loading of the applet be stopped.
+ */
+ protected synchronized void stopLoading() {
+ // REMIND: fill in the body
+ if (loaderThread != null) {
+ //System.out.println("Interrupting applet loader thread: " + loaderThread);
+ loaderThread.interrupt();
+ } else {
+ setLoadAbortRequest();
+ }
+ }
+
+
+ protected synchronized boolean okToLoad() {
+ return !loadAbortRequest;
+ }
+
+ protected synchronized void clearLoadAbortRequest() {
+ loadAbortRequest = false;
+ }
+
+ protected synchronized void setLoadAbortRequest() {
+ loadAbortRequest = true;
+ }
+
+
+ private synchronized void setLoaderThread(Thread loaderThread) {
+ this.loaderThread = loaderThread;
+ }
+
+
+ // FIXME private EventQueue appEvtQ = null;
+
+ /**
+ * Status line. Called by the AppletPanel to provide
+ * feedback on the Applet's state.
+ */
+ protected final void showAppletStatus(String status) {
+ showStatus(amh.getMessage(status));
+ }
+
+ protected final void showAppletStatus(String status, Object arg) {
+ showStatus(amh.getMessage(status, arg));
+ }
+ protected final void showAppletStatus(String status, Object arg1, Object arg2) {
+ showStatus(amh.getMessage(status, arg1, arg2));
+ }
+
+ /**
+ * Called by the AppletPanel to print to the log.
+ */
+ protected void showAppletLog(String msg) {
+ System.out.println(amh.getMessage(msg));
+ }
+
+ protected void showAppletLog(String msg, Object arg) {
+ System.out.println(amh.getMessage(msg, arg));
+ }
+
+ /**
+ * Called by the AppletPanel to provide
+ * feedback when an exception has happened.
+ */
+ protected void showAppletException(Throwable t) {
+ t.printStackTrace();
+ }
+
+ /**
+ * Get caching key for classloader cache
+ */
+ public String getClassLoaderCacheKey()
+ {
+ /**
+ * Fixed #4501142: Classlaoder sharing policy doesn't
+ * take "archive" into account. This will be overridden
+ * by Java Plug-in. [stanleyh]
+ */
+ return getCodeBase().toString();
+ }
+
+ /**
+ * The class loaders
+ */
+ private static final HashMap<String, Applet3ClassLoader> classloaders = new HashMap<String, Applet3ClassLoader>();
+
+ /**
+ * Flush a class loader.
+ */
+ public static synchronized void flushClassLoader(String key) {
+ classloaders.remove(key);
+ }
+
+ /**
+ * Flush all class loaders.
+ */
+ public static synchronized void flushClassLoaders() {
+ classloaders.clear();
+ }
+
+ /**
+ * FIXME: Hack - Until knowing which CL to use .. via {@link #getClassLoader(URL, String)}.
+ */
+ private void setClassLoader() {
+ final ClassLoader _loader0 = getAppletClassLoader();
+ if( null != _loader0 ) {
+ loader = _loader0;
+ applet3Loader = null;
+ }
+ final ClassLoader _loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ if( _loader instanceof Applet3ClassLoader ) {
+ applet3Loader = (Applet3ClassLoader)_loader;
+ } else {
+ applet3Loader = null;
+ }
+ loader = _loader;
+ }
+
+ /**
+ * Implementation may provide it's own specific ClassLoader, otherwise null.
+ */
+ public abstract ClassLoader getAppletClassLoader();
+
+ /**
+ * This method actually creates an AppletClassLoader.
+ *
+ * It can be override by subclasses (such as the Plug-in)
+ * to provide different classloaders.
+ */
+ protected Applet3ClassLoader createClassLoader(final URL codebase) {
+ return new Applet3ClassLoader(codebase);
+ }
+
+ /**
+ * Get a class loader. Create in a restricted context
+ */
+ synchronized ClassLoader getClassLoader(final URL codebase, final String key) {
+ Applet3ClassLoader c = classloaders.get(key);
+ if (c == null) {
+ final AccessControlContext acc = getAccessControlContext(codebase);
+ c = AccessController.doPrivileged(new PrivilegedAction<Applet3ClassLoader>() {
+ @Override
+ public Applet3ClassLoader run() {
+ Applet3ClassLoader ac = createClassLoader(codebase);
+ /* Should the creation of the classloader be
+ * within the class synchronized block? Since
+ * this class is used by the plugin, take care
+ * to avoid deadlocks, or specialize
+ * AppletPanel within the plugin. It may take
+ * an arbitrary amount of time to create a
+ * class loader (involving getting Jar files
+ * etc.) and may block unrelated applets from
+ * finishing createAppletThread (due to the
+ * class synchronization). If
+ * createAppletThread does not finish quickly,
+ * the applet cannot process other messages,
+ * particularly messages such as destroy
+ * (which timeout when called from the browser).
+ */
+ synchronized (getClass()) {
+ Applet3ClassLoader res = classloaders.get(key);
+ if (res == null) {
+ classloaders.put(key, ac);
+ return ac;
+ } else {
+ return res;
+ }
+ }
+ }
+ },acc);
+ }
+ return c;
+ }
+
+ /**
+ * get the context for the AppletClassLoader we are creating.
+ * the context is granted permission to create the class loader,
+ * connnect to the codebase, and whatever else the policy grants
+ * to all codebases.
+ */
+ private AccessControlContext getAccessControlContext(final URL codebase) {
+
+ PermissionCollection perms =
+ AccessController.doPrivileged(new PrivilegedAction<PermissionCollection>() {
+ @Override
+ public PermissionCollection run() {
+ Policy p = java.security.Policy.getPolicy();
+ if (p != null) {
+ return p.getPermissions(new CodeSource(null,
+ (java.security.cert.Certificate[]) null));
+ } else {
+ return null;
+ }
+ }
+ });
+
+ if (perms == null)
+ perms = new Permissions();
+
+ //XXX: this is needed to be able to create the classloader itself!
+
+ perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
+
+ Permission p;
+ java.net.URLConnection urlConnection = null;
+ try {
+ urlConnection = codebase.openConnection();
+ p = urlConnection.getPermission();
+ } catch (java.io.IOException ioe) {
+ p = null;
+ }
+
+ if (p != null)
+ perms.add(p);
+
+ if (p instanceof FilePermission) {
+
+ String path = p.getName();
+
+ int endIndex = path.lastIndexOf(File.separatorChar);
+
+ if (endIndex != -1) {
+ path = path.substring(0, endIndex+1);
+
+ if (path.endsWith(File.separator)) {
+ path += "-";
+ }
+ perms.add(new FilePermission(path,
+ SecurityConstants.FILE_READ_ACTION));
+ }
+ } else {
+ URL locUrl = codebase;
+ if (urlConnection instanceof JarURLConnection) {
+ locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
+ }
+ String host = locUrl.getHost();
+ if (host != null && (host.length() > 0))
+ perms.add(new SocketPermission(host,
+ SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
+ }
+
+ ProtectionDomain domain =
+ new ProtectionDomain(new CodeSource(codebase,
+ (java.security.cert.Certificate[]) null), perms);
+ AccessControlContext acc =
+ new AccessControlContext(new ProtectionDomain[] { domain });
+
+ return acc;
+ }
+
+ public Thread getAppletHandlerThread() {
+ return handler;
+ }
+
+ public int getAppletWidth() {
+ return currentAppletSize[0];
+ }
+
+ public int getAppletHeight() {
+ return currentAppletSize[1];
+ }
+
+ public static void changeFrameAppContext(NativeWindowDownstream nw, App3Context newAppContext) // FIXME
+ {
+ // Fixed #4754451: Applet can have methods running on main
+ // thread event queue.
+ //
+ // The cause of this bug is that the frame of the applet
+ // is created in main thread group. Thus, when certain
+ // AWT/Swing events are generated, the events will be
+ // dispatched through the wrong event dispatch thread.
+ //
+ // To fix this, we rearrange the AppContext with the frame,
+ // so the proper event queue will be looked up.
+ //
+ // Swing also maintains a Frame list for the AppContext,
+ // so we will have to rearrange it as well.
+
+ // Check if frame's AppContext has already been set properly
+ App3Context oldAppContext = App3Context.getAppContext(nw);
+
+ if (oldAppContext == newAppContext) {
+ return;
+ }
+
+ // Synchronization on Window.class is needed for locking the
+ // critical section of the window list in AppContext.
+ synchronized (NativeWindowDownstream.class)
+ {
+ WeakReference weakRef = null;
+ // Remove frame from the Window list in wrong AppContext
+ if( null != oldAppContext ) {
+ // Lookup current frame's AppContext
+ Vector<WeakReference<NativeWindowDownstream>> windowList = (Vector<WeakReference<NativeWindowDownstream>>)oldAppContext.get(NativeWindowDownstream.class);
+ if (windowList != null) {
+ for (WeakReference ref : windowList) {
+ if (ref.get() == nw) {
+ weakRef = ref;
+ break;
+ }
+ }
+ // Remove frame from wrong AppContext
+ if (weakRef != null)
+ windowList.remove(weakRef);
+ }
+ } else {
+ weakRef = new WeakReference<NativeWindowDownstream>(nw);
+ }
+
+ // Put the frame into the applet's AppContext map
+ // FIXME SunToolkit.insertTargetMapping(frame, newAppContext);
+
+ // Insert frame into the Window list in the applet's AppContext map
+ {
+ Vector<WeakReference<NativeWindowDownstream>> windowList = (Vector)newAppContext.get(NativeWindowDownstream.class);
+ if (windowList == null) {
+ windowList = new Vector<WeakReference<NativeWindowDownstream>>();
+ newAppContext.put(NativeWindowDownstream.class, windowList);
+ }
+ // use the same weakRef here as it is used elsewhere
+ windowList.add(weakRef);
+ }
+ }
+ }
+
+ // Flag to indicate if applet is targeted for JDK 1.1.
+ private boolean jdk11Applet = false;
+
+ // Flag to indicate if applet is targeted for JDK 1.2.
+ private boolean jdk12Applet = false;
+
+ /**
+ * Determine JDK level of an applet.
+ */
+ private void findAppletJDKLevel(Applet3 applet)
+ {
+ // To determine the JDK level of an applet, the
+ // most reliable way is to check the major version
+ // of the applet class file.
+
+ // synchronized on applet class object, so calling from
+ // different instances of the same applet will be
+ // serialized.
+ Class<?> appletClass = applet.getClass();
+
+ synchronized(appletClass) {
+ // Determine if the JDK level of an applet has been
+ // checked before.
+ Boolean jdk11Target = applet3Loader.isJDK11Target(appletClass);
+ Boolean jdk12Target = applet3Loader.isJDK12Target(appletClass);
+
+ // if applet JDK level has been checked before, retrieve
+ // value and return.
+ if (jdk11Target != null || jdk12Target != null) {
+ jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
+ jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
+ return;
+ }
+
+ String name = appletClass.getName();
+
+ // first convert any '.' to '/'
+ name = name.replace('.', '/');
+
+ // append .class
+ final String resourceName = name + ".class";
+
+ byte[] classHeader = new byte[8];
+
+ InputStream is = null;
+ try {
+ is = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+ @Override
+ public InputStream run() {
+ return loader.getResourceAsStream(resourceName);
+ } } );
+ // Read the first 8 bytes of the class file
+ int byteRead = is.read(classHeader, 0, 8);
+
+ // return if the header is not read in entirely
+ // for some reasons.
+ if (byteRead != 8)
+ return;
+ } catch (IOException e) {
+ return;
+ } finally {
+ if(null != is) {
+ try {
+ is.close();
+ } catch (IOException e) { }
+ }
+ }
+
+ // Check major version in class file header
+ int major_version = readShort(classHeader, 6);
+
+ // Major version in class file is as follows:
+ // 45 - JDK 1.1
+ // 46 - JDK 1.2
+ // 47 - JDK 1.3
+ // 48 - JDK 1.4
+ // 49 - JDK 1.5
+ if (major_version < 46)
+ jdk11Applet = true;
+ else if (major_version == 46)
+ jdk12Applet = true;
+
+ // Store applet JDK level in AppContext for later lookup,
+ // e.g. page switch.
+ applet3Loader.setJDK11Target(appletClass, jdk11Applet);
+ applet3Loader.setJDK12Target(appletClass, jdk12Applet);
+ }
+ }
+
+ /**
+ * Return true if applet is targeted to JDK 1.1.
+ */
+ protected boolean isJDK11Applet() {
+ return jdk11Applet;
+ }
+
+ /**
+ * Return true if applet is targeted to JDK1.2.
+ */
+ protected boolean isJDK12Applet() {
+ return jdk12Applet;
+ }
+
+ /**
+ * Read short from byte array.
+ */
+ private int readShort(byte[] b, int off) {
+ int hi = readByte(b[off]);
+ int lo = readByte(b[off + 1]);
+ return (hi << 8) | lo;
+ }
+
+ private int readByte(byte b) {
+ return (b) & 0xFF;
+ }
+
+
+ private static Applet3MessageHandler amh = new Applet3MessageHandler("appletpanel");
+}
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();
+ // }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/Launcher.java b/netx/net/sourceforge/jnlp/Launcher.java
index 6e07b22..6076267 100644
--- a/netx/net/sourceforge/jnlp/Launcher.java
+++ b/netx/net/sourceforge/jnlp/Launcher.java
@@ -31,8 +31,8 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import net.sourceforge.jnlp.util.JarFile;
+import net.sourceforge.jnlp.util.JarFile;
import net.sourceforge.jnlp.cache.CacheUtil;
import net.sourceforge.jnlp.cache.UpdatePolicy;
import net.sourceforge.jnlp.runtime.AppletInstance;
@@ -44,9 +44,10 @@ import net.sourceforge.jnlp.services.ServiceUtil;
import javax.swing.SwingUtilities;
import javax.swing.text.html.parser.ParserDelegator;
+
import net.sourceforge.jnlp.splashscreen.SplashUtils;
import net.sourceforge.jnlp.util.logging.OutputController;
-
+import sun.awt.AppContext;
import sun.awt.SunToolkit;
/**
@@ -67,6 +68,8 @@ public class Launcher {
/** shared thread group */
/*package*/static final ThreadGroup mainGroup = new ThreadGroup(R("LAllThreadGroup"));
+
+ public static ThreadGroup getMainGroup() { return mainGroup; }
/** the handler */
private LaunchHandler handler = null;
@@ -521,12 +524,16 @@ public class Launcher {
// 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);
+ final JARDesc mainJarDesc = file.getResources().getMainJAR();
+ final File f = CacheUtil.getCacheFile(mainJarDesc.getLocation(), null);
if (f != null) {
- JarFile mainJar = new JarFile(f);
- mainName = mainJar.getManifest().
- getMainAttributes().getValue("Main-Class");
+ final JarFile mainJar = new JarFile(f);
+ try {
+ mainName = mainJar.getManifest().
+ getMainAttributes().getValue("Main-Class");
+ } finally {
+ mainJar.close();
+ }
}
}
@@ -767,7 +774,7 @@ public class Launcher {
JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
ThreadGroup group = Thread.currentThread().getThreadGroup();
- ApplicationInstance app = new ApplicationInstance(file, group, loader);
+ ApplicationInstance app = new ApplicationInstance(file, group, loader, AppContext.getAppContext());
loader.setApplication(app);
return app;
diff --git a/netx/net/sourceforge/jnlp/PluginParameters.java b/netx/net/sourceforge/jnlp/PluginParameters.java
index fa4e8fa..c3a958c 100644
--- a/netx/net/sourceforge/jnlp/PluginParameters.java
+++ b/netx/net/sourceforge/jnlp/PluginParameters.java
@@ -80,7 +80,7 @@ public class PluginParameters {
*
* @return the underlying hashtable.
*/
- Hashtable<String, String> getUnderlyingHashtable() {
+ public Hashtable<String, String> getUnderlyingHashtable() {
return parameters;
}
@@ -134,6 +134,10 @@ public class PluginParameters {
return getDefaulted("code", "");
}
+ public String getIsApplet3() {
+ return getDefaulted("is_applet3", "false");
+ }
+
public String getJNLPHref() {
return get("jnlp_href");
}
diff --git a/netx/net/sourceforge/jnlp/runtime/AppletInstance.java b/netx/net/sourceforge/jnlp/runtime/AppletInstance.java
index 757a9f7..49947ff 100644
--- a/netx/net/sourceforge/jnlp/runtime/AppletInstance.java
+++ b/netx/net/sourceforge/jnlp/runtime/AppletInstance.java
@@ -19,6 +19,7 @@ package net.sourceforge.jnlp.runtime;
import java.applet.*;
import java.awt.*;
+import sun.awt.AppContext;
import net.sourceforge.jnlp.*;
import net.sourceforge.jnlp.util.logging.OutputController;
@@ -45,7 +46,7 @@ public class AppletInstance extends ApplicationInstance {
* Create a New Task based on the Specified URL
*/
public AppletInstance(JNLPFile file, ThreadGroup group, ClassLoader loader, Applet applet) {
- super(file, group, loader);
+ super(file, group, loader, AppContext.getAppContext());
this.applet = applet;
@@ -67,7 +68,7 @@ public class AppletInstance extends ApplicationInstance {
*
*/
public AppletInstance(JNLPFile file, ThreadGroup group, ClassLoader loader, Applet applet, Container cont) {
- super(file, group, loader);
+ super(file, group, loader, AppContext.getAppContext());
this.applet = applet;
this.environment = new AppletEnvironment(file, this, cont);
}
diff --git a/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java b/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java
index 0258840..a16b0b5 100644
--- a/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java
+++ b/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java
@@ -16,7 +16,7 @@
package net.sourceforge.jnlp.runtime;
-import java.awt.Window;
+// import java.awt.Window;
import java.io.File;
import java.net.URL;
import java.security.AccessControlContext;
@@ -25,17 +25,17 @@ import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
-import javax.swing.event.EventListenerList;
-
-import sun.awt.AppContext;
+// FIXME replace - currently unused
+// import javax.swing.event.EventListenerList;
import net.sourceforge.jnlp.JNLPFile;
import net.sourceforge.jnlp.PropertyDesc;
import net.sourceforge.jnlp.SecurityDesc;
import net.sourceforge.jnlp.ShortcutDesc;
import net.sourceforge.jnlp.config.DeploymentConfiguration;
-import net.sourceforge.jnlp.event.ApplicationEvent;
-import net.sourceforge.jnlp.event.ApplicationListener;
+// FIXME replace - currently unused
+// import net.sourceforge.jnlp.event.ApplicationEvent;
+// import net.sourceforge.jnlp.event.ApplicationListener;
import net.sourceforge.jnlp.security.SecurityDialogs;
import net.sourceforge.jnlp.security.SecurityDialogs.AccessType;
import net.sourceforge.jnlp.util.logging.OutputController;
@@ -56,13 +56,16 @@ public class ApplicationInstance {
// installed by the application.
/** the file */
- private JNLPFile file;
+ private final JNLPFile file;
/** the thread group */
- private ThreadGroup group;
+ private final ThreadGroup group;
/** the classloader */
- private ClassLoader loader;
+ private final ClassLoader loader;
+
+ /** whether or not this application is signed */
+ private final boolean isSigned;
/**
* Every application/applet gets its own AppContext. This allows us to do
@@ -71,51 +74,55 @@ public class ApplicationInstance {
* event queue (safely) and (possibly) more.<p>
*
* It is set to the AppContext which created this ApplicationInstance
+ *
+ * Either {@link sun.awt.AppContext} or ..
+ *
+ * FIXME: Add proper interface
*/
- private AppContext appContext;
+ private final Object appContext;
/** whether the application has stopped running */
private boolean stopped = false;
- /** weak list of windows opened by the application */
- private WeakList<Window> weakWindows = new WeakList<Window>();
+ /** weak list of UI window objects opened by the application */
+ private WeakList<Object> weakWindows = new WeakList<Object>();
/** list of application listeners */
- private EventListenerList listeners = new EventListenerList();
-
- /** whether or not this application is signed */
- private boolean isSigned = false;
+ // FIXME replace - currently unused
+ // private EventListenerList listeners = new EventListenerList();
/**
* Create an application instance for the file. This should be done in the
* appropriate {@link ThreadGroup} only.
*/
- public ApplicationInstance(JNLPFile file, ThreadGroup group, ClassLoader loader) {
+ public ApplicationInstance(JNLPFile file, ThreadGroup group, ClassLoader loader, Object appContext) {
this.file = file;
this.group = group;
this.loader = loader;
this.isSigned = ((JNLPClassLoader) loader).getSigning();
- this.appContext = AppContext.getAppContext();
+ this.appContext = appContext;
}
/**
+ // FIXME replace - currently unused
* Add an Application listener
- */
public void addApplicationListener(ApplicationListener listener) {
listeners.add(ApplicationListener.class, listener);
}
- /**
* Remove an Application Listener
- */
public void removeApplicationListener(ApplicationListener listener) {
listeners.remove(ApplicationListener.class, listener);
}
+ */
/**
* Notify listeners that the application has been terminated.
*/
protected void fireDestroyed() {
+ /**
+ // FIXME replace - currently unused
+ *
Object list[] = listeners.getListenerList();
ApplicationEvent event = null;
@@ -125,6 +132,7 @@ public class ApplicationInstance {
((ApplicationListener) list[i]).applicationDestroyed(event);
}
+ */
}
/**
@@ -280,9 +288,13 @@ public class ApplicationInstance {
try {
// destroy resources
- for (Window w : weakWindows) {
- if (w != null)
- w.dispose();
+ for (Object w : weakWindows) {
+ if (w != null) {
+ // FIXME!
+ if( w instanceof java.awt.Window ) {
+ ((java.awt.Window)w).dispose();
+ }
+ }
}
weakWindows.clear();
@@ -339,7 +351,7 @@ public class ApplicationInstance {
* Adds a window that this application opened. When the
* application is disposed, these windows will also be disposed.
*/
- protected void addWindow(Window window) {
+ public void addWindow(Object window) {
weakWindows.add(window);
weakWindows.trimToSize();
}
@@ -351,7 +363,11 @@ public class ApplicationInstance {
return isSigned;
}
- public AppContext getAppContext() {
+ /**
+ * Returns either {@link sun.awt.AppContext} or ..
+ * FIXME: Add proper interface
+ */
+ public Object getAppContext() {
return appContext;
}
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java
index c9d7397..b69d36c 100644
--- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java
@@ -16,7 +16,6 @@
package net.sourceforge.jnlp.runtime;
-import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -42,14 +41,16 @@ import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
-import javax.swing.JOptionPane;
-import javax.swing.UIManager;
-import javax.swing.text.html.parser.ParserDelegator;
+// import javax.swing.JOptionPane;
+// import javax.swing.UIManager;
+// import javax.swing.text.html.parser.ParserDelegator;
+
+
+import jogamp.plugin.jnlp.runtime.JNLP3SecurityManager;
import net.sourceforge.jnlp.DefaultLaunchHandler;
import net.sourceforge.jnlp.GuiLaunchHandler;
import net.sourceforge.jnlp.LaunchHandler;
-import net.sourceforge.jnlp.Launcher;
import net.sourceforge.jnlp.browser.BrowserAwareProxySelector;
import net.sourceforge.jnlp.cache.CacheUtil;
import net.sourceforge.jnlp.cache.DefaultDownloadIndicator;
@@ -92,7 +93,7 @@ public class JNLPRuntime {
private static ResourceBundle resources;
/** the security manager */
- private static JNLPSecurityManager security;
+ private static JNLP3SecurityManager security;
/** the security policy */
private static JNLPPolicy policy;
@@ -223,15 +224,17 @@ public class JNLPRuntime {
ServiceManager.setServiceManagerStub(new XServiceManagerStub()); // ignored if we're running under Web Start
policy = new JNLPPolicy();
- security = new JNLPSecurityManager(); // side effect: create JWindow
+ // security = new JNLPSecurityManager(); // side effect: create JWindow
+ security = new JNLP3SecurityManager(); // side effect: create JWindow
+ /** FIXME AWT
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
- }
-
+ }
doMainAppContextHacks();
+ */
if (securityEnabled) {
Policy.setPolicy(policy); // do first b/c our SM blocks setPolicy
@@ -322,7 +325,7 @@ public class JNLPRuntime {
/**
* This must NOT be called form the application ThreadGroup. An application
- * can inject events into its {@link EventQueue} and bypass the security
+ * can inject events into its {@link java.awt.EventQueue} and bypass the security
* dialogs.
*
* @return a {@link SecurityDialogMessageHandler} that can be used to post
@@ -341,18 +344,20 @@ public class JNLPRuntime {
* Performs a few hacks that are needed for the main AppContext
*
* @see Launcher#doPerApplicationAppContextHacks
- */
+ *
+ * FIXME AWT
private static void doMainAppContextHacks() {
- /*
+ *
* With OpenJDK6 (but not with 7) a per-AppContext dtd is maintained.
* This dtd is created by the ParserDelgate. However, the code in
* HTMLEditorKit (used to render HTML in labels and textpanes) creates
* the ParserDelegate only if there are no existing ParserDelegates. The
* result is that all other AppContexts see a null dtd.
- */
+ *
new ParserDelegator();
}
+ */
/**
@@ -379,9 +384,10 @@ public class JNLPRuntime {
//all exceptions are causing InstantiatizationError so this do it much more readble
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, t);
OutputController.getLogger().log(OutputController.Level.WARNING_ALL, Translator.R("RFailingToDefault"));
+ /* FIXME AWT
if (!JNLPRuntime.isHeadless()){
JOptionPane.showMessageDialog(null, getMessage("RFailingToDefault")+"\n"+t.toString());
- }
+ } */
//try to survive this unlikely exception
config.resetToDefaults();
} finally {
@@ -707,6 +713,7 @@ public class JNLPRuntime {
*/
public synchronized static void markNetxRunning() {
if (fileLock != null) return;
+ FileInputStream is = null;
try {
String message = "This file is used to check if netx is running";
@@ -723,7 +730,7 @@ public class JNLPRuntime {
}
}
- FileInputStream is = new FileInputStream(netxRunningFile);
+ is = new FileInputStream(netxRunningFile);
FileChannel channel = is.getChannel();
fileLock = channel.lock(0, 1, true);
if (!fileLock.isShared()){ // We know shared locks aren't offered on this system.
@@ -741,6 +748,12 @@ public class JNLPRuntime {
}
} catch (IOException e) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
+ } finally {
+ if( null != is ) {
+ try {
+ is.close();
+ } catch (IOException e) { }
+ }
}
Runtime.getRuntime().addShutdownHook(new Thread("JNLPRuntimeShutdownHookThread") {
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java b/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java
index 762bb91..5b6f2b0 100644
--- a/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java
@@ -442,7 +442,8 @@ class JNLPSecurityManager extends AWTSecurityManager {
*/
return mainAppContext;
} else {
- return app.getAppContext();
+ // FIXME: Remove AWT dependencies!
+ return (AppContext) app.getAppContext();
}
}
diff --git a/netx/net/sourceforge/jnlp/splashscreen/SplashUtils.java b/netx/net/sourceforge/jnlp/splashscreen/SplashUtils.java
index b43476e..8825183 100644
--- a/netx/net/sourceforge/jnlp/splashscreen/SplashUtils.java
+++ b/netx/net/sourceforge/jnlp/splashscreen/SplashUtils.java
@@ -36,6 +36,8 @@ obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package net.sourceforge.jnlp.splashscreen;
+import jogamp.plugin.jnlp.runtime.Applet3Environment;
+import jogamp.plugin.jnlp.runtime.Applet3Instance;
import net.sourceforge.jnlp.runtime.AppletEnvironment;
import net.sourceforge.jnlp.runtime.AppletInstance;
import net.sourceforge.jnlp.runtime.Boot;
@@ -83,17 +85,44 @@ public class SplashUtils {
OutputController.getLogger().log(t);
}
}
+ public static void showErrorCaught(Throwable ex, Applet3Instance appletInstance) {
+ try {
+ showError(ex, appletInstance);
+ } catch (Throwable t) {
+ // prinitng this exception is discutable. I have let it in for case that
+ //some retyping will fail
+ OutputController.getLogger().log(t);
+ }
+ }
public static void showError(Throwable ex, AppletInstance appletInstance) {
if (appletInstance == null) {
+ OutputController.getLogger().log(ex);
return;
}
AppletEnvironment ae = appletInstance.getAppletEnvironment();
showError(ex, ae);
}
+ public static void showError(Throwable ex, Applet3Instance appletInstance) {
+ if (appletInstance == null) {
+ OutputController.getLogger().log(ex);
+ return;
+ }
+ Applet3Environment ae = appletInstance.getAppletEnvironment();
+ showError(ex, ae);
+ }
public static void showError(Throwable ex, AppletEnvironment ae) {
if (ae == null) {
+ OutputController.getLogger().log(ex);
+ return;
+ }
+ SplashController p = ae.getSplashControler();
+ showError(ex, p);
+ }
+ public static void showError(Throwable ex, Applet3Environment ae) {
+ if (ae == null) {
+ OutputController.getLogger().log(ex);
return;
}
SplashController p = ae.getSplashControler();
@@ -102,6 +131,7 @@ public class SplashUtils {
public static void showError(Throwable ex, SplashController f) {
if (f == null) {
+ OutputController.getLogger().log(ex);
return;
}
diff --git a/netx/sun/applet/Applet3MessageHandler.java b/netx/sun/applet/Applet3MessageHandler.java
new file mode 100644
index 0000000..140e5eb
--- /dev/null
+++ b/netx/sun/applet/Applet3MessageHandler.java
@@ -0,0 +1,21 @@
+package sun.applet;
+
+import sun.applet.AppletMessageHandler;
+
+public class Applet3MessageHandler extends AppletMessageHandler {
+
+ public Applet3MessageHandler(String baseKey) {
+ super(baseKey);
+ }
+
+ public String getMessage(String key) {
+ return super.getMessage(key);
+ }
+ public String getMessage(String key, Object arg){
+ return super.getMessage(key, arg);
+ }
+ public String getMessage(String key, Object arg1, Object arg2){
+ return super.getMessage(key, arg1, arg2);
+ }
+
+}