4fafe7fc71ec8827b228156c065916d4e907997e
[mir.git] / source / mircoders / global / JobQueue.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30
31 package mircoders.global;
32
33
34 import java.util.Date;
35 import java.util.GregorianCalendar;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.*;
41
42 import mir.log.*;
43
44 // important: objects passed as data must not be altered once put into a job
45
46 public class JobQueue {
47   private Vector jobHandlers;
48   private Map identifierToJobHandler;
49   private int nrJobs;
50   private int jobCleanupTreshold;
51   private JobQueueRunner queueRunner;
52   private Thread thread;
53   private LoggerWrapper logger;
54
55   public static final int STATUS_CREATED = -1;
56   public static final int STATUS_PENDING = 0;
57   public static final int STATUS_PROCESSING = 1;
58   public static final int STATUS_PROCESSED = 2;
59   public static final int STATUS_CANCELLED = 3;
60   public static final int STATUS_ABORTED = 4;
61
62   public static final int PRIORITY_NORMAL = 100;
63   public static final int PRIORITY_LOW = 10;
64   public static final int PRIORITY_HIGH = 1000;
65
66   public static final int FINISHEDJOBS_LOGSIZE = 10;
67
68   public JobQueue(LoggerWrapper aLogger) {
69     logger = aLogger;
70     jobHandlers = new Vector();
71     identifierToJobHandler = new HashMap();
72     nrJobs = 0;
73     jobCleanupTreshold = 1000;
74     queueRunner = new JobQueueRunner(logger);
75     thread = new Thread(queueRunner);
76     thread.start();
77   }
78
79   public String appendJob(Job aJob, String aDescription) {
80     synchronized (jobHandlers) {
81       JobHandler jobHandler = new JobHandler(aJob, Integer.toString(nrJobs), aDescription);
82       nrJobs++;
83       jobHandlers.add(jobHandler);
84       identifierToJobHandler.put(jobHandler.getIdentifier(), jobHandler);
85       jobHandler.setPending();
86
87       return jobHandler.getIdentifier();
88     }
89   }
90
91   public List getJobsInfo() {
92     List result = new Vector();
93
94     synchronized (jobHandlers) {
95       Iterator i = jobHandlers.iterator();
96
97       while (i.hasNext()) {
98         result.add(((JobHandler) i.next()).getJobInfo());
99       }
100     }
101
102     return result;
103   }
104
105   private void cleanupJobList() {
106     synchronized (jobHandlers) {
107       Iterator i = jobHandlers.iterator();
108
109       Calendar tresholdCalendar = new GregorianCalendar();
110       tresholdCalendar.add(Calendar.SECOND, -jobCleanupTreshold);
111       Date treshold = tresholdCalendar.getTime();
112
113       while (i.hasNext()) {
114         JobHandler jobHandler = (JobHandler) i.next();
115
116         synchronized (jobHandler) {
117           if (jobHandler.isFinished() && jobHandler.getLastChange().before(treshold)) {
118             jobHandlers.remove(jobHandler);
119           }
120         }
121       }
122     }
123   }
124
125   private JobHandler acquirePendingJob() {
126     synchronized (jobHandlers) {
127       int priorityFound= 0;
128       JobHandler jobFound;
129
130       jobFound = null;
131       Iterator i = jobHandlers.iterator();
132       while (i.hasNext()) {
133         JobHandler job = (JobHandler) i.next();
134
135         if (job.isPending() && (jobFound==null || priorityFound<job.getPriority())) {
136           jobFound = job;
137           priorityFound = job.getPriority();
138         }
139       }
140
141       return jobFound;
142     }
143   }
144
145   public void cancelJobs(List aJobs) {
146     synchronized (jobHandlers) {
147       Iterator i = aJobs.iterator();
148
149       while (i.hasNext()) {
150         ((JobHandler) identifierToJobHandler.get(i.next())).cancelOrAbortJob();
151       }
152     }
153   }
154
155   public interface Job {
156     void abort();
157
158     /**
159      *
160      *
161      * @return <code>true</code> if terminated normally, <code>false</code> if aborted
162      */
163     boolean run();
164   }
165
166   public static class JobInfo {
167     private String identifier;
168     private Date lastChange;
169     private int status;
170     private long runningTime;
171     private int priority;
172     private String description;
173
174     private JobInfo(String aDescription, int aStatus, Date aLastChange, String anIdentifier, long aRunningTime, int aPriority) {
175       description = aDescription;
176       lastChange = aLastChange;
177       status = aStatus;
178       identifier = anIdentifier;
179       priority = aPriority;
180       runningTime = aRunningTime;
181     }
182
183     public String getDescription() {
184       return description;
185     }
186
187     public int getStatus() {
188       return status;
189     }
190
191     public int getPriority() {
192       return priority;
193     }
194
195     public Date getLastChange() {
196       return lastChange;
197     }
198
199     public String getIdentifier() {
200       return identifier;
201     }
202
203     public long getRunningTime() {
204       return runningTime;
205     }
206   }
207
208   public class JobHandler {
209     private Job job;
210     private String identifier;
211     private String description;
212
213     private Date lastChange;
214     private long starttime;
215     private long endtime;
216     private int status;
217     private int priority;
218     private boolean hasRun;
219
220     public JobHandler(Job aJob, String anIdentifier, String aDescription, int aPriority) {
221       job = aJob;
222       description = aDescription;
223       identifier = anIdentifier;
224       priority = aPriority;
225       status = STATUS_CREATED;
226     }
227
228     public JobHandler(Job aJob, String anIdentifier, String aDescription) {
229       this(aJob, anIdentifier, aDescription, PRIORITY_NORMAL);
230     }
231
232     public JobInfo getJobInfo() {
233       return new JobInfo(getDescription(), getStatus(), getLastChange(), getIdentifier(), getRunningTime(), priority);
234     }
235
236     private void runJob() {
237       if (setProcessing()) {
238         if (job.run())
239           setProcessed();
240         else
241           setAborted();
242       }
243     };
244
245     private void cancelOrAbortJob() {
246       synchronized (this) {
247         if (isPending())
248           setCancelled();
249         if (isProcessing())
250           job.abort();
251       }
252     };
253
254     public int getStatus() {
255       synchronized(this) {
256         return status;
257       }
258     }
259
260     public String getIdentifier() {
261       return identifier;
262     }
263
264     public String getDescription() {
265       return description;
266     }
267
268     public long getRunningTime() {
269       synchronized(this) {
270         long result = 0;
271
272         if (hasRun) {
273           if (isFinished())
274             result = endtime;
275           else
276             result = System.currentTimeMillis();
277
278           result = result - starttime;
279         }
280
281         return result;
282       }
283     }
284
285     public int getPriority() {
286       return priority;
287     }
288
289     private boolean setProcessing() {
290       return setStatus(STATUS_PENDING, STATUS_PROCESSING);
291     }
292
293     private void setProcessed() {
294       setStatus(STATUS_PROCESSING, STATUS_PROCESSED);
295     }
296
297     private void setAborted() {
298       setStatus(STATUS_PROCESSING, STATUS_ABORTED);
299     }
300
301     private void setPending() {
302       setStatus(STATUS_CREATED, STATUS_PENDING);
303     }
304
305     private boolean setCancelled() {
306       return setStatus(STATUS_PENDING, STATUS_CANCELLED);
307     }
308
309     public boolean hasBeenProcessed() {
310       return getStatus() == STATUS_PROCESSED;
311     }
312
313     public boolean hasBeenAborted() {
314       return getStatus() == STATUS_ABORTED;
315     }
316
317     public boolean isCancelled() {
318       return getStatus() == STATUS_CANCELLED;
319     }
320
321     public boolean isFinished() {
322       return hasBeenProcessed() || hasBeenAborted() || isCancelled();
323     }
324
325     public boolean isProcessing() {
326       return getStatus() == STATUS_PROCESSING;
327     }
328
329     public boolean isPending() {
330       return getStatus() == STATUS_PENDING;
331     }
332
333     public Date getLastChange() {
334       synchronized (this) {
335         return lastChange;
336       }
337     }
338
339     private boolean setStatus(int anOldStatus, int aNewStatus) {
340       synchronized(this) {
341         if (status == anOldStatus) {
342           status = aNewStatus;
343           lastChange = (new GregorianCalendar()).getTime();
344           if (isProcessing()) {
345             starttime = System.currentTimeMillis();
346             hasRun = true;
347           }
348
349           if (isFinished()) {
350             endtime = System.currentTimeMillis();
351           }
352           return true;
353         }
354         else {
355           return false;
356         }
357       }
358     }
359   }
360
361   private class JobQueueRunner implements Runnable {
362     private LoggerWrapper logger;
363
364     public JobQueueRunner(LoggerWrapper aLogger) {
365       logger = aLogger;
366     }
367
368     public void run() {
369       logger.debug("starting JobQueueRunner");
370
371       try {
372         while (true) {
373           JobHandler job = acquirePendingJob();
374           if (job != null) {
375             logger.debug("  starting job ("+job.getIdentifier()+"): " +job.getDescription());
376             job.runJob();
377             logger.debug("  finished job ("+job.getIdentifier()+"): " +job.getDescription());
378           }
379           else {
380             try {
381               Thread.sleep(1500);
382             }
383             catch (InterruptedException e) {
384             }
385           }
386         }
387       }
388       finally {
389         logger.warn("JobQueueRunner terminated");
390       }
391     }
392   }
393 }
394