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 or later
5    * as published by the Free Software Foundation. This program is distributed
6    * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7    * the 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   * © CrossWire Bible Society, 2005 - 2016
18   *
19   */
20  package org.crosswire.common.progress;
21  
22  import org.slf4j.Logger;
23  import org.slf4j.LoggerFactory;
24  
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Set;
28  import java.util.UUID;
29  import java.util.concurrent.CopyOnWriteArrayList;
30  import java.util.concurrent.CopyOnWriteArraySet;
31  
32  /**
33   * JobManager is responsible for creating jobs and informing listeners about the
34   * progress they make to completion.
35   * 
36   * <p>
37   * Example code:
38   * 
39   * <pre>
40   * final Thread worker = new Thread(&quot;DisplayPreLoader&quot;)
41   * {
42   *     public void run()
43   *     {
44   *         URL predictURI = Project.instance().getWritablePropertiesURI(&quot;save-name&quot;);
45   *         Progress job = JobManager.createJob(&quot;Job Title&quot;, predictURI, this, true);
46   *         try
47   *         {
48   *             job.setProgress(&quot;Step 1&quot;);
49   *             ...
50   *             job.setProgress(&quot;Step 2&quot;);
51   *             ...
52   *         }
53   *         catch (Exception ex)
54   *         {
55   *             ...
56   *             job.ignoreTimings();
57   *         }
58   *         finally
59   *         {
60   *             job.done();
61   *         }
62   *     }
63   * };
64   * worker.setPriority(Thread.MIN_PRIORITY);
65   * worker.start();
66   * </pre>
67   * 
68   * @see gnu.lgpl.License The GNU Lesser General Public License for details.
69   * @author Joe Walker
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       * @return the job
83       */
84      public static Progress createJob(String jobName) {
85          return createJob(UUID.randomUUID().toString(), jobName, null);
86      }
87  
88      /**
89       * Create a new Job that can be canceled.
90       * 
91       * @param jobID a unique identifier for the job
92       * @param jobName the name of the Job
93       * @param workerThread the thread on which this job runs
94       * @return the job
95       */
96      public static Progress createJob(String jobID, String jobName, Thread workerThread) {
97          Progress job = new Job(jobID, jobName, workerThread);
98          jobs.add(job);
99  
100         log.debug("job starting: {}", job.getJobName());
101 
102         return job;
103     }
104 
105     /**
106      * Add a listener to the list
107      * 
108      * @param li the interested listener
109      */
110     public static void addWorkListener(WorkListener li) {
111         listeners.add(li);
112     }
113 
114     /**
115      * Remove a listener from the list
116      * 
117      * @param li the disinterested listener
118      */
119     public static void removeWorkListener(WorkListener li) {
120         listeners.remove(li);
121     }
122 
123     /**
124      * Accessor for the currently known jobs
125      * 
126      * @return an iterator over the jobs
127      */
128     public static Iterator<Progress> iterator() {
129         return jobs.iterator();
130     }
131 
132     /**
133      * @return the number of current jobs
134      */
135     public static int getJobCount() {
136         return jobs.size();
137     }
138 
139     /**
140      * Inform the listeners that a title has changed.
141      * 
142      * @param job the job that has made progress
143      */
144     protected static void fireWorkProgressed(Progress job) {
145         final WorkEvent ev = new WorkEvent(job);
146 
147         // We ought only to tell listeners about jobs that are in our
148         // list of jobs so we need to fire before delete.
149         for (WorkListener worker : listeners) {
150             worker.workProgressed(ev);
151         }
152 
153         // Do we need to remove the job? Note that the section above will
154         // probably execute after this so we will be firing events for jobs
155         // that are no longer in our list of jobs. ho hum.
156         if (job.isFinished()) {
157             log.debug("job finished: {}", job.getJobName());
158             jobs.remove(job);
159         }
160     }
161 
162     /**
163      * List of listeners using thread safe list
164      */
165     private static List<WorkListener> listeners = new CopyOnWriteArrayList<WorkListener>();
166 
167     /**
168      * List of current jobs
169      */
170     private static Set<Progress> jobs = new CopyOnWriteArraySet<Progress>();
171 
172     /**
173      * The log stream
174      */
175     private static final Logger log = LoggerFactory.getLogger(JobManager.class);
176 }
177