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