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