source: trunk/src/org/expeditee/taskmanagement/EntityLoadManager.java@ 1428

Last change on this file since 1428 was 919, checked in by jts21, 10 years ago

Added license headers to all files, added full GPL3 license file, moved license header generator script to dev/bin/scripts

File size: 7.7 KB
Line 
1/**
2 * EntityLoadManager.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.taskmanagement;
20
21import java.util.HashMap;
22import java.util.PriorityQueue;
23
24
25
26/**
27 * Manages asynchronous loading. It is essentially a daemon that performs all expensive proccessing
28 * for a frame.
29 *
30 * The Objective / Purpose:
31 * To load data on Expeditee frames which can take long periods of time in a way such that
32 * navigating through frames with lots of data is smooth / fast.
33 *
34 * This helps reduce vital usability concerns with frame load times - a conern of high importance
35 * in KMS publications...
36 *
37 * @author Brook Novak
38 *
39 */
40public final class EntityLoadManager {
41
42 /**
43 * Singleton design pattern
44 */
45 private static EntityLoadManager _instance = new EntityLoadManager();
46 public static EntityLoadManager getInstance() {
47 return _instance;
48 }
49
50 /**
51 * Start daemon right away
52 */
53 private EntityLoaderDaemon loadDaemon;
54 private EntityLoadManager() {
55 loadDaemon = new EntityLoaderDaemon();
56 loadDaemon.start();
57 }
58
59 private PriorityQueue<QueuedEntity> loadQueue = new PriorityQueue<QueuedEntity>();
60
61 private HashMap<LoadableEntity, QueuedEntity> queueMap = new HashMap<LoadableEntity, QueuedEntity>();
62
63 private QueuedEntity busyEntity = null;
64
65 private Object entityLocker = new Object(); // union of queueMap, queueMap and busyEntity resources
66
67 /**
68 * Queue's an entity for loading.
69 * This operation is threadsafe.
70 *
71 * @param entity
72 * The entity to load.
73 *
74 * @param minDelay
75 * The minimum time to wait before loading in milliseconds.
76 *
77 * @return
78 * True if the entity was added. False if the entity is already queued prior to this call.
79 * Note that if entity is currently loading, it will still be added to the queue, this
80 * allows for requeueing a busy entity.
81 *
82 */
83 public boolean queue(LoadableEntity entity, int minDelay) {
84 synchronized(entityLocker) {
85
86 if (queueMap.containsKey(entity))
87 return false;
88
89 QueuedEntity qe = new QueuedEntity(entity, (int)minDelay, 0);
90 loadQueue.add(qe);
91 queueMap.put(entity, qe);
92 }
93
94 // Ensure daemon knows of new item
95 loadDaemon.reschedule();
96
97 return true;
98 }
99
100 /**
101 * Increasing a priority on an entity will invalidate the wait-time
102 * until it is loaded and push it up the load-queue.
103 *
104 * Proceeding calls to this on the same LoadableEntity will cumatively
105 * increase its priority so that it will continually be pushed up in the
106 * priority queue.
107 *
108 * This operation is threadsafe.
109 *
110 * @param entity The entity to increase its priority
111 */
112 public void increasePriority(LoadableEntity entity) {
113
114 synchronized(entityLocker) {
115
116 QueuedEntity qe = queueMap.remove(entity);
117 loadQueue.remove(qe);
118
119 if (qe != null) {
120 qe = qe.higherPriority();
121 loadQueue.add(qe);
122 queueMap.put(entity, qe);
123 }
124
125 }
126
127 // Ensure daemon knows of new item
128 loadDaemon.reschedule();
129
130 }
131
132 /**
133 * Cancels all loadable entities on the queue.
134 * If an entity is currently being loaded that
135 *
136 * Note that it is up to the entities descretion to stop loading.
137 * As far as the LoadTimeManager is concerned all entities are cancelled
138 * and the queue is emptied.
139 *
140 * This operation is threadsafe.
141 */
142 public void cancelAll() {
143
144 synchronized(entityLocker) {
145
146 // Notify all entities that they are cancelled
147 for (QueuedEntity qe : loadQueue) {
148 qe.entity.cancelLoadRequested();
149 }
150
151 // Clear queue / map
152 loadQueue.clear();
153 queueMap.clear();
154
155 // Cancel current entity if applicable
156 if (busyEntity != null) {
157 busyEntity.entity.cancelLoadRequested();
158 busyEntity = null;
159 }
160 }
161 }
162
163 /**
164 * Cancels a specific loadable entity from loading. Removes it from the queue
165 *
166 * @param entity
167 */
168 public void cancel(LoadableEntity entity) {
169
170 synchronized(entityLocker) {
171 QueuedEntity qe = queueMap.remove(entity);
172 if (qe != null) {
173 loadQueue.remove(qe);
174 } else if (busyEntity != null && busyEntity.entity == entity) {
175 busyEntity.entity.cancelLoadRequested();
176 busyEntity = null;
177 }
178 }
179
180 }
181
182 private class EntityLoaderDaemon extends Thread {
183
184 private Object idleLocker = new Object(); // Daemon sleeps on this
185
186 public EntityLoaderDaemon() {
187 super("EntityLoaderDaemon");
188 setDaemon(true);
189 }
190
191 /**
192 * Invoke to wake up the daemon and recheck the queue
193 */
194 public void reschedule() {
195 synchronized(idleLocker) {
196 idleLocker.notify();
197 }
198 }
199
200 @Override
201 public void run() {
202
203 while (true) { // continually load whatever needs loading (daemon)
204
205 // See if there is an item in the load queu
206 long waitTime = -1;
207 QueuedEntity qe = null;
208 synchronized(entityLocker) {
209
210 assert (busyEntity == null);
211 busyEntity = loadQueue.peek();
212
213 if (busyEntity != null) {
214 // See if it is time to load the entity
215 waitTime = busyEntity.loadTime - System.currentTimeMillis();
216 if (busyEntity.priority > 0 || waitTime <= 0) {
217 loadQueue.remove(); // remove the entity
218 queueMap.remove(busyEntity.entity);
219 qe = busyEntity;
220 } else {
221 busyEntity = null; // not ready to load yet
222 }
223 }
224
225 }
226
227 // If there is nothing to load
228 if (qe == null) {
229 try {
230 synchronized(idleLocker) { // obtain idleLocker's monitor
231 if (waitTime > 0) { // if there is a entity to load in a few momenets, wait until delay elapsed
232 idleLocker.wait(waitTime);
233 } else { // If there is nothing to load, then wait until something is added
234 idleLocker.wait();
235 }
236 }
237 } catch (InterruptedException e) { // Something added/re-prioritized.
238 /* Consume */
239 }
240
241 } else { // load a queued entity
242
243 try {
244 qe.entity.performLoad();
245 } catch (Exception e) { // safety
246 e.printStackTrace();
247 }
248
249 synchronized(entityLocker) { // Not busy anymore
250 busyEntity = null;
251 }
252
253 }
254
255 } // continue running daemon
256 }
257 }
258
259 /**
260 * Used for prioritized queue
261 *
262 * @author Brook
263 */
264 private class QueuedEntity implements Comparable {
265
266 QueuedEntity(LoadableEntity entity, int minDelay, int priority) {
267 this.entity = entity;
268 this.priority = priority;
269 loadTime = System.currentTimeMillis() + minDelay;
270 }
271
272 private QueuedEntity(LoadableEntity entity, long loadTime, int priority) {
273 this.entity = entity;
274 this.priority = priority;
275 this.loadTime = loadTime;
276 }
277
278 public QueuedEntity higherPriority() {
279 return new QueuedEntity(entity, loadTime, priority + 1);
280 }
281
282 private LoadableEntity entity;
283 private long loadTime;
284 private int priority;
285
286 public int compareTo(Object obj) {
287 QueuedEntity other = (QueuedEntity)obj;
288 if (priority == other.priority) {
289 return new Long(loadTime).compareTo(other.loadTime);
290 } else if (priority < 0) {
291 return -1;
292 }
293 return 1;
294
295 }
296
297 @Override
298 public int hashCode() {
299 return entity.hashCode();
300 }
301
302 @Override
303 public boolean equals(Object obj) {
304 return entity.equals(obj);
305 }
306 }
307
308
309}
Note: See TracBrowser for help on using the repository browser.