From 39c0b5e5d2ff4352d36bb6705121dc5ed14ba81b Mon Sep 17 00:00:00 2001 From: jprocter Date: Fri, 14 May 2010 17:08:57 +0000 Subject: [PATCH 1/1] First patch for * JAL-493 --- src/jalview/gui/AlignFrame.java | 17 +- src/jalview/ws/{WSThread.java => AWSThread.java} | 823 +++++++++------------- src/jalview/ws/AWsJob.java | 200 ++++++ src/jalview/ws/Discoverer.java | 4 +- src/jalview/ws/JPredClient.java | 2 +- src/jalview/ws/JPredThread.java | 15 +- src/jalview/ws/JWS1Thread.java | 47 ++ src/jalview/ws/JobStateSummary.java | 129 ++++ src/jalview/ws/MsaWSClient.java | 2 +- src/jalview/ws/MsaWSThread.java | 15 +- src/jalview/ws/SeqSearchWSClient.java | 2 +- src/jalview/ws/SeqSearchWSThread.java | 14 +- src/jalview/ws/WS1Client.java | 111 +++ src/jalview/ws/WSClient.java | 86 +-- src/jalview/ws/WSJob.java | 123 ++++ src/jalview/ws/jws2/AWS2Thread.java | 16 + src/jalview/ws/jws2/JWs2Job.java | 126 ++++ src/jalview/ws/jws2/Jws2Client.java | 54 ++ src/jalview/ws/jws2/Jws2Discoverer.java | 192 +++++ src/jalview/ws/jws2/MsaWSClient.java | 205 ++++++ src/jalview/ws/jws2/MsaWSThread.java | 765 ++++++++++++++++++++ 21 files changed, 2353 insertions(+), 595 deletions(-) rename src/jalview/ws/{WSThread.java => AWSThread.java} (52%) create mode 100644 src/jalview/ws/AWsJob.java create mode 100644 src/jalview/ws/JWS1Thread.java create mode 100644 src/jalview/ws/JobStateSummary.java create mode 100644 src/jalview/ws/WS1Client.java create mode 100644 src/jalview/ws/WSJob.java create mode 100644 src/jalview/ws/jws2/AWS2Thread.java create mode 100644 src/jalview/ws/jws2/JWs2Job.java create mode 100644 src/jalview/ws/jws2/Jws2Client.java create mode 100644 src/jalview/ws/jws2/Jws2Discoverer.java create mode 100644 src/jalview/ws/jws2/MsaWSClient.java create mode 100644 src/jalview/ws/jws2/MsaWSThread.java diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 2e6ea52..a6caa51 100755 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -36,6 +36,7 @@ import jalview.io.*; import jalview.jbgui.*; import jalview.schemes.*; import jalview.ws.*; +import jalview.ws.jws2.Jws2Discoverer; /** * DOCUMENT ME! @@ -3816,7 +3817,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws .get(i); - jalview.ws.WSClient impl = jalview.ws.Discoverer + jalview.ws.WSMenuEntryProviderI impl = jalview.ws.Discoverer .getServiceClient(sh); impl.attachWSMenuEntry(msawsmenu, this); @@ -3831,7 +3832,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr .get(i); - jalview.ws.WSClient impl = jalview.ws.Discoverer + jalview.ws.WSMenuEntryProviderI impl = jalview.ws.Discoverer .getServiceClient(sh); impl.attachWSMenuEntry(secstrmenu, this); } @@ -3845,12 +3846,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch .elementAt(i); - jalview.ws.WSClient impl = jalview.ws.Discoverer + jalview.ws.WSMenuEntryProviderI impl = jalview.ws.Discoverer .getServiceClient(sh); impl.attachWSMenuEntry(seqsrchmenu, this); } wsmenu.add(seqsrchmenu); } + // TODO: move into separate menu builder class. + { + Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer(); + if (jws2servs!=null && jws2servs.hasServices()) + { + JMenu jws2men = new JMenu("Jalview 2 Services"); + jws2servs.attachWSMenuEntry(jws2men, this); + wsmenu.add(jws2men); + } + } // finally, add the whole shebang onto the webservices menu resetWebServiceMenu(); for (int i = 0, j = wsmenu.size(); i < j; i++) diff --git a/src/jalview/ws/WSThread.java b/src/jalview/ws/AWSThread.java similarity index 52% rename from src/jalview/ws/WSThread.java rename to src/jalview/ws/AWSThread.java index 65c1c96..1de2c01 100644 --- a/src/jalview/ws/WSThread.java +++ b/src/jalview/ws/AWSThread.java @@ -1,479 +1,344 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5) - * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle - * - * 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 . - */ -package jalview.ws; - -import javax.swing.*; - -import jalview.bin.*; -import jalview.datamodel.*; -import jalview.gui.*; -import jalview.gui.FeatureRenderer.FeatureRendererSettings; - -public abstract class WSThread extends Thread -{ - /** - * Generic properties for Web Service Client threads. - */ - /** - * view that this job was associated with - */ - AlignmentI currentView = null; - - /** - * feature settings from view that job was associated with - */ - FeatureRendererSettings featureSettings = null; - - /** - * metadata about this web service - */ - WebserviceInfo wsInfo = null; - - /** - * original input data for this job - */ - AlignmentView input = null; - - /** - * dataset sequence relationships to be propagated onto new results - */ - AlignedCodonFrame[] codonframe = null; - - /** - * are there jobs still running in this thread. - */ - boolean jobComplete = false; - - abstract class WSJob - { - /** - * Generic properties for an individual job within a Web Service Client - * thread - */ - int jobnum = 0; // WebServiceInfo pane for this job - - String jobId; // ws job ticket - - /** - * has job been cancelled - */ - boolean cancelled = false; - - /** - * number of exceptions left before job dies - */ - int allowedServerExceptions = 3; - - /** - * has job been submitted - */ - boolean submitted = false; - - /** - * are all sub-jobs complete - */ - boolean subjobComplete = false; - - /** - * - * @return true if job has completed and valid results are available - */ - abstract boolean hasResults(); - - /** - * - * @return boolean true if job can be submitted. - */ - abstract boolean hasValidInput(); - - /** - * The last result object returned by the service. - */ - vamsas.objects.simple.Result result; - } - - class JobStateSummary - { - /** - * number of jobs running - */ - int running = 0; - - /** - * number of jobs queued - */ - int queuing = 0; - - /** - * number of jobs finished - */ - int finished = 0; - - /** - * number of jobs failed - */ - int error = 0; - - /** - * number of jobs stopped due to server error - */ - int serror = 0; - - /** - * number of jobs cancelled - */ - int cancelled = 0; - - /** - * number of jobs finished with results - */ - int results = 0; - - /** - * processes WSJob and updates job status counters and WebService status - * displays - * - * @param wsInfo - * @param OutputHeader - * @param j - */ - void updateJobPanelState(WebserviceInfo wsInfo, String OutputHeader, - WSJob j) - { - if (j.result != null) - { - String progheader = ""; - // Parse state of job[j] - if (j.result.isRunning()) - { - running++; - wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING); - } - else if (j.result.isQueued()) - { - queuing++; - wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING); - } - else if (j.result.isFinished()) - { - finished++; - j.subjobComplete = true; - if (j.hasResults()) - { - results++; - } - wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK); - } - else if (j.result.isFailed()) - { - progheader += "Job failed.\n"; - j.subjobComplete = true; - wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); - error++; - } - else if (j.result.isServerError()) - { - serror++; - j.subjobComplete = true; - wsInfo.setStatus(j.jobnum, - WebserviceInfo.STATE_STOPPED_SERVERERROR); - } - else if (j.result.isBroken() || j.result.isFailed()) - { - error++; - j.subjobComplete = true; - wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); - } - // and pass on any sub-job messages to the user - wsInfo.setProgressText(j.jobnum, OutputHeader); - wsInfo.appendProgressText(j.jobnum, progheader); - if (j.result.getStatus() != null) - { - wsInfo.appendProgressText(j.jobnum, j.result.getStatus()); - } - } - else - { - if (j.submitted && j.subjobComplete) - { - if (j.allowedServerExceptions == 0) - { - serror++; - } - else if (j.result == null) - { - error++; - } - } - } - } - } - - /** - * one or more jobs being managed by this thread. - */ - WSJob jobs[] = null; - - /** - * full name of service - */ - String WebServiceName = null; - - String OutputHeader; - - String WsUrl = null; - - /** - * query web service for status of job. on return, job.result must not be null - * - if it is then it will be assumed that the job status query timed out and - * a server exception will be logged. - * - * @param job - * @throws Exception - * will be logged as a server exception for this job - */ - abstract void pollJob(WSJob job) throws Exception; - - public void run() - { - JobStateSummary jstate = null; - if (jobs == null) - { - jobComplete = true; - } - while (!jobComplete) - { - jstate = new JobStateSummary(); - for (int j = 0; j < jobs.length; j++) - { - - if (!jobs[j].submitted && jobs[j].hasValidInput()) - { - StartJob(jobs[j]); - } - - if (jobs[j].submitted && !jobs[j].subjobComplete) - { - try - { - pollJob(jobs[j]); - if (jobs[j].result == null) - { - throw (new Exception( - "Timed out when communicating with server\nTry again later.\n")); - } - jalview.bin.Cache.log.debug("Job " + j + " Result state " - + jobs[j].result.getState() + "(ServerError=" - + jobs[j].result.isServerError() + ")"); - } catch (Exception ex) - { - // Deal with Transaction exceptions - wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName - + " Server exception!\n" + ex.getMessage()); - Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum - + ") Server exception: " + ex.getMessage()); - - if (jobs[j].allowedServerExceptions > 0) - { - jobs[j].allowedServerExceptions--; - Cache.log.debug("Sleeping after a server exception."); - try - { - Thread.sleep(5000); - } catch (InterruptedException ex1) - { - } - } - else - { - Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId); - jobs[j].subjobComplete = true; - wsInfo.setStatus(jobs[j].jobnum, - WebserviceInfo.STATE_STOPPED_SERVERERROR); - } - } catch (OutOfMemoryError er) - { - jobComplete = true; - jobs[j].subjobComplete = true; - jobs[j].result = null; // may contain out of date result object - wsInfo.setStatus(jobs[j].jobnum, - WebserviceInfo.STATE_STOPPED_ERROR); - Cache.log.error("Out of memory when retrieving Job " + j - + " id:" + WsUrl + "/" + jobs[j].jobId, er); - new jalview.gui.OOMWarning("retrieving result for " - + WebServiceName, er); - System.gc(); - } - } - jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]); - } - // Decide on overall state based on collected jobs[] states - if (jstate.running > 0) - { - wsInfo.setStatus(WebserviceInfo.STATE_RUNNING); - } - else if (jstate.queuing > 0) - { - wsInfo.setStatus(WebserviceInfo.STATE_QUEUING); - } - else - { - jobComplete = true; - if (jstate.finished > 0) - { - wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK); - } - else if (jstate.error > 0) - { - wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); - } - else if (jstate.serror > 0) - { - wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); - } - } - if (!jobComplete) - { - try - { - Thread.sleep(5000); - } catch (InterruptedException e) - { - Cache.log - .debug("Interrupted sleep waiting for next job poll.", e); - } - // System.out.println("I'm alive "+alTitle); - } - } - if (jobComplete && jobs != null) - { - parseResult(); // tidy up and make results available to user - } - else - { - Cache.log - .debug("WebServiceJob poll loop finished with no jobs created."); - wsInfo.setFinishedNoResults(); - } - } - - /** - * submit job to web service - * - * @param job - */ - abstract void StartJob(WSJob job); - - /** - * process the set of WSJob objects into a set of results, and tidy up. - */ - abstract void parseResult(); - - /** - * helper function to conserve dataset references to sequence objects returned - * from web services 1. Propagates AlCodonFrame data from - * codonframe to al - * - * @param al - */ - protected void propagateDatasetMappings(Alignment al) - { - if (codonframe != null) - { - SequenceI[] alignment = al.getSequencesArray(); - for (int sq = 0; sq < alignment.length; sq++) - { - for (int i = 0; i < codonframe.length; i++) - { - if (codonframe[i] != null - && codonframe[i].involvesSequence(alignment[sq])) - { - al.addCodonFrame(codonframe[i]); - codonframe[i] = null; - break; - } - } - } - } - } - - /** - * - * @param alignFrame - * reference for copying mappings across - * @param wsInfo - * gui attachment point - * @param input - * input data for the calculation - * @param webServiceName - * name of service - * @param wsUrl - * url of the service being invoked - */ - public WSThread(AlignFrame alignFrame, WebserviceInfo wsinfo, - AlignmentView input, String webServiceName, String wsUrl) - { - this(alignFrame, wsinfo, input, wsUrl); - WebServiceName = webServiceName; - } - - char defGapChar = '-'; - - /** - * - * @return gap character to use for any alignment generation - */ - public char getGapChar() - { - return defGapChar; - } - - /** - * - * @param alframe - * - reference for copying mappings and display styles across - * @param wsinfo2 - * - gui attachment point - * @param alview - * - input data for the calculation - * @param wsurl2 - * - url of the service being invoked - */ - public WSThread(AlignFrame alframe, WebserviceInfo wsinfo2, - AlignmentView alview, String wsurl2) - { - super(); - // this.alignFrame = alframe; - currentView = alframe.getCurrentView().getAlignment(); - featureSettings = alframe.getFeatureRenderer().getSettings(); - defGapChar = alframe.getViewport().getGapCharacter(); - this.wsInfo = wsinfo2; - this.input = alview; - WsUrl = wsurl2; - if (alframe != null) - { - AlignedCodonFrame[] cf = alframe.getViewport().getAlignment() - .getCodonFrames(); - if (cf != null) - { - codonframe = new AlignedCodonFrame[cf.length]; - System.arraycopy(cf, 0, codonframe, 0, cf.length); - } - } - } -} +package jalview.ws; + +import jalview.bin.Cache; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignFrame; +import jalview.gui.WebserviceInfo; +import jalview.gui.FeatureRenderer.FeatureRendererSettings; + +public abstract class AWSThread extends Thread +{ + + /** + * view that this job was associated with + */ + protected AlignmentI currentView = null; + /** + * feature settings from view that job was associated with + */ + protected FeatureRendererSettings featureSettings = null; + /** + * metadata about this web service + */ + protected WebserviceInfo wsInfo = null; + /** + * original input data for this job + */ + protected AlignmentView input = null; + /** + * dataset sequence relationships to be propagated onto new results + */ + protected AlignedCodonFrame[] codonframe = null; + /** + * are there jobs still running in this thread. + */ + protected boolean jobComplete = false; + /** + * one or more jobs being managed by this thread. + */ + protected AWsJob jobs[] = null; + /** + * full name of service + */ + protected String WebServiceName = null; + protected char defGapChar = '-'; + /** + * header prepended to all output from job + */ + protected String OutputHeader; + + /** + * only used when reporting a web service out of memory error - the job ID will be concatenated to the URL + */ + protected String WsUrl = null; + + /** + * generic web service job/subjob poll loop + */ + public void run() + { + JobStateSummary jstate = null; + if (jobs == null) + { + jobComplete = true; + } + while (!jobComplete) + { + jstate = new JobStateSummary(); + for (int j = 0; j < jobs.length; j++) + { + + if (!jobs[j].submitted && jobs[j].hasValidInput()) + { + StartJob(jobs[j]); + } + + if (jobs[j].submitted && !jobs[j].subjobComplete) + { + try + { + pollJob(jobs[j]); + if (!jobs[j].hasResponse()) + { + throw (new Exception( + "Timed out when communicating with server\nTry again later.\n")); + } + jalview.bin.Cache.log.debug("Job " + j + " Result state " + + jobs[j].getState() + "(ServerError=" + + jobs[j].isServerError() + ")"); + } catch (Exception ex) + { + if (Cache.log.isDebugEnabled()) + { + Cache.log.debug(ex); + } + // Deal with Transaction exceptions + wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName + + " Server exception!\n" + ex.getMessage()); + Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum + + ") Server exception: " + ex.getMessage()); + + if (jobs[j].allowedServerExceptions > 0) + { + jobs[j].allowedServerExceptions--; + Cache.log.debug("Sleeping after a server exception."); + try + { + Thread.sleep(5000); + } catch (InterruptedException ex1) + { + } + } + else + { + Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId); + jobs[j].subjobComplete = true; + wsInfo.setStatus(jobs[j].jobnum, + WebserviceInfo.STATE_STOPPED_SERVERERROR); + } + } catch (OutOfMemoryError er) + { + jobComplete = true; + jobs[j].subjobComplete = true; + jobs[j].clearResponse(); // may contain out of date result data + wsInfo.setStatus(jobs[j].jobnum, + WebserviceInfo.STATE_STOPPED_ERROR); + Cache.log.error("Out of memory when retrieving Job " + j + + " id:" + WsUrl + "/" + jobs[j].jobId, er); + new jalview.gui.OOMWarning("retrieving result for " + + WebServiceName, er); + System.gc(); + } + } + jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]); + } + // Decide on overall state based on collected jobs[] states + if (jstate.running > 0) + { + wsInfo.setStatus(WebserviceInfo.STATE_RUNNING); + } + else if (jstate.queuing > 0) + { + wsInfo.setStatus(WebserviceInfo.STATE_QUEUING); + } + else + { + jobComplete = true; + if (jstate.finished > 0) + { + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK); + } + else if (jstate.error > 0) + { + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); + } + else if (jstate.serror > 0) + { + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); + } + } + if (!jobComplete) + { + try + { + Thread.sleep(5000); + } catch (InterruptedException e) + { + Cache.log + .debug("Interrupted sleep waiting for next job poll.", e); + } + // System.out.println("I'm alive "+alTitle); + } + } + if (jobComplete && jobs != null) + { + parseResult(); // tidy up and make results available to user + } + else + { + Cache.log + .debug("WebServiceJob poll loop finished with no jobs created."); + wsInfo.setFinishedNoResults(); + } + } + + + public AWSThread() + { + super(); + } + + public AWSThread(Runnable target) + { + super(target); + } + + public AWSThread(String name) + { + super(name); + } + + public AWSThread(ThreadGroup group, Runnable target) + { + super(group, target); + } + + public AWSThread(ThreadGroup group, String name) + { + super(group, name); + } + + public AWSThread(Runnable target, String name) + { + super(target, name); + } + + public AWSThread(ThreadGroup group, Runnable target, String name) + { + super(group, target, name); + } + + /** + * query web service for status of job. on return, job.result must not be null + * - if it is then it will be assumed that the job status query timed out and + * a server exception will be logged. + * + * @param job + * @throws Exception + * will be logged as a server exception for this job + */ + public abstract void pollJob(AWsJob job) throws Exception; + + /** + * submit job to web service + * + * @param job + */ + public abstract void StartJob(AWsJob job); + + /** + * process the set of AWsJob objects into a set of results, and tidy up. + */ + public abstract void parseResult(); + + /** + * helper function to conserve dataset references to sequence objects returned + * from web services 1. Propagates AlCodonFrame data from + * codonframe to al + * TODO: refactor to datamodel + * @param al + */ + public void propagateDatasetMappings(Alignment al) + { + if (codonframe != null) + { + SequenceI[] alignment = al.getSequencesArray(); + for (int sq = 0; sq < alignment.length; sq++) + { + for (int i = 0; i < codonframe.length; i++) + { + if (codonframe[i] != null + && codonframe[i].involvesSequence(alignment[sq])) + { + al.addCodonFrame(codonframe[i]); + codonframe[i] = null; + break; + } + } + } + } + } + + public AWSThread(ThreadGroup group, Runnable target, String name, + long stackSize) + { + super(group, target, name, stackSize); + } + + /** + * + * @return gap character to use for any alignment generation + */ + public char getGapChar() + { + return defGapChar; + } + + /** + * + * @param alignFrame + * reference for copying mappings across + * @param wsInfo + * gui attachment point + * @param input + * input data for the calculation + * @param webServiceName + * name of service + * @param wsUrl + * url of the service being invoked + */ + public AWSThread(AlignFrame alignFrame, WebserviceInfo wsinfo, + AlignmentView input, String webServiceName, String wsUrl) + { + this(alignFrame, wsinfo, input, wsUrl); + WebServiceName = webServiceName; + } + + /** + * + * @param alframe + * - reference for copying mappings and display styles across + * @param wsinfo2 + * - gui attachment point + * @param alview + * - input data for the calculation + * @param wsurl2 + * - url of the service being invoked + */ + public AWSThread(AlignFrame alframe, WebserviceInfo wsinfo2, + AlignmentView alview, String wsurl2) + { + super(); + // this.alignFrame = alframe; + currentView = alframe.getCurrentView().getAlignment(); + featureSettings = alframe.getFeatureRenderer().getSettings(); + defGapChar = alframe.getViewport().getGapCharacter(); + this.wsInfo = wsinfo2; + this.input = alview; + WsUrl = wsurl2; + if (alframe != null) + { + AlignedCodonFrame[] cf = alframe.getViewport().getAlignment() + .getCodonFrames(); + if (cf != null) + { + codonframe = new AlignedCodonFrame[cf.length]; + System.arraycopy(cf, 0, codonframe, 0, cf.length); + } + } + } +} diff --git a/src/jalview/ws/AWsJob.java b/src/jalview/ws/AWsJob.java new file mode 100644 index 0000000..2f036bf --- /dev/null +++ b/src/jalview/ws/AWsJob.java @@ -0,0 +1,200 @@ +package jalview.ws; + +/** + * Generic properties for an individual job within a Web Service Client thread. + * Derived from jalview web services version 1 statuses, and revised for Jws2. + */ + +public abstract class AWsJob +{ + protected int jobnum = 0; + + protected String jobId; + + /** + * @param jobId the jobId to set + */ + public void setJobId(String jobId) + { + this.jobId = jobId; + } + + /** + * has job been cancelled + */ + protected boolean cancelled = false; + + /** + * number of exceptions left before job dies + */ + int allowedServerExceptions = 3; + + /** + * @param allowedServerExceptions the allowedServerExceptions to set + */ + public void setAllowedServerExceptions(int allowedServerExceptions) + { + this.allowedServerExceptions = allowedServerExceptions; + } + + /** + * has job been submitted to server ? if false, then no state info is + * available. + */ + protected boolean submitted = false; + + /** + * @param jobnum the jobnum to set + */ + public void setJobnum(int jobnum) + { + this.jobnum = jobnum; + } + + /** + * @param submitted the submitted to set + */ + public void setSubmitted(boolean submitted) + { + this.submitted = submitted; + } + + /** + * @param subjobComplete the subjobComplete to set + */ + public void setSubjobComplete(boolean subjobComplete) + { + this.subjobComplete = subjobComplete; + } + + /** + * @return the jobnum + */ + public int getJobnum() + { + return jobnum; + } + + /** + * @return the jobId + */ + public String getJobId() + { + return jobId; + } + + /** + * @return the cancelled + */ + public boolean isCancelled() + { + return cancelled; + } + + /** + * @return the allowedServerExceptions + */ + public int getAllowedServerExceptions() + { + return allowedServerExceptions; + } + + /** + * @return the submitted + */ + public boolean isSubmitted() + { + return submitted; + } + + /** + * @return the subjobComplete + */ + public boolean isSubjobComplete() + { + return subjobComplete; + } + + /** + * are all sub-jobs complete + */ + protected boolean subjobComplete = false; + + public AWsJob() + { + } + + /** + * + * @return true if job has completed and valid results are available + */ + abstract public boolean hasResults(); + + /** + * + * @return boolean true if job can be submitted. + */ + public abstract boolean hasValidInput(); + + /** + * + * @return true if job is running + */ + abstract public boolean isRunning(); + + /** + * + * @return true if job is queued + */ + abstract public boolean isQueued(); + + /** + * + * @return true if job has finished + */ + abstract public boolean isFinished(); + + /** + * + * @return true if the job failed due to some problem with the input data or + * parameters. + */ + abstract public boolean isFailed(); + + /** + * + * @return true if job failed due to an unhandled technical issue + */ + abstract public boolean isBroken(); + + /** + * + * @return true if there was a problem contacting the server. + */ + abstract public boolean isServerError(); + + /** + * + * @return true if the job has status text. + */ + abstract public boolean hasStatus(); + + /** + * + * @return status text for job to be displayed to user. + */ + abstract public String getStatus(); + + abstract public boolean hasResponse(); + abstract public void clearResponse(); + abstract public String getState(); + /** + * generates response using the abstract service flags. + * @return a standard state response + */ + protected String _defaultState() { + + String state = ""; + return state; + } +} \ No newline at end of file diff --git a/src/jalview/ws/Discoverer.java b/src/jalview/ws/Discoverer.java index f481aa1..5d20dd4 100755 --- a/src/jalview/ws/Discoverer.java +++ b/src/jalview/ws/Discoverer.java @@ -411,7 +411,7 @@ public class Discoverer extends Thread implements Runnable */ private static Hashtable serviceClientBindings; - public static WSClient getServiceClient(ServiceHandle sh) + public static WS1Client getServiceClient(ServiceHandle sh) { if (serviceClientBindings == null) { @@ -421,7 +421,7 @@ public class Discoverer extends Thread implements Runnable serviceClientBindings.put("SecStrPred", new JPredClient()); serviceClientBindings.put("SeqSearch", new SeqSearchWSClient()); } - WSClient instance = (WSClient) serviceClientBindings.get(sh + WS1Client instance = (WS1Client) serviceClientBindings.get(sh .getAbstractName()); if (instance == null) { diff --git a/src/jalview/ws/JPredClient.java b/src/jalview/ws/JPredClient.java index bb691dc..23f4de6 100755 --- a/src/jalview/ws/JPredClient.java +++ b/src/jalview/ws/JPredClient.java @@ -29,7 +29,7 @@ import jalview.bin.*; import jalview.datamodel.*; import jalview.gui.*; -public class JPredClient extends WSClient +public class JPredClient extends WS1Client { /** * crate a new GUI JPred Job diff --git a/src/jalview/ws/JPredThread.java b/src/jalview/ws/JPredThread.java index 98c9506..3589c16 100644 --- a/src/jalview/ws/JPredThread.java +++ b/src/jalview/ws/JPredThread.java @@ -22,17 +22,16 @@ import java.util.*; import jalview.analysis.*; import jalview.bin.*; import jalview.datamodel.*; -import jalview.datamodel.Alignment; import jalview.gui.*; import jalview.io.*; import jalview.util.*; import vamsas.objects.simple.JpredResult; -class JPredThread extends WSThread implements WSClientI +class JPredThread extends JWS1Thread implements WSClientI { // TODO: put mapping between JPredJob input and input data here - // JNetAnnotation adding is done after result parsing. - class JPredJob extends WSThread.WSJob + class JPredJob extends WSJob { // TODO: make JPredJob deal only with what was sent to and received from a // JNet service @@ -67,7 +66,7 @@ class JPredThread extends WSThread implements WSClientI return false; } - boolean hasValidInput() + public boolean hasValidInput() { if (sequence != null) { @@ -421,7 +420,7 @@ class JPredThread extends WSThread implements WSClientI } } - void StartJob(WSJob j) + public void StartJob(AWsJob j) { if (!(j instanceof JPredJob)) { @@ -499,7 +498,7 @@ class JPredThread extends WSThread implements WSClientI } } - void parseResult() + public void parseResult() { int results = 0; // number of result sets received JobStateSummary finalState = new JobStateSummary(); @@ -642,9 +641,9 @@ class JPredThread extends WSThread implements WSClientI } } - void pollJob(WSJob job) throws Exception + public void pollJob(AWsJob job) throws Exception { - job.result = server.getresult(job.jobId); + ((JPredJob)job).result = server.getresult(job.jobId); } public boolean isCancellable() diff --git a/src/jalview/ws/JWS1Thread.java b/src/jalview/ws/JWS1Thread.java new file mode 100644 index 0000000..c4d4c16 --- /dev/null +++ b/src/jalview/ws/JWS1Thread.java @@ -0,0 +1,47 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5) + * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle + * + * 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 . + */ +package jalview.ws; + +import javax.swing.*; + +import jalview.bin.*; +import jalview.datamodel.*; +import jalview.gui.*; + +/** + * specific methods for Jalview WS1 web service jobs + * (will probably disappear) + * @author JimP + * + */ +public abstract class JWS1Thread extends AWSThread +{ + + public JWS1Thread(AlignFrame alFrame, WebserviceInfo wsinfo, + AlignmentView alview, String wsname, String wsUrl) + { + super(alFrame, wsinfo, alview, wsname, wsUrl); + } + + public JWS1Thread(AlignFrame alframe, WebserviceInfo wsinfo, + AlignmentView alview, String wsurl) + { + super(alframe, wsinfo, alview, wsurl); + } + +} diff --git a/src/jalview/ws/JobStateSummary.java b/src/jalview/ws/JobStateSummary.java new file mode 100644 index 0000000..0983dc0 --- /dev/null +++ b/src/jalview/ws/JobStateSummary.java @@ -0,0 +1,129 @@ +package jalview.ws; + +import jalview.gui.WebserviceInfo; + +/** + * bookkeeper class for the WebServiceInfo GUI, maintaining records of web + * service jobs handled by the window and reflecting any status updates. + * + * @author JimP + * + */ +public class JobStateSummary +{ + /** + * number of jobs running + */ + int running = 0; + + /** + * number of jobs queued + */ + int queuing = 0; + + /** + * number of jobs finished + */ + int finished = 0; + + /** + * number of jobs failed + */ + int error = 0; + + /** + * number of jobs stopped due to server error + */ + int serror = 0; + + /** + * number of jobs cancelled + */ + int cancelled = 0; + + /** + * number of jobs finished with results + */ + int results = 0; + + /** + * processes an AWSJob's status and updates job status counters and WebService + * status displays + * + * @param wsInfo + * @param OutputHeader + * @param j + */ + public void updateJobPanelState(WebserviceInfo wsInfo, String OutputHeader, + AWsJob j) + { + if (j.submitted) + { + String progheader = ""; + // Parse state of job[j] + if (j.isRunning()) + { + running++; + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING); + } + else if (j.isQueued()) + { + queuing++; + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING); + } + else if (j.isFinished()) + { + finished++; + j.subjobComplete = true; + if (j.hasResults()) + { + results++; + } + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK); + } + else if (j.isFailed()) + { + progheader += "Job failed.\n"; + j.subjobComplete = true; + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); + error++; + } + else if (j.isServerError()) + { + serror++; + j.subjobComplete = true; + wsInfo + .setStatus(j.jobnum, + WebserviceInfo.STATE_STOPPED_SERVERERROR); + } + else if (j.isBroken()) + { + progheader += "Job was broken.\n"; + error++; + j.subjobComplete = true; + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); + } + // and pass on any sub-job messages to the user + wsInfo.setProgressText(j.jobnum, OutputHeader); + wsInfo.appendProgressText(j.jobnum, progheader); + if (j.hasStatus()) + { + wsInfo.appendProgressText(j.jobnum, j.getStatus()); + } + } + else + { + if (j.submitted && j.subjobComplete) + { + if (j.allowedServerExceptions == 0) + { + serror++; + } + else if (!j.hasResults()) + { + error++; + } + } + } + } +} diff --git a/src/jalview/ws/MsaWSClient.java b/src/jalview/ws/MsaWSClient.java index 19fa0d3..d58e2dc 100755 --- a/src/jalview/ws/MsaWSClient.java +++ b/src/jalview/ws/MsaWSClient.java @@ -32,7 +32,7 @@ import jalview.gui.*; * @author $author$ * @version $Revision$ */ -public class MsaWSClient extends WSClient +public class MsaWSClient extends WS1Client { /** * server is a WSDL2Java generated stub for an archetypal MsaWSI service. diff --git a/src/jalview/ws/MsaWSThread.java b/src/jalview/ws/MsaWSThread.java index 7142e5c..ab3dfbe 100644 --- a/src/jalview/ws/MsaWSThread.java +++ b/src/jalview/ws/MsaWSThread.java @@ -22,7 +22,6 @@ import java.util.*; import jalview.analysis.*; import jalview.bin.*; import jalview.datamodel.*; -import jalview.datamodel.Alignment; import jalview.gui.*; import vamsas.objects.simple.MsaResult; @@ -46,7 +45,7 @@ import vamsas.objects.simple.MsaResult; * @author not attributable * @version 1.0 */ -class MsaWSThread extends WSThread implements WSClientI +class MsaWSThread extends JWS1Thread implements WSClientI { boolean submitGaps = false; // pass sequences including gaps to alignment @@ -56,7 +55,7 @@ class MsaWSThread extends WSThread implements WSClientI // order - class MsaWSJob extends WSThread.WSJob + class MsaWSJob extends WSJob { // hold special input for this vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet(); @@ -280,7 +279,7 @@ class MsaWSThread extends WSThread implements WSClientI * * @return boolean true if job can be submitted. */ - boolean hasValidInput() + public boolean hasValidInput() { if (seqs.getSeqs() != null) { @@ -438,12 +437,12 @@ class MsaWSThread extends WSThread implements WSClientI } } - void pollJob(WSJob job) throws Exception + public void pollJob(AWsJob job) throws Exception { ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId); } - void StartJob(WSJob job) + public void StartJob(AWsJob job) { if (!(job instanceof MsaWSJob)) { @@ -531,7 +530,7 @@ class MsaWSThread extends WSThread implements WSClientI return msa; } - void parseResult() + public void parseResult() { int results = 0; // number of result sets received JobStateSummary finalState = new JobStateSummary(); @@ -544,7 +543,7 @@ class MsaWSThread extends WSThread implements WSClientI && jobs[j].hasResults()) { results++; - vamsas.objects.simple.Alignment valign = ((MsaResult) jobs[j].result) + vamsas.objects.simple.Alignment valign = ((MsaResult)((MsaWSJob) jobs[j]).result) .getMsa(); if (valign != null) { diff --git a/src/jalview/ws/SeqSearchWSClient.java b/src/jalview/ws/SeqSearchWSClient.java index 2e246ab..fd45209 100644 --- a/src/jalview/ws/SeqSearchWSClient.java +++ b/src/jalview/ws/SeqSearchWSClient.java @@ -37,7 +37,7 @@ import jalview.gui.*; * @author $author$ * @version $Revision$ */ -public class SeqSearchWSClient extends WSClient +public class SeqSearchWSClient extends WS1Client { /** * server is a WSDL2Java generated stub for an archetypal MsaWSI service. diff --git a/src/jalview/ws/SeqSearchWSThread.java b/src/jalview/ws/SeqSearchWSThread.java index 2f28d64..550ae43 100644 --- a/src/jalview/ws/SeqSearchWSThread.java +++ b/src/jalview/ws/SeqSearchWSThread.java @@ -47,13 +47,13 @@ import vamsas.objects.simple.SeqSearchResult; * @author not attributable * @version 1.0 */ -class SeqSearchWSThread extends WSThread implements WSClientI +class SeqSearchWSThread extends JWS1Thread implements WSClientI { String dbs = null; boolean profile = false; - class SeqSearchWSJob extends WSThread.WSJob + class SeqSearchWSJob extends WSJob { // hold special input for this vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet(); @@ -293,7 +293,7 @@ class SeqSearchWSThread extends WSThread implements WSClientI * * @return boolean true if job can be submitted. */ - boolean hasValidInput() + public boolean hasValidInput() { if (seqs.getSeqs() != null) { @@ -451,13 +451,13 @@ class SeqSearchWSThread extends WSThread implements WSClientI } } - void pollJob(WSJob job) throws Exception + public void pollJob(AWsJob job) throws Exception { ((SeqSearchWSJob) job).result = server .getResult(((SeqSearchWSJob) job).jobId); } - void StartJob(WSJob job) + public void StartJob(AWsJob job) { if (!(job instanceof SeqSearchWSJob)) { @@ -545,7 +545,7 @@ class SeqSearchWSThread extends WSThread implements WSClientI return msa; } - void parseResult() + public void parseResult() { int results = 0; // number of result sets received JobStateSummary finalState = new JobStateSummary(); @@ -558,7 +558,7 @@ class SeqSearchWSThread extends WSThread implements WSClientI && jobs[j].hasResults()) { results++; - vamsas.objects.simple.Alignment valign = ((SeqSearchResult) jobs[j].result) + vamsas.objects.simple.Alignment valign = ((SeqSearchResult) ((SeqSearchWSJob)jobs[j]).result) .getAlignment(); if (valign != null) { diff --git a/src/jalview/ws/WS1Client.java b/src/jalview/ws/WS1Client.java new file mode 100644 index 0000000..a05bf7f --- /dev/null +++ b/src/jalview/ws/WS1Client.java @@ -0,0 +1,111 @@ +/** + * + */ +package jalview.ws; + +import jalview.gui.AlignFrame; +import jalview.gui.WebserviceInfo; + +import javax.swing.JMenu; + +import ext.vamsas.ServiceHandle; + +/** + * JWS1 Specific UI attributes and methods + * @author JimP + * + */ +public abstract class WS1Client extends WSClient implements WSMenuEntryProviderI +{ + + /** + * original service handle that this client was derived from + */ + ServiceHandle serviceHandle = null; + + /** + * default constructor + */ + public WS1Client() + { + super(); + } + + /** + * initialise WSClient service information attributes from the service handle + * + * @param sh + * @return the service instance information GUI for this client and job. + */ + protected WebserviceInfo setWebService(ServiceHandle sh) + { + return setWebService(sh, false); + } + + /** + * initialise WSClient service information attributes from the service handle + * + * @param sh + * @param headless + * true implies no GUI objects will be created. + * @return the service instance information GUI for this client and job. + */ + protected WebserviceInfo setWebService(ServiceHandle sh, boolean headless) + { + WebServiceName = sh.getName(); + if (ServiceActions.containsKey(sh.getAbstractName())) + { + WebServiceJobTitle = sh.getName(); // TODO: control sh.Name specification + // properly + // add this for short names. +(String) + // ServiceActions.get(sh.getAbstractName()); + } + else + { + WebServiceJobTitle = sh.getAbstractName() + " using " + sh.getName(); + + } + WebServiceReference = sh.getDescription(); + WsURL = sh.getEndpointURL(); + WebserviceInfo wsInfo = null; + if (!headless) + { + wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceReference); + } + return wsInfo; + } + + /** + * convenience method to pass the serviceHandle reference that instantiated + * this service on to the menu entry constructor + * + * @param wsmenu + * the menu to which any menu entries/sub menus are to be attached + * @param alignFrame + * the alignFrame instance that provides input data for the service + */ + public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame) + { + if (serviceHandle == null) + { + throw new Error( + "IMPLEMENTATION ERROR: cannot attach WS Menu Entry without service handle reference!"); + } + attachWSMenuEntry(wsmenu, serviceHandle, alignFrame); + } + + /** + * method implemented by each concrete WS1Client implementation that creates menu + * entries that enact their service using data from alignFrame. + * + * @param wsmenu + * where new menu entries (and submenus) are to be attached + * @param serviceHandle + * the serviceHandle document for the service that entries are + * created for + * @param alignFrame + */ + public abstract void attachWSMenuEntry(JMenu wsmenu, final ServiceHandle serviceHandle, + final AlignFrame alignFrame); + +} diff --git a/src/jalview/ws/WSClient.java b/src/jalview/ws/WSClient.java index 1490da1..4a5c7dd 100755 --- a/src/jalview/ws/WSClient.java +++ b/src/jalview/ws/WSClient.java @@ -17,12 +17,10 @@ */ package jalview.ws; -import javax.swing.JMenu; -import ext.vamsas.*; import jalview.gui.*; -public abstract class WSClient implements WSMenuEntryProviderI +public abstract class WSClient // implements WSMenuEntryProviderI { /** * WSClient holds the basic attributes that are displayed to the user for all @@ -74,86 +72,4 @@ public abstract class WSClient implements WSMenuEntryProviderI public WSClient() { } - - /** - * initialise WSClient service information attributes from the service handle - * - * @param sh - * @return the service instance information GUI for this client and job. - */ - protected WebserviceInfo setWebService(ServiceHandle sh) - { - return setWebService(sh, false); - } - - /** - * original service handle that this client was derived from - */ - ServiceHandle serviceHandle = null; - - /** - * initialise WSClient service information attributes from the service handle - * - * @param sh - * @param headless - * true implies no GUI objects will be created. - * @return the service instance information GUI for this client and job. - */ - protected WebserviceInfo setWebService(ServiceHandle sh, boolean headless) - { - WebServiceName = sh.getName(); - if (ServiceActions.containsKey(sh.getAbstractName())) - { - WebServiceJobTitle = sh.getName(); // TODO: control sh.Name specification - // properly - // add this for short names. +(String) - // ServiceActions.get(sh.getAbstractName()); - } - else - { - WebServiceJobTitle = sh.getAbstractName() + " using " + sh.getName(); - - } - WebServiceReference = sh.getDescription(); - WsURL = sh.getEndpointURL(); - WebserviceInfo wsInfo = null; - if (!headless) - { - wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceReference); - } - return wsInfo; - } - - /** - * convenience method to pass the serviceHandle reference that instantiated - * this service on to the menu entry constructor - * - * @param wsmenu - * the menu to which any menu entries/sub menus are to be attached - * @param alignFrame - * the alignFrame instance that provides input data for the service - */ - public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame) - { - if (serviceHandle == null) - { - throw new Error( - "IMPLEMENTATION ERROR: cannot attach WS Menu Entry without service handle reference!"); - } - attachWSMenuEntry(wsmenu, serviceHandle, alignFrame); - } - - /** - * method implemented by each WSClient implementation that creates menu - * entries that enact their service using data from alignFrame. - * - * @param wsmenu - * where new menu entries (and submenus) are to be attached - * @param serviceHandle - * the serviceHandle document for the service that entries are - * created for - * @param alignFrame - */ - public abstract void attachWSMenuEntry(JMenu wsmenu, - final ServiceHandle serviceHandle, final AlignFrame alignFrame); } diff --git a/src/jalview/ws/WSJob.java b/src/jalview/ws/WSJob.java new file mode 100644 index 0000000..2a2e315 --- /dev/null +++ b/src/jalview/ws/WSJob.java @@ -0,0 +1,123 @@ +/** + * + */ +package jalview.ws; + +abstract class WSJob extends AWsJob +{ + /* (non-Javadoc) + * @see jalview.ws.AWsJob#clearResponse() + */ + @Override + public void clearResponse() + { + result = null; + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#hasResponse() + */ + @Override + public boolean hasResponse() + { + return result!=null; + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#hasStatus() + */ + @Override + public boolean hasStatus() + { + return result!=null && result.getStatus()!=null; + } + + /** + * The last result object returned by the service. + */ + vamsas.objects.simple.Result result; + + /** + * @return + * @see vamsas.objects.simple.Result#getStatus() + */ + public String getStatus() + { + return result==null ? null : result.getStatus(); + } + + public String getState() { + return result==null ? "NULL result" : ""+result.getState(); + } + /** + * @return + * @see vamsas.objects.simple.Result#isBroken() + */ + public boolean isBroken() + { + return result!=null && result.isBroken(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isFailed() + */ + public boolean isFailed() + { + return result!=null && result.isFailed(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isFinished() + */ + public boolean isFinished() + { + return result!=null && result.isFinished(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isInvalid() + */ + public boolean isInvalid() + { + return result!=null && result.isInvalid(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isJobFailed() + */ + public boolean isJobFailed() + { + return result!=null && result.isJobFailed(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isQueued() + */ + public boolean isQueued() + { + return result!=null && result.isQueued(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isRunning() + */ + public boolean isRunning() + { + return result!=null && result.isRunning(); + } + + /** + * @return + * @see vamsas.objects.simple.Result#isServerError() + */ + public boolean isServerError() + { + return result!=null && result.isServerError(); + } +} \ No newline at end of file diff --git a/src/jalview/ws/jws2/AWS2Thread.java b/src/jalview/ws/jws2/AWS2Thread.java new file mode 100644 index 0000000..a706f43 --- /dev/null +++ b/src/jalview/ws/jws2/AWS2Thread.java @@ -0,0 +1,16 @@ +package jalview.ws.jws2; + +import jalview.datamodel.AlignmentView; +import jalview.gui.AlignFrame; +import jalview.gui.WebserviceInfo; + +abstract public class AWS2Thread extends jalview.ws.AWSThread +{ + + public AWS2Thread(AlignFrame alFrame, WebserviceInfo wsinfo, + AlignmentView alview, String wsname, String wsUrl) + { + super(alFrame, wsinfo, alview, wsname, wsUrl); + } + +} diff --git a/src/jalview/ws/jws2/JWs2Job.java b/src/jalview/ws/jws2/JWs2Job.java new file mode 100644 index 0000000..4bf97a3 --- /dev/null +++ b/src/jalview/ws/jws2/JWs2Job.java @@ -0,0 +1,126 @@ +/** + * + */ +package jalview.ws.jws2; + +import compbio.metadata.JobStatus; + +import jalview.ws.AWsJob; + +/** + * job status processing for JWS2 jobs. + * @author JimP + * + */ +public abstract class JWs2Job extends AWsJob +{ + JobStatus status=null; + public void setjobStatus(JobStatus jobStatus) + { + status = jobStatus; + // update flags + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#clearResponse() + */ + @Override + public void clearResponse() + { + status = null; + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#getState() + */ + @Override + public String getState() + { + return status==null ? ("Unknown") : status.toString(); + } + /* (non-Javadoc) + * @see jalview.ws.AWsJob#hasResponse() + */ + @Override + public boolean hasResponse() + { + // TODO Auto-generated method stub + return status!=null; + } + /* + StringBuffer statusBuffer = null; + * (non-Javadoc) + * @see jalview.ws.AWsJob#getStatus() + * + @Override + public String getStatus() + { + return statusBuffer.toString(); + } + * (non-Javadoc) + * @see jalview.ws.AWsJob#hasStatus() + * + @Override + public boolean hasStatus() + { + return statusBuffer!=null; + } +*/ + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#isBroken() + */ + @Override + public boolean isBroken() + { + return status.equals(status.UNDEFINED); + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#isFailed() + */ + @Override + public boolean isFailed() + { + return status.equals(status.FAILED); + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#isFinished() + */ + @Override + public boolean isFinished() + { + return status.equals(status.FINISHED); + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#isQueued() + */ + @Override + public boolean isQueued() + { + return status.equals(status.SUBMITTED) || status.equals(status.PENDING); + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#isRunning() + */ + @Override + public boolean isRunning() + { + // TODO Auto-generated method stub + return status.equals(status.RUNNING) || status.equals(status.STARTED); + } + + /* (non-Javadoc) + * @see jalview.ws.AWsJob#isServerError() + */ + @Override + public boolean isServerError() + { + // server errors are raised as exceptions on the service method calls. + return false; // status.equals(status.FAILED); + } + +} diff --git a/src/jalview/ws/jws2/Jws2Client.java b/src/jalview/ws/jws2/Jws2Client.java new file mode 100644 index 0000000..d44be01 --- /dev/null +++ b/src/jalview/ws/jws2/Jws2Client.java @@ -0,0 +1,54 @@ +package jalview.ws.jws2; + +import javax.swing.JMenu; + +import jalview.gui.AlignFrame; +import jalview.gui.WebserviceInfo; +import jalview.ws.jws2.Jws2Discoverer.Jws2Instance; + +/** + * provides metadata for a jws2 service instance - resolves names, etc. + * + * @author JimP + * + */ +public abstract class Jws2Client extends jalview.ws.WSClient +{ + protected WebserviceInfo setWebService(Jws2Instance serv, boolean b) + { + // serviceHandle = serv; + String serviceInstance = serv.service.getClass().getName(); + WebServiceName = serv.serviceType; + WebServiceJobTitle = serv.serviceType; + WsURL = serv.hosturl; + if (!b) + { + return new WebserviceInfo(WebServiceJobTitle, WebServiceJobTitle + + " using service hosted at " + serv.hosturl); + } + return null; + } + /* + Jws2Instance serviceHandle; + * (non-Javadoc) + * @see jalview.ws.WSMenuEntryProviderI#attachWSMenuEntry(javax.swing.JMenu, jalview.gui.AlignFrame) + * + @Override + public void attachWSMenuEntry(JMenu wsmenu, AlignFrame alignFrame) + { + if (serviceHandle==null) + { + throw new Error("Implementation error: No service handle for this Jws2 service."); + } + attachWSMenuEntry(wsmenu, serviceHandle, alignFrame); + }*/ + /** + * add the menu item for a particular jws2 service instance + * @param wsmenu + * @param service + * @param alignFrame + */ + abstract void attachWSMenuEntry(JMenu wsmenu, + final Jws2Instance service, final AlignFrame alignFrame); + +} diff --git a/src/jalview/ws/jws2/Jws2Discoverer.java b/src/jalview/ws/jws2/Jws2Discoverer.java new file mode 100644 index 0000000..da3eecb --- /dev/null +++ b/src/jalview/ws/jws2/Jws2Discoverer.java @@ -0,0 +1,192 @@ +package jalview.ws.jws2; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashSet; +import java.util.Vector; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import org.apache.log4j.Level; + +import jalview.bin.Cache; +import jalview.datamodel.AlignmentView; +import jalview.gui.AlignFrame; +import jalview.ws.WSMenuEntryProviderI; +import compbio.data.msa.MsaWS; +import compbio.ws.client.Jws2Base; +import compbio.ws.client.Jws2Base.Services; + +/** + * discoverer for jws2 services. Follows the lightweight service discoverer + * pattern (archetyped by EnfinEnvision2OneWay) + * + * @author JimP + * + */ +public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI +{ + compbio.data.msa.MsaWS service; + boolean running=false; + @Override + public void run() + { + if (running) + { + return; + } + running=true; +// Cache.initLogger(); +// Cache.log.setLevel(Level.DEBUG); + // TODO: Document and PACK JWS2 + String jwsservers = Cache.getDefault("JWS2HOSTURLS", + "http://webservices.compbio.dundee.ac.uk:8084/jws2"); + try + { + if (Jws2Base.validURL(jwsservers)) + { + // look for services + for (Services srv : Jws2Base.Services.values()) + { + MsaWS service = null; + try + { + service = Jws2Base.connect(jwsservers, srv); + } catch (Exception e) + { + } + ; + if (service != null) + { + addService(jwsservers, srv, service); + } + } + + } + else + { + Cache.log.info("Ignoring invalid Jws2 service url " + jwsservers); + } + } catch (Exception e) + { + Cache.log.warn("Exception when discovering Jws2 services.", e); + } catch (Error e) + { + Cache.log.error("Exception when discovering Jws2 services.", e); + } + running=false; + + } + + /** + * record this service endpoint so we can use it + * + * @param jwsservers + * @param srv + * @param service2 + */ + private void addService(String jwsservers, Services srv, MsaWS service2) + { + Cache.log.debug("Discovered service: " + jwsservers + " " + + srv.toString()); + if (services==null) { + services = new Vector(); + } + services.add(new Jws2Instance(jwsservers, "Align with " + + srv.toString(), service2)); + } + + public class Jws2Instance + { + String hosturl; + + String serviceType; + + MsaWS service; + + public Jws2Instance(String hosturl, String serviceType, MsaWS service) + { + super(); + this.hosturl = hosturl; + this.serviceType = serviceType; + this.service = service; + } + + }; + + /** + * holds list of services. + */ + Vector services; + + @Override + public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame) + { + if (running || services==null || services.size() == 0) + { + return; + } + /** + * eventually, JWS2 services will appear under the same align/etc submenus. + * for moment we keep them separate. + */ + JMenu jws2 = new JMenu("JWS2 Alignment"); + MsaWSClient msacl = new MsaWSClient(); + for (final Jws2Instance service : services) + { + msacl.attachWSMenuEntry(jws2, service, alignFrame); + /*JMenuItem sitem = new JMenuItem(service.serviceType); + sitem.setToolTipText("Hosted at " + service.hosturl); + sitem.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + AlignmentView msa = alignFrame.gatherSequencesForAlignment(); + MsaWSClient client = new MsaWSClient(service, + "JWS2 Alignment of " + alignFrame.getTitle(), msa, false, + true, alignFrame.getViewport().getAlignment().getDataset(), + alignFrame); + } + });*/ + } + if (services.size()>0) + { + wsmenu.add(jws2); + } + + } + public static void main(String[] args) + { + Thread runner = new Thread(getDiscoverer()); + runner.start(); + while (runner.isAlive()) + { + try + { + Thread.sleep(50); + } catch (InterruptedException e) + { + } + ; + } + } + private static Jws2Discoverer discoverer; + public static Jws2Discoverer getDiscoverer() + { + if (discoverer==null) + { + discoverer = new Jws2Discoverer(); + } + return discoverer; + } + + public boolean hasServices() + { + // TODO Auto-generated method stub + return services!=null && services.size()>0; + } + +} diff --git a/src/jalview/ws/jws2/MsaWSClient.java b/src/jalview/ws/jws2/MsaWSClient.java new file mode 100644 index 0000000..a557cf7 --- /dev/null +++ b/src/jalview/ws/jws2/MsaWSClient.java @@ -0,0 +1,205 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5) + * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle + * + * 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 . + */ +package jalview.ws.jws2; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + +import jalview.datamodel.*; +import jalview.gui.*; +import compbio.data.msa.MsaWS; +import jalview.ws.jws2.Jws2Discoverer.Jws2Instance; + +/** + * DOCUMENT ME! + * + * @author $author$ + * @version $Revision$ + */ +public class MsaWSClient extends Jws2Client +{ + /** + * server is a WSDL2Java generated stub for an archetypal MsaWSI service. + */ + MsaWS server; + AlignFrame alignFrame; + + /** + * Creates a new MsaWSClient object that uses a service given by an externally + * retrieved ServiceHandle + * + * @param sh + * service handle of type AbstractName(MsaWS) + * @param altitle + * DOCUMENT ME! + * @param msa + * DOCUMENT ME! + * @param submitGaps + * DOCUMENT ME! + * @param preserveOrder + * DOCUMENT ME! + */ + + public MsaWSClient(Jws2Discoverer.Jws2Instance sh, String altitle, + jalview.datamodel.AlignmentView msa, boolean submitGaps, + boolean preserveOrder, Alignment seqdataset, + AlignFrame _alignFrame) + { + super(); + alignFrame = _alignFrame; + if (!(sh.service instanceof MsaWS)) + { + // redundant at mo - but may change + JOptionPane + .showMessageDialog( + Desktop.desktop, + "The Service called \n" + + sh.serviceType + + "\nis not a \nMultiple Sequence Alignment Service !", + "Internal Jalview Error", JOptionPane.WARNING_MESSAGE); + + return; + } + server = sh.service; + if ((wsInfo = setWebService(sh, false)) == null) + { + JOptionPane.showMessageDialog(Desktop.desktop, + "The Multiple Sequence Alignment Service named " + + sh.serviceType + " is unknown", + "Internal Jalview Error", JOptionPane.WARNING_MESSAGE); + + return; + } + startMsaWSClient(altitle, msa, submitGaps, preserveOrder, seqdataset); + + } + + public MsaWSClient() + { + super(); + // add a class reference to the list + } + + private void startMsaWSClient(String altitle, AlignmentView msa, + boolean submitGaps, boolean preserveOrder, Alignment seqdataset) + { + //if (!locateWebService()) + // { + // return; + // } + + wsInfo.setProgressText(((submitGaps) ? "Re-alignment" : "Alignment") + + " of " + altitle + "\nJob details\n"); + String jobtitle = WebServiceName.toLowerCase(); + if (jobtitle.endsWith("alignment")) + { + if (submitGaps + && (!jobtitle.endsWith("realignment") || jobtitle + .indexOf("profile") == -1)) + { + int pos = jobtitle.indexOf("alignment"); + jobtitle = WebServiceName.substring(0, pos) + "re-alignment of " + + altitle; + } + else + { + jobtitle = WebServiceName + " of " + altitle; + } + } + else + { + jobtitle = WebServiceName + (submitGaps ? " re" : " ") + + "alignment of " + altitle; + } + + MsaWSThread msathread = new MsaWSThread(server, WsURL, wsInfo, + alignFrame, WebServiceName, jobtitle, msa, submitGaps, + preserveOrder, seqdataset); + wsInfo.setthisService(msathread); + msathread.start(); + } + + protected String getServiceActionKey() + { + return "MsaWS"; + } + + protected String getServiceActionDescription() + { + return "Multiple Sequence Alignment"; + } + + /** + * look at ourselves and work out if we are a service that can take a profile + * and align to it + * + * @return true if we can send gapped sequences to the alignment service + */ + private boolean canSubmitGaps() + { + // TODO: query service or extract service handle props to check if we can + // realign + return (WebServiceName.indexOf("lustal") > -1); // cheat! + } + + public void attachWSMenuEntry(JMenu msawsmenu, + final Jws2Instance service, final AlignFrame alignFrame) + { + setWebService(service, true); // headless + JMenuItem method = new JMenuItem(WebServiceName); + method.setToolTipText(WsURL); + method.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + AlignmentView msa = alignFrame.gatherSequencesForAlignment(); + new MsaWSClient(service, alignFrame.getTitle(), + msa, false, true, alignFrame.getViewport().getAlignment() + .getDataset(), alignFrame); + + } + + }); + msawsmenu.add(method); + if (canSubmitGaps()) + { + // We know that ClustalWS can accept partial alignments for refinement. + final JMenuItem methodR = new JMenuItem(WebServiceName + + " (With Gaps)"); + methodR.setToolTipText(WsURL); + methodR.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + AlignmentView msa = alignFrame.gatherSequencesForAlignment(); + new MsaWSClient(service, alignFrame.getTitle(), + msa, true, true, alignFrame.getViewport().getAlignment() + .getDataset(), alignFrame); + + } + + }); + msawsmenu.add(methodR); + + } + + } + +} diff --git a/src/jalview/ws/jws2/MsaWSThread.java b/src/jalview/ws/jws2/MsaWSThread.java new file mode 100644 index 0000000..c8c49c5 --- /dev/null +++ b/src/jalview/ws/jws2/MsaWSThread.java @@ -0,0 +1,765 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5) + * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle + * + * 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 . + */ +package jalview.ws.jws2; + +import java.util.*; + +import compbio.data.msa.MsaWS; +import compbio.data.sequence.AlignmentMetadata; +import compbio.data.sequence.Program; +import compbio.metadata.ChunkHolder; +import compbio.metadata.JobStatus; + +import jalview.analysis.*; +import jalview.bin.*; +import jalview.datamodel.*; +import jalview.gui.*; +import jalview.ws.AWsJob; +import jalview.ws.WSClientI; +import jalview.ws.JobStateSummary; + +/** + *

+ * Title: + *

+ * + *

+ * Description: + *

+ * + *

+ * Copyright: Copyright (c) 2004 + *

+ * + *

+ * Company: Dundee University + *

+ * + * @author not attributable + * @version 1.0 + */ +class MsaWSThread extends AWS2Thread implements WSClientI +{ + boolean submitGaps = false; // pass sequences including gaps to alignment + + // service + + boolean preserveOrder = true; // and always store and recover sequence + + // order + + class MsaWSJob extends JWs2Job + { + long lastChunk=0; + + /** + * input + */ + ArrayList seqs = new ArrayList(); + /** + * output + */ + compbio.data.sequence.Alignment alignment; + // set if the job didn't get run - then the input is simply returned to the user + private boolean returnInput=false; + + /** + * MsaWSJob + * + * @param jobNum + * int + * @param jobId + * String + */ + public MsaWSJob(int jobNum, SequenceI[] inSeqs) + { + this.jobnum = jobNum; + if (!prepareInput(inSeqs, 2)) + { + submitted = true; + subjobComplete = true; + returnInput = true; + } + + } + + Hashtable SeqNames = new Hashtable(); + + Vector emptySeqs = new Vector(); + + /** + * prepare input sequences for MsaWS service + * + * @param seqs + * jalview sequences to be prepared + * @param minlen + * minimum number of residues required for this MsaWS service + * @return true if seqs contains sequences to be submitted to service. + */ + // TODO: return compbio.seqs list or nothing to indicate validity. + private boolean prepareInput(SequenceI[] seqs, int minlen) + { + int nseqs = 0; + if (minlen < 0) + { + throw new Error( + "Implementation error: minlen must be zero or more."); + } + for (int i = 0; i < seqs.length; i++) + { + if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1) + { + nseqs++; + } + } + boolean valid = nseqs > 1; // need at least two seqs + compbio.data.sequence.FastaSequence seq; + for (int i = 0, n = 0; i < seqs.length; i++) + { + + String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same + // for + // any + // subjob + SeqNames.put(newname, jalview.analysis.SeqsetUtils + .SeqCharacterHash(seqs[i])); + if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1) + { + // make new input sequence with or without gaps + seq = new compbio.data.sequence.FastaSequence(newname, + (submitGaps) ? seqs[i].getSequenceAsString() + : AlignSeq.extractGaps(jalview.util.Comparison.GapChars, + seqs[i].getSequenceAsString())); + this.seqs.add(seq); + } + else + { + String empty = null; + if (seqs[i].getEnd() >= seqs[i].getStart()) + { + empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq + .extractGaps(jalview.util.Comparison.GapChars, seqs[i] + .getSequenceAsString()); + } + emptySeqs.add(new String[] + { newname, empty }); + } + } + return valid; + } + + /** + * + * @return true if getAlignment will return a valid alignment result. + */ + public boolean hasResults() + { + if (subjobComplete && isFinished() && (alignment!=null || (emptySeqs!=null && emptySeqs.size()>0))) + { + return true; + } + return false; + } + + /** + * + * get the alignment including any empty sequences in the original order with original ids. Caller must access the alignment.getMetadata() object to annotate the final result passsed to the user. + * @return { SequenceI[], AlignmentOrder } + */ + public Object[] getAlignment() + { + // is this a generic subjob or a Jws2 specific Object[] return signature + if (hasResults()) + { + SequenceI[] alseqs = null; + char alseq_gapchar = '-'; + int alseq_l = 0; + if (alignment.getSequences().size()>0) + { + alseqs = new SequenceI[alignment.getSequences().size()]; + for (compbio.data.sequence.FastaSequence seq: alignment.getSequences()) + { + alseqs[alseq_l++] = new Sequence(seq.getId(),seq.getSequence()); + } + alseq_gapchar = alignment.getMetadata().getGapchar(); + + } + // add in the empty seqs. + if (emptySeqs.size() > 0) + { + SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()]; + // get width + int i, w = 0; + if (alseq_l > 0) + { + for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++) + { + if (w < alseqs[i].getLength()) + { + w = alseqs[i].getLength(); + } + t_alseqs[i] = alseqs[i]; + alseqs[i] = null; + } + } + // check that aligned width is at least as wide as emptySeqs width. + int ow = w, nw = w; + for (i = 0, w = emptySeqs.size(); i < w; i++) + { + String[] es = (String[]) emptySeqs.get(i); + if (es != null && es[1] != null) + { + int sw = es[1].length(); + if (nw < sw) + { + nw = sw; + } + } + } + // make a gapped string. + StringBuffer insbuff = new StringBuffer(w); + for (i = 0; i < nw; i++) + { + insbuff.append(alseq_gapchar); + } + if (ow < nw) + { + for (i = 0; i < alseq_l; i++) + { + int sw = t_alseqs[i].getLength(); + if (nw > sw) + { + // pad at end + alseqs[i].setSequence(t_alseqs[i].getSequenceAsString() + + insbuff.substring(0, sw - nw)); + } + } + } + for (i = 0, w = emptySeqs.size(); i < w; i++) + { + String[] es = (String[]) emptySeqs.get(i); + if (es[1] == null) + { + t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0], + insbuff.toString(), 1, 0); + } + else + { + if (es[1].length() < nw) + { + t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence( + es[0], + es[1] + insbuff.substring(0, nw - es[1].length()), + 1, 1 + es[1].length()); + } + else + { + t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence( + es[0], es[1]); + } + } + } + alseqs = t_alseqs; + } + AlignmentOrder msaorder = new AlignmentOrder(alseqs); + // always recover the order - makes parseResult()'s life easier. + jalview.analysis.AlignmentSorter.recoverOrder(alseqs); + // account for any missing sequences + jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs); + return new Object[] + { alseqs, msaorder }; + } + return null; + } + + /** + * mark subjob as cancelled and set result object appropriatly + */ + void cancel() + { + cancelled = true; + subjobComplete = true; + alignment = null; + } + + /** + * + * @return boolean true if job can be submitted. + */ + public boolean hasValidInput() + { + // TODO: get attributes for this MsaWS instance to check if it can do two sequence alignment. + if (seqs != null && seqs.size()>=2) // two or more sequences is valid ? + { + return true; + } + return false; + } + StringBuffer jobProgress=new StringBuffer(); + public void setStatus(String string) + { + jobProgress.setLength(0); + jobProgress.append(string); + } + + @Override + public String getStatus() + { + return jobProgress.toString(); + } + + @Override + public boolean hasStatus() + { + return jobProgress!=null; + } + + /** + * @return the lastChunk + */ + public long getLastChunk() + { + return lastChunk; + } + + /** + * @param lastChunk the lastChunk to set + */ + public void setLastChunk(long lastChunk) + { + this.lastChunk = lastChunk; + } + + } + + String alTitle; // name which will be used to form new alignment window. + + Alignment dataset; // dataset to which the new alignment will be + + // associated. + + @SuppressWarnings("unchecked") + MsaWS server = null; + + /** + * set basic options for this (group) of Msa jobs + * + * @param subgaps + * boolean + * @param presorder + * boolean + */ + MsaWSThread(MsaWS server, String wsUrl, + WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, + AlignmentView alview, String wsname, boolean subgaps, + boolean presorder) + { + super(alFrame, wsinfo, alview, wsname, wsUrl); + this.server = server; + this.submitGaps = subgaps; + this.preserveOrder = presorder; + } + + /** + * create one or more Msa jobs to align visible seuqences in _msa + * + * @param title + * String + * @param _msa + * AlignmentView + * @param subgaps + * boolean + * @param presorder + * boolean + * @param seqset + * Alignment + */ + MsaWSThread(MsaWS server2, String wsUrl, + WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, + String wsname, String title, AlignmentView _msa, boolean subgaps, + boolean presorder, Alignment seqset) + { + this(server2,wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder); + OutputHeader = wsInfo.getProgressText(); + alTitle = title; + dataset = seqset; + + SequenceI[][] conmsa = _msa.getVisibleContigs('-'); + if (conmsa != null) + { + int njobs = conmsa.length; + jobs = new MsaWSJob[njobs]; + for (int j = 0; j < njobs; j++) + { + if (j != 0) + { + jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]); + } + else + { + jobs[j] = new MsaWSJob(0, conmsa[j]); + } + if (njobs > 0) + { + wsinfo + .setProgressName("region " + jobs[j].getJobnum(), + jobs[j].getJobnum()); + } + wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader); + } + } + } + + public boolean isCancellable() + { + return true; + } + + public void cancelJob() + { + if (!jobComplete && jobs != null) + { + boolean cancelled = true; + for (int job = 0; job < jobs.length; job++) + { + if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete()) + { + String cancelledMessage = ""; + try + { + boolean cancelledJob = server + .cancelJob(jobs[job].getJobId()); + if (cancelledJob) + { + // CANCELLED_JOB + cancelledMessage = "Job cancelled."; + ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this ugliness - + wsInfo.setStatus(jobs[job].getJobnum(), + WebserviceInfo.STATE_CANCELLED_OK); + } + else + { + // VALID UNSTOPPABLE JOB + cancelledMessage += "Server cannot cancel this job. just close the window.\n"; + cancelled = false; + // wsInfo.setStatus(jobs[job].jobnum, + // WebserviceInfo.STATE_RUNNING); + } + } catch (Exception exc) + { + cancelledMessage += ("\nProblems cancelling the job : Exception received...\n" + + exc + "\n"); + Cache.log.warn( + "Exception whilst cancelling " + jobs[job].getJobId(), exc); + } + wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader + + cancelledMessage + "\n"); + } + } + if (cancelled) + { + wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK); + jobComplete = true; + } + this.interrupt(); // kick thread to update job states. + } + else + { + if (!jobComplete) + { + wsInfo + .setProgressText(OutputHeader + + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n"); + } + } + } + + public void pollJob(AWsJob job) throws Exception + { + // TODO: investigate if we still need to cast here in J1.6 + MsaWSJob j=((MsaWSJob) job); + j.setjobStatus(server.getJobStatus(job.getJobId())); + StringBuffer response = j.jobProgress; + ChunkHolder chunk = server.pullExecStatistics(job.getJobId(), j.getLastChunk()); + response.append(chunk.getChunk()); + j.setLastChunk(chunk.getNextPosition()); + + } + + public void StartJob(AWsJob job) + { + // boiler plate template + if (!(job instanceof MsaWSJob)) + { + throw new Error("StartJob(MsaWSJob) called on a WSJobInstance " + + job.getClass()); + } + MsaWSJob j = (MsaWSJob) job; + if (j.isSubmitted()) + { + if (Cache.log.isDebugEnabled()) + { + Cache.log.debug("Tried to submit an already submitted job " + + j.getJobId()); + } + return; + } + // end boilerplate + + if (j.seqs == null || j.seqs.size()==0) + { + // special case - selection consisted entirely of empty sequences... + j.setjobStatus(JobStatus.FINISHED); + j.setStatus("Empty Alignment Job"); + } + try + { + // TODO: get the parameters (if any) for this job and submit the job + j.setJobId(server.align(j.seqs)); + + if (j.getJobId()!= null) + { + j.setSubmitted(true); + j.setSubjobComplete(false); + // System.out.println(WsURL + " Job Id '" + jobId + "'"); + } + else + { + throw new Exception( + "Server at " + + WsUrl + + " returned null string for job id, it probably cannot be contacted. Try again later ?"); + } + } catch (Exception e) + { + // Boilerplate code here + // TODO: JBPNote catch timeout or other fault types explicitly + // For unexpected errors + System.err + .println(WebServiceName + + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n" + + "When contacting Server:" + WsUrl + "\n" + + e.toString() + "\n"); + j.setAllowedServerExceptions(0); + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); + wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_SERVERERROR); + wsInfo + .appendProgressText( + j.getJobnum(), + "Failed to submit sequences for alignment.\n" + + "It is most likely that there is a problem with the server.\n" + + "Just close the window\n"); + + // e.printStackTrace(); // TODO: JBPNote DEBUG + } + } + + + public void parseResult() + { + int results = 0; // number of result sets received + JobStateSummary finalState = new JobStateSummary(); + try + { + for (int j = 0; j < jobs.length; j++) + { + MsaWSJob msjob = ((MsaWSJob) jobs[j]); + if (jobs[j].isFinished() && msjob.alignment==null) + { + try { + msjob.alignment = server.getResult(msjob.getJobId()); + } + catch (Exception e) + { + Cache.log.error("Couldn't get Alignment for job.",e); + } + } + finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]); + if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete() + && jobs[j].hasResults()) + { + results++; + compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment; + if (alignment != null) + { + wsInfo.appendProgressText(jobs[j].getJobnum(), + "\nAlignment Object Method Notes\n"); + wsInfo.appendProgressText(jobs[j].getJobnum(), "Calculated with "+alignment.getMetadata().getProgram().toString()); + // JBPNote The returned files from a webservice could be + // hidden behind icons in the monitor window that, + // when clicked, pop up their corresponding data + } + } + } + } catch (Exception ex) + { + + Cache.log.error("Unexpected exception when processing results for " + + alTitle, ex); + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); + } + if (results > 0) + { + wsInfo.showResultsNewFrame + .addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent evt) + { + displayResults(true); + } + }); + wsInfo.mergeResults + .addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent evt) + { + displayResults(false); + } + }); + wsInfo.setResultsReady(); + } + else + { + wsInfo.setFinishedNoResults(); + } + } + + void displayResults(boolean newFrame) + { + // view input or result data for each block + Vector alorders = new Vector(); + SequenceI[][] results = new SequenceI[jobs.length][]; + AlignmentOrder[] orders = new AlignmentOrder[jobs.length]; + String lastProgram = null; + MsaWSJob msjob; + for (int j = 0; j < jobs.length; j++) + { + if (jobs[j].hasResults()) + { + msjob = (MsaWSJob)jobs[j]; + Object[] res = msjob.getAlignment(); + lastProgram = msjob.alignment.getMetadata().getProgram().name(); + alorders.add(res[1]); + results[j] = (SequenceI[]) res[0]; + orders[j] = (AlignmentOrder) res[1]; + + // SequenceI[] alignment = input.getUpdated + } + else + { + results[j] = null; + } + } + Object[] newview = input.getUpdatedView(results, orders, getGapChar()); + // trash references to original result data + for (int j = 0; j < jobs.length; j++) + { + results[j] = null; + orders[j] = null; + } + SequenceI[] alignment = (SequenceI[]) newview[0]; + ColumnSelection columnselection = (ColumnSelection) newview[1]; + Alignment al = new Alignment(alignment); + // TODO: add 'provenance' property to alignment from the method notes + if (lastProgram!=null) { + al.setProperty("Alignment Program", lastProgram); + } + // accompanying each subjob + if (dataset != null) + { + al.setDataset(dataset); + } + + propagateDatasetMappings(al); + // JBNote- TODO: warn user if a block is input rather than aligned data ? + + if (newFrame) + { + AlignFrame af = new AlignFrame(al, columnselection, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + + // initialise with same renderer settings as in parent alignframe. + af.getFeatureRenderer().transferSettings(this.featureSettings); + // update orders + if (alorders.size() > 0) + { + if (alorders.size() == 1) + { + af.addSortByOrderMenuItem(WebServiceName + " Ordering", + (AlignmentOrder) alorders.get(0)); + } + else + { + // construct a non-redundant ordering set + Vector names = new Vector(); + for (int i = 0, l = alorders.size(); i < l; i++) + { + String orderName = new String(" Region " + i); + int j = i + 1; + + while (j < l) + { + if (((AlignmentOrder) alorders.get(i)) + .equals(((AlignmentOrder) alorders.get(j)))) + { + alorders.remove(j); + l--; + orderName += "," + j; + } + else + { + j++; + } + } + + if (i == 0 && j == 1) + { + names.add(new String("")); + } + else + { + names.add(orderName); + } + } + for (int i = 0, l = alorders.size(); i < l; i++) + { + af.addSortByOrderMenuItem(WebServiceName + + ((String) names.get(i)) + " Ordering", + (AlignmentOrder) alorders.get(i)); + } + } + } + + Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + + } + else + { + System.out.println("MERGE WITH OLD FRAME"); + // TODO: modify alignment in original frame, replacing old for new + // alignment using the commands.EditCommand model to ensure the update can + // be undone + } + } + + public boolean canMergeResults() + { + return false; + } +} -- 1.7.10.2