Save This Page
Home » openjdk-7 » sun » applet » [javadoc | source]
    1   /*
    2    * Copyright 1995-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package sun.applet;
   27   
   28   import java.applet;
   29   import java.awt;
   30   import java.awt.event;
   31   import java.awt.image.ColorModel;
   32   import java.awt.image.MemoryImageSource;
   33   import java.io;
   34   import java.lang.ref.WeakReference;
   35   import java.lang.reflect.InvocationTargetException;
   36   import java.lang.reflect.Method;
   37   import java.net.InetAddress;
   38   import java.net.JarURLConnection;
   39   import java.net.MalformedURLException;
   40   import java.net.SocketPermission;
   41   import java.net.URL;
   42   import java.net.UnknownHostException;
   43   import java.security;
   44   import java.util;
   45   import java.util.Collections;
   46   import java.util.Locale;
   47   import java.util.WeakHashMap;
   48   import javax.swing.SwingUtilities;
   49   import sun.awt.AppContext;
   50   import sun.awt.EmbeddedFrame;
   51   import sun.awt.SunToolkit;
   52   import sun.misc.MessageUtils;
   53   import sun.misc.PerformanceLogger;
   54   import sun.misc.Queue;
   55   import sun.security.util.SecurityConstants;
   56   
   57   /**
   58    * Applet panel class. The panel manages and manipulates the
   59    * applet as it is being loaded. It forks a separate thread in a new
   60    * thread group to call the applet's init(), start(), stop(), and
   61    * destroy() methods.
   62    *
   63    * @author      Arthur van Hoff
   64    */
   65   public
   66   abstract class AppletPanel extends Panel implements AppletStub, Runnable {
   67   
   68       /**
   69        * The applet (if loaded).
   70        */
   71       Applet applet;
   72   
   73       /**
   74        * Applet will allow initialization.  Should be
   75        * set to false if loading a serialized applet
   76        * that was pickled in the init=true state.
   77        */
   78       protected boolean doInit = true;
   79   
   80   
   81       /**
   82        * The classloader for the applet.
   83        */
   84       AppletClassLoader loader;
   85   
   86       /* applet event ids */
   87       public final static int APPLET_DISPOSE = 0;
   88       public final static int APPLET_LOAD = 1;
   89       public final static int APPLET_INIT = 2;
   90       public final static int APPLET_START = 3;
   91       public final static int APPLET_STOP = 4;
   92       public final static int APPLET_DESTROY = 5;
   93       public final static int APPLET_QUIT = 6;
   94       public final static int APPLET_ERROR = 7;
   95   
   96       /* send to the parent to force relayout */
   97       public final static int APPLET_RESIZE = 51234;
   98   
   99       /* sent to a (distant) parent to indicate that the applet is being
  100        * loaded or as completed loading
  101        */
  102       public final static int APPLET_LOADING = 51235;
  103       public final static int APPLET_LOADING_COMPLETED = 51236;
  104   
  105       /**
  106        * The current status. One of:
  107        *    APPLET_DISPOSE,
  108        *    APPLET_LOAD,
  109        *    APPLET_INIT,
  110        *    APPLET_START,
  111        *    APPLET_STOP,
  112        *    APPLET_DESTROY,
  113        *    APPLET_ERROR.
  114        */
  115       protected int status;
  116   
  117       /**
  118        * The thread for the applet.
  119        */
  120       Thread handler;
  121   
  122   
  123       /**
  124        * The initial applet size.
  125        */
  126       Dimension defaultAppletSize = new Dimension(10, 10);
  127   
  128       /**
  129        * The current applet size.
  130        */
  131       Dimension currentAppletSize = new Dimension(10, 10);
  132   
  133       MessageUtils mu = new MessageUtils();
  134   
  135       /**
  136        * The thread to use during applet loading
  137        */
  138   
  139       Thread loaderThread = null;
  140   
  141       /**
  142        * Flag to indicate that a loading has been cancelled
  143        */
  144       boolean loadAbortRequest = false;
  145   
  146       /* abstract classes */
  147       abstract protected String getCode();
  148       abstract protected String getJarFiles();
  149       abstract protected String getSerializedObject();
  150   
  151       abstract public int    getWidth();
  152       abstract public int    getHeight();
  153       abstract public boolean hasInitialFocus();
  154   
  155       private static int threadGroupNumber = 0;
  156   
  157       protected void setupAppletAppContext() {
  158           // do nothing
  159       }
  160   
  161       /*
  162        * Creates a thread to run the applet. This method is called
  163        * each time an applet is loaded and reloaded.
  164        */
  165       synchronized void createAppletThread() {
  166           // Create a thread group for the applet, and start a new
  167           // thread to load the applet.
  168           String nm = "applet-" + getCode();
  169           loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
  170           loader.grab(); // Keep this puppy around!
  171   
  172           // 4668479: Option to turn off codebase lookup in AppletClassLoader
  173           // during resource requests. [stanley.ho]
  174           String param = getParameter("codebase_lookup");
  175   
  176           if (param != null && param.equals("false"))
  177               loader.setCodebaseLookup(false);
  178           else
  179               loader.setCodebaseLookup(true);
  180   
  181   
  182           ThreadGroup appletGroup = loader.getThreadGroup();
  183   
  184           handler = new Thread(appletGroup, this, "thread " + nm);
  185           // set the context class loader for this thread
  186           AccessController.doPrivileged(new PrivilegedAction() {
  187                   public Object run() {
  188                       handler.setContextClassLoader(loader);
  189                       return null;
  190                   }
  191               });
  192           handler.start();
  193       }
  194   
  195       void joinAppletThread() throws InterruptedException {
  196           if (handler != null) {
  197               handler.join();
  198               handler = null;
  199           }
  200       }
  201   
  202       void release() {
  203           if (loader != null) {
  204               loader.release();
  205               loader = null;
  206           }
  207       }
  208   
  209       /**
  210        * Construct an applet viewer and start the applet.
  211        */
  212       public void init() {
  213           try {
  214               // Get the width (if any)
  215               defaultAppletSize.width = getWidth();
  216               currentAppletSize.width = defaultAppletSize.width;
  217   
  218               // Get the height (if any)
  219               defaultAppletSize.height = getHeight();
  220               currentAppletSize.height = defaultAppletSize.height;
  221   
  222           } catch (NumberFormatException e) {
  223               // Turn on the error flag and let TagAppletPanel
  224               // do the right thing.
  225               status = APPLET_ERROR;
  226               showAppletStatus("badattribute.exception");
  227               showAppletLog("badattribute.exception");
  228               showAppletException(e);
  229           }
  230   
  231           setLayout(new BorderLayout());
  232   
  233           createAppletThread();
  234       }
  235   
  236       /**
  237        * Minimum size
  238        */
  239       public Dimension minimumSize() {
  240           return new Dimension(defaultAppletSize.width,
  241                                defaultAppletSize.height);
  242       }
  243   
  244       /**
  245        * Preferred size
  246        */
  247       public Dimension preferredSize() {
  248           return new Dimension(currentAppletSize.width,
  249                                currentAppletSize.height);
  250       }
  251   
  252       private AppletListener listeners;
  253   
  254       /**
  255        * AppletEvent Queue
  256        */
  257       private Queue queue = null;
  258   
  259   
  260       synchronized public void addAppletListener(AppletListener l) {
  261           listeners = AppletEventMulticaster.add(listeners, l);
  262       }
  263   
  264       synchronized public void removeAppletListener(AppletListener l) {
  265           listeners = AppletEventMulticaster.remove(listeners, l);
  266       }
  267   
  268       /**
  269        * Dispatch event to the listeners..
  270        */
  271       public void dispatchAppletEvent(int id, Object argument) {
  272           //System.out.println("SEND= " + id);
  273           if (listeners != null) {
  274               AppletEvent evt = new AppletEvent(this, id, argument);
  275               listeners.appletStateChanged(evt);
  276           }
  277       }
  278   
  279       /**
  280        * Send an event. Queue it for execution by the handler thread.
  281        */
  282       public void sendEvent(int id) {
  283           synchronized(this) {
  284               if (queue == null) {
  285                   //System.out.println("SEND0= " + id);
  286                   queue = new Queue();
  287               }
  288               Integer eventId = Integer.valueOf(id);
  289               queue.enqueue(eventId);
  290               notifyAll();
  291           }
  292           if (id == APPLET_QUIT) {
  293               try {
  294                   joinAppletThread(); // Let the applet event handler exit
  295               } catch (InterruptedException e) {
  296               }
  297   
  298               // AppletClassLoader.release() must be called by a Thread
  299               // not within the applet's ThreadGroup
  300               if (loader == null)
  301                   loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
  302               release();
  303           }
  304       }
  305   
  306       /**
  307        * Get an event from the queue.
  308        */
  309       synchronized AppletEvent getNextEvent() throws InterruptedException {
  310           while (queue == null || queue.isEmpty()) {
  311               wait();
  312           }
  313           Integer eventId = (Integer)queue.dequeue();
  314           return new AppletEvent(this, eventId.intValue(), null);
  315       }
  316   
  317       boolean emptyEventQueue() {
  318           if ((queue == null) || (queue.isEmpty()))
  319               return true;
  320           else
  321               return false;
  322       }
  323   
  324       /**
  325        * This kludge is specific to get over AccessControlException thrown during
  326        * Applet.stop() or destroy() when static thread is suspended.  Set a flag
  327        * in AppletClassLoader to indicate that an
  328        * AccessControlException for RuntimePermission "modifyThread" or
  329        * "modifyThreadGroup" had occurred.
  330        */
  331        private void setExceptionStatus(AccessControlException e) {
  332        Permission p = e.getPermission();
  333        if (p instanceof RuntimePermission) {
  334            if (p.getName().startsWith("modifyThread")) {
  335                if (loader == null)
  336                    loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
  337                loader.setExceptionStatus();
  338            }
  339        }
  340        }
  341   
  342       /**
  343        * Execute applet events.
  344        * Here is the state transition diagram
  345        *
  346        *   Note: (XXX) is the action
  347        *         APPLET_XXX is the state
  348        *  (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- (
  349        *   applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet
  350        *   destroyed called) --> APPLET_DESTROY -->(applet gets disposed) -->
  351        *   APPLET_DISPOSE -->....
  352        *
  353        * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays
  354        * in the APPLET_START state unless the applet goes away(refresh page or leave the page).
  355        * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet
  356        * is revisited, it will call applet start method and enter the APPLET_START state and stay there.
  357        *
  358        * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle
  359        * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP
  360        * state and then applet destroyed method gets called and enters APPLET_DESTROY state.
  361        *
  362        * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from
  363        * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT .
  364        *
  365        * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case).
  366            * Same as APPLET_LOAD to
  367        * APPLET_DISPOSE since all of this are triggered by browser.
  368        *
  369        *
  370        */
  371       public void run() {
  372   
  373           Thread curThread = Thread.currentThread();
  374           if (curThread == loaderThread) {
  375               // if we are in the loader thread, cause
  376               // loading to occur.  We may exit this with
  377               // status being APPLET_DISPOSE, APPLET_ERROR,
  378               // or APPLET_LOAD
  379               runLoader();
  380               return;
  381           }
  382   
  383           boolean disposed = false;
  384           while (!disposed && !curThread.isInterrupted()) {
  385               AppletEvent evt;
  386               try {
  387                   evt = getNextEvent();
  388               } catch (InterruptedException e) {
  389                   showAppletStatus("bail");
  390                   return;
  391               }
  392   
  393               //showAppletStatus("EVENT = " + evt.getID());
  394               try {
  395                   switch (evt.getID()) {
  396                     case APPLET_LOAD:
  397                         if (!okToLoad()) {
  398                             break;
  399                         }
  400                         // This complexity allows loading of applets to be
  401                         // interruptable.  The actual thread loading runs
  402                         // in a separate thread, so it can be interrupted
  403                         // without harming the applet thread.
  404                         // So that we don't have to worry about
  405                         // concurrency issues, the main applet thread waits
  406                         // until the loader thread terminates.
  407                         // (one way or another).
  408                         if (loaderThread == null) {
  409                             // REMIND: do we want a name?
  410                             //System.out.println("------------------- loading applet");
  411                             setLoaderThread(new Thread(this));
  412                             loaderThread.start();
  413                             // we get to go to sleep while this runs
  414                             loaderThread.join();
  415                             setLoaderThread(null);
  416                         } else {
  417                             // REMIND: issue an error -- this case should never
  418                             // occur.
  419                         }
  420                         break;
  421   
  422                     case APPLET_INIT:
  423                       // AppletViewer "Restart" will jump from destroy method to
  424                       // init, that is why we need to check status w/ APPLET_DESTROY
  425                         if (status != APPLET_LOAD && status != APPLET_DESTROY) {
  426                             showAppletStatus("notloaded");
  427                             break;
  428                         }
  429                         applet.resize(defaultAppletSize);
  430                         if (doInit) {
  431                             if (PerformanceLogger.loggingEnabled()) {
  432                                 PerformanceLogger.setTime("Applet Init");
  433                                 PerformanceLogger.outputLog();
  434                             }
  435                             applet.init();
  436                         }
  437   
  438                         //Need the default(fallback) font to be created in this AppContext
  439                         Font f = getFont();
  440                         if (f == null ||
  441                             "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) &&
  442                             f.getSize() == 12 && f.getStyle() == Font.PLAIN) {
  443                             setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
  444                         }
  445   
  446                         doInit = true;    // allow restarts
  447   
  448                         // Validate the applet in event dispatch thread
  449                         // to avoid deadlock.
  450                         try {
  451                             final AppletPanel p = this;
  452   
  453                             SwingUtilities.invokeAndWait(new Runnable() {
  454                                     public void run() {
  455                                         p.validate();
  456                                     }
  457                                 });
  458                         }
  459                         catch(InterruptedException ie) {
  460                         }
  461                         catch(InvocationTargetException ite) {
  462                         }
  463   
  464                         status = APPLET_INIT;
  465                         showAppletStatus("inited");
  466                         break;
  467   
  468                     case APPLET_START:
  469                     {
  470                         if (status != APPLET_INIT && status != APPLET_STOP) {
  471                             showAppletStatus("notinited");
  472                             break;
  473                         }
  474                         applet.resize(currentAppletSize);
  475                         applet.start();
  476   
  477                         // Validate and show the applet in event dispatch thread
  478                         // to avoid deadlock.
  479                         try {
  480                             final AppletPanel p = this;
  481                             final Applet a = applet;
  482   
  483                             SwingUtilities.invokeAndWait(new Runnable() {
  484                                     public void run() {
  485                                         p.validate();
  486                                         a.setVisible(true);
  487   
  488                                         // Fix for BugTraq ID 4041703.
  489                                         // Set the default focus for an applet.
  490                                         if (hasInitialFocus())
  491                                           setDefaultFocus();
  492                                     }
  493                                 });
  494                         }
  495                         catch(InterruptedException ie) {
  496                         }
  497                         catch(InvocationTargetException ite) {
  498                         }
  499   
  500                         status = APPLET_START;
  501                         showAppletStatus("started");
  502                         break;
  503                     }
  504   
  505                   case APPLET_STOP:
  506                       if (status != APPLET_START) {
  507                           showAppletStatus("notstarted");
  508                           break;
  509                       }
  510                       status = APPLET_STOP;
  511   
  512                       // Hide the applet in event dispatch thread
  513                       // to avoid deadlock.
  514                       try {
  515                           final Applet a = applet;
  516   
  517                           SwingUtilities.invokeAndWait(new Runnable() {
  518                                   public void run()
  519                                   {
  520                                       a.setVisible(false);
  521                                   }
  522                               });
  523                       }
  524                       catch(InterruptedException ie) {
  525                       }
  526                       catch(InvocationTargetException ite) {
  527                       }
  528   
  529   
  530                       // During Applet.stop(), any AccessControlException on an involved Class remains in
  531                       // the "memory" of the AppletClassLoader.  If the same instance of the ClassLoader is
  532                       // reused, the same exception will occur during class loading.  Set the AppletClassLoader's
  533                       // exceptionStatusSet flag to allow recognition of what had happened
  534                       // when reusing AppletClassLoader object.
  535                       try {
  536                           applet.stop();
  537                       } catch (java.security.AccessControlException e) {
  538                           setExceptionStatus(e);
  539                           // rethrow exception to be handled as it normally would be.
  540                           throw e;
  541                       }
  542                       showAppletStatus("stopped");
  543                       break;
  544   
  545                   case APPLET_DESTROY:
  546                       if (status != APPLET_STOP && status != APPLET_INIT) {
  547                           showAppletStatus("notstopped");
  548                           break;
  549                       }
  550                       status = APPLET_DESTROY;
  551   
  552                       // During Applet.destroy(), any AccessControlException on an involved Class remains in
  553                       // the "memory" of the AppletClassLoader.  If the same instance of the ClassLoader is
  554                       // reused, the same exception will occur during class loading.  Set the AppletClassLoader's
  555                       // exceptionStatusSet flag to allow recognition of what had happened
  556                       // when reusing AppletClassLoader object.
  557                       try {
  558                           applet.destroy();
  559                       } catch (java.security.AccessControlException e) {
  560                           setExceptionStatus(e);
  561                           // rethrow exception to be handled as it normally would be.
  562                           throw e;
  563                       }
  564                       showAppletStatus("destroyed");
  565                       break;
  566   
  567                   case APPLET_DISPOSE:
  568                       if (status != APPLET_DESTROY && status != APPLET_LOAD) {
  569                           showAppletStatus("notdestroyed");
  570                           break;
  571                       }
  572                       status = APPLET_DISPOSE;
  573   
  574                       try
  575                       {
  576                           final Applet a = applet;
  577   
  578                           EventQueue.invokeAndWait(new Runnable()
  579                           {
  580                               public void run()
  581                               {
  582                                   remove(a);
  583                               }
  584                           });
  585                       }
  586                       catch(InterruptedException ie)
  587                       {
  588                       }
  589                       catch(InvocationTargetException ite)
  590                       {
  591                       }
  592                       applet = null;
  593                       showAppletStatus("disposed");
  594                       disposed = true;
  595                       break;
  596   
  597                   case APPLET_QUIT:
  598                       return;
  599                   }
  600               } catch (Exception e) {
  601                   status = APPLET_ERROR;
  602                   if (e.getMessage() != null) {
  603                       showAppletStatus("exception2", e.getClass().getName(),
  604                                        e.getMessage());
  605                   } else {
  606                       showAppletStatus("exception", e.getClass().getName());
  607                   }
  608                   showAppletException(e);
  609               } catch (ThreadDeath e) {
  610                   showAppletStatus("death");
  611                   return;
  612               } catch (Error e) {
  613                   status = APPLET_ERROR;
  614                   if (e.getMessage() != null) {
  615                       showAppletStatus("error2", e.getClass().getName(),
  616                                        e.getMessage());
  617                   } else {
  618                       showAppletStatus("error", e.getClass().getName());
  619                   }
  620                   showAppletException(e);
  621               }
  622               clearLoadAbortRequest();
  623           }
  624       }
  625   
  626       /**
  627        * Gets most recent focus owner component associated with the given window.
  628        * It does that without calling Window.getMostRecentFocusOwner since it
  629        * provides its own logic contradicting with setDefautlFocus. Instead, it
  630        * calls KeyboardFocusManager directly.
  631        */
  632       private Component getMostRecentFocusOwnerForWindow(Window w) {
  633           Method meth = (Method)AccessController.doPrivileged(new PrivilegedAction() {
  634                   public Object run() {
  635                       Method meth = null;
  636                       try {
  637                           meth = KeyboardFocusManager.class.getDeclaredMethod("getMostRecentFocusOwner", new Class[] {Window.class});
  638                           meth.setAccessible(true);
  639                       } catch (Exception e) {
  640                           // Must never happen
  641                           e.printStackTrace();
  642                       }
  643                       return meth;
  644                   }
  645               });
  646           if (meth != null) {
  647               // Meth refers static method
  648               try {
  649                   return (Component)meth.invoke(null, new Object[] {w});
  650               } catch (Exception e) {
  651                   // Must never happen
  652                   e.printStackTrace();
  653               }
  654           }
  655           // Will get here if exception was thrown or meth is null
  656           return w.getMostRecentFocusOwner();
  657       }
  658   
  659       /*
  660        * Fix for BugTraq ID 4041703.
  661        * Set the focus to a reasonable default for an Applet.
  662        */
  663       private void setDefaultFocus() {
  664           Component toFocus = null;
  665           Container parent = getParent();
  666   
  667           if(parent != null) {
  668               if (parent instanceof Window) {
  669                   toFocus = getMostRecentFocusOwnerForWindow((Window)parent);
  670                   if (toFocus == parent || toFocus == null) {
  671                       toFocus = parent.getFocusTraversalPolicy().
  672                           getInitialComponent((Window)parent);
  673                   }
  674               } else if (parent.isFocusCycleRoot()) {
  675                   toFocus = parent.getFocusTraversalPolicy().
  676                       getDefaultComponent(parent);
  677               }
  678           }
  679   
  680           if (toFocus != null) {
  681               if (parent instanceof EmbeddedFrame) {
  682                   ((EmbeddedFrame)parent).synthesizeWindowActivation(true);
  683               }
  684               // EmbeddedFrame might have focus before the applet was added.
  685               // Thus after its activation the most recent focus owner will be
  686               // restored. We need the applet's initial focusabled component to
  687               // be focused here.
  688               toFocus.requestFocusInWindow();
  689           }
  690       }
  691   
  692       /**
  693        * Load the applet into memory.
  694        * Runs in a seperate (and interruptible) thread from the rest of the
  695        * applet event processing so that it can be gracefully interrupted from
  696        * things like HotJava.
  697        */
  698       private void runLoader() {
  699           if (status != APPLET_DISPOSE) {
  700               showAppletStatus("notdisposed");
  701               return;
  702           }
  703   
  704           dispatchAppletEvent(APPLET_LOADING, null);
  705   
  706           // REMIND -- might be cool to visually indicate loading here --
  707           // maybe do animation?
  708           status = APPLET_LOAD;
  709   
  710           // Create a class loader
  711           loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
  712   
  713           // Load the archives if present.
  714           // REMIND - this probably should be done in a separate thread,
  715           // or at least the additional archives (epll).
  716   
  717           String code = getCode();
  718   
  719           // setup applet AppContext
  720           // this must be called before loadJarFiles
  721           setupAppletAppContext();
  722   
  723           try {
  724               loadJarFiles(loader);
  725               applet = createApplet(loader);
  726           } catch (ClassNotFoundException e) {
  727               status = APPLET_ERROR;
  728               showAppletStatus("notfound", code);
  729               showAppletLog("notfound", code);
  730               showAppletException(e);
  731               return;
  732           } catch (InstantiationException e) {
  733               status = APPLET_ERROR;
  734               showAppletStatus("nocreate", code);
  735               showAppletLog("nocreate", code);
  736               showAppletException(e);
  737               return;
  738           } catch (IllegalAccessException e) {
  739               status = APPLET_ERROR;
  740               showAppletStatus("noconstruct", code);
  741               showAppletLog("noconstruct", code);
  742               showAppletException(e);
  743               // sbb -- I added a return here
  744               return;
  745           } catch (Exception e) {
  746               status = APPLET_ERROR;
  747               showAppletStatus("exception", e.getMessage());
  748               showAppletException(e);
  749               return;
  750           } catch (ThreadDeath e) {
  751               status = APPLET_ERROR;
  752               showAppletStatus("death");
  753               return;
  754           } catch (Error e) {
  755               status = APPLET_ERROR;
  756               showAppletStatus("error", e.getMessage());
  757               showAppletException(e);
  758               return;
  759           } finally {
  760               // notify that loading is no longer going on
  761               dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
  762           }
  763   
  764           // Fixed #4508194: NullPointerException thrown during
  765           // quick page switch
  766           //
  767           if (applet != null)
  768           {
  769               // Stick it in the frame
  770               applet.setStub(this);
  771               applet.hide();
  772               add("Center", applet);
  773               showAppletStatus("loaded");
  774               validate();
  775           }
  776       }
  777   
  778       protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException,
  779                                                                            IllegalAccessException, IOException, InstantiationException, InterruptedException {
  780           final String serName = getSerializedObject();
  781           String code = getCode();
  782   
  783           if (code != null && serName != null) {
  784               System.err.println(amh.getMessage("runloader.err"));
  785   //          return null;
  786               throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both.");
  787           }
  788           if (code == null && serName == null) {
  789               String msg = "nocode";
  790               status = APPLET_ERROR;
  791               showAppletStatus(msg);
  792               showAppletLog(msg);
  793               repaint();
  794           }
  795           if (code != null) {
  796               applet = (Applet)loader.loadCode(code).newInstance();
  797               doInit = true;
  798           } else {
  799               // serName is not null;
  800               InputStream is = (InputStream)
  801                   java.security.AccessController.doPrivileged(
  802                                                               new java.security.PrivilegedAction() {
  803                                                                   public Object run() {
  804                                                                       return loader.getResourceAsStream(serName);
  805                                                                   }
  806                                                               });
  807               ObjectInputStream ois =
  808                   new AppletObjectInputStream(is, loader);
  809               Object serObject = ois.readObject();
  810               applet = (Applet) serObject;
  811               doInit = false; // skip over the first init
  812           }
  813   
  814           // Determine the JDK level that the applet targets.
  815           // This is critical for enabling certain backward
  816           // compatibility switch if an applet is a JDK 1.1
  817           // applet. [stanley.ho]
  818           findAppletJDKLevel(applet);
  819   
  820           if (Thread.interrupted()) {
  821               try {
  822                   status = APPLET_DISPOSE; // APPLET_ERROR?
  823                   applet = null;
  824                   // REMIND: This may not be exactly the right thing: the
  825                   // status is set by the stop button and not necessarily
  826                   // here.
  827                   showAppletStatus("death");
  828               } finally {
  829                   Thread.currentThread().interrupt(); // resignal interrupt
  830               }
  831               return null;
  832           }
  833           return applet;
  834       }
  835   
  836       protected void loadJarFiles(AppletClassLoader loader) throws IOException,
  837                                                                    InterruptedException {
  838           // Load the archives if present.
  839           // REMIND - this probably should be done in a separate thread,
  840           // or at least the additional archives (epll).
  841           String jarFiles = getJarFiles();
  842   
  843           if (jarFiles != null) {
  844               StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
  845               while(st.hasMoreTokens()) {
  846                   String tok = st.nextToken().trim();
  847                   try {
  848                       loader.addJar(tok);
  849                   } catch (IllegalArgumentException e) {
  850                       // bad archive name
  851                       continue;
  852                   }
  853               }
  854           }
  855       }
  856   
  857       /**
  858        * Request that the loading of the applet be stopped.
  859        */
  860       protected synchronized void stopLoading() {
  861           // REMIND: fill in the body
  862           if (loaderThread != null) {
  863               //System.out.println("Interrupting applet loader thread: " + loaderThread);
  864               loaderThread.interrupt();
  865           } else {
  866               setLoadAbortRequest();
  867           }
  868       }
  869   
  870   
  871       protected synchronized boolean okToLoad() {
  872           return !loadAbortRequest;
  873       }
  874   
  875       protected synchronized void clearLoadAbortRequest() {
  876           loadAbortRequest = false;
  877       }
  878   
  879       protected synchronized void setLoadAbortRequest() {
  880           loadAbortRequest = true;
  881       }
  882   
  883   
  884       private synchronized void setLoaderThread(Thread loaderThread) {
  885           this.loaderThread = loaderThread;
  886       }
  887   
  888       /**
  889        * Return true when the applet has been started.
  890        */
  891       public boolean isActive() {
  892           return status == APPLET_START;
  893       }
  894   
  895   
  896       private EventQueue appEvtQ = null;
  897       /**
  898        * Is called when the applet wants to be resized.
  899        */
  900       public void appletResize(int width, int height) {
  901           currentAppletSize.width = width;
  902           currentAppletSize.height = height;
  903           final Dimension currentSize = new Dimension(currentAppletSize.width,
  904                                                       currentAppletSize.height);
  905   
  906           if(loader != null) {
  907               AppContext appCtxt = loader.getAppContext();
  908               if(appCtxt != null)
  909                   appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY);
  910           }
  911   
  912           final AppletPanel ap = this;
  913           if (appEvtQ != null){
  914               appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
  915                                                     new Runnable(){
  916                                                         public void run(){
  917                                                             if(ap != null)
  918                                                             {
  919                                                                 ap.dispatchAppletEvent(APPLET_RESIZE, currentSize);
  920                                                             }
  921                                                         }
  922                                                     }));
  923           }
  924       }
  925   
  926       public void setBounds(int x, int y, int width, int height) {
  927           super.setBounds(x, y, width, height);
  928           currentAppletSize.width = width;
  929           currentAppletSize.height = height;
  930       }
  931   
  932       public Applet getApplet() {
  933           return applet;
  934       }
  935   
  936       /**
  937        * Status line. Called by the AppletPanel to provide
  938        * feedback on the Applet's state.
  939        */
  940       protected void showAppletStatus(String status) {
  941           getAppletContext().showStatus(amh.getMessage(status));
  942       }
  943   
  944       protected void showAppletStatus(String status, Object arg) {
  945           getAppletContext().showStatus(amh.getMessage(status, arg));
  946       }
  947       protected void showAppletStatus(String status, Object arg1, Object arg2) {
  948           getAppletContext().showStatus(amh.getMessage(status, arg1, arg2));
  949       }
  950   
  951       /**
  952        * Called by the AppletPanel to print to the log.
  953        */
  954       protected void showAppletLog(String msg) {
  955           System.out.println(amh.getMessage(msg));
  956       }
  957   
  958       protected void showAppletLog(String msg, Object arg) {
  959           System.out.println(amh.getMessage(msg, arg));
  960       }
  961   
  962       /**
  963        * Called by the AppletPanel to provide
  964        * feedback when an exception has happened.
  965        */
  966       protected void showAppletException(Throwable t) {
  967           t.printStackTrace();
  968           repaint();
  969       }
  970   
  971       /**
  972        * Get caching key for classloader cache
  973        */
  974       public String getClassLoaderCacheKey()
  975       {
  976           /**
  977            * Fixed #4501142: Classlaoder sharing policy doesn't
  978            * take "archive" into account. This will be overridden
  979            * by Java Plug-in.                     [stanleyh]
  980            */
  981           return getCodeBase().toString();
  982       }
  983   
  984       /**
  985        * The class loaders
  986        */
  987       private static HashMap classloaders = new HashMap();
  988   
  989       /**
  990        * Flush a class loader.
  991        */
  992       public static synchronized void flushClassLoader(String key) {
  993           classloaders.remove(key);
  994       }
  995   
  996       /**
  997        * Flush all class loaders.
  998        */
  999       public static synchronized void flushClassLoaders() {
 1000           classloaders = new HashMap();
 1001       }
 1002   
 1003       /**
 1004        * This method actually creates an AppletClassLoader.
 1005        *
 1006        * It can be override by subclasses (such as the Plug-in)
 1007        * to provide different classloaders.
 1008        */
 1009       protected AppletClassLoader createClassLoader(final URL codebase) {
 1010           return new AppletClassLoader(codebase);
 1011       }
 1012   
 1013       /**
 1014        * Get a class loader. Create in a restricted context
 1015        */
 1016       synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) {
 1017           AppletClassLoader c = (AppletClassLoader)classloaders.get(key);
 1018           if (c == null) {
 1019               AccessControlContext acc =
 1020                   getAccessControlContext(codebase);
 1021               c = (AppletClassLoader)
 1022                   AccessController.doPrivileged(new PrivilegedAction() {
 1023                           public Object run() {
 1024                               AppletClassLoader ac = createClassLoader(codebase);
 1025                               /* Should the creation of the classloader be
 1026                                * within the class synchronized block?  Since
 1027                                * this class is used by the plugin, take care
 1028                                * to avoid deadlocks, or specialize
 1029                                * AppletPanel within the plugin.  It may take
 1030                                * an arbitrary amount of time to create a
 1031                                * class loader (involving getting Jar files
 1032                                * etc.) and may block unrelated applets from
 1033                                * finishing createAppletThread (due to the
 1034                                * class synchronization). If
 1035                                * createAppletThread does not finish quickly,
 1036                                * the applet cannot process other messages,
 1037                                * particularly messages such as destroy
 1038                                * (which timeout when called from the browser).
 1039                                */
 1040                               synchronized (getClass()) {
 1041                                   AppletClassLoader res =
 1042                                       (AppletClassLoader)classloaders.get(key);
 1043                                   if (res == null) {
 1044                                       classloaders.put(key, ac);
 1045                                       return ac;
 1046                                   } else {
 1047                                       return res;
 1048                                   }
 1049                               }
 1050                           }
 1051                       },acc);
 1052           }
 1053           return c;
 1054       }
 1055   
 1056       /**
 1057        * get the context for the AppletClassLoader we are creating.
 1058        * the context is granted permission to create the class loader,
 1059        * connnect to the codebase, and whatever else the policy grants
 1060        * to all codebases.
 1061        */
 1062       private AccessControlContext getAccessControlContext(final URL codebase) {
 1063   
 1064           PermissionCollection perms = (PermissionCollection)
 1065               AccessController.doPrivileged(new PrivilegedAction() {
 1066                       public Object run() {
 1067                           Policy p = java.security.Policy.getPolicy();
 1068                           if (p != null) {
 1069                               return p.getPermissions(new CodeSource(null,
 1070                                                                      (java.security.cert.Certificate[]) null));
 1071                           } else {
 1072                               return null;
 1073                           }
 1074                       }
 1075                   });
 1076   
 1077           if (perms == null)
 1078               perms = new Permissions();
 1079   
 1080           //XXX: this is needed to be able to create the classloader itself!
 1081   
 1082           perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
 1083   
 1084           Permission p;
 1085           java.net.URLConnection urlConnection = null;
 1086           try {
 1087               urlConnection = codebase.openConnection();
 1088               p = urlConnection.getPermission();
 1089           } catch (java.io.IOException ioe) {
 1090               p = null;
 1091           }
 1092   
 1093           if (p != null)
 1094               perms.add(p);
 1095   
 1096           if (p instanceof FilePermission) {
 1097   
 1098               String path = p.getName();
 1099   
 1100               int endIndex = path.lastIndexOf(File.separatorChar);
 1101   
 1102               if (endIndex != -1) {
 1103                   path = path.substring(0, endIndex+1);
 1104   
 1105                   if (path.endsWith(File.separator)) {
 1106                       path += "-";
 1107                   }
 1108                   perms.add(new FilePermission(path,
 1109                                                SecurityConstants.FILE_READ_ACTION));
 1110               }
 1111           } else {
 1112               URL locUrl = codebase;
 1113               if (urlConnection instanceof JarURLConnection) {
 1114                   locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
 1115               }
 1116               String host = locUrl.getHost();
 1117               if (host != null && (host.length() > 0))
 1118                   perms.add(new SocketPermission(host,
 1119                                                  SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
 1120           }
 1121   
 1122           ProtectionDomain domain =
 1123               new ProtectionDomain(new CodeSource(codebase,
 1124                                                   (java.security.cert.Certificate[]) null), perms);
 1125           AccessControlContext acc =
 1126               new AccessControlContext(new ProtectionDomain[] { domain });
 1127   
 1128           return acc;
 1129       }
 1130   
 1131       public Thread getAppletHandlerThread() {
 1132           return handler;
 1133       }
 1134   
 1135       public int getAppletWidth() {
 1136           return currentAppletSize.width;
 1137       }
 1138   
 1139       public int getAppletHeight() {
 1140           return currentAppletSize.height;
 1141       }
 1142   
 1143       public static void changeFrameAppContext(Frame frame, AppContext newAppContext)
 1144       {
 1145           // Fixed #4754451: Applet can have methods running on main
 1146           // thread event queue.
 1147           //
 1148           // The cause of this bug is that the frame of the applet
 1149           // is created in main thread group. Thus, when certain
 1150           // AWT/Swing events are generated, the events will be
 1151           // dispatched through the wrong event dispatch thread.
 1152           //
 1153           // To fix this, we rearrange the AppContext with the frame,
 1154           // so the proper event queue will be looked up.
 1155           //
 1156           // Swing also maintains a Frame list for the AppContext,
 1157           // so we will have to rearrange it as well.
 1158   
 1159           // Check if frame's AppContext has already been set properly
 1160           AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
 1161   
 1162           if (oldAppContext == newAppContext)
 1163               return;
 1164   
 1165           // Synchronization on Window.class is needed for locking the
 1166           // critical section of the window list in AppContext.
 1167           synchronized (Window.class)
 1168           {
 1169               WeakReference weakRef = null;
 1170               // Remove frame from the Window list in wrong AppContext
 1171               {
 1172                   // Lookup current frame's AppContext
 1173                   Vector<WeakReference<Window>> windowList = (Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
 1174                   if (windowList != null) {
 1175                       for (WeakReference ref : windowList) {
 1176                           if (ref.get() == frame) {
 1177                               weakRef = ref;
 1178                               break;
 1179                           }
 1180                       }
 1181                       // Remove frame from wrong AppContext
 1182                       if (weakRef != null)
 1183                           windowList.remove(weakRef);
 1184                   }
 1185               }
 1186   
 1187               // Put the frame into the applet's AppContext map
 1188               SunToolkit.insertTargetMapping(frame, newAppContext);
 1189   
 1190               // Insert frame into the Window list in the applet's AppContext map
 1191               {
 1192                   Vector<WeakReference<Window>> windowList = (Vector)newAppContext.get(Window.class);
 1193                   if (windowList == null) {
 1194                       windowList = new Vector<WeakReference<Window>>();
 1195                       newAppContext.put(Window.class, windowList);
 1196                   }
 1197                   // use the same weakRef here as it is used elsewhere
 1198                   windowList.add(weakRef);
 1199               }
 1200           }
 1201       }
 1202   
 1203       // Flag to indicate if applet is targeted for JDK 1.1.
 1204       private boolean jdk11Applet = false;
 1205   
 1206       // Flag to indicate if applet is targeted for JDK 1.2.
 1207       private boolean jdk12Applet = false;
 1208   
 1209       /**
 1210        * Determine JDK level of an applet.
 1211        */
 1212       private void findAppletJDKLevel(Applet applet)
 1213       {
 1214           // To determine the JDK level of an applet, the
 1215           // most reliable way is to check the major version
 1216           // of the applet class file.
 1217   
 1218           // synchronized on applet class object, so calling from
 1219           // different instances of the same applet will be
 1220           // serialized.
 1221           Class appletClass = applet.getClass();
 1222   
 1223           synchronized(appletClass)  {
 1224               // Determine if the JDK level of an applet has been
 1225               // checked before.
 1226               Boolean jdk11Target = (Boolean) loader.isJDK11Target(appletClass);
 1227               Boolean jdk12Target = (Boolean) loader.isJDK12Target(appletClass);
 1228   
 1229               // if applet JDK level has been checked before, retrieve
 1230               // value and return.
 1231               if (jdk11Target != null || jdk12Target != null) {
 1232                   jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
 1233                   jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
 1234                   return;
 1235               }
 1236   
 1237               String name = appletClass.getName();
 1238   
 1239               // first convert any '.' to '/'
 1240               name = name.replace('.', '/');
 1241   
 1242               // append .class
 1243               final String resourceName = name + ".class";
 1244   
 1245               InputStream is = null;
 1246               byte[] classHeader = new byte[8];
 1247   
 1248               try {
 1249                   is = (InputStream) java.security.AccessController.doPrivileged(
 1250                       new java.security.PrivilegedAction() {
 1251                           public Object run() {
 1252                               return loader.getResourceAsStream(resourceName);
 1253                           }
 1254                       });
 1255   
 1256                   // Read the first 8 bytes of the class file
 1257                   int byteRead = is.read(classHeader, 0, 8);
 1258                   is.close();
 1259   
 1260                   // return if the header is not read in entirely
 1261                   // for some reasons.
 1262                   if (byteRead != 8)
 1263                       return;
 1264               }
 1265               catch (IOException e)   {
 1266                   return;
 1267               }
 1268   
 1269               // Check major version in class file header
 1270               int major_version = readShort(classHeader, 6);
 1271   
 1272               // Major version in class file is as follows:
 1273               //   45 - JDK 1.1
 1274               //   46 - JDK 1.2
 1275               //   47 - JDK 1.3
 1276               //   48 - JDK 1.4
 1277               //   49 - JDK 1.5
 1278               if (major_version < 46)
 1279                   jdk11Applet = true;
 1280               else if (major_version == 46)
 1281                   jdk12Applet = true;
 1282   
 1283               // Store applet JDK level in AppContext for later lookup,
 1284               // e.g. page switch.
 1285               loader.setJDK11Target(appletClass, jdk11Applet);
 1286               loader.setJDK12Target(appletClass, jdk12Applet);
 1287           }
 1288       }
 1289   
 1290       /**
 1291        * Return true if applet is targeted to JDK 1.1.
 1292        */
 1293       protected boolean isJDK11Applet()   {
 1294           return jdk11Applet;
 1295       }
 1296   
 1297       /**
 1298        * Return true if applet is targeted to JDK1.2.
 1299        */
 1300       protected boolean isJDK12Applet()   {
 1301           return jdk12Applet;
 1302       }
 1303   
 1304       /**
 1305        * Read short from byte array.
 1306        */
 1307       private int readShort(byte[] b, int off)    {
 1308           int hi = readByte(b[off]);
 1309           int lo = readByte(b[off + 1]);
 1310           return (hi << 8) | lo;
 1311       }
 1312   
 1313       private int readByte(byte b) {
 1314           return ((int)b) & 0xFF;
 1315       }
 1316   
 1317   
 1318       private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel");
 1319   }

Save This Page
Home » openjdk-7 » sun » applet » [javadoc | source]