DisEMBL and GlobProt web services further work
[jabaws.git] / webservices / compbio / ws / client / Jws2Client.java
1 /* Copyright (c) 2009 Peter Troshin\r
2  *  \r
3  *  JAva Bioinformatics Analysis Web Services (JABAWS) @version: 1.0     \r
4  * \r
5  *  This library is free software; you can redistribute it and/or modify it under the terms of the\r
6  *  Apache License version 2 as published by the Apache Software Foundation\r
7  * \r
8  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\r
9  *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache \r
10  *  License for more details.\r
11  * \r
12  *  A copy of the license is in apache_license.txt. It is also available here:\r
13  * @see: http://www.apache.org/licenses/LICENSE-2.0.txt\r
14  * \r
15  * Any republication or derived work distributed in source code form\r
16  * must include this copyright and license notice.\r
17  */\r
18 \r
19 package compbio.ws.client;\r
20 \r
21 import static compbio.ws.client.Constraints.hostkey;\r
22 import static compbio.ws.client.Constraints.inputkey;\r
23 import static compbio.ws.client.Constraints.limitList;\r
24 import static compbio.ws.client.Constraints.outputkey;\r
25 import static compbio.ws.client.Constraints.paramFile;\r
26 import static compbio.ws.client.Constraints.paramList;\r
27 import static compbio.ws.client.Constraints.presetList;\r
28 import static compbio.ws.client.Constraints.presetkey;\r
29 import static compbio.ws.client.Constraints.pseparator;\r
30 import static compbio.ws.client.Constraints.servicekey;\r
31 \r
32 import java.io.Closeable;\r
33 import java.io.File;\r
34 import java.io.FileInputStream;\r
35 import java.io.IOException;\r
36 import java.io.OutputStream;\r
37 import java.net.MalformedURLException;\r
38 import java.net.URL;\r
39 import java.util.Arrays;\r
40 import java.util.HashMap;\r
41 import java.util.HashSet;\r
42 import java.util.List;\r
43 import java.util.Map;\r
44 import java.util.logging.Level;\r
45 import java.util.logging.Logger;\r
46 \r
47 import javax.xml.ws.Service;\r
48 import javax.xml.ws.WebServiceException;\r
49 \r
50 import compbio.data.msa.JABAService;\r
51 import compbio.data.msa.Metadata;\r
52 import compbio.data.msa.MsaWS;\r
53 import compbio.data.msa.SequenceAnnotation;\r
54 import compbio.data.sequence.Alignment;\r
55 import compbio.data.sequence.FastaSequence;\r
56 import compbio.data.sequence.Score;\r
57 import compbio.data.sequence.SequenceUtil;\r
58 import compbio.data.sequence.UnknownFileFormatException;\r
59 import compbio.metadata.JobSubmissionException;\r
60 import compbio.metadata.Option;\r
61 import compbio.metadata.Preset;\r
62 import compbio.metadata.ResultNotAvailableException;\r
63 import compbio.metadata.WrongParameterException;\r
64 \r
65 /**\r
66  * A command line client for JAva Bioinformatics Analysis Web Services\r
67  * \r
68  * @author pvtroshin\r
69  * @version 1.0\r
70  */\r
71 public class Jws2Client {\r
72 \r
73         /*\r
74          * Use java.util.Logger instead of log4j logger to reduce the size of the\r
75          * client package\r
76          */\r
77         private static final Logger log = Logger.getLogger(Jws2Client.class\r
78                         .getCanonicalName());\r
79 \r
80         // JABAWS version 1.0 service name\r
81         static final String QUALIFIED_SERVICE_NAME = "http://msa.data.compbio/01/01/2010/";\r
82 \r
83         // JABAWS version 2.0 service name\r
84         static final String V2_QUALIFIED_SERVICE_NAME = "http://msa.data.compbio/01/12/2010/";\r
85 \r
86         /**\r
87          * Attempt to construct the URL object from the string\r
88          * \r
89          * @param urlstr\r
90          * @return true if it succeed false otherwise\r
91          */\r
92         public static boolean validURL(String urlstr) {\r
93                 try {\r
94                         if (urlstr == null || urlstr.trim().length() == 0) {\r
95                                 return false;\r
96                         }\r
97                         new URL(urlstr);\r
98                 } catch (MalformedURLException e) {\r
99                         return false;\r
100                 }\r
101                 return true;\r
102         }\r
103 \r
104         /**\r
105          * Connects to the service and do the job as requested, if something goes\r
106          * wrong reports or/and prints usage help.\r
107          * \r
108          * @param <T>\r
109          *            web service type\r
110          * @param cmd\r
111          *            command line options\r
112          * @throws IOException\r
113          */\r
114         <T> Jws2Client(String[] cmd) throws IOException {\r
115 \r
116                 String hostname = CmdHelper.getHost(cmd);\r
117                 if (hostname == null) {\r
118                         System.out.println("Host name is not provided!");\r
119                         printUsage(1);\r
120                 }\r
121 \r
122                 if (!validURL(hostname)) {\r
123                         System.out.println("Host name is not valid!");\r
124                         printUsage(1);\r
125                 }\r
126                 String serviceName = CmdHelper.getServiceName(cmd);\r
127                 if (serviceName == null) {\r
128                         System.out.println("Service name is no provided!");\r
129                         printUsage(1);\r
130                 }\r
131                 Services service = Services.getService(serviceName);\r
132                 if (service == null) {\r
133                         System.out.println("Service " + serviceName\r
134                                         + " is no supported! Valid values are: "\r
135                                         + Arrays.toString(Services.values()));\r
136                         printUsage(1);\r
137                 }\r
138                 File inputFile = IOHelper.getFile(cmd, inputkey, true);\r
139                 File outFile = IOHelper.getFile(cmd, outputkey, false);\r
140                 File parametersFile = IOHelper.getFile(cmd, paramFile, true);\r
141                 String presetName = CmdHelper.getPresetName(cmd);\r
142 \r
143                 Metadata<T> msaws = (Metadata<T>) connect(hostname, service);\r
144                 Preset<T> preset = null;\r
145                 if (presetName != null) {\r
146                         preset = MetadataHelper.getPreset(msaws, presetName);\r
147                 }\r
148                 List<Option<T>> customOptions = null;\r
149                 if (parametersFile != null) {\r
150                         List<String> prms = IOHelper.loadParameters(parametersFile);\r
151                         customOptions = MetadataHelper.processParameters(prms,\r
152                                         msaws.getRunnerOptions());\r
153                 }\r
154                 Alignment alignment = null;\r
155                 if (inputFile != null) {\r
156                         OutputStream outStream = null;\r
157                         if (outFile != null) {\r
158                                 outStream = IOHelper.getOutStream(outFile);\r
159                         } else {\r
160                                 // this stream is going to be closed later which is fine as\r
161                                 // std.out will not be\r
162                                 outStream = System.out;\r
163                         }\r
164                         if (service.getServiceType() == SequenceAnnotation.class) {\r
165                                 Map<String, HashSet<Score>> result = analize(inputFile,\r
166                                                 ((SequenceAnnotation<T>) msaws), preset, customOptions);\r
167                                 assert result != null && !result.values().isEmpty() : "No Result reported!";\r
168                                 IOHelper.writeOut(outStream, result);\r
169                         } else {\r
170                                 alignment = align(inputFile, (MsaWS<T>) msaws, preset,\r
171                                                 customOptions);\r
172                                 IOHelper.writeOut(outStream, alignment);\r
173                         }\r
174                         outStream.close();\r
175                 }\r
176 \r
177                 boolean listParameters = CmdHelper.listParameters(cmd);\r
178                 if (listParameters) {\r
179                         System.out.println(MetadataHelper.getParametersList(msaws));\r
180                 }\r
181                 boolean listPreset = CmdHelper.listPresets(cmd);\r
182                 if (listPreset) {\r
183                         System.out.println(MetadataHelper.getPresetList(msaws));\r
184                 }\r
185                 boolean listLimits = CmdHelper.listLimits(cmd);\r
186                 if (listLimits) {\r
187                         System.out.println(MetadataHelper.getLimits(msaws));\r
188                 }\r
189                 log.fine("Disconnecting...");\r
190                 ((Closeable) msaws).close();\r
191                 log.fine("Disconnected successfully!");\r
192         }\r
193         /**\r
194          * Calculate conservation for sequences loaded from the file\r
195          * \r
196          * @param wsproxy\r
197          *            a web service proxy\r
198          * @param file\r
199          *            the file to read the results from\r
200          * @param preset\r
201          *            Preset to use optional\r
202          * @param customOptions\r
203          *            the list of options\r
204          * @return Set<Score> the conservation scores\r
205          * @throws UnknownFileFormatException\r
206          */\r
207         <T> HashMap<String, HashSet<Score>> analize(File file,\r
208                         SequenceAnnotation<T> wsproxy, Preset<T> preset,\r
209                         List<Option<T>> customOptions) {\r
210 \r
211                 List<FastaSequence> fastalist = null;\r
212                 HashMap<String, HashSet<Score>> scores = null;\r
213                 try {\r
214                         fastalist = SequenceUtil.openInputStream(file.getAbsolutePath());\r
215 \r
216                         String jobId = null;\r
217                         if (customOptions != null && preset != null) {\r
218                                 System.out\r
219                                                 .println("WARN: Parameters (-f) are defined together with a preset (-r) ignoring preset!");\r
220                         }\r
221                         if (customOptions != null) {\r
222                                 jobId = wsproxy.customAnalize(fastalist, customOptions);\r
223                         } else if (preset != null) {\r
224                                 jobId = wsproxy.presetAnalize(fastalist, preset);\r
225                         } else {\r
226                                 jobId = wsproxy.analize(fastalist);\r
227                         }\r
228                         Thread.sleep(2000);\r
229                         scores = wsproxy.getAnnotation(jobId);\r
230                         assert scores != null && !scores.values().isEmpty() : "Scores are NULL";\r
231 \r
232                 } catch (IOException e) {\r
233                         System.err\r
234                                         .println("Exception while reading the input file. "\r
235                                                         + "Check that the input file contains a list of fasta formatted sequences! "\r
236                                                         + "Exception details are below:");\r
237                         e.printStackTrace();\r
238                 } catch (JobSubmissionException e) {\r
239                         System.err\r
240                                         .println("Exception while submitting job to a web server. "\r
241                                                         + "Exception details are below:");\r
242                         e.printStackTrace();\r
243                 } catch (ResultNotAvailableException e) {\r
244                         System.err.println("Exception while waiting for results. "\r
245                                         + "Exception details are below:");\r
246                         e.printStackTrace();\r
247                 } catch (InterruptedException ignored) {\r
248                         // ignore and propagate an interruption\r
249                         Thread.currentThread().interrupt();\r
250                 } catch (WrongParameterException e) {\r
251                         System.err\r
252                                         .println("Exception while parsing the web method input parameters. "\r
253                                                         + "Exception details are below:");\r
254                         e.printStackTrace();\r
255                 } catch (UnknownFileFormatException e) {\r
256                         System.err\r
257                                         .println("Exception while attempting to read the input file "\r
258                                                         + "Exception details are below:");\r
259                         System.out.println(e.getMessage());\r
260                         e.printStackTrace();\r
261                 }\r
262                 return scores;\r
263         }\r
264 \r
265         /**\r
266          * Connects to a web service by the host and the service name\r
267          * \r
268          * @param T\r
269          *            web service type\r
270          * @param host\r
271          * @param service\r
272          * @return MsaWS<T>\r
273          * @throws WebServiceException\r
274          */\r
275         public static JABAService connect(String host, Services service)\r
276                         throws WebServiceException {\r
277                 URL url = null;\r
278                 log.log(Level.FINE, "Attempting to connect...");\r
279                 try {\r
280                         url = new URL(host + "/" + service.toString() + "?wsdl");\r
281                 } catch (MalformedURLException e) {\r
282                         e.printStackTrace();\r
283                         // ignore as the host name is already verified\r
284                 }\r
285                 Service serv = null;\r
286                 try {\r
287                         serv = service.getService(url, QUALIFIED_SERVICE_NAME);\r
288                 } catch (WebServiceException wse) {\r
289                         System.out.println("Conecting to JABAWS version 2 service");\r
290                         if (isV2service(wse)) {\r
291                                 serv = service.getService(url, V2_QUALIFIED_SERVICE_NAME);\r
292                         }\r
293                 }\r
294                 if (serv == null) {\r
295                         System.err.println("Could not connect to " + url);\r
296                         // FIXME\r
297                 }\r
298                 JABAService serviceIF = service.getInterface(serv);\r
299                 log.log(Level.FINE, "Connected successfully!");\r
300 \r
301                 return serviceIF;\r
302         }\r
303 \r
304         static boolean isV2service(WebServiceException wse) {\r
305                 String message = wse.getMessage();\r
306                 int idx = message.indexOf("not a valid service");\r
307                 if (idx > 0) {\r
308                         if (message.substring(idx).contains(V2_QUALIFIED_SERVICE_NAME)) {\r
309                                 return true;\r
310                         }\r
311                 }\r
312                 return false;\r
313         }\r
314 \r
315         /**\r
316          * Align sequences from the file using MsaWS\r
317          * \r
318          * @param <T>\r
319          *            web service type e.g. Clustal\r
320          * @param file\r
321          *            to write the resulting alignment to\r
322          * @param msaws\r
323          *            MsaWS required\r
324          * @param preset\r
325          *            Preset to use optional\r
326          * @param customOptions\r
327          *            file which contains new line separated list of options\r
328          * @return Alignment\r
329          */\r
330         static <T> Alignment align(File file, MsaWS<T> msaws, Preset<T> preset,\r
331                         List<Option<T>> customOptions) {\r
332                 FileInputStream instream = null;\r
333                 List<FastaSequence> fastalist = null;\r
334                 Alignment alignment = null;\r
335                 try {\r
336                         instream = new FileInputStream(file);\r
337                         fastalist = SequenceUtil.readFasta(instream);\r
338                         instream.close();\r
339                         String jobId = null;\r
340                         if (customOptions != null && preset != null) {\r
341                                 System.out\r
342                                                 .println("WARN: Parameters (-f) are defined together with a preset (-r) ignoring preset!");\r
343                         }\r
344                         if (customOptions != null) {\r
345                                 jobId = msaws.customAlign(fastalist, customOptions);\r
346                         } else if (preset != null) {\r
347                                 jobId = msaws.presetAlign(fastalist, preset);\r
348                         } else {\r
349                                 jobId = msaws.align(fastalist);\r
350                         }\r
351                         Thread.sleep(1000);\r
352                         alignment = msaws.getResult(jobId);\r
353 \r
354                 } catch (IOException e) {\r
355                         System.err\r
356                                         .println("Exception while reading the input file. "\r
357                                                         + "Check that the input file contains a list of fasta formatted sequences! "\r
358                                                         + "Exception details are below:");\r
359                         e.printStackTrace();\r
360                 } catch (JobSubmissionException e) {\r
361                         System.err\r
362                                         .println("Exception while submitting job to a web server. "\r
363                                                         + "Exception details are below:");\r
364                         e.printStackTrace();\r
365                 } catch (ResultNotAvailableException e) {\r
366                         System.err.println("Exception while waiting for results. "\r
367                                         + "Exception details are below:");\r
368                         e.printStackTrace();\r
369                 } catch (InterruptedException ignored) {\r
370                         // ignore and propagate an interruption\r
371                         Thread.currentThread().interrupt();\r
372                 } catch (WrongParameterException e) {\r
373                         e.printStackTrace();\r
374                 } finally {\r
375                         if (instream != null) {\r
376                                 try {\r
377                                         instream.close();\r
378                                 } catch (IOException ignored) {\r
379                                         // ignore\r
380                                 }\r
381                         }\r
382                 }\r
383                 return alignment;\r
384         }\r
385 \r
386         /**\r
387          * Prints Jws2Client usage information to standard out\r
388          * \r
389          * @param exitStatus\r
390          */\r
391         static void printUsage(int exitStatus) {\r
392                 System.out.println();\r
393                 System.out.println("Usage: <Class or Jar file name> " + hostkey\r
394                                 + pseparator + "host_and_context " + servicekey + pseparator\r
395                                 + "serviceName ACTION [OPTIONS] ");\r
396                 System.out.println();\r
397                 System.out\r
398                                 .println(hostkey\r
399                                                 + pseparator\r
400                                                 + "<host_and_context> - a full URL to the JWS2 web server including context path e.g. http://10.31.1.159:8080/ws");\r
401                 System.out.println(servicekey + pseparator + "<ServiceName> - one of "\r
402                                 + Arrays.toString(Services.values()));\r
403                 System.out.println();\r
404                 System.out.println("ACTIONS: ");\r
405                 System.out\r
406                                 .println(inputkey\r
407                                                 + pseparator\r
408                                                 + "<inputFile> - full path to fasta formatted sequence file, from which to align sequences");\r
409                 System.out.println(paramList\r
410                                 + " - lists parameters supported by web service");\r
411                 System.out.println(presetList\r
412                                 + " - lists presets supported by web service");\r
413                 System.out.println(limitList + " - lists web services limits");\r
414                 System.out\r
415                                 .println("Please note that if input file is specified other actions are ignored");\r
416 \r
417                 System.out.println();\r
418                 System.out.println("OPTIONS (only for use with -i action):");\r
419 \r
420                 System.out.println(presetkey + pseparator\r
421                                 + "<presetName> - name of the preset to use");\r
422                 System.out\r
423                                 .println(outputkey\r
424                                                 + pseparator\r
425                                                 + "<outputFile> - full path to the file where to write an alignment");\r
426                 System.out\r
427                                 .println("-f=<parameterInputFile> - the name of the file with the list of parameters to use.");\r
428                 System.out\r
429                                 .println("Please note that -r and -f options cannot be used together. "\r
430                                                 + "Alignment is done with either preset or a parameters from the file, but not both!");\r
431 \r
432                 System.exit(exitStatus);\r
433         }\r
434 \r
435         /**\r
436          * Starts command line client, if no parameter are supported print help. Two\r
437          * parameters are required for successful call the JWS2 host name and a\r
438          * service name.\r
439          * \r
440          * @param args\r
441          *            Usage: <Class or Jar file name> -h=host_and_context\r
442          *            -s=serviceName ACTION [OPTIONS]\r
443          * \r
444          *            -h=<host_and_context> - a full URL to the JWS2 web server\r
445          *            including context path e.g. http://10.31.1.159:8080/ws\r
446          * \r
447          *            -s=<ServiceName> - one of [MafftWS, MuscleWS, ClustalWS,\r
448          *            TcoffeeWS, ProbconsWS] ACTIONS:\r
449          * \r
450          *            -i=<inputFile> - full path to fasta formatted sequence file,\r
451          *            from which to align sequences\r
452          * \r
453          *            -parameters - lists parameters supported by web service\r
454          * \r
455          *            -presets - lists presets supported by web service\r
456          * \r
457          *            -limits - lists web services limits Please note that if input\r
458          *            file is specified other actions are ignored\r
459          * \r
460          *            OPTIONS: (only for use with -i action):\r
461          * \r
462          *            -r=<presetName> - name of the preset to use\r
463          * \r
464          *            -o=<outputFile> - full path to the file where to write an\r
465          *            alignment -f=<parameterInputFile> - the name of the file with\r
466          *            the list of parameters to use. Please note that -r and -f\r
467          *            options cannot be used together. Alignment is done with either\r
468          *            preset or a parameters from the file, but not both!\r
469          * \r
470          */\r
471         public static void main(String[] args) {\r
472 \r
473                 if (args == null) {\r
474                         printUsage(1);\r
475                 }\r
476                 if (args.length < 2) {\r
477                         System.out.println("Host and service names are required!");\r
478                         printUsage(1);\r
479                 }\r
480 \r
481                 try {\r
482                         new Jws2Client(args);\r
483                 } catch (IOException e) {\r
484                         log.log(Level.SEVERE, "IOException in client! " + e.getMessage(),\r
485                                         e.getCause());\r
486                         System.err.println("Cannot write output file! Stack trace: ");\r
487                         e.printStackTrace();\r
488                 }\r
489         }\r
490 }\r