X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fws%2Fjws2%2FSeqAnnotationServiceCalcWorker.java;h=fee2b38c3f93f54dbfda17d2214b001c16317585;hb=ab58313e7bb31264ddb2715a1c548cdc789d7fa6;hp=6793f1ee3edd893ade0863b553168a7e3d2cca07;hpb=ba2c0286e9a47af8ffd46057f91a22df0d4850ea;p=jalview.git diff --git a/src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java b/src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java index 6793f1e..fee2b38 100644 --- a/src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java +++ b/src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java @@ -21,29 +21,42 @@ package jalview.ws.jws2; import jalview.analysis.AlignSeq; +import jalview.analysis.AlignmentAnnotationUtils; import jalview.analysis.SeqsetUtils; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureColourI; +import jalview.api.PollableAlignCalcWorkerI; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AnnotatedCollectionI; +import jalview.datamodel.Annotation; +import jalview.datamodel.ContiguousI; +import jalview.datamodel.Mapping; import jalview.datamodel.SequenceI; +import jalview.datamodel.features.FeatureMatcherSetI; import jalview.gui.AlignFrame; import jalview.gui.Desktop; import jalview.gui.IProgressIndicator; import jalview.gui.IProgressIndicatorHandler; import jalview.gui.JvOptionPane; +import jalview.gui.WebserviceInfo; +import jalview.schemes.FeatureSettingsAdapter; import jalview.schemes.ResidueProperties; +import jalview.util.MapList; import jalview.util.MessageManager; import jalview.workers.AlignCalcWorker; +import jalview.ws.JobStateSummary; import jalview.ws.api.CancellableI; +import jalview.ws.api.JalviewServiceEndpointProviderI; import jalview.ws.api.JobId; import jalview.ws.api.SequenceAnnotationServiceI; +import jalview.ws.api.ServiceWithParameters; import jalview.ws.api.WSAnnotationCalcManagerI; import jalview.ws.gui.AnnotationWsJob; import jalview.ws.jws2.dm.AAConSettings; -import jalview.ws.jws2.jabaws2.Jws2Instance; import jalview.ws.params.ArgumentI; import jalview.ws.params.WsParamSetI; @@ -53,10 +66,10 @@ import java.util.List; import java.util.Map; public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker - implements WSAnnotationCalcManagerI + implements WSAnnotationCalcManagerI, PollableAlignCalcWorkerI { - protected Jws2Instance service; + protected ServiceWithParameters service; protected WsParamSetI preset; @@ -151,16 +164,11 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker return gapMap; } - public SeqAnnotationServiceCalcWorker(AlignViewportI alignViewport, - AlignmentViewPanel alignPanel) - { - super(alignViewport, alignPanel); - } - - public SeqAnnotationServiceCalcWorker(Jws2Instance service, AlignFrame alignFrame, + public SeqAnnotationServiceCalcWorker(ServiceWithParameters service, + AlignFrame alignFrame, WsParamSetI preset, List paramset) { - this(alignFrame.getCurrentView(), alignFrame.alignPanel); + super(alignFrame.getCurrentView(), alignFrame.alignPanel); // TODO: both these fields needed ? this.alignFrame = alignFrame; this.guiProgress = alignFrame; @@ -169,11 +177,12 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker this.service = service; try { - annotService = (jalview.ws.api.SequenceAnnotationServiceI) service + annotService = (jalview.ws.api.SequenceAnnotationServiceI) ((JalviewServiceEndpointProviderI) service) .getEndpoint(); } catch (ClassCastException cce) { - JvOptionPane.showMessageDialog(Desktop.desktop, + annotService = null; + JvOptionPane.showMessageDialog(Desktop.getInstance(), MessageManager.formatMessage( "label.service_called_is_not_an_annotation_service", new String[] @@ -182,32 +191,20 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker JvOptionPane.WARNING_MESSAGE); } + cancellable = CancellableI.class.isInstance(annotService); // configure submission flags - if (service.getAlignAnalysisUI() != null) + proteinAllowed = service.isProteinService(); + nucleotidesAllowed = service.isNucleotideService(); + alignedSeqs = service.isNeedsAlignedSequences(); + bySequence = !service.isAlignmentAnalysis(); + filterNonStandardResidues = service.isFilterSymbols(); + min_valid_seqs = service.getMinimumInputSequences(); + submitGaps = service.isAlignmentAnalysis(); + + if (service.isInteractiveUpdate()) { - // instantaneous calculation. Right now that's either AACons or RNAAliFold - proteinAllowed = service.getAlignAnalysisUI().isPr(); - nucleotidesAllowed = service.getAlignAnalysisUI().isNa(); - alignedSeqs = service.getAlignAnalysisUI().isNeedsAlignedSeqs(); - bySequence = !service.getAlignAnalysisUI().isAA(); - filterNonStandardResidues = service.getAlignAnalysisUI() - .isFilterSymbols(); - min_valid_seqs = service.getAlignAnalysisUI().getMinimumSequences(); initViewportParams(); } - else - { - // assume disorder prediction : per-sequence protein only no gaps - // analysis. - // TODO - move configuration to UIInfo base class for all these flags ! - alignedSeqs = false; - bySequence = true; - filterNonStandardResidues = true; - nucleotidesAllowed = false; - proteinAllowed = true; - submitGaps = false; - min_valid_seqs = 1; - } } /** @@ -219,7 +216,8 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker return annotService != null; } - protected jalview.ws.api.SequenceAnnotationServiceI annotService = null; + protected SequenceAnnotationServiceI annotService; + protected final boolean cancellable; volatile JobId rslt = null; @@ -227,265 +225,244 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker private int min_valid_seqs; - @Override - public void run() + + private long progressId = -1; + JobStateSummary job = null; + WebserviceInfo info = null; + List seqs = null; + + @Override public void startUp() throws Throwable { + if (alignViewport.isClosed()) + { + abortAndDestroy(); + return; + } if (!hasService()) { return; } - long progressId = -1; - - int serverErrorsLeft = 3; - final boolean cancellable = CancellableI.class - .isAssignableFrom(annotService.getClass()); StringBuffer msg = new StringBuffer(); - try + job = new JobStateSummary(); + info = new WebserviceInfo("foo", "bar", false); + + seqs = getInputSequences( + alignViewport.getAlignment(), + bySequence ? alignViewport.getSelectionGroup() : null); + + if (seqs == null || !checkValidInputSeqs(seqs)) { - if (checkDone()) - { - return; - } - List seqs = getInputSequences( - alignViewport.getAlignment(), - bySequence ? alignViewport.getSelectionGroup() : null); + jalview.bin.Console.debug( + "Sequences for analysis service were null or not valid"); + return; + } - if (seqs == null || !checkValidInputSeqs(seqs)) - { - jalview.bin.Cache.log.debug( - "Sequences for analysis service were null or not valid"); - calcMan.workerComplete(this); - return; - } + if (guiProgress != null) + { + guiProgress.setProgressBar(service.getActionText(), + progressId = System.currentTimeMillis()); + } + jalview.bin.Console.debug("submitted " + seqs.size() + + " sequences to " + service.getActionText()); - AlignmentAnnotation[] aa = alignViewport.getAlignment() - .getAlignmentAnnotation(); - if (guiProgress != null) - { - guiProgress.setProgressBar(service.getActionText(), - progressId = System.currentTimeMillis()); - } - jalview.bin.Cache.log.debug("submitted " + seqs.size() - + " sequences to " + service.getActionText()); + rslt = annotService.submitToService(seqs, getPreset(), + getArguments()); + if (rslt == null) + { + return; + } + // TODO: handle job submission error reporting here. + Console.debug("Service " + service.getUri() + "\nSubmitted job ID: " + + rslt); + // /// + // otherwise, construct WsJob and any UI handlers + running = new AnnotationWsJob(); + running.setJobHandle(rslt); + running.setSeqNames(seqNames); + running.setStartPos(start); + running.setSeqs(seqs); + job.updateJobPanelState(info, "", running); + if (guiProgress != null) + { + guiProgress.registerHandler(progressId, + new IProgressIndicatorHandler() + { - rslt = annotService.submitToService(seqs, getPreset(), - getArguments()); - if (rslt == null) - { - return; - } - // TODO: handle job submission error reporting here. - - // /// - // otherwise, construct WsJob and any UI handlers - running = new AnnotationWsJob(); - running.setJobHandle(rslt); - running.setSeqNames(seqNames); - running.setStartPos(start); - running.setSeqs(seqs); - - if (guiProgress != null) - { - guiProgress.registerHandler(progressId, - new IProgressIndicatorHandler() + @Override + public boolean cancelActivity(long id) { + calcMan.cancelWorker(SeqAnnotationServiceCalcWorker.this); + return true; + } - @Override - public boolean cancelActivity(long id) - { - ((CancellableI) annotService).cancel(running); - return true; - } - - @Override - public boolean canCancel() - { - return cancellable; - } - }); - } - - // /// - // and poll for updates until job finishes, fails or becomes stale - - boolean finished = false; - long rpos = 0; - do - { - Cache.log.debug("Updating status for annotation service."); - annotService.updateStatus(running); + @Override + public boolean canCancel() + { + return cancellable; + } + }); + } + } + + @Override public boolean poll() throws Throwable + { + boolean finished = false; + + Console.debug("Updating status for annotation service."); + annotService.updateStatus(running); + job.updateJobPanelState(info, "", running); + if (running.isSubjobComplete()) + { + Console.debug( + "Finished polling analysis service job: status reported is " + + running.getState()); + finished = true; + } + else + { + Console.debug("Status now " + running.getState()); + } - if (running.isFinished()) - { - Cache.log.debug("Analysis service job reported finished."); - finished = true; - } - else + // pull any stats - some services need to flush log output before + // results are available + Console.debug("Updating progress log for annotation service."); + + try + { + annotService.updateJobProgress(running); + } catch (Throwable thr) + { + Console.debug("Ignoring exception during progress update.", + thr); + } + Console.debug("Result of poll: " + running.getStatus()); + + + if (finished) + { + Console.debug("Job poll loop exited. Job is " + running.getState()); + if (running.isFinished()) + { + // expect there to be results to collect + // configure job with the associated view's feature renderer, if one + // exists. + // TODO: here one would also grab the 'master feature renderer' in order + // to enable/disable + // features automatically according to user preferences + running.setFeatureRenderer( + ((jalview.gui.AlignmentPanel) ap).cloneFeatureRenderer()); + Console.debug("retrieving job results."); + final Map featureColours = new HashMap<>(); + final Map featureFilters = new HashMap<>(); + List returnedAnnot = annotService + .getAnnotationResult(running.getJobHandle(), seqs, + featureColours, featureFilters); + + Console.debug("Obtained " + (returnedAnnot == null ? "no rows" + : ("" + returnedAnnot.size()))); + Console.debug("There were " + featureColours.size() + + " feature colours and " + featureFilters.size() + + " filters defined."); + + // TODO + // copy over each annotation row reurned and also defined on each + // sequence, excluding regions not annotated due to gapMap/column + // visibility + + // update calcId if it is not already set on returned annotation + if (returnedAnnot != null) { - Cache.log.debug("Status now " + running.getState()); + for (AlignmentAnnotation aa : returnedAnnot) + { + // assume that any CalcIds already set + if (getCalcId() != null && aa.getCalcId() == null + || "".equals(aa.getCalcId())) + { + aa.setCalcId(getCalcId()); + } + // autocalculated annotation are created by interactive alignment + // analysis services + aa.autoCalculated = service.isAlignmentAnalysis() + && service.isInteractiveUpdate(); + } } - if (calcMan.isPending(this) && isInteractiveUpdate()) + running.setAnnotation(returnedAnnot); + + if (running.hasResults()) { - Cache.log.debug("Analysis service job is stale. aborting."); - // job has become stale. - if (!finished) { - finished = true; - // cancel this job and yield to the new job - try + jalview.bin.Console.debug("Updating result annotation from Job " + + rslt + " at " + service.getUri()); + updateResultAnnotation(true); + if (running.isTransferSequenceFeatures()) + { + // TODO + // look at each sequence and lift over any features, excluding + // regions + // not annotated due to gapMap/column visibility + + jalview.bin.Console.debug( + "Updating feature display settings and transferring features from Job " + + rslt + " at " + service.getUri()); + // TODO: consider merge rather than apply here + alignViewport.applyFeaturesStyle(new FeatureSettingsAdapter() { - if (cancellable - && ((CancellableI) annotService).cancel(running)) + @Override + public FeatureColourI getFeatureColour(String type) { - System.err.println("Cancelled AACon job: " + rslt); + return featureColours.get(type); } - else + + @Override + public FeatureMatcherSetI getFeatureFilters(String type) { - System.err.println("FAILED TO CANCEL AACon job: " + rslt); + return featureFilters.get(type); } - - } catch (Exception x) - { - - } - } - rslt = running.getJobHandle(); - return; - } - // pull any stats - some services need to flush log output before - // results are available - Cache.log.debug("Updating progress log for annotation service."); - - try - { - annotService.updateJobProgress(running); - } catch (Throwable thr) - { - Cache.log.debug("Ignoring exception during progress update.", - thr); - } - Cache.log.trace("Result of poll: " + running.getStatus()); + @Override + public boolean isFeatureDisplayed(String type) + { + return featureColours.containsKey(type); + } - if (!finished && !running.isFailed()) - { - try - { - Cache.log.debug("Analysis service job thread sleeping."); - Thread.sleep(200); - Cache.log.debug("Analysis service job thread woke."); - } catch (InterruptedException x) - { - } - ; - } - } while (!finished); + }); + // TODO: JAL-1150 - create sequence feature settings API for + // defining + // styles and enabling/disabling feature overlay on alignment panel - // TODO: need to poll/retry - if (serverErrorsLeft > 0) - { - try - { - Thread.sleep(200); - } catch (InterruptedException x) - { - } - } - // configure job with the associated view's feature renderer, if one - // exists. - // TODO: here one would also grab the 'master feature renderer' in order - // to enable/disable - // features automatically according to user preferences - running.setFeatureRenderer( - ((jalview.gui.AlignmentPanel) ap).cloneFeatureRenderer()); - Cache.log.debug("retrieving job results."); - List returnedAnnot = annotService - .getAlignmentAnnotation(running, this); - Cache.log.debug("Obtained " + (returnedAnnot == null ? "no rows" - : ("" + returnedAnnot.size()))); - running.setAnnotation(returnedAnnot); - - if (running.hasResults()) - { - jalview.bin.Cache.log.debug("Updating result annotation from Job " - + rslt + " at " + service.getUri()); - updateResultAnnotation(true); - if (running.isTransferSequenceFeatures()) - { - jalview.bin.Cache.log.debug( - "Updating feature display settings and transferring features from Job " - + rslt + " at " + service.getUri()); - ((jalview.gui.AlignmentPanel) ap) - .updateFeatureRendererFrom(running.getFeatureRenderer()); - // TODO: JAL-1150 - create sequence feature settings API for defining - // styles and enabling/disabling feature overlay on alignment panel - - if (alignFrame.alignPanel == ap) - { - // only do this if the alignFrame is currently showing this view. - Desktop.getAlignFrameFor(alignViewport) - .setShowSeqFeatures(true); + if (alignFrame.alignPanel == ap) + { + alignViewport.setShowSequenceFeatures(true); + alignFrame.setMenusForViewport(); + } } + ap.adjustAnnotationHeight(); } - ap.adjustAnnotationHeight(); } - Cache.log.debug("Annotation Service Worker thread finished."); - } -// TODO: use service specitic exception handlers -// catch (JobSubmissionException x) -// { -// -// System.err.println( -// "submission error with " + getServiceActionText() + " :"); -// x.printStackTrace(); -// calcMan.disableWorker(this); -// } catch (ResultNotAvailableException x) -// { -// System.err.println("collection error:\nJob ID: " + rslt); -// x.printStackTrace(); -// calcMan.disableWorker(this); -// -// } catch (OutOfMemoryError error) -// { -// calcMan.disableWorker(this); -// -// ap.raiseOOMWarning(getServiceActionText(), error); -// } - catch (Throwable x) - { - calcMan.disableWorker(this); + Console.debug("Annotation Service Worker thread finished."); - System.err - .println("Blacklisting worker due to unexpected exception:"); - x.printStackTrace(); - } finally + } + + return finished; + } + + @Override public void cancel() + { + cancelCurrentJob(); + } + + @Override public void done() + { + if (ap != null) { - - calcMan.workerComplete(this); - if (ap != null) + if (guiProgress != null && progressId != -1) { - calcMan.workerComplete(this); - if (guiProgress != null && progressId != -1) - { - guiProgress.setProgressBar("", progressId); - } - // TODO: may not need to paintAlignment again ! - ap.paintAlignment(false, false); - } - if (msg.length() > 0) - { - // TODO: stash message somewhere in annotation or alignment view. - // code below shows result in a text box popup - /* - * jalview.gui.CutAndPasteTransfer cap = new - * jalview.gui.CutAndPasteTransfer(); cap.setText(msg.toString()); - * jalview.gui.Desktop.addInternalFrame(cap, - * "Job Status for "+getServiceActionText(), 600, 400); - */ + guiProgress.removeProgressBar(progressId); } + // TODO: may not need to paintAlignment again ! + ap.paintAlignment(false, false); } - } /** @@ -518,7 +495,7 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker try { String id = running.getJobId(); - if (((CancellableI) annotService).cancel(running)) + if (cancellable && ((CancellableI) annotService).cancel(running)) { System.err.println("Cancelled job " + id); } @@ -692,46 +669,155 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker if ((immediate || !calcMan.isWorking(this)) && running != null && running.hasResults()) { - List ourAnnot = running.getAnnotation(); - updateOurAnnots(ourAnnot); - } - } - - /** - * notify manager that we have started, and wait for a free calculation slot - * - * @return true if slot is obtained and work still valid, false if another - * thread has done our work for us. - */ - protected boolean checkDone() - { - calcMan.notifyStart(this); - ap.paintAlignment(false, false); - while (!calcMan.notifyWorking(this)) - { - if (calcMan.isWorking(this)) + List ourAnnot = running.getAnnotation(), + newAnnots = new ArrayList<>(); + // + // update graphGroup for all annotation + // + /** + * find a graphGroup greater than any existing ones this could be a method + * provided by alignment Alignment.getNewGraphGroup() - returns next + * unused graph group + */ + int graphGroup = 1; + if (alignViewport.getAlignment().getAlignmentAnnotation() != null) { - return true; + for (AlignmentAnnotation ala : alignViewport.getAlignment() + .getAlignmentAnnotation()) + { + if (ala.graphGroup > graphGroup) + { + graphGroup = ala.graphGroup; + } + } } - try + /** + * update graphGroup in the annotation rows returned from service + */ + // TODO: look at sequence annotation rows and update graph groups in the + // case of reference annotation. + for (AlignmentAnnotation ala : ourAnnot) { - if (ap != null) + if (ala.graphGroup > 0) + { + ala.graphGroup += graphGroup; + } + SequenceI aseq = null; + + /** + * transfer sequence refs and adjust gapmap + */ + if (ala.sequenceRef != null) { - ap.paintAlignment(false, false); + SequenceI seq = running.getSeqNames() + .get(ala.sequenceRef.getName()); + aseq = seq; + // This part doesn't do anything. I suppose bad merge. + while (seq.getDatasetSequence() != null) + { + seq = seq.getDatasetSequence(); + } + } + /* Prepare an array for annotations and copy returned annotations to it. + * Similar process happens in develop:JabawsCalcWorker#constructAnnotationFromScore. + * However, JabawsCalcWorker fills the array form the beginning, not + * the start position (start - column of the submitted region). + * Also, JabawsCalcWorker adds those annotations to an AlignmentAnnotation + * retrieved from the current alignment. */ + Annotation[] resAnnot = ala.annotations, + gappedAnnot = new Annotation[Math.max( + alignViewport.getAlignment().getWidth(), + gapMap.length)]; + for (int p = 0, ap = start; ap < gappedAnnot.length; ap++) + { + if (gapMap != null && gapMap.length > ap && !gapMap[ap]) + { + gappedAnnot[ap] = new Annotation("", "", ' ', Float.NaN); + } + else if (p < resAnnot.length) + { + gappedAnnot[ap] = resAnnot[p++]; + } + } + /* Annotations are set in-place on the AlignmentAnnotation object returned by the service. + * First, if #updateResultAnnotation is called again the output will be mangled + * Second, the mapping of positions to annotations held internally by + * the `ala` object is now incorrect. */ + ala.sequenceRef = aseq; + ala.annotations = gappedAnnot; + AlignmentAnnotation newAnnot = getAlignViewport().getAlignment() + .updateFromOrCopyAnnotation(ala); + if (aseq != null) + { + + aseq.addAlignmentAnnotation(newAnnot); + /* adjustForAlignment is meant to move annotations to their columns + * according to the sequenceMapping. + * Since the mapping is carried from the alignment returned by the + * service it simply maps annotations to consecutive positions 1, 2, 3... + * Resulting in the annotation row being always aligned to the beginning + * of the alignment. */ + newAnnot.adjustForAlignment(); + + AlignmentAnnotationUtils.replaceAnnotationOnAlignmentWith( + newAnnot, newAnnot.label, newAnnot.getCalcId()); } + newAnnots.add(newAnnot); - Thread.sleep(200); - } catch (Exception ex) + } + for (SequenceI sq : running.getSeqs()) { - ex.printStackTrace(); + if (!sq.getFeatures().hasFeatures() + && (sq.getDBRefs() == null || sq.getDBRefs().size() == 0)) + { + continue; + } + running.setTransferSequenceFeatures(true); + SequenceI seq = running.getSeqNames().get(sq.getName()); + SequenceI dseq; + ContiguousI seqRange = seq.findPositions(start, end); + + while ((dseq = seq).getDatasetSequence() != null) + { + seq = seq.getDatasetSequence(); + } + List sourceRange = new ArrayList(); + if (gapMap != null && gapMap.length >= end) + { + int lastcol = start, col = start; + do + { + if (col == end || !gapMap[col]) + { + if (lastcol <= (col - 1)) + { + seqRange = seq.findPositions(lastcol, col); + sourceRange.add(seqRange); + } + lastcol = col + 1; + } + } while (++col <= end); + } + else + { + sourceRange.add(seq.findPositions(start, end)); + } + int i = 0; + int source_startend[] = new int[sourceRange.size() * 2]; + + for (ContiguousI range : sourceRange) + { + source_startend[i++] = range.getBegin(); + source_startend[i++] = range.getEnd(); + } + Mapping mp = new Mapping( + new MapList(source_startend, new int[] + { seq.getStart(), seq.getEnd() }, 1, 1)); + dseq.transferAnnotation(sq, mp); + } + updateOurAnnots(newAnnots); } - if (alignViewport.isClosed()) - { - abortAndDestroy(); - return true; - } - return false; } protected void updateOurAnnots(List ourAnnot) @@ -753,15 +839,17 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker } } our.clear(); - // validate rows and update Alignmment state - for (AlignmentAnnotation an : ourAnnots) - { - alignViewport.getAlignment().validateAnnotation(an); - } - // TODO: may need a menu refresh after this - // af.setMenusForViewport(); - ap.adjustAnnotationHeight(); } + + // validate rows and update Alignmment state + for (AlignmentAnnotation an : ourAnnots) + { + alignViewport.getAlignment().validateAnnotation(an); + } + // TODO: may need a menu refresh after this + // af.setMenusForViewport(); + ap.adjustAnnotationHeight(); + } public SequenceAnnotationServiceI getService()