JAL-1807 Bob's JalviewJS prototype first commit
[jalviewjs.git] / src / jalview / workers / AlignCalcManager.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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 jalview.api.AlignCalcManagerI;
24 import jalview.api.AlignCalcWorkerI;
25 import jalview.datamodel.AlignmentAnnotation;
26
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashSet;
30 import java.util.Hashtable;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
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         
192         Thread workerAsThread = (Thread) worker;
193         if (workerAsThread.isAlive()) {
194                 workerAsThread.interrupt();
195                 worker = worker.getNewWorker();
196         }
197         worker.setName(worker.getClass().getName());
198         worker.start();
199   }
200
201   @Override
202   public boolean isWorking(AlignCalcWorkerI worker)
203   {
204     synchronized (inProgress)
205     {// System.err.println("isWorking : worker "+(worker!=null ?
206      // worker.getClass():"null")+ " "+hashCode());
207       return worker != null && inProgress.get(worker.getClass()) == worker;
208     }
209   }
210
211   @Override
212   public boolean isWorking()
213   {
214     synchronized (inProgress)
215     {
216       // System.err.println("isWorking "+hashCode());
217       return inProgress.size() > 0;
218     }
219   }
220
221   @Override
222   public void registerWorker(AlignCalcWorkerI worker)
223   {
224     synchronized (restartable)
225     {
226       if (!restartable.contains(worker))
227       {
228         restartable.add(worker);
229       }
230       startWorker(worker);
231     }
232   }
233
234   @Override
235   public void restartWorkers()
236   {
237     synchronized (restartable)
238     {
239       for (AlignCalcWorkerI worker : restartable)
240       {
241         startWorker(worker);
242       }
243     }
244   }
245
246   @Override
247   public boolean workingInvolvedWith(AlignmentAnnotation alignmentAnnotation)
248   {
249     synchronized (inProgress)
250     {
251       for (AlignCalcWorkerI worker : inProgress.values())
252       {
253         if (worker.involves(alignmentAnnotation))
254         {
255           return true;
256         }
257       }
258     }
259     synchronized (updating)
260     {
261       for (List<AlignCalcWorkerI> workers : updating.values())
262       {
263         for (AlignCalcWorkerI worker : workers)
264         {
265           if (worker.involves(alignmentAnnotation))
266           {
267             return true;
268           }
269         }
270       }
271     }
272     return false;
273   }
274
275   @Override
276   public void updateAnnotationFor(Class workerClass)
277   {
278
279     AlignCalcWorkerI[] workers;
280     synchronized (canUpdate)
281     {
282       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
283     }
284     for (AlignCalcWorkerI worker : workers)
285     {
286       if (workerClass.equals(worker.getClass()))
287       {
288         worker.updateAnnotation();
289       }
290     }
291   }
292
293   @Override
294   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
295           Class workerClass)
296   {
297     List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
298     AlignCalcWorkerI[] workers;
299     synchronized (canUpdate)
300     {
301       workers = canUpdate.toArray(new AlignCalcWorkerI[canUpdate.size()]);
302     }
303     for (AlignCalcWorkerI worker : workers)
304     {
305       if (workerClass.equals(worker.getClass()))
306       {
307         workingClass.add(worker);
308       }
309     }
310     return (workingClass.size() == 0) ? null : workingClass;
311   }
312
313   @Override
314   public boolean startRegisteredWorkersOfClass(Class workerClass)
315   {
316     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(workerClass);
317     if (workers == null)
318     {
319       return false;
320     }
321     for (AlignCalcWorkerI worker : workers)
322     {
323       if (!isPending(worker))
324       {
325         startWorker(worker);
326       }
327       else
328       {
329         System.err.println("Pending exists for " + workerClass);
330       }
331     }
332     return true;
333   }
334
335   @Override
336   public void workerMayRun(AlignCalcWorkerI worker)
337   {
338     synchronized (blackList)
339     {
340       if (blackList.contains(worker.getClass()))
341       {
342         blackList.remove(worker.getClass());
343       }
344     }
345   }
346
347   @Override
348   public void removeRegisteredWorkersOfClass(Class typeToRemove)
349   {
350     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(typeToRemove);
351     List<AlignCalcWorkerI> removable = new ArrayList<AlignCalcWorkerI>();
352     Set<AlignCalcWorkerI> toremovannot = new HashSet<AlignCalcWorkerI>();
353     synchronized (restartable)
354     {
355       for (AlignCalcWorkerI worker : restartable)
356       {
357         if (typeToRemove.equals(worker.getClass()))
358         {
359           removable.add(worker);
360           toremovannot.add(worker);
361         }
362       }
363       restartable.removeAll(removable);
364     }
365     synchronized (canUpdate)
366     {
367       for (AlignCalcWorkerI worker : canUpdate)
368       {
369         if (typeToRemove.equals(worker.getClass()))
370         {
371           removable.add(worker);
372           toremovannot.add(worker);
373         }
374       }
375       canUpdate.removeAll(removable);
376     }
377     // TODO: finish testing this extension
378
379     /*
380      * synchronized (inProgress) { // need to kill or mark as dead any running
381      * threads... (inProgress.get(typeToRemove)); }
382      * 
383      * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers)
384      * {
385      * 
386      * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); }
387      * else { System.err.println("Pending exists for " + workerClass); } }
388      */
389   }
390 }