1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: JobManager.java 2053 2010-12-10 19:34:53Z dmsmith $
21   */
22  package org.crosswire.common.progress;
23  
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  import org.crosswire.common.util.Logger;
30  
31  /**
32   * JobManager is responsible for creating jobs and informing listeners about the
33   * progress they make to completion.
34   * 
35   * <p>
36   * Example code:
37   * 
38   * <pre>
39   * final Thread worker = new Thread(&quot;DisplayPreLoader&quot;)
40   * {
41   *     public void run()
42   *     {
43   *         URL predictURI = Project.instance().getWritablePropertiesURI(&quot;save-name&quot;);
44   *         Progress job = JobManager.createJob(&quot;Job Title&quot;, predictURI, this, true);
45   *         try
46   *         {
47   *             job.setProgress(&quot;Step 1&quot;);
48   *             ...
49   *             job.setProgress(&quot;Step 2&quot;);
50   *             ...
51   *         }
52   *         catch (Exception ex)
53   *         {
54   *             ...
55   *             job.ignoreTimings();
56   *         }
57   *         finally
58   *         {
59   *             job.done();
60   *         }
61   *     }
62   * };
63   * worker.setPriority(Thread.MIN_PRIORITY);
64   * worker.start();
65   * </pre>
66   * 
67   * @see gnu.lgpl.License for license details.<br>
68   *      The copyright to this program is held by it's authors.
69   * @author Joe Walker [joe at eireneh dot com]
70   */
71  public final class JobManager {
72      /**
73       * Prevent instantiation
74       */
75      private JobManager() {
76      }
77  
78      /**
79       * Create a new Job that cannot be canceled.
80       * 
81       * @param jobName the name of the Job
82       */
83      public static Progress createJob(String jobName) {
84          return createJob(jobName, null);
85      }
86  
87      /**
88       * Create a new Job that can be canceled.
89       * 
90       * @param jobName the name of the Job
91       * @param workerThread the thread on which this job runs
92       */
93      public static Progress createJob(String jobName, Thread workerThread) {
94          Progress job = new Job(jobName, workerThread);
95          jobs.add(job);
96  
97          log.debug("job starting: " + job.getJobName());
98  
99          return job;
100     }
101 
102     /**
103      * Add a listener to the list
104      */
105     public static synchronized void addWorkListener(WorkListener li) {
106         List<WorkListener> temp = new ArrayList<WorkListener>();
107         temp.addAll(listeners);
108 
109         if (!temp.contains(li)) {
110             temp.add(li);
111             listeners = temp;
112         }
113     }
114 
115     /**
116      * Remote a listener from the list
117      */
118     public static synchronized void removeWorkListener(WorkListener li) {
119         if (listeners.contains(li)) {
120             List<WorkListener> temp = new ArrayList<WorkListener>();
121             temp.addAll(listeners);
122             temp.remove(li);
123             listeners = temp;
124         }
125     }
126 
127     /**
128      * Accessor for the currently known jobs
129      */
130     public static synchronized Set<Progress> getJobs() {
131         Set<Progress> reply = new HashSet<Progress>();
132         reply.addAll(jobs);
133         return reply;
134     }
135 
136     /**
137      * Inform the listeners that a title has changed.
138      */
139     protected static void fireWorkProgressed(Progress job) {
140         final WorkEvent ev = new WorkEvent(job);
141 
142         // we need to keep the synchronized section very small to avoid deadlock
143         // certainly keep the event dispatch clear of the synchronized block or
144         // there will be a deadlock
145         final List<WorkListener> temp = new ArrayList<WorkListener>();
146         synchronized (JobManager.class) {
147             temp.addAll(listeners);
148         }
149 
150         // We ought only to tell listeners about jobs that are in our
151         // list of jobs so we need to fire before delete.
152         if (listeners != null) {
153             for (WorkListener worker : temp) {
154                 worker.workProgressed(ev);
155             }
156         }
157 
158         // Do we need to remove the job? Note that the section above will
159         // probably execute after this so we will be firing events for jobs
160         // that are no longer in our list of jobs. ho hum.
161         synchronized (JobManager.class) {
162             if (job.isFinished()) {
163                 log.debug("job finished: " + job.getJobName());
164                 jobs.remove(job);
165             }
166         }
167     }
168 
169     /**
170      * List of listeners
171      */
172     private static List<WorkListener> listeners = new ArrayList<WorkListener>();
173 
174     /**
175      * List of current jobs
176      */
177     private static Set<Progress> jobs = new HashSet<Progress>();
178 
179     /**
180      * The log stream
181      */
182     private static final Logger log = Logger.getLogger(JobManager.class);
183 }
184