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