JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / workers / AlignCalcManager.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
3  * Copyright (C) 2015 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     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         {
260           if (worker.involves(alignmentAnnotation))
261           {
262             return true;
263           }
264         }
265       }
266     }
267     return false;
268   }
269
270   @Override
271   public void updateAnnotationFor(Class workerClass)
272   {
273
274     AlignCalcWorkerI[] workers;
275     synchronized (canUpdate)
276     {
277       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
278     }
279     for (AlignCalcWorkerI worker : workers)
280     {
281       if (workerClass.equals(worker.getClass()))
282       {
283         worker.updateAnnotation();
284       }
285     }
286   }
287
288   @Override
289   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
290           Class workerClass)
291   {
292     List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
293     AlignCalcWorkerI[] workers;
294     synchronized (canUpdate)
295     {
296       workers = canUpdate.toArray(new AlignCalcWorkerI[canUpdate.size()]);
297     }
298     for (AlignCalcWorkerI worker : workers)
299     {
300       if (workerClass.equals(worker.getClass()))
301       {
302         workingClass.add(worker);
303       }
304     }
305     return (workingClass.size() == 0) ? null : workingClass;
306   }
307
308   @Override
309   public boolean startRegisteredWorkersOfClass(Class workerClass)
310   {
311     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(workerClass);
312     if (workers == null)
313     {
314       return false;
315     }
316     for (AlignCalcWorkerI worker : workers)
317     {
318       if (!isPending(worker))
319       {
320         startWorker(worker);
321       }
322       else
323       {
324         System.err.println("Pending exists for " + workerClass);
325       }
326     }
327     return true;
328   }
329
330   @Override
331   public void workerMayRun(AlignCalcWorkerI worker)
332   {
333     synchronized (blackList)
334     {
335       if (blackList.contains(worker.getClass()))
336       {
337         blackList.remove(worker.getClass());
338       }
339     }
340   }
341
342   @Override
343   public void removeRegisteredWorkersOfClass(Class typeToRemove)
344   {
345     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(typeToRemove);
346     List<AlignCalcWorkerI> removable = new ArrayList<AlignCalcWorkerI>();
347     Set<AlignCalcWorkerI> toremovannot = new HashSet<AlignCalcWorkerI>();
348     synchronized (restartable)
349     {
350       for (AlignCalcWorkerI worker : restartable)
351       {
352         if (typeToRemove.equals(worker.getClass()))
353         {
354           removable.add(worker);
355           toremovannot.add(worker);
356         }
357       }
358       restartable.removeAll(removable);
359     }
360     synchronized (canUpdate)
361     {
362       for (AlignCalcWorkerI worker : canUpdate)
363       {
364         if (typeToRemove.equals(worker.getClass()))
365         {
366           removable.add(worker);
367           toremovannot.add(worker);
368         }
369       }
370       canUpdate.removeAll(removable);
371     }
372     // TODO: finish testing this extension
373
374     /*
375      * synchronized (inProgress) { // need to kill or mark as dead any running
376      * threads... (inProgress.get(typeToRemove)); }
377      * 
378      * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers)
379      * {
380      * 
381      * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); }
382      * else { System.err.println("Pending exists for " + workerClass); } }
383      */
384   }
385 }