diff options
Diffstat (limited to 'netx/jogamp/applet/Applet3Panel.java')
-rw-r--r-- | netx/jogamp/applet/Applet3Panel.java | 1536 |
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"); +} |