JAL-3878 Wire web services logic to UI elements.
[jalview.git] / src / jalview / ws2 / slivka / SlivkaWebService.java
1 package jalview.ws2.slivka;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.EnumMap;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Set;
13
14 import jalview.bin.Cache;
15 import jalview.datamodel.AlignmentI;
16 import jalview.datamodel.SequenceI;
17 import jalview.io.DataSourceType;
18 import jalview.io.FileFormat;
19 import jalview.io.FileFormatI;
20 import jalview.io.FormatAdapter;
21 import jalview.ws.gui.WsJob;
22 import jalview.ws.params.ArgumentI;
23 import jalview.ws.params.ParamDatastoreI;
24 import jalview.ws.params.WsParamSetI;
25 import jalview.ws.slivkaws.SlivkaDatastore;
26 import jalview.ws2.WebServiceI;
27 import jalview.ws2.operations.Operation;
28 import jalview.ws2.ResultSupplier;
29 import jalview.ws2.WSJob;
30 import jalview.ws2.WSJobStatus;
31 import javajs.http.ClientProtocolException;
32 import uk.ac.dundee.compbio.slivkaclient.Job;
33 import uk.ac.dundee.compbio.slivkaclient.Parameter;
34 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
35 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
36 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
37
38 public class SlivkaWebService implements WebServiceI
39 {
40   protected final SlivkaClient client;
41
42   protected final SlivkaService service;
43
44   protected SlivkaDatastore store = null;
45
46   protected final ArrayList<Operation> operations = new ArrayList<>();
47
48   protected int typeFlags = 0;
49
50   protected static final EnumMap<Job.Status, WSJobStatus> statusMap = new EnumMap<>(
51           Job.Status.class);
52   {
53     statusMap.put(Job.Status.PENDING, WSJobStatus.SUBMITTED);
54     statusMap.put(Job.Status.REJECTED, WSJobStatus.INVALID);
55     statusMap.put(Job.Status.ACCEPTED, WSJobStatus.QUEUED);
56     statusMap.put(Job.Status.QUEUED, WSJobStatus.QUEUED);
57     statusMap.put(Job.Status.RUNNING, WSJobStatus.RUNNING);
58     statusMap.put(Job.Status.COMPLETED, WSJobStatus.FINISHED);
59     statusMap.put(Job.Status.INTERRUPTED, WSJobStatus.CANCELLED);
60     statusMap.put(Job.Status.DELETED, WSJobStatus.CANCELLED);
61     statusMap.put(Job.Status.FAILED, WSJobStatus.FAILED);
62     statusMap.put(Job.Status.ERROR, WSJobStatus.SERVER_ERROR);
63     statusMap.put(Job.Status.UNKNOWN, WSJobStatus.UNKNOWN);
64   }
65
66   public SlivkaWebService(SlivkaClient client, SlivkaService service)
67   {
68     this.client = client;
69     this.service = service;
70   }
71
72   @Override
73   public String getHostName()
74   {
75     return client.getUrl().toString();
76   }
77
78   @Override
79   public String getProviderName()
80   {
81     return "slivka";
82   }
83
84   @Override
85   public String getName()
86   {
87     return service.getName();
88   }
89
90   @Override
91   public String getDescription()
92   {
93     return service.getDescription();
94   }
95
96   @Override
97   public List<Operation> getOperations()
98   {
99     return operations;
100   }
101
102   void addOperation(Operation operation)
103   {
104     operations.add(operation);
105   }
106
107   void removeOperation(Operation operation)
108   {
109     operations.remove(operation);
110   }
111
112   @Override
113   public boolean hasParameters()
114   {
115     return getParamStore().getServiceParameters().size() > 0;
116   }
117
118   @Override
119   public ParamDatastoreI getParamStore()
120   {
121     if (store == null)
122     {
123       store = new SlivkaDatastore(service);
124     }
125     return store;
126   }
127
128   @Override
129   public String submit(List<SequenceI> sequences, List<ArgumentI> args)
130           throws IOException
131   {
132     var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest();
133     for (Parameter param : service.getParameters())
134     {
135       if (param instanceof Parameter.FileParameter)
136       {
137         // if finds a file input, gives it sequences stream
138         Parameter.FileParameter fileParam = (Parameter.FileParameter) param;
139         FileFormat format;
140         switch (fileParam.getMediaType())
141         {
142         case "application/pfam":
143           format = FileFormat.Pfam;
144           break;
145         case "application/stockholm":
146           format = FileFormat.Stockholm;
147           break;
148         case "application/clustal":
149           format = FileFormat.Clustal;
150           break;
151         case "application/fasta":
152         default:
153           format = FileFormat.Fasta;
154           break;
155         }
156         InputStream stream = new ByteArrayInputStream(format.getWriter(null)
157                 .print(sequences.toArray(new SequenceI[0]), false)
158                 .getBytes());
159         request.addFile(param.getId(), stream);
160       }
161     }
162     if (args != null)
163     {
164       for (ArgumentI arg : args)
165       {
166         // multiple choice field names are name$number to avoid duplications
167         // the number is stripped here
168         String paramId = arg.getName().split("\\$", 2)[0];
169         Parameter param = service.getParameter(paramId);
170         if (param instanceof Parameter.FlagParameter)
171         {
172           if (arg.getValue() != null && !arg.getValue().isBlank())
173             request.addData(paramId, true);
174           else
175             request.addData(paramId, false);
176         }
177         else
178         {
179           request.addData(paramId, arg.getValue());
180         }
181       }
182     }
183     var job = service.submitJob(request);
184     return job.getId();
185   }
186
187   @Override
188   public void updateProgress(WSJob job) throws IOException
189   {
190     var slivkaJob = client.getJob(job.getJobId());
191     job.setStatus(statusMap.get(slivkaJob.getStatus()));
192   }
193
194   @Override
195   public void cancel(WSJob job) throws IOException
196   {
197     Cache.log.warn("Slivka does not support job cancellation yet.");
198   }
199
200   @Override
201   public boolean handleSubmissionError(WSJob job, Exception ex)
202   {
203     if (ex instanceof ClientProtocolException)
204     {
205       Cache.log.error("Job submission failed due to exception.", ex);
206       return true;
207     }
208     return false;
209   }
210
211   @Override
212   public boolean handleCollectionError(WSJob job, Exception ex)
213   {
214     // TODO Auto-generated method stub
215     return false;
216   }
217
218   public AlignmentI getAlignment(WSJob job) throws IOException
219   {
220     Collection<RemoteFile> files;
221     var slivkaJob = client.getJob(job.getJobId());
222     files = slivkaJob.getResults();
223     for (RemoteFile f : files)
224     {
225       if (f.getMediaType().equals("application/clustal"))
226       {
227         return new FormatAdapter().readFile(f.getContentUrl().toString(),
228                 DataSourceType.URL, FileFormat.Clustal);
229       }
230       else if (f.getMediaType().equals("application/fasta"))
231       {
232         return new FormatAdapter().readFile(f.getContentUrl().toString(),
233                 DataSourceType.URL, FileFormat.Fasta);
234       }
235     }
236     return null;
237   }
238
239   @Override
240   public String toString()
241   {
242     return String.format("SlivkaWebService[%s]", getName());
243   }
244 }