001: /*
002: The contents of this file are subject to the Common Public Attribution License
003: Version 1.0 (the "License"); you may not use this file except in compliance with
004: the License. You may obtain a copy of the License at
005: http://www.projity.com/license . The License is based on the Mozilla Public
006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
007: software over a computer network and provide for limited attribution for the
008: Original Developer. In addition, Exhibit A has been modified to be consistent
009: with Exhibit B.
010:
011: Software distributed under the License is distributed on an "AS IS" basis,
012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
013: specific language governing rights and limitations under the License. The
014: Original Code is OpenProj. The Original Developer is the Initial Developer and
015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
017:
018: Alternatively, the contents of this file may be used under the terms of the
019: Projity End-User License Agreeement (the Projity License), in which case the
020: provisions of the Projity License are applicable instead of those above. If you
021: wish to allow use of your version of this file only under the terms of the
022: Projity License and not to allow others to use your version of this file under
023: the CPAL, indicate your decision by deleting the provisions above and replace
024: them with the notice and other provisions required by the Projity License. If
025: you do not delete the provisions above, a recipient may use your version of this
026: file under either the CPAL or the Projity License.
027:
028: [NOTE: The text of this license may differ slightly from the text of the notices
029: in Exhibits A and B of the license at http://www.projity.com/license. You should
030: use the latest text at http://www.projity.com/license for your modifications.
031: You may not remove this license text from the source files.]
032:
033: Attribution Information: Attribution Copyright Notice: Copyright � 2006, 2007
034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
035: an open source solution from Projity. Attribution URL: http://www.projity.com
036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
037: alternatives listed on http://www.projity.com/logo
038:
039: Display of Attribution Information is required in Larger Works which are defined
040: in the CPAL as a work which combines Covered Code or portions thereof with code
041: not governed by the terms of the CPAL. However, in addition to the other notice
042: obligations, all copies of the Covered Code in Executable and Source Code form
043: distributed must, as a form of attribution of the original author, include on
044: each user interface screen the "OpenProj" logo visible to all users. The
045: OpenProj logo should be located horizontally aligned with the menu bar and left
046: justified on the top left of the screen adjacent to the File menu. The logo
047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
048: must direct them back to http://www.projity.com.
049: */
050: package com.projity.job;
051:
052: import java.util.ArrayList;
053: import java.util.Hashtable;
054: import java.util.Iterator;
055: import java.util.List;
056: import java.util.ListIterator;
057: import java.util.Set;
058:
059: import javax.swing.ProgressMonitor;
060: import javax.swing.SwingUtilities;
061:
062: import org.apache.commons.collections.Closure;
063:
064: import com.projity.server.access.ErrorLogger;
065: import com.projity.util.Alert;
066:
067: /**
068: *
069: */
070: public class Job extends Thread {
071: // static Log log = LogFactory.getLog(Job.class);
072:
073: protected float progress, progressStart;
074: protected float weight = 0.0f;
075: protected boolean canceled = false;
076: protected long jobId;
077: protected long t;
078: protected ProgressMonitor progressMonitor = null;
079: protected Thread monitorChecker = null;
080: protected Closure cancelMonitorClosure = null;
081: protected JobQueue jobQueue;
082: protected String title;
083: protected boolean showProgess, sync;
084: protected List runnables = new ArrayList();
085: protected InternalRunnable exceptionHandlerRunnable;
086: protected JobMutex mutex;
087: protected Mutex globalMutex, groupMutex;
088: protected InternalRunnable previousRunnable = null;
089: protected boolean queued = true;
090: protected boolean customCriticalSection;
091:
092: public Job(JobQueue jobQueue, String name, String title,
093: boolean showProgress) {
094: super (jobQueue, name);
095: this .jobQueue = jobQueue;
096: this .title = title;
097: this .showProgess = showProgress;
098: this .sync = false;
099: jobId = System.currentTimeMillis();
100: mutex = new JobMutex("JobMutex");
101: globalMutex = new Mutex("GlobalMutex");
102: groupMutex = new Mutex("GroupMutex");
103: progress = 0.0f;
104: }
105:
106: public void log(String s) {
107: // System.out.println("Job "+getName()+": "+s);
108: // log.info("Job "+getName()+": "+s);
109: }
110:
111: private final Hashtable times = new Hashtable();
112:
113: public void logBegin(String s) {
114: log(s + "...");
115: times.put(s, new Long(System.currentTimeMillis()));
116: }
117:
118: public void logEnd(String s) {
119: Long t = (Long) times.get(s);
120: if (t == null)
121: log(s + "...end");
122: else
123: log(s + "...end, "
124: + (System.currentTimeMillis() - t.longValue())
125: + " ms");
126: }
127:
128: public long getId() {
129: return hashCode(); //return super.getId() for jdk1.5
130: }
131:
132: public JobQueue getJobQueue() {
133: return jobQueue;
134: }
135:
136: public boolean isQueued() {
137: return queued;
138: }
139:
140: public boolean isCustomCriticalSection() {
141: return customCriticalSection;
142: }
143:
144: public void setCustomCriticalSection(boolean customCriticalSection) {
145: this .customCriticalSection = customCriticalSection;
146: }
147:
148: public void setQueued(boolean queued) {
149: this .queued = queued;
150: }
151:
152: public void setCancelMonitorClosure(Closure cancelMonitorClosure) {
153: this .cancelMonitorClosure = cancelMonitorClosure;
154: }
155:
156: public synchronized float getProgress() {
157: return progress;
158: }
159:
160: public synchronized void setProgress(float runnableProgress,
161: JobRunnable runnable) {
162: setProgress(runnableProgress, null, runnable);
163: }
164:
165: private JobRunnable lastFinishedRunnable = null;
166:
167: public synchronized void setProgress(float runnableProgress,
168: final String note, JobRunnable runnable) {
169: log("setProgress("
170: + runnableProgress
171: + ","
172: + note
173: + ","
174: + runnable.getName()
175: + "): progress="
176: + progress
177: + ", weight="
178: + runnable.getWeight()
179: + "/"
180: + weight
181: + ", lastFinishedRunnable="
182: + ((lastFinishedRunnable == null) ? null
183: : lastFinishedRunnable.getName())
184: + ", progressStart=" + progressStart);
185: if (canceled || runnable == lastFinishedRunnable)
186: return;
187: float relativeProgress = runnableProgress
188: * runnable.getWeight() / getWeight();
189: if (progressStart + relativeProgress > progress) {
190: progress = progressStart + relativeProgress;
191: if (showProgess && progressMonitor != null) {
192: SwingUtilities.invokeLater(new Runnable() {
193: public void run() {
194: progressMonitor.setProgress((int) Math
195: .round(getProgress()
196: * JobQueue.MAX_PROGRESS));
197: progressMonitor.setNote(note);
198:
199: }
200: });
201: }
202: if (runnableProgress == 1.0f
203: && runnable != lastFinishedRunnable) {
204: lastFinishedRunnable = runnable;
205: progressStart += relativeProgress;
206: }
207: }
208: log("\tsetProgress: progress="
209: + progress
210: + ", lastFinishedRunnable="
211: + ((lastFinishedRunnable == null) ? null
212: : lastFinishedRunnable.getName())
213: + ", progressStart=" + progressStart);
214: }
215:
216: public ProgressMonitor getProgressMonitor() {
217: return progressMonitor;
218: }
219:
220: public synchronized void cancel() {
221: canceled = true;
222: if (showProgess && progressMonitor != null) {
223: SwingUtilities.invokeLater(new Runnable() {
224: public void run() {
225: progressMonitor.close();
226: }
227: });
228: }
229: }
230:
231: public synchronized boolean isCanceled() {
232: if (progressMonitor != null && progressMonitor.isCanceled())
233: canceled = true;
234: return canceled;
235: }
236:
237: protected void end() {
238: //((JobQueue)getThreadGroup()).startNext();
239: }
240:
241: ListIterator runnableIterator;
242: InternalRunnable freeRunnable = null;
243: InternalRunnable lastRunnable = null;
244:
245: public void execute() {
246: boolean asyncExecuting = false;
247: try {
248: jobQueue.addExecutingJob(this );
249: //detection
250: runnableIterator = runnables.listIterator();
251: boolean beginSync = false;
252: boolean endSync = false;
253: boolean async = false;
254: InternalRunnable r;
255: while (runnableIterator.hasNext()) {
256: r = (InternalRunnable) runnableIterator.next();
257: if (r.isExceptionHandler())
258: continue;
259: if (r.isSync() == true) {
260: beginSync = true;
261: lastRunnable = r;
262: } else {
263: runnableIterator.previous();
264: break;
265: }
266: }
267: while (runnableIterator.hasNext()) {
268: r = (InternalRunnable) runnableIterator.next();
269: if (r.isExceptionHandler())
270: continue;
271: if (r.isSync() == false) {
272: async = true;
273: freeRunnable = r;
274: if (!r.isSwing())
275: lastRunnable = r;
276: } else {
277: runnableIterator.previous();
278: freeRunnable = null;
279: break;
280: }
281: }
282: if (runnableIterator.hasNext()) {
283: r = (InternalRunnable) runnableIterator.next();
284: if (!r.isExceptionHandler()) {
285: lastRunnable = r;
286: endSync = true;
287: }
288: }
289:
290: if (lastRunnable != null
291: && !Job.this .isCustomCriticalSection())
292: jobQueue.beginCriticalSection(Job.this );
293: globalMutex.lock();
294: logBegin("global");
295:
296: //run
297: runnableIterator = runnables.listIterator();
298: //valid sequence: sync...sync thread...thread sync...sync
299: if (isCanceled()) {
300: log("Job canceled");
301: return;
302: }
303: if (beginSync) {
304: if (async)
305: groupMutex.lock(); //not needed
306: run(true);
307: if (async)
308: groupMutex.waitUntilUnlocked(); //not needed
309: }
310: if (async) {
311: if (isCanceled()) {
312: log("Job canceled");
313: return;
314: }
315: if (endSync)
316: groupMutex.lock();
317: asyncExecuting = true;
318: start();
319: if (endSync) {
320: groupMutex.waitUntilUnlocked();
321: if (isCanceled()) {
322: log("Job canceled");
323: return;
324: }
325: run(true);
326: }
327: }
328: } finally {
329: //mutex.unlock();//just in case an exception occurs
330: logEnd("global");
331: globalMutex.unlock();
332: if (!asyncExecuting)
333: jobQueue.removeExecutingJob(this ); //because it's done in run(false);
334: }
335: }
336:
337: public void run(boolean sync) {
338: try {
339: //System.out.println("run("+sync+")...ok");
340: final JobQueue jobQueue = getJobQueue();
341: //jobQueue.enableComponent(false);
342: if (showProgess) {
343: progressMonitor = jobQueue.getProgressMonitor(title);
344: if (progressMonitor != null
345: && cancelMonitorClosure != null
346: && monitorChecker == null) {
347: monitorChecker = new Thread(getName()
348: + "_cancelMonitor") {
349: public void run() {
350: if (isInterrupted() || isCanceled()) // if thread is not alive, do nothing
351: return;
352:
353: while (true) {
354: if (progressMonitor.isCanceled()) {
355: cancelMonitorClosure.execute(null);
356: break;
357: }
358: try {
359: sleep(100);
360: } catch (InterruptedException e) {
361: }
362: }
363: }
364: };
365: monitorChecker.start();
366: }
367: }
368:
369: while (runnableIterator.hasNext()) {
370: if (isInterrupted() || isCanceled()) {
371: log("Job canceled");
372: return;
373: }
374: InternalRunnable runnable = (InternalRunnable) runnableIterator
375: .next();
376: if (previousRunnable != null
377: && previousRunnable.getException() != null) {//an exception occured
378: if (!runnable.isExceptionHandler())
379: continue;
380: } else {
381: if (runnable.isExceptionHandler())
382: continue;
383: }
384: if (runnable.isSync() != sync) {
385: runnableIterator.previous();
386: break;
387: }
388:
389: boolean lock = !sync && runnable != freeRunnable;
390:
391: log(runnable.runnable.getName() + ": lock=" + lock);
392:
393: JobMutex runMutex;
394: if (lock) {
395: runMutex = mutex;
396: runMutex.lock();
397: } else {
398: runMutex = null;
399: groupMutex.unlock();
400: }
401: runnable.setPrevious(previousRunnable);
402:
403: runThread(runnable, runMutex); //will unlock mutex
404: previousRunnable = runnable;
405: if (runMutex != null) {
406: if (lock)
407: runMutex.waitUntilUnlocked();
408: }
409: if (isInterrupted() || isCanceled()) {
410: log("Job canceled");
411: return;
412: }
413: if (runnable.getException() != null) {
414: log(runnable.runnable.getName()
415: + ": Aborting job, exception="
416: + runnable.getException().getMessage());
417: // if (exceptionHandlerRunnable!=null){
418: // runMutex=mutex;
419: // runMutex.lock();
420: // runThread(exceptionHandlerRunnable,runMutex);
421: // runMutex.waitUntilUnlocked();
422: // }
423: // break;
424: }
425: if (runnable.isExceptionHandler())
426: break;
427: }
428: } finally {
429: groupMutex.unlock();
430: jobQueue.removeExecutingJob(this );
431: }
432: }
433:
434: public void run() {
435: run(false);
436: }
437:
438: private void runThread(final InternalRunnable runnable,
439: final JobMutex runMutex) {
440: if (runnable.isCreateThread()) {
441: Thread t = new Thread() {
442: public void run() {
443: runSwing(runnable, runMutex);
444: }
445: };
446: t.setDaemon(isDaemon());
447: t.start();
448: } else {
449: runSwing(runnable, runMutex);
450: }
451: }
452:
453: private void runSwing(final InternalRunnable runnable,
454: final JobMutex runMutex) {
455: if (runnable.isSwing()) {
456: SwingUtilities.invokeLater(new Runnable() {
457: public void run() {
458: try {
459: if (isInterrupted() || isCanceled()) // if thread is not alive, do nothing
460: return;
461: logBegin("running "
462: + runnable.runnable.getName());
463: runnable.run();
464: //if (runMutex!=null&&runMutex.getException()!=null) cancel();
465: if (runnable.getException() != null)
466: cancel();
467: } finally {
468: logEnd("running " + runnable.runnable.getName());
469: if (runMutex != null)
470: runMutex.unlock();
471: if (!isCustomCriticalSection()
472: && (runnable == lastRunnable || runnable
473: .getException() != null))
474: jobQueue.endCriticalSection(Job.this );
475: }
476: }
477: });
478: } else {
479: if (isInterrupted() || isCanceled()) // if thread is not alive, do nothing
480: return;
481: try {
482: logBegin("running " + runnable.runnable.getName());
483: runnable.run();
484: //if (runnable.getException()!=null) cancel();
485: if (runnable.getException() != null)
486: cancel();
487: } finally {
488: logEnd("running " + runnable.runnable.getName());
489: if (runMutex != null)
490: runMutex.unlock();
491: //Error lastRunnable swing in case of exception
492: if (!isCustomCriticalSection()
493: && (runnable == lastRunnable || runnable
494: .getException() != null))
495: jobQueue.endCriticalSection(Job.this );
496: }
497: }
498: }
499:
500: public void warm(final String message, boolean wait) {
501: final Mutex alertMutex = new Mutex();
502: if (wait)
503: alertMutex.lock();
504: SwingUtilities.invokeLater(new Runnable() {
505: public void run() {
506: Alert.warn(message);
507: alertMutex.unlock();
508: }
509: });
510: if (wait)
511: alertMutex.waitUntilUnlocked();
512: }
513:
514: public void error(final String message, boolean wait) {
515: final Mutex alertMutex = new Mutex();
516: if (wait)
517: alertMutex.lock();
518: SwingUtilities.invokeLater(new Runnable() {
519: public void run() {
520: Alert.error(message);
521: alertMutex.unlock();
522: }
523: });
524: if (wait)
525: alertMutex.waitUntilUnlocked();
526: }
527:
528: private static class IntResultHolder {
529: int result;
530: };
531:
532: public int confirm(final String message, boolean wait) {
533: final Mutex alertMutex = new Mutex();
534: final IntResultHolder result = new IntResultHolder();
535: if (wait)
536: alertMutex.lock();
537: SwingUtilities.invokeLater(new Runnable() {
538: public void run() {
539: result.result = Alert.confirm(message);
540: alertMutex.unlock();
541: }
542: });
543: if (wait)
544: alertMutex.waitUntilUnlocked();
545: return result.result;
546: }
547:
548: private static class BooleanResultHolder {
549: boolean result;
550: };
551:
552: public boolean okCancel(final String message, boolean wait) {
553: final Mutex alertMutex = new Mutex();
554: final BooleanResultHolder result = new BooleanResultHolder();
555: if (wait)
556: alertMutex.lock();
557: SwingUtilities.invokeLater(new Runnable() {
558: public void run() {
559: result.result = Alert.okCancel(message);
560: alertMutex.unlock();
561: }
562: });
563: if (wait)
564: alertMutex.waitUntilUnlocked();
565: return result.result;
566: }
567:
568: private static class StringResultHolder {
569: String result;
570: };
571:
572: public String renameProject(final String name,
573: final Set projectNames, boolean wait, final boolean saveAs) {
574: final Mutex alertMutex = new Mutex();
575: final StringResultHolder result = new StringResultHolder();
576: if (wait)
577: alertMutex.lock();
578: SwingUtilities.invokeLater(new Runnable() {
579: public void run() {
580: result.result = Alert.renameProject(name, projectNames,
581: saveAs);
582: alertMutex.unlock();
583: }
584: });
585: if (wait)
586: alertMutex.waitUntilUnlocked();
587: return result.result;
588: }
589:
590: protected Object getResult() {
591: return (previousRunnable == null) ? null : previousRunnable
592: .getResult();
593: }
594:
595: // public Object getResult(int index){
596: // return ((InternalRunnable)runnables.get(index)).getResult();
597: // }
598: // public Exception getException(){
599: // return (previousRunnable==null)?null:previousRunnable.getException();
600: // }
601: // public Object getException(int index){
602: // return ((InternalRunnable)runnables.get(index)).getException();
603: // }
604:
605: public Object waitResult() throws Exception {
606: globalMutex.waitUntilUnlocked();
607: if (previousRunnable == null)
608: return null;
609: if (previousRunnable.getException() != null)
610: throw previousRunnable.getException();
611: return previousRunnable.getResult();
612: }
613:
614: public void logDuration(String title) {
615: long t1 = System.currentTimeMillis();
616: log(title + ": " + (t1 - t) + "ms");
617: t = t1;
618: }
619:
620: public void addRunnable(JobRunnable runnable, boolean sync,
621: boolean createThread, boolean swing,
622: boolean exceptionHandler) {
623: runnables.add(new InternalRunnable(runnable, sync,
624: createThread, swing, exceptionHandler));
625: runnable.setJob(this );
626: weight += runnable.getWeight();
627: }
628:
629: public void addRunnable(JobRunnable runnable, boolean sync) {
630: runnables.add(new InternalRunnable(runnable, sync, false,
631: false, false));
632: runnable.setJob(this );
633: weight += runnable.getWeight();
634: }
635:
636: public void addSwingRunnable(JobRunnable runnable, boolean sync) {
637: runnables.add(new InternalRunnable(runnable, sync, false, true,
638: false));
639: runnable.setJob(this );
640: weight += runnable.getWeight();
641: }
642:
643: public void addRunnable(JobRunnable runnable) {
644: addRunnable(runnable, false, false, false, false);
645: }
646:
647: public void addSwingRunnable(JobRunnable runnable) {
648: addRunnable(runnable, false, false, true, false);
649: }
650:
651: public void addSync() {
652: addRunnable(new JobRunnable("Sync: " + getName()) {
653: public Object run() throws Exception {
654: return getResult();
655: }
656: }, true, false, false, false);
657: }
658:
659: public void addExceptionRunnable(JobRunnable runnable) {
660: runnables.add(new InternalRunnable(runnable, false, false,
661: true, true));
662: weight += runnable.getWeight();
663: }
664:
665: public void addJob(Job job) {
666: for (Iterator i = job.runnables.iterator(); i.hasNext();) {
667: InternalRunnable runnable = (InternalRunnable) i.next();
668: runnable.getRunnable().setJob(this );
669: runnables.add(runnable);
670: weight += runnable.getRunnable().getWeight();
671: }
672: }
673:
674: private class InternalRunnable implements Runnable {
675: protected boolean sync = false;
676: protected boolean createThread = false;
677: protected boolean swing = false;
678: protected boolean exceptionHandler = false;
679: protected JobRunnable runnable;
680: protected Object result = null;
681: protected Exception exception = null;
682: protected InternalRunnable previous = null;
683:
684: public InternalRunnable(JobRunnable runnable, boolean sync,
685: boolean createThread, boolean swing,
686: boolean exceptionHandler) {
687: super ();
688: // TODO Auto-generated constructor stub
689: this .sync = sync;
690: this .createThread = createThread;
691: this .swing = swing;
692: this .exceptionHandler = exceptionHandler;
693: this .runnable = runnable;
694: }
695:
696: public boolean isCreateThread() {
697: return createThread;
698: }
699:
700: public void setCreateThread(boolean createThread) {
701: this .createThread = createThread;
702: }
703:
704: public boolean isSwing() {
705: return swing;
706: }
707:
708: public void setSwing(boolean swing) {
709: this .swing = swing;
710: }
711:
712: public boolean isExceptionHandler() {
713: return exceptionHandler;
714: }
715:
716: public void setExceptionHandler(boolean exceptionHandler) {
717: this .exceptionHandler = exceptionHandler;
718: }
719:
720: public JobRunnable getRunnable() {
721: return runnable;
722: }
723:
724: public void setRunnable(JobRunnable runnable) {
725: this .runnable = runnable;
726: }
727:
728: public boolean isSync() {
729: return sync;
730: }
731:
732: public void setSync(boolean sync) {
733: this .sync = sync;
734: }
735:
736: public void run() {
737: try {
738: //System.out.println("Start: "+getName());
739: result = runnable.run();
740: //System.out.println("End: "+getName());
741: } catch (Exception e) {
742: //System.out.println("Exception: "+getName());
743: exception = e;
744: if (!(e instanceof JobCanceledException)) {
745: e.printStackTrace();
746: ErrorLogger.log("Job Exception: " + getName(), e);
747: }
748: }
749: }
750:
751: public Exception getException() {
752: return exception;
753: }
754:
755: public Object getResult() {
756: return result;
757: }
758:
759: public Object getPreviousResult() {
760: return (previousRunnable == null) ? null : previousRunnable
761: .getResult();
762: }
763:
764: public Exception getPreviousException() {
765: return (previousRunnable == null) ? null : previousRunnable
766: .getException();
767: }
768:
769: public InternalRunnable getPrevious() {
770: return previous;
771: }
772:
773: public void setPrevious(InternalRunnable previous) {
774: this .previous = previous;
775: if (previous != null) {
776: runnable.setPreviousResult(previous.getResult());
777: runnable.setPreviousException(previous.getException());
778: }
779: }
780:
781: }
782:
783: public float getWeight() {
784: return weight;
785: }
786:
787: public void setWeight(float weight) {
788: this.weight = weight;
789: }
790:
791: }
|