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