/* * 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 <fkung@redhat.com> */ 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 uKeyToTG = new HashMap(); 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 appContextCreated = new ConcurrentHashMap(); public NetxApplet3Panel(long nativeWindowHandle, int xpos, int ypos, int width, int height, URL documentURL, PluginParameters params) { super(nativeWindowHandle, xpos, ypos, 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); } @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"); } } final ThreadGroup tg = getThreadGroup(); loaderThread = new Thread(tg, this, "NetxLoaderThread @ " + this.documentURL); loaderThread.start(); handler = new Thread(tg, 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 boolean useCustomAppletClassLoader() { return true; } @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(); } }