Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.workers

File AlignCalcManager2.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

60
161
40
4
535
472
82
0.51
4.03
10
2.05

Classes

Class Line # Actions
AlignCalcManager2 29 92 44
0.7465753674.7%
AlignCalcManager2.WorkerManager 31 12 9
1.0100%
AlignCalcManager2.SimpleWorkerManager 86 18 11
0.821428682.1%
AlignCalcManager2.PollableWorkerManager 146 39 18
0.00%
 

Contributing tests

This file is covered by 258 tests. .

Source view

1    package jalview.workers;
2   
3    import java.util.HashMap;
4    import java.util.List;
5    import java.util.Map;
6    import java.util.NoSuchElementException;
7    import java.util.Objects;
8    import java.util.WeakHashMap;
9    import java.util.concurrent.CopyOnWriteArrayList;
10    import java.util.concurrent.Executors;
11    import java.util.concurrent.Future;
12    import java.util.concurrent.ScheduledExecutorService;
13    import java.util.concurrent.TimeUnit;
14   
15    import static java.lang.String.format;
16    import static java.util.Collections.synchronizedMap;
17    import static java.util.Collections.unmodifiableList;
18   
19    import java.util.ArrayList;
20   
21    import jalview.api.AlignCalcListener;
22    import jalview.api.AlignCalcManagerI2;
23    import jalview.api.AlignCalcWorkerI;
24    import jalview.api.PollableAlignCalcWorkerI;
25    import jalview.bin.Cache;
26    import jalview.bin.Console;
27    import jalview.datamodel.AlignmentAnnotation;
28   
 
29    public class AlignCalcManager2 implements AlignCalcManagerI2
30    {
 
31    private abstract class WorkerManager
32    {
33    protected volatile boolean enabled = true;
34   
35    protected AlignCalcWorkerI worker;
36   
 
37  1878 toggle WorkerManager(AlignCalcWorkerI worker)
38    {
39  1878 this.worker = worker;
40    }
41   
 
42  32341 toggle protected AlignCalcWorkerI getWorker()
43    {
44  32341 return worker;
45    }
46   
 
47  6797 toggle boolean isEnabled()
48    {
49  6797 return enabled;
50    }
51   
 
52  7 toggle void setEnabled(boolean enabled)
53    {
54  7 this.enabled = enabled;
55    }
56   
 
57  6790 toggle synchronized void restart()
58    {
59  6790 if (!isEnabled())
60    {
61  1 return;
62    }
63  6789 if (!isRegistered())
64    {
65  3 setEnabled(false);
66    }
67  6789 if (isWorking())
68    {
69  3289 cancel();
70    }
71  6789 submit();
72    }
73   
 
74  10654 toggle protected boolean isRegistered()
75    {
76  10654 return registered.containsKey(getWorker());
77    }
78   
79    abstract boolean isWorking();
80   
81    protected abstract void submit();
82   
83    abstract void cancel();
84    }
85   
 
86    private class SimpleWorkerManager extends WorkerManager
87    {
88    private Future<?> task = null;
89   
 
90  1878 toggle SimpleWorkerManager(AlignCalcWorkerI worker)
91    {
92  1878 super(worker);
93    }
94   
 
95  33100 toggle @Override
96    boolean isWorking()
97    {
98  33100 return task != null && !task.isDone();
99    }
100   
 
101  6789 toggle @Override
102    protected void submit()
103    {
104  6789 if (task != null && !(task.isDone() || task.isCancelled()))
105    {
106  0 throw new IllegalStateException(
107    "Cannot submit new task if the prevoius one is still running");
108    }
109  6789 Console.debug(
110    format("Worker %s queued", getWorker()));
111  6789 task = executor.submit(() -> {
112  3871 try
113    {
114  3871 Console.debug(format("Worker %s started", getWorker()));
115  3871 getWorker().run();
116  3847 Console.debug(format("Worker %s finished", getWorker()));
117    } catch (InterruptedException e)
118    {
119  0 Console.debug(format("Worker %s interrupted", getWorker()));
120    } catch (Throwable th)
121    {
122  18 Console.debug(format("Worker %s failed", getWorker()), th);
123    } finally
124    {
125  3865 if (!isRegistered())
126    {
127    // delete worker reference so garbage collector can remove it
128  24 worker = null;
129    }
130    }
131    });
132    }
133   
 
134  3291 toggle @Override
135    synchronized void cancel()
136    {
137  3291 if (!isWorking())
138    {
139  0 return;
140    }
141  3291 Console.debug(format("Cancelling worker %s", getWorker()));
142  3291 task.cancel(true);
143    }
144    }
145   
 
146    private class PollableWorkerManager extends WorkerManager
147    {
148    private Future<?> task = null;
149   
 
150  0 toggle PollableWorkerManager(PollableAlignCalcWorkerI worker)
151    {
152  0 super(worker);
153    }
154   
 
155  0 toggle @Override
156    protected PollableAlignCalcWorkerI getWorker()
157    {
158  0 return (PollableAlignCalcWorkerI) super.getWorker();
159    }
160   
 
161  0 toggle @Override
162    boolean isWorking()
163    {
164  0 return task != null && !task.isDone();
165    }
166   
 
167  0 toggle protected void submit()
168    {
169  0 if (task != null && !(task.isDone() || task.isCancelled()))
170    {
171  0 throw new IllegalStateException(
172    "Cannot submit new task if the previous one is still running");
173    }
174  0 Console.debug( format("Worker %s queued", getWorker()));
175  0 final var runnable = new Runnable()
176    {
177    private boolean started = false;
178   
179    private boolean completed = false;
180   
181    Future<?> future = null;
182   
 
183  0 toggle @Override
184    public void run()
185    {
186  0 try
187    {
188  0 if (!started)
189    {
190  0 Console.debug(format("Worker %s started", getWorker()));
191  0 getWorker().startUp();
192  0 started = true;
193    }
194  0 else if (!completed)
195    {
196  0 Console.debug(format("Polling worker %s", getWorker()));
197  0 if (getWorker().poll())
198    {
199  0 Console.debug(format("Worker %s finished", getWorker()));
200  0 completed = true;
201    }
202    }
203    } catch (Throwable th)
204    {
205  0 Console.debug(format("Worker %s failed", getWorker()), th);
206  0 completed = true;
207    }
208  0 if (completed)
209    {
210  0 final var worker = getWorker();
211  0 if (!isRegistered())
212  0 PollableWorkerManager.super.worker = null;
213  0 Console.debug(format("Finalizing completed worker %s", worker));
214  0 worker.done();
215    // almost impossible, but the future may be null at this point
216    // let it throw NPE to cancel forcefully
217  0 future.cancel(false);
218    }
219    }
220    };
221  0 runnable.future = task = executor.scheduleWithFixedDelay(runnable, 10,
222    1000, TimeUnit.MILLISECONDS);
223    }
224   
 
225  0 toggle synchronized protected void cancel()
226    {
227  0 if (!isWorking())
228    {
229  0 return;
230    }
231  0 Console.debug(format("Cancelling worker %s", getWorker()));
232  0 task.cancel(false);
233  0 executor.submit(() -> {
234  0 final var worker = getWorker();
235  0 if (!isRegistered())
236  0 PollableWorkerManager.super.worker = null;
237  0 if (worker != null)
238    {
239  0 worker.cancel();
240  0 Console.debug(format("Finalizing cancelled worker %s", worker));
241  0 worker.done();
242    }
243    });
244    }
245    }
246   
247    private final ScheduledExecutorService executor = Executors
248    .newSingleThreadScheduledExecutor();
249   
250    private final Map<AlignCalcWorkerI, WorkerManager> registered = synchronizedMap(
251    new HashMap<>());
252   
253    private final Map<AlignCalcWorkerI, WorkerManager> oneshot = synchronizedMap(
254    new WeakHashMap<>());
255   
256    private final List<AlignCalcListener> listeners = new CopyOnWriteArrayList<>();
257   
 
258  1878 toggle private WorkerManager createManager(AlignCalcWorkerI worker)
259    {
260  1878 if (worker instanceof PollableAlignCalcWorkerI)
261    {
262  0 return new PollableWorkerManager((PollableAlignCalcWorkerI) worker);
263    }
264    else
265    {
266  1878 return new SimpleWorkerManager(worker);
267    }
268    }
269   
 
270  1875 toggle @Override
271    public void registerWorker(AlignCalcWorkerI worker)
272    {
273  1875 Objects.requireNonNull(worker);
274  1875 WorkerManager manager = createManager(worker);
275  1875 registered.putIfAbsent(worker, manager);
276  1875 startWorker(worker);
277    }
278   
 
279  6474 toggle @Override
280    public List<AlignCalcWorkerI> getWorkers()
281    {
282  6474 List<AlignCalcWorkerI> result = new ArrayList<>(registered.size());
283  6474 result.addAll(registered.keySet());
284  6474 return result;
285    }
286   
 
287  6442 toggle @Override
288    public List<AlignCalcWorkerI> getWorkersOfClass(
289    Class<? extends AlignCalcWorkerI> cls)
290    {
291  6442 List<AlignCalcWorkerI> collected = new ArrayList<>();
292  6442 for (var worker : getWorkers())
293    {
294  20364 if (worker.getClass().equals(cls))
295    {
296  2734 collected.add(worker);
297    }
298    }
299  6442 return unmodifiableList(collected);
300    }
301   
 
302  9 toggle @Override
303    public void removeWorker(AlignCalcWorkerI worker)
304    {
305  9 if (worker.isDeletable())
306    {
307  6 registered.remove(worker);
308    }
309    }
310   
 
311  3 toggle @Override
312    public void removeWorkerForAnnotation(AlignmentAnnotation annot)
313    {
314  3 synchronized (registered)
315    {
316  3 for (var worker : getWorkers())
317    {
318  18 if (worker.involves(annot))
319    {
320  3 removeWorker(worker);
321    }
322    }
323    }
324    }
325   
 
326  2 toggle @Override
327    public void removeWorkersOfClass(Class<? extends AlignCalcWorkerI> cls)
328    {
329  2 synchronized (registered)
330    {
331  2 for (var worker : getWorkers())
332    {
333  13 if (worker.getClass().equals(cls))
334    {
335  4 removeWorker(worker);
336    }
337    }
338    }
339    }
340   
 
341  2 toggle @Override
342    public void disableWorker(AlignCalcWorkerI worker)
343    {
344    // Null pointer check might be needed
345  2 registered.get(worker).setEnabled(false);
346    }
347   
 
348  2 toggle @Override
349    public void enableWorker(AlignCalcWorkerI worker)
350    {
351    // Null pointer check might be needed
352  2 registered.get(worker).setEnabled(true);
353    }
354   
 
355  8 toggle @Override
356    public boolean isDisabled(AlignCalcWorkerI worker)
357    {
358  8 if (registered.containsKey(worker))
359    {
360  7 return !registered.get(worker).isEnabled();
361    }
362    else
363    {
364  1 return false;
365    }
366    }
367   
 
368  26 toggle @Override
369    public boolean isWorking(AlignCalcWorkerI worker)
370    {
371  26 var manager = registered.get(worker);
372  26 if (manager == null)
373  0 manager = oneshot.get(worker);
374  26 if (manager == null)
375  0 return false;
376    else
377  26 return manager.isWorking();
378    }
379   
 
380  11619 toggle @Override
381    public boolean isWorkingWithAnnotation(AlignmentAnnotation annot)
382    {
383  11619 synchronized (registered)
384    {
385  11619 for (var entry : registered.entrySet())
386  45050 if (entry.getKey().involves(annot) && entry.getValue().isWorking())
387  322 return true;
388    }
389  11297 synchronized (oneshot)
390    {
391  11297 for (var entry : registered.entrySet())
392  44266 if (entry.getKey().involves(annot) && entry.getValue().isWorking())
393  0 return true;
394    }
395  11297 return false;
396    }
397   
 
398  3613 toggle @Override
399    public boolean isWorking()
400    {
401  3613 synchronized (registered)
402    {
403  3613 for (var manager : registered.values())
404  12676 if (manager.isWorking())
405  630 return true;
406    }
407  2983 synchronized (oneshot)
408    {
409  2983 for (var manager : oneshot.values())
410  0 if (manager.isWorking())
411  0 return true;
412    }
413  2983 return false;
414    }
415   
 
416  3269 toggle @Override
417    public void startWorker(AlignCalcWorkerI worker)
418    {
419  3269 Objects.requireNonNull(worker);
420  3269 var manager = registered.get(worker);
421  3269 if (manager == null)
422    {
423  3 Console.warn("Starting unregistered worker " + worker);
424  3 manager = createManager(worker);
425  3 oneshot.put(worker, manager);
426    }
427  3269 manager.restart();
428    }
429   
 
430  934 toggle @Override
431    public void restartWorkers()
432    {
433  934 synchronized (registered)
434    {
435  934 for (var manager : registered.values())
436    {
437  3521 manager.restart();
438    }
439    }
440    }
441   
 
442  12 toggle @Override
443    public void cancelWorker(AlignCalcWorkerI worker)
444    {
445  12 Objects.requireNonNull(worker);
446  12 var manager = registered.get(worker);
447  12 if (manager == null)
448  11 manager = oneshot.get(worker);
449  12 if (manager == null)
450    {
451  10 throw new NoSuchElementException();
452    }
453  2 manager.cancel();
454    }
455   
 
456  0 toggle private void notifyQueued(AlignCalcWorkerI worker)
457    {
458  0 for (AlignCalcListener listener : listeners)
459    {
460  0 listener.workerQueued(worker);
461    }
462    }
463   
 
464  0 toggle private void notifyStarted(AlignCalcWorkerI worker)
465    {
466  0 for (AlignCalcListener listener : listeners)
467    {
468  0 listener.workerStarted(worker);
469    }
470    }
471   
 
472  0 toggle private void notifyCompleted(AlignCalcWorkerI worker)
473    {
474  0 for (AlignCalcListener listener : listeners)
475    {
476  0 try
477    {
478  0 listener.workerCompleted(worker);
479    } catch (RuntimeException e)
480    {
481  0 e.printStackTrace();
482    }
483    }
484    }
485   
 
486  0 toggle private void notifyCancelled(AlignCalcWorkerI worker)
487    {
488  0 for (AlignCalcListener listener : listeners)
489    {
490  0 try
491    {
492  0 listener.workerCancelled(worker);
493    } catch (RuntimeException e)
494    {
495  0 e.printStackTrace();
496    }
497    }
498    }
499   
 
500  0 toggle private void notifyExceptional(AlignCalcWorkerI worker,
501    Throwable throwable)
502    {
503  0 for (AlignCalcListener listener : listeners)
504    {
505  0 try
506    {
507  0 listener.workerExceptional(worker, throwable);
508    } catch (RuntimeException e)
509    {
510  0 e.printStackTrace();
511    }
512    }
513    }
514   
 
515  0 toggle @Override
516    public void addAlignCalcListener(AlignCalcListener listener)
517    {
518  0 listeners.add(listener);
519    }
520   
 
521  0 toggle @Override
522    public void removeAlignCalcListener(AlignCalcListener listener)
523    {
524  0 listeners.remove(listener);
525    }
526   
 
527  346 toggle @Override
528    public void shutdown()
529    {
530  346 executor.shutdownNow();
531  346 listeners.clear();
532  346 registered.clear();
533    }
534   
535    }