History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/applet/AppletPanel.java (1 of 18352 files). Show all

Unified · Split

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