X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FSequenceFetcher.java;h=ae4fffd76457042ea5aa2b6252470454f371526d;hb=d23b0d22d59c3f8c0cea88f41f2361d7a4f12a94;hp=b8e3b3390e8fcca353b41f406317d6e08f421778;hpb=2ea1fb1e8751ac7272c421820000449525664f02;p=jalview.git diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index b8e3b33..ae4fffd 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -36,6 +36,7 @@ import jalview.util.Platform; import jalview.ws.seqfetcher.DbSourceProxy; import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -57,6 +58,8 @@ import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingConstants; +import javajs.async.AsyncSwingWorker; + /** * A panel where the use may choose a database source, and enter one or more * accessions, to retrieve entries from the database. @@ -65,7 +68,7 @@ import javax.swing.SwingConstants; * instead to perform the search and selection. */ @SuppressWarnings("serial") -public class SequenceFetcher extends JPanel implements Runnable +public class SequenceFetcher extends JPanel { JLabel exampleAccession; @@ -131,8 +134,8 @@ public class SequenceFetcher extends JPanel implements Runnable frame = new JInternalFrame(); frame.setContentPane(this); - Desktop.addInternalFrame(frame, getFrameTitle(), true, 400, - Platform.isAMacAndNotJS() ? 240 : 180); + Desktop.addInternalFrame(frame, getFrameTitle(), true, 400, + Platform.isAMacAndNotJS() ? 240 : 180); } private String getFrameTitle() @@ -412,9 +415,9 @@ public class SequenceFetcher extends JPanel implements Runnable text = text.replace(",", ";"); } text = text.replaceAll("(\\s|[; ])+", ";"); - if (!t0.equals(text)) + if (!t0.equals(text)) { - textArea.setText(text); + textArea.setText(text); } if (text.isEmpty()) { @@ -429,12 +432,29 @@ public class SequenceFetcher extends JPanel implements Runnable okBtn.setEnabled(false); closeBtn.setEnabled(false); backBtn.setEnabled(false); + fetch(null, true); + } - Thread worker = new Thread(this); - worker.start(); + public void fetch(String ids, boolean isAsync) + { + if (ids == null) + { + ids = textArea.getText(); + } + else + { + textArea.setText(ids); + } + Component parent = null; // or this + String title = null; // or some title for the progress monitor + int min = AsyncFetchTask.STATE_INIT; + int max = AsyncFetchTask.STATE_DONE; + int msDelay = (isAsync ? 5 : 0); + new AsyncFetchTask(ids, parent, title, msDelay, min, max).execute(); } - private void resetDialog() + + protected void resetDialog() { exampleBtn.setEnabled(true); textArea.setEnabled(true); @@ -443,176 +463,364 @@ public class SequenceFetcher extends JPanel implements Runnable backBtn.setEnabled(parentSearchPanel != null); } - @Override - public void run() + /** + * This asynchronous class allows for a single-threaded state machine + * SwingWorker to process a list of requests from multiple sources. + * + * A standard ProcessMonitor could be attached to this task, but it is + * currently not. + * + * @author hansonr + * + */ + private class AsyncFetchTask extends AsyncSwingWorker { - boolean addToLast = false; - List aresultq = new ArrayList<>(); - List presultTitle = new ArrayList<>(); - List presult = new ArrayList<>(); - List aresult = new ArrayList<>(); - List sources = jalview.ws.SequenceFetcher.getInstance() - .getSourceProxy((String) database.getSelectedItem()); - Iterator proxies = sources.iterator(); - String[] qries = textArea.getText().trim().split(";"); - List nextFetch = Arrays.asList(qries); - Iterator en = Arrays.asList(new String[0]).iterator(); - int nqueries = qries.length; - - FeatureSettingsModelI preferredFeatureColours = null; - while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0)) - { - if (!en.hasNext() && nextFetch.size() > 0) - { - en = nextFetch.iterator(); - nqueries = nextFetch.size(); - // save the remaining queries in the original array - qries = nextFetch.toArray(new String[nqueries]); - nextFetch = new ArrayList<>(); - } - DbSourceProxy proxy = proxies.next(); - try + protected boolean addToLast = false; + + protected List aresultq = new ArrayList<>(); + + protected List presultTitle = new ArrayList<>(); + + protected List presult = new ArrayList<>(); + + protected List aresult = new ArrayList<>(); + + protected FeatureSettingsModelI preferredFeatureColours = null; + + protected List sources; + + protected Iterator sourceIterator; + + protected String[] fetchArray; + + protected List fetchList; + + protected Iterator fetchIterator; + + protected int fetchCount; + + protected DbSourceProxy source; + + protected String ids; + + protected long myID; + + protected boolean isAsync; + + protected Throwable taskError; + + public AsyncFetchTask(String ids, Component owner, String title, + int delayMillis, + int min, int max) + { + super(owner, title, delayMillis, min, max); + this.ids = ids; + isAsync = (delayMillis != 0); + myID = Thread.currentThread().getId(); + } + + @Override + public void initAsync() + { + sources = jalview.ws.SequenceFetcher.getInstance() + .getSourceProxy((String) database.getSelectedItem()); + sourceIterator = sources.iterator(); + fetchArray = ids.trim().split(";"); + fetchList = Arrays.asList(fetchArray); + } + + private final static int STATE_INIT = 0; + + private final static int STATE_NEXT_SOURCE = 10; + + private final static int STATE_FETCH_SINGLE = 30; + + private final static int STATE_FETCH_MULTIPLE = 40; + + private final static int STATE_PROCESS = 50; + + private final static int STATE_PARSE_RESULTS = 80; + + private final static int STATE_DONE = 100; + + protected static final int STATE_TASK_ERROR = 99; + + @Override + public int doInBackgroundAsync(int progress) + { + System.out.println("SequenceFetcher.AsyncFetchTask " + isAsync + " " + + progress + " " + + taskError); + switch (progress) { - // update status - guiWindow.setProgressBar(MessageManager.formatMessage( + case STATE_INIT: + case STATE_NEXT_SOURCE: + boolean doneFetching = (fetchIterator == null + || !fetchIterator.hasNext()); + boolean havePending = (fetchList.size() > 0); + if (!sourceIterator.hasNext() || doneFetching && !havePending) + { + showProgress((presult.size() > 0) + ? MessageManager.getString("status.parsing_results") + : MessageManager.getString("status.processing")); + return STATE_PARSE_RESULTS; + } + source = sourceIterator.next(); + if (doneFetching) + { + // if we are here, we must have some pending still + fetchCount = fetchList.size(); + fetchIterator = fetchList.iterator(); + // save the remaining queries in the original array + fetchArray = fetchList.toArray(new String[fetchCount]); + // and clear the + fetchList = new ArrayList<>(); + } + showProgress(MessageManager.formatMessage( "status.fetching_sequence_queries_from", new String[] - { Integer.valueOf(nqueries).toString(), - proxy.getDbName() }), - Thread.currentThread().hashCode()); - if (proxy.getMaximumQueryCount() == 1) + { Integer.valueOf(fetchCount).toString(), + source.getDbName() })); + return (source.getMaximumQueryCount() == 1 ? STATE_FETCH_SINGLE + : STATE_FETCH_MULTIPLE); + case STATE_FETCH_SINGLE: + if (fetchIterator.hasNext()) { - /* - * proxy only handles one accession id at a time - */ - while (en.hasNext()) + return runSubtask(new Runnable() { - String acc = en.next(); - if (!fetchSingleAccession(proxy, acc, aresultq, aresult)) + + @Override + public void run() { - nextFetch.add(acc); + // source only handles one accession id at a time + try + { + if (!isAsync) + { + // for CrossRef2xmlTest only; to "allow the server to breathe" + // (but we are doing that already when isAsync is true) + Thread.sleep(5); + } + String notFound = fetchSingleAccession(source, + fetchIterator.next(), aresultq, aresult); + if (notFound != null) + { + fetchList.add(notFound); + } + } catch (Throwable e) + { + taskError = e; + } } - } + }); } - else + return STATE_PROCESS; + case STATE_FETCH_MULTIPLE: + // proxy can fetch multiple accessions at one time + setProgressAsync(STATE_PROCESS); + return runSubtask(new Runnable() { - /* - * proxy can fetch multiple accessions at one time - */ - fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch); - } - } catch (Exception e) - { - showErrorMessage("Error retrieving " + textArea.getText() + " from " - + database.getSelectedItem()); - // error - // +="Couldn't retrieve sequences from "+database.getSelectedItem(); - System.err.println("Retrieval failed for source ='" - + database.getSelectedItem() + "' and query\n'" - + textArea.getText() + "'\n"); - e.printStackTrace(); - } catch (OutOfMemoryError e) - { - showErrorMessage("Out of Memory when retrieving " - + textArea.getText() + " from " + database.getSelectedItem() - + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n"); - e.printStackTrace(); - } catch (Error e) - { - showErrorMessage("Serious Error retrieving " + textArea.getText() - + " from " + database.getSelectedItem()); - e.printStackTrace(); - } - // Stack results ready for opening in alignment windows - if (aresult != null && aresult.size() > 0) - { - FeatureSettingsModelI proxyColourScheme = proxy - .getFeatureColourScheme(); - if (proxyColourScheme != null) - { - preferredFeatureColours = proxyColourScheme; - } + @Override + public void run() + { + try + { + fetchMultipleAccessions(source, fetchIterator, aresultq, + aresult, fetchList); + } catch (Throwable e) + { + taskError = e; + } + } - AlignmentI ar = null; - if (proxy.isAlignmentSource()) + }); + case STATE_PROCESS: + // Stack results ready for opening in alignment windows + if (aresult != null && aresult.size() > 0) { - addToLast = false; - // new window for each result - while (aresult.size() > 0) + FeatureSettingsModelI proxyColourScheme = source + .getFeatureColourScheme(); + if (proxyColourScheme != null) { - presult.add(aresult.remove(0)); - presultTitle.add( - aresultq.remove(0) + " " + getDefaultRetrievalTitle()); + preferredFeatureColours = proxyColourScheme; } - } - else - { - String titl = null; - if (addToLast && presult.size() > 0) + + AlignmentI ar = null; + if (source.isAlignmentSource()) { - ar = presult.remove(presult.size() - 1); - titl = presultTitle.remove(presultTitle.size() - 1); + addToLast = false; + // new window for each result + while (aresult.size() > 0) + { + presult.add(aresult.remove(0)); + presultTitle.add(aresultq.remove(0) + " " + + getDefaultRetrievalTitle()); + } } - // concatenate all results in one window - while (aresult.size() > 0) + else { - if (ar == null) + String titl = null; + if (addToLast && presult.size() > 0) { - ar = aresult.remove(0); + ar = presult.remove(presult.size() - 1); + titl = presultTitle.remove(presultTitle.size() - 1); } - else + // concatenate all results in one window + while (aresult.size() > 0) { - ar.append(aresult.remove(0)); + if (ar == null) + { + ar = aresult.remove(0); + } + else + { + ar.append(aresult.remove(0)); + } } + addToLast = true; + presult.add(ar); + presultTitle.add(titl); } - addToLast = true; - presult.add(ar); - presultTitle.add(titl); } + showProgress(MessageManager.getString("status.finshed_querying")); + return STATE_NEXT_SOURCE; + case STATE_PARSE_RESULTS: + while (presult.size() > 0) + { + parseResult(presult.remove(0), presultTitle.remove(0), null, + preferredFeatureColours); + } + break; + case STATE_TASK_ERROR: + showProgress(null); + super.cancelAsync(); + return 0; // arbitrary; ignored } - guiWindow.setProgressBar( - MessageManager.getString("status.finshed_querying"), - Thread.currentThread().hashCode()); + return STATE_DONE; } - guiWindow - .setProgressBar( - (presult.size() > 0) - ? MessageManager - .getString("status.parsing_results") - : MessageManager.getString("status.processing"), - Thread.currentThread().hashCode()); - // process results - while (presult.size() > 0) - { - parseResult(presult.remove(0), presultTitle.remove(0), null, - preferredFeatureColours); + + protected void resume() + { + if (!isAsync) + { + return; + } + super.setPaused(false); + super.stateLoop(); } - // only remove visual delay after we finished parsing. - guiWindow.setProgressBar(null, Thread.currentThread().hashCode()); - if (nextFetch.size() > 0) - { - StringBuffer sb = new StringBuffer(); - sb.append("Didn't retrieve the following " - + (nextFetch.size() == 1 ? "query" - : nextFetch.size() + " queries") - + ": \n"); - int l = sb.length(), lr = 0; - for (String s : nextFetch) + + private int runSubtask(Runnable subtask) + { + + Runnable r = new Runnable() { - if (l != sb.length()) + + @Override + public void run() { - sb.append("; "); + try + { + subtask.run(); + } catch (Throwable e) + { + taskError = e; + } + if (checkError(taskError)) + { + setProgressAsync(STATE_TASK_ERROR); + } + else + { + taskError = null; + } + if (isAsync) + { + resume(); + } } - if (lr - sb.length() > 40) + }; + + if (isAsync) + { + super.setPaused(true); + new Thread(r).start(); + return 0; // arbitrary return -- ignored because we are paused. + } + r.run(); + return super.getProgressAsync(); + } + + @Override + public void doneAsync() + { + showProgress(null); + if (fetchList.size() > 0) + { + StringBuffer sb = new StringBuffer(); + sb.append("Didn't retrieve the following " + + (fetchList.size() == 1 ? "query" + : fetchList.size() + " queries") + + ": \n"); + int l = sb.length(), lr = 0; + for (String s : fetchList) { - sb.append("\n"); + if (l != sb.length()) + { + sb.append("; "); + } + if (lr - sb.length() > 40) + { + sb.append("\n"); + } + sb.append(s); } - sb.append(s); + showErrorMessage(sb.toString()); } - showErrorMessage(sb.toString()); + resetDialog(); } - resetDialog(); + + /** + * + * @param e + * @return true if there was an error and we need to STOP + */ + protected boolean checkError(Throwable e) + { + if (e == null) + { + return false; + } + String problem = "retrieving " + ids + " from " + + database.getSelectedItem(); + if (e instanceof Exception) + { + showErrorMessage("Error " + problem); + System.err.println("Retrieval failed for source ='" + + database.getSelectedItem() + "' and query\n'" + + ids + "'\n"); + } + else if (e instanceof OutOfMemoryError) + { + showErrorMessage("Out of Memory when " + problem + + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n"); + // option here to return false and quit this, but that is not how + // original code works - BH + // return false; + } + else + { + showErrorMessage("Serious Error " + problem); + } + e.printStackTrace(); + return true; + } + + private void showProgress(String msg) + { + guiWindow.setProgressBar(msg, myID); + } + } /** @@ -680,48 +888,19 @@ public class SequenceFetcher extends JPanel implements Runnable * a list of successful queries to add to * @param aresult * a list of retrieved alignments to add to - * @return true if the fetch was successful, else false + * @return null if the fetch was successful; the accession if not */ - boolean fetchSingleAccession(DbSourceProxy proxy, String accession, - List aresultq, List aresult) + String fetchSingleAccession(DbSourceProxy proxy, String accession, + List aresultq, List aresult) throws Throwable { - boolean success = false; - try - { - if (aresult != null) - { - try - { - // give the server a chance to breathe - Thread.sleep(5); - } catch (Exception e) - { - // - } - } - - AlignmentI indres = null; - try - { - indres = proxy.getSequenceRecords(accession); - } catch (OutOfMemoryError oome) - { - new OOMWarning( - "fetching " + accession + " from " + proxy.getDbName(), - oome, this); - } - if (indres != null) - { - aresultq.add(accession); - aresult.add(indres); - success = true; - } - } catch (Exception e) + AlignmentI indres = proxy.getSequenceRecords(accession); + if (indres == null) { - Cache.log.info("Error retrieving " + accession + " from " - + proxy.getDbName(), e); + return accession; } - return success; + aresultq.add(accession); + aresult.add(indres); + return null; } /** @@ -741,12 +920,12 @@ public class SequenceFetcher extends JPanel implements Runnable for (String q : queries) { - // BH 2019.01.25 dbr is never used. -// DBRefEntry dbr = new DBRefEntry(); -// dbr.setSource(proxy.getDbSource()); -// dbr.setVersion(null); + // BH 2019.01.25 dbr is never used. + // DBRefEntry dbr = new DBRefEntry(); + // dbr.setSource(proxy.getDbSource()); + // dbr.setVersion(null); String accId = proxy.getAccessionIdFromQuery(q); -// dbr.setAccessionId(accId); + // dbr.setAccessionId(accId); boolean rfound = false; for (int r = 0, nr = rs.length; r < nr; r++) { @@ -849,7 +1028,8 @@ public class SequenceFetcher extends JPanel implements Runnable @Override public void run() { - JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), error, + JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), + error, MessageManager.getString("label.error_retrieving_data"), JvOptionPane.WARNING_MESSAGE); } @@ -875,11 +1055,6 @@ public class SequenceFetcher extends JPanel implements Runnable frame.setVisible(false); } - public void setQuery(String ids) - { - textArea.setText(ids); - } - /** * Called to modify the search panel for embedding as an alternative tab of a * free text search panel. The database choice list is hidden (since the @@ -894,4 +1069,5 @@ public class SequenceFetcher extends JPanel implements Runnable backBtn.setVisible(true); parentSearchPanel = parentPanel; } + }