16c9d3ad200fc097d156db6f6a33c883cdd8ab40
[jalview.git] / src / jalview / ws2 / client / slivka / SlivkaWSClient.java
1 package jalview.ws2.client.slivka;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.File;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.nio.charset.StandardCharsets;
9 import java.util.EnumMap;
10 import java.util.List;
11 import java.util.regex.Pattern;
12
13 import jalview.bin.Cache;
14 import jalview.datamodel.AlignmentI;
15 import jalview.datamodel.SequenceI;
16 import jalview.io.DataSourceType;
17 import jalview.io.FileFormat;
18 import jalview.io.FormatAdapter;
19 import jalview.ws.params.ArgumentI;
20 import jalview.ws2.api.Credentials;
21 import jalview.ws2.api.JobStatus;
22 import jalview.ws2.api.WebServiceJobHandle;
23 import jalview.ws2.client.api.AlignmentWebServiceClientI;
24 import jalview.ws2.client.api.WebServiceClientI;
25 import uk.ac.dundee.compbio.slivkaclient.Job;
26 import uk.ac.dundee.compbio.slivkaclient.Parameter;
27 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
28 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
29
30 public class SlivkaWSClient implements WebServiceClientI
31 {
32   final SlivkaService service;
33
34   final SlivkaClient client;
35
36   SlivkaWSClient(SlivkaService service)
37   {
38     this.service = service;
39     this.client = service.getClient();
40   }
41
42   @Override
43   public String getUrl()
44   {
45     return client.getUrl().toString();
46   }
47
48   @Override
49   public String getClientName()
50   {
51     return "slivka";
52   }
53
54   // pattern for matching media types
55   static final Pattern mediaTypePattern = Pattern.compile("(?:text|application)\\/(?:x-)?(\\w+)");
56
57   @Override
58   public WebServiceJobHandle submit(List<SequenceI> sequences,
59       List<ArgumentI> args, Credentials credentials) throws IOException
60   {
61     var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest();
62     for (Parameter param : service.getParameters())
63     {
64       // TODO: restrict input sequences parameter name to "sequences"
65       if (param instanceof Parameter.FileParameter)
66       {
67         Parameter.FileParameter fileParam = (Parameter.FileParameter) param;
68         FileFormat format = null;
69         var match = mediaTypePattern.matcher(fileParam.getMediaType());
70         if (match.find())
71         {
72           String fmt = match.group(1);
73           if (fmt.equalsIgnoreCase("pfam"))
74             format = FileFormat.Pfam;
75           else if (fmt.equalsIgnoreCase("stockholm"))
76             format = FileFormat.Stockholm;
77           else if (fmt.equalsIgnoreCase("clustal"))
78             format = FileFormat.Clustal;
79           else if (fmt.equalsIgnoreCase("fasta"))
80             format = FileFormat.Fasta;
81         }
82         if (format == null)
83         {
84           Cache.log.warn(String.format(
85               "Unknown input format %s, assuming fasta.",
86               fileParam.getMediaType()));
87           format = FileFormat.Fasta;
88         }
89         InputStream stream = new ByteArrayInputStream(format.getWriter(null)
90             .print(sequences.toArray(new SequenceI[0]), false)
91             .getBytes());
92         request.addFile(param.getId(), stream);
93       }
94     }
95     if (args != null)
96     {
97       for (ArgumentI arg : args)
98       {
99         // multiple choice field names are name$number to avoid duplications
100         // the number is stripped here
101         String paramId = arg.getName().split("\\$", 2)[0];
102         Parameter param = service.getParameter(paramId);
103         if (param instanceof Parameter.FlagParameter)
104         {
105           if (arg.getValue() != null && !arg.getValue().isBlank())
106             request.addData(paramId, true);
107           else
108             request.addData(paramId, false);
109         }
110         else if (param instanceof Parameter.FileParameter)
111         {
112           request.addFile(paramId, new File(arg.getValue()));
113         }
114         else
115         {
116           request.addData(paramId, arg.getValue());
117         }
118       }
119     }
120     var job = service.submitJob(request);
121     return createJobHandle(job.getId());
122   }
123
124   protected WebServiceJobHandle createJobHandle(String jobId)
125   {
126     return new WebServiceJobHandle(
127         getClientName(), service.getName(), client.getUrl().toString(),
128         jobId);
129   }
130
131   @Override
132   public JobStatus getStatus(WebServiceJobHandle job) throws IOException
133   {
134     var slivkaJob = client.getJob(job.getJobId());
135     return statusMap.getOrDefault(slivkaJob.getStatus(), JobStatus.UNKNOWN);
136   }
137
138   protected static final EnumMap<Job.Status, JobStatus> statusMap = new EnumMap<>(Job.Status.class);
139   static
140   {
141     statusMap.put(Job.Status.PENDING, JobStatus.SUBMITTED);
142     statusMap.put(Job.Status.REJECTED, JobStatus.INVALID);
143     statusMap.put(Job.Status.ACCEPTED, JobStatus.SUBMITTED);
144     statusMap.put(Job.Status.QUEUED, JobStatus.QUEUED);
145     statusMap.put(Job.Status.RUNNING, JobStatus.RUNNING);
146     statusMap.put(Job.Status.COMPLETED, JobStatus.COMPLETED);
147     statusMap.put(Job.Status.INTERRUPTED, JobStatus.CANCELLED);
148     statusMap.put(Job.Status.DELETED, JobStatus.CANCELLED);
149     statusMap.put(Job.Status.FAILED, JobStatus.FAILED);
150     statusMap.put(Job.Status.ERROR, JobStatus.SERVER_ERROR);
151     statusMap.put(Job.Status.UNKNOWN, JobStatus.UNKNOWN);
152   }
153
154   @Override
155   public String getLog(WebServiceJobHandle job) throws IOException
156   {
157     var slivkaJob = client.getJob(job.getJobId());
158     for (var f : slivkaJob.getResults())
159     {
160       if (f.getLabel().equals("log"))
161       {
162         ByteArrayOutputStream stream = new ByteArrayOutputStream();
163         f.writeTo(stream);
164         return stream.toString(StandardCharsets.UTF_8);
165       }
166     }
167     return "";
168   }
169
170   @Override
171   public String getErrorLog(WebServiceJobHandle job) throws IOException
172   {
173     var slivkaJob = client.getJob(job.getJobId());
174     for (var f : slivkaJob.getResults())
175     {
176       if (f.getLabel().equals("error-log"))
177       {
178         ByteArrayOutputStream stream = new ByteArrayOutputStream();
179         f.writeTo(stream);
180         return stream.toString(StandardCharsets.UTF_8);
181       }
182     }
183     return "";
184   }
185
186   @Override
187   public void cancel(WebServiceJobHandle job)
188       throws IOException, UnsupportedOperationException
189   {
190     throw new UnsupportedOperationException(
191         "slivka client does not support job cancellation");
192   }
193 }
194
195 class SlivkaAlignmentWSClient extends SlivkaWSClient
196     implements AlignmentWebServiceClientI
197 {
198
199   SlivkaAlignmentWSClient(SlivkaService service)
200   {
201     super(service);
202   }
203
204   @Override
205   public AlignmentI getAlignment(WebServiceJobHandle job) throws IOException
206   {
207     var slivkaJob = client.getJob(job.getJobId());
208     for (var f : slivkaJob.getResults())
209     {
210       // TODO: restrict result file label to "alignment"
211       FileFormat format;
212       var match = mediaTypePattern.matcher(f.getMediaType());
213       if (!match.find())
214         continue;
215       String fmt = match.group(1);
216       if (fmt.equalsIgnoreCase("clustal"))
217         format = FileFormat.Clustal;
218       else if (fmt.equalsIgnoreCase("fasta"))
219         format = FileFormat.Fasta;
220       else
221         continue;
222       return new FormatAdapter().readFile(f.getContentUrl().toString(),
223           DataSourceType.URL, format);
224     }
225     Cache.log.warn("No alignment found on the server");
226     throw new IOException("no alignment found");
227   }
228
229 }