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