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 }