1 package jalview.workers;
4 import java.util.Objects;
5 import java.util.Optional;
7 import java.util.concurrent.Callable;
8 import java.util.concurrent.CancellationException;
9 import java.util.concurrent.CopyOnWriteArrayList;
10 import java.util.concurrent.ExecutionException;
11 import java.util.concurrent.ExecutorService;
12 import java.util.concurrent.Executors;
13 import java.util.concurrent.FutureTask;
14 import java.util.stream.Collectors;
16 import jalview.api.AlignCalcListener;
17 import jalview.api.AlignCalcManagerI2;
18 import jalview.api.AlignCalcWorkerI;
19 import jalview.bin.Cache;
20 import jalview.datamodel.AlignmentAnnotation;
22 import static java.util.Collections.synchronizedList;
23 import static java.util.Collections.synchronizedSet;
24 import static java.util.Collections.unmodifiableList;
26 import java.util.ArrayList;
27 import java.util.HashSet;
29 import static java.lang.String.format;
31 public class AlignCalcManager2 implements AlignCalcManagerI2
33 class AlignCalcTask extends FutureTask<Void>
35 final AlignCalcWorkerI worker;
37 public AlignCalcTask(AlignCalcWorkerI worker)
39 super(new Callable<Void>() {
40 public Void call() throws Exception {
41 Cache.log.debug(format("Worker %s started%n", worker.getClass().getName()));
42 notifyStarted(worker);
50 public AlignCalcWorkerI getWorker()
58 boolean success = false;
59 Throwable exception = null;
65 catch (CancellationException e) {
66 Cache.log.debug(format("Worker %s cancelled%n", getWorker().getClass().getName()));
67 notifyCancelled(worker);
69 catch (ExecutionException e)
71 exception = e.getCause();
72 if (exception instanceof OutOfMemoryError) {
73 disableWorker(getWorker());
80 inProgress.remove(getWorker());
85 Cache.log.debug(format("Worker %s finished%n", getWorker().getClass().getName()));
86 notifyCompleted(worker);
88 else if (exception != null){
89 Cache.log.warn(format("Worker %s failed%n", getWorker().getClass().getName()));
90 exception.printStackTrace();
91 notifyExceptional(worker, exception);
96 // main executor for running workers one-by-one
97 private final ExecutorService executor = Executors.newSingleThreadExecutor();
99 private final List<AlignCalcListener> listeners = new CopyOnWriteArrayList<AlignCalcListener>();
101 // list of all registered workers (other collections are subsets of this)
102 private final List<AlignCalcWorkerI> registered = synchronizedList(new ArrayList<>());
104 // list of tasks holding queued and running workers
105 private final List<AlignCalcTask> tasks = synchronizedList(new ArrayList<>());
107 // the collection of currently running workers
108 private final Set<AlignCalcWorkerI> inProgress = synchronizedSet(new HashSet<>());
110 // the collection of workers that will not be started
111 private final Set<AlignCalcWorkerI> disabled = synchronizedSet(new HashSet<>());
114 * Register the worker with this manager and scheduler for execution.
117 public void registerWorker(AlignCalcWorkerI worker)
119 Objects.requireNonNull(worker);
120 synchronized (registered)
122 if (!registered.contains(worker))
123 registered.add(worker);
129 public List<AlignCalcWorkerI> getWorkers()
131 return unmodifiableList(new ArrayList<>(registered));
135 public List<AlignCalcWorkerI> getWorkersOfClass(
136 Class<? extends AlignCalcWorkerI> cls)
138 synchronized (registered)
140 return registered.stream()
141 .filter(worker -> worker.getClass().equals(cls))
142 .collect(Collectors.toUnmodifiableList());
147 public void removeWorker(AlignCalcWorkerI worker)
149 registered.remove(worker);
150 disabled.remove(worker);
154 public void removeWorkersOfClass(Class<? extends AlignCalcWorkerI> cls)
156 synchronized (registered)
158 for (var worker : registered)
160 if (worker.getClass().equals(cls))
162 removeWorker(worker);
169 public void removeWorkerForAnnotation(AlignmentAnnotation annot)
171 synchronized (registered)
173 for (var worker : registered)
175 if (worker.involves(annot) && worker.isDeletable())
177 removeWorker(worker);
184 public void disableWorker(AlignCalcWorkerI worker)
186 disabled.add(worker);
190 public void enableWorker(AlignCalcWorkerI worker)
192 disabled.remove(worker);
196 public void restartWorkers()
198 synchronized (registered)
200 for (AlignCalcWorkerI worker : registered)
202 if (!isDisabled(worker))
209 public void startWorker(AlignCalcWorkerI worker)
211 Objects.requireNonNull(worker);
212 AlignCalcTask newTask = new AlignCalcTask(worker);
213 synchronized (inProgress)
215 cancelWorker(worker);
216 inProgress.add(worker);
219 notifyQueued(worker);
220 executor.execute(newTask);
224 public void cancelWorker(AlignCalcWorkerI worker)
226 if (isWorking(worker))
230 Optional<AlignCalcTask> oldTask = tasks.stream()
231 .filter(task -> task.getWorker().equals(worker))
233 if (oldTask.isPresent()) {
234 oldTask.get().cancel(true);
241 public boolean isDisabled(AlignCalcWorkerI worker)
243 return disabled.contains(worker);
247 public boolean isWorking(AlignCalcWorkerI worker)
249 return inProgress.contains(worker);
253 public boolean isWorking()
255 return !inProgress.isEmpty();
259 public boolean isWorkingWithAnnotation(AlignmentAnnotation annot)
261 synchronized (inProgress)
263 for (AlignCalcWorkerI worker : inProgress)
265 if (worker.involves(annot))
274 private void notifyQueued(AlignCalcWorkerI worker)
276 for (AlignCalcListener listener : listeners)
278 listener.workerQueued(worker);
282 private void notifyStarted(AlignCalcWorkerI worker)
284 for (AlignCalcListener listener : listeners)
286 listener.workerStarted(worker);
290 private void notifyCompleted(AlignCalcWorkerI worker)
292 for (AlignCalcListener listener : listeners)
295 listener.workerCompleted(worker);
296 } catch (RuntimeException e)
303 private void notifyCancelled(AlignCalcWorkerI worker)
305 for (AlignCalcListener listener : listeners)
308 listener.workerCancelled(worker);
309 } catch (RuntimeException e)
316 private void notifyExceptional(AlignCalcWorkerI worker,
319 for (AlignCalcListener listener : listeners)
322 listener.workerExceptional(worker, throwable);
323 } catch (RuntimeException e)
331 public void addAlignCalcListener(AlignCalcListener listener)
333 listeners.add(listener);
337 public void removeAlignCalcListener(AlignCalcListener listener)
339 listeners.remove(listener);