Merge branch 'JAL-1601-direct-jpred4-rest-service' into development/Release_2_12_Branch
[jalview.git] / src / jalview / ws2 / gui / SearchServiceGuiHandler.java
1 package jalview.ws2.gui;
2
3 import java.util.List;
4
5 import javax.swing.SwingUtilities;
6
7 import jalview.analysis.AlignmentUtils;
8 import jalview.bin.Console;
9 import jalview.datamodel.Alignment;
10 import jalview.datamodel.AlignmentAnnotation;
11 import jalview.datamodel.AlignmentI;
12 import jalview.datamodel.HiddenColumns;
13 import jalview.gui.AlignFrame;
14 import jalview.gui.Desktop;
15 import jalview.gui.JvOptionPane;
16 import jalview.gui.WebserviceInfo;
17 import jalview.util.ArrayUtils;
18 import jalview.util.MessageManager;
19 import jalview.ws2.actions.api.ActionI;
20 import jalview.ws2.actions.api.JobI;
21 import jalview.ws2.actions.api.TaskEventListener;
22 import jalview.ws2.actions.api.TaskI;
23 import jalview.ws2.api.JobStatus;
24 import jalview.ws2.api.WebService;
25 import jalview.ws2.helpers.WSClientTaskWrapper;
26
27 import static java.lang.String.format;
28
29 class SearchServiceGuiHandler implements TaskEventListener<AlignmentI>
30 {
31   private final AlignFrame parentFrame;
32
33   private final ActionI<?> action;
34
35   private final WebService<?> service;
36
37   private WebserviceInfo infoPanel;
38
39   private JobI[] jobs = new JobI[0];
40
41   private int[] tabs = new int[0];
42
43   private int[] logOffset = new int[0];
44
45   private int[] errLogOffset = new int[0];
46
47   public SearchServiceGuiHandler(ActionI<?> action, AlignFrame parentFrame)
48   {
49     this.parentFrame = parentFrame;
50     this.action = action;
51     this.service = action.getWebService();
52     var info = String.format("%s search using service at %s%n%s",
53             service.getName(), service.getUrl(), service.getDescription());
54     this.infoPanel = new WebserviceInfo(service.getName(), info, false);
55   }
56
57   @Override
58   public void taskStarted(TaskI<AlignmentI> source,
59           List<? extends JobI> subJobs)
60   {
61     Console.debug(format("task %s#%x started with %d sub-jobs",
62             service.getName(), source.getUid(), subJobs.size()));
63     jobs = subJobs.toArray(new JobI[subJobs.size()]);
64     tabs = new int[subJobs.size()];
65     logOffset = new int[subJobs.size()];
66     errLogOffset = new int[subJobs.size()];
67     for (int i = 0; i < subJobs.size(); i++)
68     {
69       JobI job = jobs[i];
70       int tabIndex = infoPanel.addJobPane();
71       tabs[i] = tabIndex;
72       infoPanel.setProgressName(format("region %d", i), tabIndex);
73       infoPanel.setProgressText(tabIndex, "Job details:\n");
74       // jobs should not have states other than invalid or ready at this point
75       if (job.getStatus() == JobStatus.INVALID)
76         infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_STOPPED_OK);
77       else if (job.getStatus() == JobStatus.READY)
78         infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_QUEUING);
79     }
80   }
81
82   @Override
83   public void taskStatusChanged(TaskI<AlignmentI> source, JobStatus status)
84   {
85     Console.debug(format("task %s#%x status changed to %s",
86             service.getName(), source.getUid(), status));
87     switch (status)
88     {
89     case INVALID:
90       infoPanel.setVisible(false);
91       JvOptionPane.showMessageDialog(parentFrame,
92               MessageManager.getString("info.invalid_search_input"),
93               MessageManager.getString("info.invalid_search_input"),
94               JvOptionPane.INFORMATION_MESSAGE);
95       break;
96     case READY:
97       infoPanel.setthisService(new WSClientTaskWrapper(source));
98       infoPanel.setVisible(true);
99       // intentional no break
100     case SUBMITTED:
101     case QUEUED:
102       infoPanel.setStatus(WebserviceInfo.STATE_QUEUING);
103       break;
104     case RUNNING:
105     case UNKNOWN: // unsure what to do with unknown
106       infoPanel.setStatus(WebserviceInfo.STATE_RUNNING);
107       break;
108     case COMPLETED:
109       infoPanel.setProgressBar(
110               MessageManager.getString("status.collecting_job_results"),
111               jobs[0].getInternalId());
112       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_OK);
113       break;
114     case FAILED:
115       infoPanel.removeProgressBar(jobs[0].getInternalId());
116       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
117       break;
118     case CANCELLED:
119       infoPanel.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
120       break;
121     case SERVER_ERROR:
122       infoPanel.removeProgressBar(jobs[0].getInternalId());
123       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
124       break;
125     }
126   }
127
128   @Override
129   public void taskCompleted(TaskI<AlignmentI> source, AlignmentI result)
130   {
131     Console.debug(format("task %s#%x completed", service.getName(),
132             source.getUid()));
133     SwingUtilities.invokeLater(
134             () -> infoPanel.removeProgressBar(jobs[0].getInternalId()));
135     if (result == null)
136     {
137       SwingUtilities.invokeLater(infoPanel::setFinishedNoResults);
138       return;
139     }
140     infoPanel.showResultsNewFrame.addActionListener(evt -> {
141       // copy alignment for each frame to have its own instance
142       var alnCpy = AlignmentUtils.deepCopyAlignment(result);
143       displayResultsNewFrame(alnCpy);
144     });
145     SwingUtilities.invokeLater(infoPanel::setResultsReady);
146   }
147
148   private void displayResultsNewFrame(AlignmentI aln)
149   {
150     AlignFrame frame = new AlignFrame(aln, AlignFrame.DEFAULT_WIDTH,
151             AlignFrame.DEFAULT_HEIGHT);
152     frame.getFeatureRenderer().transferSettings(
153             parentFrame.getFeatureRenderer().getSettings());
154     var actionName = action.getName() != null ? action.getName() : "Search";
155     var title = String.format("%s %s of %s", service.getName(), actionName,
156             parentFrame.getTitle());
157     Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
158             AlignFrame.DEFAULT_HEIGHT);
159   }
160
161   @Override
162   public void taskException(TaskI<AlignmentI> source, Exception e)
163   {
164     Console.error(format("Task %s#%x raised an exception.",
165             service.getName(), source.getUid()), e);
166     infoPanel.appendProgressText(e.getMessage());
167   }
168
169   @Override
170   public void subJobStatusChanged(TaskI<AlignmentI> source, JobI job,
171           JobStatus status)
172   {
173     Console.debug(format("sub-job %x status changed to %s",
174             job.getInternalId(), status));
175     int i = ArrayUtils.indexOf(jobs, job);
176     assert i >= 0 : "job does not exist";
177     if (i < 0)
178       // safeguard that should not happen irl
179       return;
180     int wsStatus;
181     switch (status)
182     {
183     case INVALID:
184     case COMPLETED:
185       wsStatus = WebserviceInfo.STATE_STOPPED_OK;
186       break;
187     case READY:
188     case SUBMITTED:
189     case QUEUED:
190       wsStatus = WebserviceInfo.STATE_QUEUING;
191       break;
192     case RUNNING:
193     case UNKNOWN:
194       wsStatus = WebserviceInfo.STATE_RUNNING;
195       break;
196     case FAILED:
197       wsStatus = WebserviceInfo.STATE_STOPPED_ERROR;
198       break;
199     case CANCELLED:
200       wsStatus = WebserviceInfo.STATE_CANCELLED_OK;
201       break;
202     case SERVER_ERROR:
203       wsStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR;
204       break;
205     default:
206       throw new AssertionError("Non-exhaustive switch statement");
207     }
208     infoPanel.setStatus(tabs[i], wsStatus);
209   }
210
211   @Override
212   public void subJobLogChanged(TaskI<AlignmentI> source, JobI job,
213           String log)
214   {
215     int i = ArrayUtils.indexOf(jobs, job);
216     assert i >= 0 : "job does not exist";
217     if (i < 0)
218       // safeguard that should never happen
219       return;
220     infoPanel.appendProgressText(tabs[i], log.substring(logOffset[i]));
221   }
222
223   @Override
224   public void subJobErrorLogChanged(TaskI<AlignmentI> source, JobI job,
225           String log)
226   {
227     int i = ArrayUtils.indexOf(jobs, job);
228     assert i >= 0 : "job does not exist";
229     if (i < 0)
230       // safeguard that should never happen
231       return;
232     infoPanel.appendProgressText(tabs[i], log.substring(errLogOffset[i]));
233   }
234 }