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