001: /*
002: * @(#)ImageFetcher.java 1.29 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.awt.image;
029:
030: import java.util.Vector;
031: import sun.awt.AppContext;
032: import java.security.AccessController;
033: import java.security.PrivilegedAction;
034:
035: /**
036: * An ImageFetcher is a thread used to fetch ImageFetchable objects.
037: * Once an ImageFetchable object has been fetched, the ImageFetcher
038: * thread may also be used to animate it if necessary, via the
039: * startingAnimation() / stoppingAnimation() methods.
040: *
041: * There can be up to FetcherInfo.MAX_NUM_FETCHERS_PER_APPCONTEXT
042: * ImageFetcher threads for each AppContext. A per-AppContext queue
043: * of ImageFetchables is used to track objects to fetch.
044: *
045: */
046: class ImageFetcher extends Thread {
047: static final int HIGH_PRIORITY = 8;
048: static final int LOW_PRIORITY = 3;
049: static final int ANIM_PRIORITY = 2;
050:
051: static final int TIMEOUT = 5000; // Time in milliseconds to wait for an
052:
053: // ImageFetchable to be added to the
054: // queue before an ImageFetcher dies
055:
056: /**
057: * Constructor for ImageFetcher -- only called by add() below.
058: */
059: private ImageFetcher(ThreadGroup threadGroup, int index) {
060: super (threadGroup, "Image Fetcher " + index);
061: setDaemon(true);
062: }
063:
064: /**
065: * Adds an ImageFetchable to the queue of items to fetch. Instantiates
066: * a new ImageFetcher if it's reasonable to do so.
067: */
068: public static void add(ImageFetchable src) {
069: final FetcherInfo info = FetcherInfo.getFetcherInfo();
070: synchronized (info.waitList) {
071: if (!info.waitList.contains(src)) {
072: info.waitList.addElement(src);
073: if (info.numWaiting == 0
074: && info.numFetchers < info.fetchers.length) {
075: createFetchers(info);
076: }
077: info.waitList.notify();
078: }
079: }
080: }
081:
082: /**
083: * Removes an ImageFetchable from the queue of items to fetch.
084: */
085: public static void remove(ImageFetchable src) {
086: final FetcherInfo info = FetcherInfo.getFetcherInfo();
087: synchronized (info.waitList) {
088: if (info.waitList.contains(src)) {
089: info.waitList.removeElement(src);
090: }
091: }
092: }
093:
094: /**
095: * Checks to see if the given thread is one of the ImageFetchers.
096: */
097: public static boolean isFetcher(Thread t) {
098: final FetcherInfo info = FetcherInfo.getFetcherInfo();
099: synchronized (info.waitList) {
100: for (int i = 0; i < info.fetchers.length; i++) {
101: if (info.fetchers[i] == t) {
102: return true;
103: }
104: }
105: }
106: return false;
107: }
108:
109: /**
110: * Checks to see if the current thread is one of the ImageFetchers.
111: */
112: public static boolean amFetcher() {
113: return isFetcher(Thread.currentThread());
114: }
115:
116: /**
117: * Returns the next ImageFetchable to be processed. If TIMEOUT
118: * elapses in the mean time, or if the ImageFetcher is interrupted,
119: * null is returned.
120: */
121: private static ImageFetchable nextImage() {
122: final FetcherInfo info = FetcherInfo.getFetcherInfo();
123: synchronized (info.waitList) {
124: ImageFetchable src = null;
125: long end = System.currentTimeMillis() + TIMEOUT;
126: while (src == null) {
127: while (info.waitList.size() == 0) {
128: long now = System.currentTimeMillis();
129: if (now >= end) {
130: return null;
131: }
132: try {
133: info.numWaiting++;
134: info.waitList.wait(end - now);
135: } catch (InterruptedException e) {
136: // A normal occurrence as an AppContext is disposed
137: return null;
138: } finally {
139: info.numWaiting--;
140: }
141: }
142: src = (ImageFetchable) info.waitList.elementAt(0);
143: info.waitList.removeElement(src);
144: }
145: return src;
146: }
147: }
148:
149: /**
150: * The main run() method of an ImageFetcher Thread. Calls fetchloop()
151: * to do the work, then removes itself from the array of ImageFetchers.
152: */
153: public void run() {
154: final FetcherInfo info = FetcherInfo.getFetcherInfo();
155: try {
156: fetchloop();
157: } catch (Exception e) {
158: e.printStackTrace();
159: } finally {
160: synchronized (info.waitList) {
161: Thread me = Thread.currentThread();
162: for (int i = 0; i < info.fetchers.length; i++) {
163: if (info.fetchers[i] == me) {
164: info.fetchers[i] = null;
165: info.numFetchers--;
166: }
167: }
168: }
169: }
170: }
171:
172: /**
173: * The main ImageFetcher loop. Repeatedly calls nextImage(), and
174: * fetches the returned ImageFetchable objects until nextImage()
175: * returns null.
176: */
177: private void fetchloop() {
178: Thread me = Thread.currentThread();
179: while (isFetcher(me)) {
180:
181: // 6263016: Problems with ImageFetcher and MediaTracker.
182: // See fix below for 4789067.
183: /*
184: if (Thread.interrupted())
185: return; // Bail if interrupted
186: */
187:
188: // we're ignoring the return value and just clearing
189: // the interrupted flag, instead of bailing out if
190: // the fetcher was interrupted, as we used to,
191: // because there may be other images waiting
192: // to be fetched (see 4789067)
193: me.interrupted();
194: me.setPriority(HIGH_PRIORITY);
195: ImageFetchable src = nextImage();
196: if (src == null) {
197: return;
198: }
199: try {
200: src.doFetch();
201: } catch (Exception e) {
202: System.err.println("Uncaught error fetching image:");
203: e.printStackTrace();
204: }
205: stoppingAnimation(me);
206: }
207: }
208:
209: /**
210: * Recycles this ImageFetcher thread as an image animator thread.
211: * Removes this ImageFetcher from the array of ImageFetchers, and
212: * resets the thread name to "ImageAnimator".
213: */
214: static void startingAnimation() {
215: final FetcherInfo info = FetcherInfo.getFetcherInfo();
216: Thread me = Thread.currentThread();
217: synchronized (info.waitList) {
218: for (int i = 0; i < info.fetchers.length; i++) {
219: if (info.fetchers[i] == me) {
220: info.fetchers[i] = null;
221: info.numFetchers--;
222: me.setName("Image Animator " + i);
223: if (info.waitList.size() > info.numWaiting) {
224: createFetchers(info);
225: }
226: return;
227: }
228: }
229: }
230: me.setPriority(ANIM_PRIORITY);
231: me.setName("Image Animator");
232: }
233:
234: /**
235: * Returns this image animator thread back to service as an ImageFetcher
236: * if possible. Puts it back into the array of ImageFetchers and sets
237: * the thread name back to "Image Fetcher". If there are already the
238: * maximum number of ImageFetchers, this method simply returns, and
239: * fetchloop() will drop out when it sees that this thread isn't one of
240: * the ImageFetchers, and this thread will die.
241: */
242: private static void stoppingAnimation(Thread me) {
243: final FetcherInfo info = FetcherInfo.getFetcherInfo();
244: synchronized (info.waitList) {
245: int index = -1;
246: for (int i = 0; i < info.fetchers.length; i++) {
247: if (info.fetchers[i] == me) {
248: return;
249: }
250: if (info.fetchers[i] == null) {
251: index = i;
252: }
253: }
254: if (index >= 0) {
255: info.fetchers[index] = me;
256: info.numFetchers++;
257: me.setName("Image Fetcher " + index);
258: return;
259: }
260: }
261: }
262:
263: /**
264: * Create and start ImageFetcher threads in the appropriate ThreadGroup.
265: */
266: private static void createFetchers(final FetcherInfo info) {
267: // We need to instantiate a new ImageFetcher thread.
268: // First, figure out which ThreadGroup we'll put the
269: // new ImageFetcher into
270: final AppContext appContext = AppContext.getAppContext();
271: ThreadGroup threadGroup = appContext.getThreadGroup();
272: ThreadGroup fetcherThreadGroup;
273: try {
274: if (threadGroup.getParent() != null) {
275: // threadGroup is not the root, so we proceed
276: fetcherThreadGroup = threadGroup;
277: } else {
278: // threadGroup is the root ("system") ThreadGroup.
279: // We instead want to use its child: the "main"
280: // ThreadGroup. Thus, we start with the current
281: // ThreadGroup, and go up the tree until
282: // threadGroup.getParent().getParent() == null.
283: threadGroup = Thread.currentThread().getThreadGroup();
284: ThreadGroup parent = threadGroup.getParent();
285: while ((parent != null) && (parent.getParent() != null)) {
286: threadGroup = parent;
287: parent = threadGroup.getParent();
288: }
289: fetcherThreadGroup = threadGroup;
290: }
291: } catch (SecurityException e) {
292: // Not allowed access to parent ThreadGroup -- just use
293: // the AppContext's ThreadGroup
294: fetcherThreadGroup = appContext.getThreadGroup();
295: }
296: final ThreadGroup fetcherGroup = fetcherThreadGroup;
297:
298: java.security.AccessController
299: .doPrivileged(new java.security.PrivilegedAction() {
300: public Object run() {
301: for (int i = 0; i < info.fetchers.length; i++) {
302: if (info.fetchers[i] == null) {
303: info.fetchers[i] = new ImageFetcher(
304: fetcherGroup, i);
305: info.fetchers[i].start();
306: info.numFetchers++;
307: break;
308: }
309: }
310: return null;
311: }
312: });
313: return;
314: }
315:
316: }
317:
318: /**
319: * The FetcherInfo class encapsulates the per-AppContext ImageFetcher
320: * information. This includes the array of ImageFetchers, as well as
321: * the queue of ImageFetchable objects.
322: */
323: class FetcherInfo {
324: static final int MAX_NUM_FETCHERS_PER_APPCONTEXT = 4;
325:
326: Thread[] fetchers;
327: int numFetchers;
328: int numWaiting;
329: Vector waitList;
330:
331: private FetcherInfo() {
332: fetchers = new Thread[MAX_NUM_FETCHERS_PER_APPCONTEXT];
333: numFetchers = 0;
334: numWaiting = 0;
335: waitList = new Vector();
336: }
337:
338: /* The key to put()/get() the FetcherInfo into/from the AppContext. */
339: private static final Object FETCHER_INFO_KEY = new StringBuffer(
340: "FetcherInfo");
341:
342: static FetcherInfo getFetcherInfo() {
343: AppContext appContext = AppContext.getAppContext();
344: synchronized (appContext) {
345: FetcherInfo info = (FetcherInfo) appContext
346: .get(FETCHER_INFO_KEY);
347: if (info == null) {
348: info = new FetcherInfo();
349: appContext.put(FETCHER_INFO_KEY, info);
350: }
351: return info;
352: }
353: }
354: }
|