aboutsummaryrefslogtreecommitdiffstats
path: root/netx/jogamp/applet/Applet3Panel.java
diff options
context:
space:
mode:
Diffstat (limited to 'netx/jogamp/applet/Applet3Panel.java')
-rw-r--r--netx/jogamp/applet/Applet3Panel.java1536
1 files changed, 1536 insertions, 0 deletions
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");
+}