JAL-1620 version bump and release notes
[jalview.git] / src / jalview / workers / AlignCalcManager.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.workers;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.Hashtable;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import jalview.api.AlignCalcManagerI;
32 import jalview.api.AlignCalcWorkerI;
33 import jalview.datamodel.AlignmentAnnotation;
34
35 public class AlignCalcManager implements AlignCalcManagerI
36 {
37   private volatile List<AlignCalcWorkerI> restartable = Collections
38           .synchronizedList(new ArrayList<AlignCalcWorkerI>());
39
40   private volatile List<Class> blackList = Collections
41           .synchronizedList(new ArrayList<Class>());
42
43   /**
44    * global record of calculations in progress
45    */
46   private volatile Map<Class, AlignCalcWorkerI> inProgress = Collections
47           .synchronizedMap(new Hashtable<Class, AlignCalcWorkerI>());
48
49   /**
50    * record of calculations pending or in progress in the current context
51    */
52   private volatile Map<Class, List<AlignCalcWorkerI>> updating = Collections
53           .synchronizedMap(new Hashtable<Class, List<AlignCalcWorkerI>>());
54
55   @Override
56   public void notifyStart(AlignCalcWorkerI worker)
57   {
58     synchronized (updating)
59     {
60       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
61       if (upd == null)
62       {
63         updating.put(
64                 worker.getClass(),
65                 upd = Collections
66                         .synchronizedList(new ArrayList<AlignCalcWorkerI>()));
67       }
68       synchronized (upd)
69       {
70         upd.add(worker);
71       }
72     }
73   }
74
75   @Override
76   public boolean alreadyDoing(AlignCalcWorkerI worker)
77   {
78     synchronized (inProgress)
79     {
80       return inProgress.containsKey(worker.getClass());
81     }
82   }
83
84   /*
85    * (non-Javadoc)
86    * 
87    * @see jalview.api.AlignCalcManagerI#isPending(jalview.api.AlignCalcWorkerI)
88    */
89   @Override
90   public boolean isPending(AlignCalcWorkerI workingClass)
91   {
92     List<AlignCalcWorkerI> upd;
93     synchronized (updating)
94     {
95       upd = updating.get(workingClass.getClass());
96       if (upd == null)
97       {
98         return false;
99       }
100       synchronized (upd)
101       {
102         if (upd.size() > 1)
103         {
104           return true;
105         }
106       }
107       return false;
108     }
109   }
110
111   // TODO make into api method if needed ?
112   public int numberLive(AlignCalcWorkerI worker)
113   {
114     synchronized (updating)
115     {
116       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
117       if (upd == null)
118       {
119         return 0;
120       }
121       ;
122       return upd.size();
123     }
124   }
125
126   @Override
127   public boolean notifyWorking(AlignCalcWorkerI worker)
128   {
129     synchronized (inProgress)
130     {
131       // TODO: decide if we should throw exceptions here if multiple workers
132       // start to work
133       if (inProgress.get(worker.getClass()) != null)
134       {
135         if (false)
136         {
137           System.err
138                   .println("Warning: Multiple workers are running of type "
139                           + worker.getClass());
140         }
141         return false;
142       }
143       inProgress.put(worker.getClass(), worker);
144     }
145     return true;
146   }
147
148   private final HashSet<AlignCalcWorkerI> canUpdate = new HashSet<AlignCalcWorkerI>();
149
150   @Override
151   public void workerComplete(AlignCalcWorkerI worker)
152   {
153     synchronized (inProgress)
154     {
155       // System.err.println("Worker "+worker.getClass()+" marked as complete.");
156       inProgress.remove(worker.getClass());
157       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
158       if (upd != null)
159       {
160         synchronized (upd)
161         {
162           upd.remove(worker);
163         }
164         canUpdate.add(worker);
165       }
166     }
167   }
168
169   @Override
170   public void workerCannotRun(AlignCalcWorkerI worker)
171   {
172     synchronized (blackList)
173     {
174       blackList.add(worker.getClass());
175     }
176   }
177
178   public boolean isBlackListed(Class workerType)
179   {
180     synchronized (blackList)
181     {
182       return blackList.contains(workerType);
183     }
184   }
185
186   @Override
187   public void startWorker(AlignCalcWorkerI worker)
188   {
189     // System.err.println("Starting "+worker.getClass());
190     // new Exception("").printStackTrace();
191     Thread tw = new Thread(worker);
192     tw.setName(worker.getClass().toString());
193     tw.start();
194   }
195
196   @Override
197   public boolean isWorking(AlignCalcWorkerI worker)
198   {
199     synchronized (inProgress)
200     {// System.err.println("isWorking : worker "+(worker!=null ?
201      // worker.getClass():"null")+ " "+hashCode());
202       return worker != null && inProgress.get(worker.getClass()) == worker;
203     }
204   }
205
206   @Override
207   public boolean isWorking()
208   {
209     synchronized (inProgress)
210     {
211       // System.err.println("isWorking "+hashCode());
212       return inProgress.size() > 0;
213     }
214   }
215
216   @Override
217   public void registerWorker(AlignCalcWorkerI worker)
218   {
219     synchronized (restartable)
220     {
221       if (!restartable.contains(worker))
222       {
223         restartable.add(worker);
224       }
225       startWorker(worker);
226     }
227   }
228
229   @Override
230   public void restartWorkers()
231   {
232     synchronized (restartable)
233     {
234       for (AlignCalcWorkerI worker : restartable)
235       {
236         startWorker(worker);
237       }
238     }
239   }
240
241   @Override
242   public boolean workingInvolvedWith(AlignmentAnnotation alignmentAnnotation)
243   {
244     synchronized (inProgress)
245     {
246       for (AlignCalcWorkerI worker : inProgress.values())
247       {
248         if (worker.involves(alignmentAnnotation))
249         {
250           return true;
251         }
252       }
253     }
254     synchronized (updating)
255     {
256       for (List<AlignCalcWorkerI> workers : updating.values())
257       {
258         for (AlignCalcWorkerI worker : workers)
259           if (worker.involves(alignmentAnnotation))
260           {
261             return true;
262           }
263       }
264     }
265     return false;
266   }
267
268   @Override
269   public void updateAnnotationFor(Class workerClass)
270   {
271
272     AlignCalcWorkerI[] workers;
273     synchronized (canUpdate)
274     {
275       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
276     }
277     for (AlignCalcWorkerI worker : workers)
278     {
279       if (workerClass.equals(worker.getClass()))
280       {
281         worker.updateAnnotation();
282       }
283     }
284   }
285
286   @Override
287   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
288           Class workerClass)
289   {
290     List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
291     AlignCalcWorkerI[] workers;
292     synchronized (canUpdate)
293     {
294       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
295     }
296     for (AlignCalcWorkerI worker : workers)
297     {
298       if (workerClass.equals(worker.getClass()))
299       {
300         workingClass.add(worker);
301       }
302     }
303     return (workingClass.size() == 0) ? null : workingClass;
304   }
305
306   @Override
307   public boolean startRegisteredWorkersOfClass(Class workerClass)
308   {
309     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(workerClass);
310     if (workers == null)
311     {
312       return false;
313     }
314     for (AlignCalcWorkerI worker : workers)
315     {
316       if (!isPending(worker))
317       {
318         startWorker(worker);
319       }
320       else
321       {
322         System.err.println("Pending exists for " + workerClass);
323       }
324     }
325     return true;
326   }
327
328   @Override
329   public void workerMayRun(AlignCalcWorkerI worker)
330   {
331     synchronized (blackList)
332     {
333       if (blackList.contains(worker.getClass()))
334       {
335         blackList.remove(worker.getClass());
336       }
337     }
338   }
339
340   @Override
341   public void removeRegisteredWorkersOfClass(Class typeToRemove)
342   {
343     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(typeToRemove);
344     List<AlignCalcWorkerI> removable = new ArrayList<AlignCalcWorkerI>();
345     Set<AlignCalcWorkerI> toremovannot = new HashSet<AlignCalcWorkerI>();
346     synchronized (restartable)
347     {
348       for (AlignCalcWorkerI worker : restartable)
349       {
350         if (typeToRemove.equals(worker.getClass()))
351         {
352           removable.add(worker);
353           toremovannot.add(worker);
354         }
355       }
356       restartable.removeAll(removable);
357     }
358     synchronized (canUpdate)
359     {
360       for (AlignCalcWorkerI worker : canUpdate)
361       {
362         if (typeToRemove.equals(worker.getClass()))
363         {
364           removable.add(worker);
365           toremovannot.add(worker);
366         }
367       }
368       canUpdate.removeAll(removable);
369     }
370     // TODO: finish testing this extension
371
372     /*
373      * synchronized (inProgress) { // need to kill or mark as dead any running
374      * threads... (inProgress.get(typeToRemove)); }
375      * 
376      * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers)
377      * {
378      * 
379      * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); }
380      * else { System.err.println("Pending exists for " + workerClass); } }
381      */
382   }
383 }