/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.workers; import jalview.api.AlignCalcManagerI; import jalview.api.AlignCalcWorkerI; import jalview.datamodel.AlignmentAnnotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; public class AlignCalcManager implements AlignCalcManagerI { /* * list of registered workers */ private volatile List restartable; /* * types of worker _not_ to run (for example, because they have * previously thrown errors) */ private volatile List> blackList; /* * global record of calculations in progress */ private volatile List inProgress; /* * record of calculations pending or in progress in the current context */ private volatile Map, List> updating; /* * workers that have run to completion so are candidates for visual-only * update of their results */ private HashSet canUpdate; /** * Constructor */ public AlignCalcManager() { restartable = Collections .synchronizedList(new ArrayList()); blackList = Collections.synchronizedList( new ArrayList>()); inProgress = Collections .synchronizedList(new ArrayList()); updating = Collections.synchronizedMap( new Hashtable, List>()); canUpdate = new HashSet(); } @Override public void notifyStart(AlignCalcWorkerI worker) { synchronized (updating) { List upd = updating.get(worker.getClass()); if (upd == null) { updating.put(worker.getClass(), upd = Collections .synchronizedList(new ArrayList())); } synchronized (upd) { upd.add(worker); } } } /* * (non-Javadoc) * * @see jalview.api.AlignCalcManagerI#isPending(jalview.api.AlignCalcWorkerI) */ @Override public boolean isPending(AlignCalcWorkerI workingClass) { List upd; synchronized (updating) { upd = updating.get(workingClass.getClass()); if (upd == null) { return false; } synchronized (upd) { if (upd.size() > 1) { return true; } } return false; } } @Override public boolean notifyWorking(AlignCalcWorkerI worker) { synchronized (inProgress) { if (inProgress.contains(worker)) { return false; // worker is already working, so ask caller to wait around } else { inProgress.add(worker); } } return true; } @Override public void workerComplete(AlignCalcWorkerI worker) { synchronized (inProgress) { // jalview.bin.Console.errPrintln("Worker " + worker + " marked as // complete."); inProgress.remove(worker); List upd = updating.get(worker.getClass()); if (upd != null) { synchronized (upd) { upd.remove(worker); } canUpdate.add(worker); } } } @Override public void disableWorker(AlignCalcWorkerI worker) { synchronized (blackList) { blackList.add(worker.getClass()); } } @Override public boolean isDisabled(AlignCalcWorkerI worker) { synchronized (blackList) { return blackList.contains(worker.getClass()); } } @Override public void startWorker(AlignCalcWorkerI worker) { if (!isDisabled(worker)) { Thread tw = new Thread(worker); tw.setName(worker.getClass().toString()); tw.start(); } } @Override public boolean isWorking(AlignCalcWorkerI worker) { synchronized (inProgress) {// jalview.bin.Console.errPrintln("isWorking : worker "+(worker!=null ? // worker.getClass():"null")+ " "+hashCode()); return worker != null && inProgress.contains(worker); } } @Override public boolean isWorking() { boolean working = false; synchronized (inProgress) { // jalview.bin.Console.errPrintln("isWorking "+hashCode()); working |= inProgress.size() > 0; } synchronized (updating) { Collection> workersLists = updating.values(); synchronized (workersLists) { for (List workers : workersLists) { if (workers != null) { synchronized (workers) { working |= workers.size() > 0; } } } } } return working; } @Override public void registerWorker(AlignCalcWorkerI worker) { synchronized (restartable) { if (!restartable.contains(worker)) { restartable.add(worker); } startWorker(worker); } } @Override public void restartWorkers() { synchronized (restartable) { for (AlignCalcWorkerI worker : restartable) { startWorker(worker); } } } @Override public boolean workingInvolvedWith( AlignmentAnnotation alignmentAnnotation) { synchronized (inProgress) { for (AlignCalcWorkerI worker : inProgress) { if (worker.involves(alignmentAnnotation)) { return true; } } } synchronized (updating) { for (List workers : updating.values()) { for (AlignCalcWorkerI worker : workers) { if (worker.involves(alignmentAnnotation)) { return true; } } } } return false; } @Override public void updateAnnotationFor( Class workerClass) { AlignCalcWorkerI[] workers; synchronized (canUpdate) { workers = canUpdate.toArray(new AlignCalcWorkerI[0]); } for (AlignCalcWorkerI worker : workers) { if (workerClass.equals(worker.getClass())) { worker.updateAnnotation(); } } } @Override public List getRegisteredWorkersOfClass( Class workerClass) { List workingClass = new ArrayList(); AlignCalcWorkerI[] workers; synchronized (canUpdate) { workers = canUpdate.toArray(new AlignCalcWorkerI[0]); } for (AlignCalcWorkerI worker : workers) { if (workerClass.equals(worker.getClass())) { workingClass.add(worker); } } return (workingClass.size() == 0) ? null : workingClass; } @Override public void enableWorker(AlignCalcWorkerI worker) { synchronized (blackList) { blackList.remove(worker.getClass()); } } @Override public void removeRegisteredWorkersOfClass( Class typeToRemove) { List removable = new ArrayList(); Set toremovannot = new HashSet(); synchronized (restartable) { for (AlignCalcWorkerI worker : restartable) { if (typeToRemove.equals(worker.getClass())) { removable.add(worker); toremovannot.add(worker); } } restartable.removeAll(removable); } synchronized (canUpdate) { for (AlignCalcWorkerI worker : canUpdate) { if (typeToRemove.equals(worker.getClass())) { removable.add(worker); toremovannot.add(worker); } } canUpdate.removeAll(removable); } // TODO: finish testing this extension /* * synchronized (inProgress) { // need to kill or mark as dead any running * threads... (inProgress.get(typeToRemove)); } * * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers) * { * * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); } * else { jalview.bin.Console.errPrintln("Pending exists for " + workerClass); } } */ } /** * Deletes the worker that update the given annotation, provided it is marked * as deletable. */ @Override public void removeWorkerForAnnotation(AlignmentAnnotation ann) { /* * first just find those to remove (to avoid * ConcurrentModificationException) */ List toRemove = new ArrayList(); for (AlignCalcWorkerI worker : restartable) { if (worker.involves(ann)) { if (worker.isDeletable()) { toRemove.add(worker); } } } /* * remove all references to deleted workers so any references * they hold to annotation data can be garbage collected */ for (AlignCalcWorkerI worker : toRemove) { restartable.remove(worker); blackList.remove(worker.getClass()); inProgress.remove(worker); canUpdate.remove(worker); synchronized (updating) { List upd = updating.get(worker.getClass()); if (upd != null) { upd.remove(worker); } } } } }