refactored to create generic polling thread and subjobs
[jalview.git] / src / jalview / ws / WSThread.java
1 package jalview.ws;
2
3 import jalview.gui.AlignFrame;
4 import jalview.gui.WebserviceInfo;
5 import jalview.datamodel.AlignmentView;
6 import jalview.gui.Desktop;
7 import javax.swing.JOptionPane;
8 import jalview.bin.Cache;
9
10 public abstract class WSThread extends Thread
11 {
12     /**
13      * Generic properties for Web Service Client threads.
14      */
15     AlignFrame alignFrame;
16   WebserviceInfo wsInfo = null;
17   AlignmentView input;
18   boolean jobComplete = false;
19   abstract class WSJob {
20       /**
21        * Generic properties for an individual job within a Web Service Client thread
22        */
23     int jobnum = 0; // WebServiceInfo pane for this job
24     String jobId; // ws job ticket
25     boolean cancelled = false;
26     int allowedServerExceptions = 3; // job dies if too many exceptions.
27     boolean submitted = false;
28     boolean subjobComplete = false;
29     /**
30      *
31      * @return true if job has completed and valid results are available
32      */
33     abstract boolean hasResults();
34     /**
35      *
36      * @return boolean true if job can be submitted.
37      */
38     abstract boolean hasValidInput();
39     vamsas.objects.simple.Result result;
40   }
41   class JobStateSummary {
42     int running = 0;
43     int queuing = 0;
44     int finished = 0;
45     int error = 0;
46     int serror = 0;
47     int cancelled = 0;
48     int results = 0;
49     void updateJobPanelState(WebserviceInfo wsInfo, String OutputHeader,
50                              WSJob j)
51     {
52       if (j.result != null)
53       {
54         String progheader = "";
55         // Parse state of job[j]
56         if (j.result.isRunning())
57         {
58           running++;
59           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING);
60         }
61         else if (j.result.isQueued())
62         {
63           queuing++;
64           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING);
65         }
66         else if (j.result.isFinished())
67         {
68           finished++;
69           j.subjobComplete = true;
70           if (j.hasResults())
71             results++;
72           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK);
73         }
74         else if (j.result.isFailed())
75         {
76           progheader += "Job failed.\n";
77           j.subjobComplete = true;
78           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
79           error++;
80         }
81         else if (j.result.isServerError())
82         {
83           serror++;
84           j.subjobComplete = true;
85           wsInfo.setStatus(j.jobnum,
86                            WebserviceInfo.STATE_STOPPED_SERVERERROR);
87         }
88         else if (j.result.isBroken() || j.result.isFailed())
89         {
90           error++;
91           j.subjobComplete = true;
92           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
93         }
94         // and pass on any sub-job messages to the user
95         wsInfo.setProgressText(j.jobnum, OutputHeader);
96         wsInfo.appendProgressText(j.jobnum, progheader);
97         if (j.result.getStatus() != null)
98         {
99           wsInfo.appendProgressText(j.jobnum, j.result.getStatus());
100         }
101       }
102       else
103       {
104         if (j.submitted && j.subjobComplete)
105         {
106           if (j.allowedServerExceptions == 0)
107           {
108             serror++;
109           }
110           else if (j.result == null)
111           {
112             error++;
113           }
114         }
115       }
116     }
117   }
118
119   WSJob jobs[] = null;
120   String WebServiceName = null;
121   String OutputHeader;
122   String WsUrl = null;
123   abstract void pollJob(WSJob job) throws Exception;
124   public void run()
125   {
126   JobStateSummary jstate=null;
127   if (jobs==null)
128     jobComplete=true;
129   while (!jobComplete)
130   {
131     jstate = new JobStateSummary();
132     for (int j = 0; j < jobs.length; j++)
133     {
134
135       if (!jobs[j].submitted && jobs[j].hasValidInput())
136       {
137         StartJob(jobs[j]);
138       }
139
140       if (jobs[j].submitted && !jobs[j].subjobComplete)
141       {
142         try
143         {
144           pollJob(jobs[j]);
145           if (jobs[j].result == null)
146           {
147             throw (new Exception(
148                 "Timed out when communicating with server\nTry again later.\n"));
149           }
150           jalview.bin.Cache.log.debug("Job " + j + " Result state " +
151                                       jobs[j].result.getState()
152                                       + "(ServerError=" +
153                                       jobs[j].result.isServerError() + ")");
154         }
155         catch (Exception ex)
156         {
157           // Deal with Transaction exceptions
158           wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
159                                     + " Server exception!\n" + ex.getMessage());
160           Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
161                          + ") Server exception: " + ex.getMessage());
162
163           if (jobs[j].allowedServerExceptions > 0)
164           {
165             jobs[j].allowedServerExceptions--;
166             Cache.log.debug("Sleeping after a server exception.");
167             try
168             {
169               Thread.sleep(5000);
170             }
171             catch (InterruptedException ex1)
172             {
173             }
174           }
175           else
176           {
177             Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
178             jobs[j].subjobComplete = true;
179             wsInfo.setStatus(jobs[j].jobnum,
180                              WebserviceInfo.STATE_STOPPED_SERVERERROR);
181           }
182         }
183         catch (OutOfMemoryError er)
184         {
185           jobComplete = true;
186           jobs[j].subjobComplete = true;
187           jobs[j].result = null; // may contain out of date result object
188           wsInfo.setStatus(jobs[j].jobnum,
189                            WebserviceInfo.STATE_STOPPED_ERROR);
190           JOptionPane
191               .showInternalMessageDialog(
192                   Desktop.desktop,
193                   "Out of memory handling result for job !!"
194                   +
195               "\nSee help files for increasing Java Virtual Machine memory.",
196                   "Out of memory", JOptionPane.WARNING_MESSAGE);
197           Cache.log.error("Out of memory when retrieving Job " + j + " id:" +
198                           WsUrl + "/" + jobs[j].jobId, er);
199           System.gc();
200         }
201       }
202       jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
203     }
204     // Decide on overall state based on collected jobs[] states
205     if (jstate.running > 0)
206     {
207       wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
208     }
209     else if (jstate.queuing > 0)
210     {
211       wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
212     }
213     else
214     {
215       jobComplete = true;
216       if (jstate.finished > 0)
217       {
218         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
219       }
220       else if (jstate.error > 0)
221       {
222         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
223       }
224       else if (jstate.serror > 0)
225       {
226         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
227       }
228     }
229     if (!jobComplete)
230     {
231       try
232       {
233         Thread.sleep(5000);
234       }
235       catch (InterruptedException e)
236       {
237         Cache.log.debug("Interrupted sleep waiting for next job poll.", e);
238       }
239       // System.out.println("I'm alive "+alTitle);
240     }
241   }
242   if (jobComplete)
243   {
244     parseResult(); // tidy up and make results available to user
245   }
246 }
247 abstract void StartJob(WSJob job);
248 abstract void parseResult();
249 }