new services are registered in wsbuild
[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.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                         assert !fastalist.isEmpty() : "Input is empty!";\r
216 \r
217                         String jobId = null;\r
218                         if (customOptions != null && preset != null) {\r
219                                 System.out\r
220                                                 .println("WARN: Parameters (-f) are defined together with a preset (-r) ignoring preset!");\r
221                         }\r
222                         if (customOptions != null) {\r
223                                 jobId = wsproxy.customAnalize(fastalist, customOptions);\r
224                         } else if (preset != null) {\r
225                                 jobId = wsproxy.presetAnalize(fastalist, preset);\r
226                         } else {\r
227                                 System.out.println("\n\ncalling analise.........");\r
228                                 jobId = wsproxy.analize(fastalist);\r
229                         }\r
230                         Thread.sleep(2000);\r
231 \r
232                         scores = wsproxy.getAnnotation(jobId);\r
233                         assert scores != null && !scores.values().isEmpty() : "Scores are NULL for job: "\r
234                                         + jobId;\r
235 \r
236                 } catch (IOException e) {\r
237                         System.err\r
238                                         .println("Exception while reading the input file. "\r
239                                                         + "Check that the input file contains a list of fasta formatted sequences! "\r
240                                                         + "Exception details are below:");\r
241                         e.printStackTrace();\r
242                 } catch (JobSubmissionException e) {\r
243                         System.err\r
244                                         .println("Exception while submitting job to a web server. "\r
245                                                         + "Exception details are below:");\r
246                         e.printStackTrace();\r
247                 } catch (ResultNotAvailableException e) {\r
248                         System.err.println("Exception while waiting for results. "\r
249                                         + "Exception details are below:");\r
250                         e.printStackTrace();\r
251                 } catch (InterruptedException ignored) {\r
252                         // ignore and propagate an interruption\r
253                         Thread.currentThread().interrupt();\r
254                 } catch (WrongParameterException e) {\r
255                         System.err\r
256                                         .println("Exception while parsing the web method input parameters. "\r
257                                                         + "Exception details are below:");\r
258                         e.printStackTrace();\r
259                 } catch (UnknownFileFormatException e) {\r
260                         System.err\r
261                                         .println("Exception while attempting to read the input file "\r
262                                                         + "Exception details are below:");\r
263                         System.out.println(e.getMessage());\r
264                         e.printStackTrace();\r
265                 }\r
266                 return scores;\r
267         }\r
268 \r
269         /**\r
270          * Connects to a web service by the host and the service name\r
271          * \r
272          * @param T\r
273          *            web service type\r
274          * @param host\r
275          * @param service\r
276          * @return MsaWS<T>\r
277          * @throws WebServiceException\r
278          */\r
279         public static JABAService connect(String host, Services service)\r
280                         throws WebServiceException {\r
281                 URL url = null;\r
282                 log.log(Level.FINE, "Attempting to connect...");\r
283                 try {\r
284                         url = new URL(host + "/" + service.toString() + "?wsdl");\r
285                 } catch (MalformedURLException e) {\r
286                         e.printStackTrace();\r
287                         // ignore as the host name is already verified\r
288                 }\r
289                 Service serv = null;\r
290                 try {\r
291                         serv = service.getService(url, QUALIFIED_SERVICE_NAME);\r
292                 } catch (WebServiceException wse) {\r
293                         System.out.println("Conecting to JABAWS version 2 service");\r
294                         if (isV2service(wse)) {\r
295                                 serv = service.getService(url, V2_QUALIFIED_SERVICE_NAME);\r
296                         }\r
297                 }\r
298                 if (serv == null) {\r
299                         System.err.println("Could not connect to " + url\r
300                                         + " the server is down?");\r
301                         // FIXME\r
302                 }\r
303                 JABAService serviceIF = service.getInterface(serv);\r
304                 log.log(Level.INFO, "Connected successfully!");\r
305 \r
306                 return serviceIF;\r
307         }\r
308 \r
309         static boolean isV2service(WebServiceException wse) {\r
310                 String message = wse.getMessage();\r
311                 int idx = message.indexOf("not a valid service");\r
312                 if (idx > 0) {\r
313                         if (message.substring(idx).contains(V2_QUALIFIED_SERVICE_NAME)) {\r
314                                 return true;\r
315                         }\r
316                 }\r
317                 return false;\r
318         }\r
319 \r
320         /**\r
321          * Align sequences from the file using MsaWS\r
322          * \r
323          * @param <T>\r
324          *            web service type e.g. Clustal\r
325          * @param file\r
326          *            to write the resulting alignment to\r
327          * @param msaws\r
328          *            MsaWS required\r
329          * @param preset\r
330          *            Preset to use optional\r
331          * @param customOptions\r
332          *            file which contains new line separated list of options\r
333          * @return Alignment\r
334          */\r
335         static <T> Alignment align(File file, MsaWS<T> msaws, Preset<T> preset,\r
336                         List<Option<T>> customOptions) {\r
337                 FileInputStream instream = null;\r
338                 List<FastaSequence> fastalist = null;\r
339                 Alignment alignment = null;\r
340                 try {\r
341                         instream = new FileInputStream(file);\r
342                         fastalist = SequenceUtil.readFasta(instream);\r
343                         instream.close();\r
344                         String jobId = null;\r
345                         if (customOptions != null && preset != null) {\r
346                                 System.out\r
347                                                 .println("WARN: Parameters (-f) are defined together with a preset (-r) ignoring preset!");\r
348                         }\r
349                         if (customOptions != null) {\r
350                                 jobId = msaws.customAlign(fastalist, customOptions);\r
351                         } else if (preset != null) {\r
352                                 jobId = msaws.presetAlign(fastalist, preset);\r
353                         } else {\r
354                                 jobId = msaws.align(fastalist);\r
355                         }\r
356                         Thread.sleep(1000);\r
357                         alignment = msaws.getResult(jobId);\r
358 \r
359                 } catch (IOException e) {\r
360                         System.err\r
361                                         .println("Exception while reading the input file. "\r
362                                                         + "Check that the input file contains a list of fasta formatted sequences! "\r
363                                                         + "Exception details are below:");\r
364                         e.printStackTrace();\r
365                 } catch (JobSubmissionException e) {\r
366                         System.err\r
367                                         .println("Exception while submitting job to a web server. "\r
368                                                         + "Exception details are below:");\r
369                         e.printStackTrace();\r
370                 } catch (ResultNotAvailableException e) {\r
371                         System.err.println("Exception while waiting for results. "\r
372                                         + "Exception details are below:");\r
373                         e.printStackTrace();\r
374                 } catch (InterruptedException ignored) {\r
375                         // ignore and propagate an interruption\r
376                         Thread.currentThread().interrupt();\r
377                 } catch (WrongParameterException e) {\r
378                         e.printStackTrace();\r
379                 } finally {\r
380                         if (instream != null) {\r
381                                 try {\r
382                                         instream.close();\r
383                                 } catch (IOException ignored) {\r
384                                         // ignore\r
385                                 }\r
386                         }\r
387                 }\r
388                 return alignment;\r
389         }\r
390 \r
391         /**\r
392          * Prints Jws2Client usage information to standard out\r
393          * \r
394          * @param exitStatus\r
395          */\r
396         static void printUsage(int exitStatus) {\r
397                 System.out.println();\r
398                 System.out.println("Usage: <Class or Jar file name> " + hostkey\r
399                                 + pseparator + "host_and_context " + servicekey + pseparator\r
400                                 + "serviceName ACTION [OPTIONS] ");\r
401                 System.out.println();\r
402                 System.out\r
403                                 .println(hostkey\r
404                                                 + pseparator\r
405                                                 + "<host_and_context> - a full URL to the JWS2 web server including context path e.g. http://10.31.1.159:8080/ws");\r
406                 System.out.println(servicekey + pseparator + "<ServiceName> - one of "\r
407                                 + Arrays.toString(Services.values()));\r
408                 System.out.println();\r
409                 System.out.println("ACTIONS: ");\r
410                 System.out\r
411                                 .println(inputkey\r
412                                                 + pseparator\r
413                                                 + "<inputFile> - full path to fasta formatted sequence file, from which to align sequences");\r
414                 System.out.println(paramList\r
415                                 + " - lists parameters supported by web service");\r
416                 System.out.println(presetList\r
417                                 + " - lists presets supported by web service");\r
418                 System.out.println(limitList + " - lists web services limits");\r
419                 System.out\r
420                                 .println("Please note that if input file is specified other actions are ignored");\r
421 \r
422                 System.out.println();\r
423                 System.out.println("OPTIONS (only for use with -i action):");\r
424 \r
425                 System.out.println(presetkey + pseparator\r
426                                 + "<presetName> - name of the preset to use");\r
427                 System.out\r
428                                 .println(outputkey\r
429                                                 + pseparator\r
430                                                 + "<outputFile> - full path to the file where to write an alignment");\r
431                 System.out\r
432                                 .println("-f=<parameterInputFile> - the name of the file with the list of parameters to use.");\r
433                 System.out\r
434                                 .println("Please note that -r and -f options cannot be used together. "\r
435                                                 + "Alignment is done with either preset or a parameters from the file, but not both!");\r
436 \r
437                 System.exit(exitStatus);\r
438         }\r
439 \r
440         /**\r
441          * Starts command line client, if no parameter are supported print help. Two\r
442          * parameters are required for successful call the JWS2 host name and a\r
443          * service name.\r
444          * \r
445          * @param args\r
446          *            Usage: <Class or Jar file name> -h=host_and_context\r
447          *            -s=serviceName ACTION [OPTIONS]\r
448          * \r
449          *            -h=<host_and_context> - a full URL to the JWS2 web server\r
450          *            including context path e.g. http://10.31.1.159:8080/ws\r
451          * \r
452          *            -s=<ServiceName> - one of [MafftWS, MuscleWS, ClustalWS,\r
453          *            TcoffeeWS, ProbconsWS] ACTIONS:\r
454          * \r
455          *            -i=<inputFile> - full path to fasta formatted sequence file,\r
456          *            from which to align sequences\r
457          * \r
458          *            -parameters - lists parameters supported by web service\r
459          * \r
460          *            -presets - lists presets supported by web service\r
461          * \r
462          *            -limits - lists web services limits Please note that if input\r
463          *            file is specified other actions are ignored\r
464          * \r
465          *            OPTIONS: (only for use with -i action):\r
466          * \r
467          *            -r=<presetName> - name of the preset to use\r
468          * \r
469          *            -o=<outputFile> - full path to the file where to write an\r
470          *            alignment -f=<parameterInputFile> - the name of the file with\r
471          *            the list of parameters to use. Please note that -r and -f\r
472          *            options cannot be used together. Alignment is done with either\r
473          *            preset or a parameters from the file, but not both!\r
474          * \r
475          */\r
476         public static void main(String[] args) {\r
477 \r
478                 if (args == null) {\r
479                         printUsage(1);\r
480                 }\r
481                 if (args.length < 2) {\r
482                         System.out.println("Host and service names are required!");\r
483                         printUsage(1);\r
484                 }\r
485 \r
486                 try {\r
487                         new Jws2Client(args);\r
488                 } catch (IOException e) {\r
489                         log.log(Level.SEVERE, "IOException in client! " + e.getMessage(),\r
490                                         e.getCause());\r
491                         System.err.println("Cannot write output file! Stack trace: ");\r
492                         e.printStackTrace();\r
493                 }\r
494         }\r
495 }\r