t2-server 0.2.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  = Taverna[http://www.taverna.org.uk/] 2 Server Interaction Gem
2
2
 
3
3
  Authors:: Robert Haines
4
- Gem Version:: 0.2.1
4
+ Gem Version:: 0.5.0
5
5
  API Version:: 2.2a1
6
6
  Contact:: mailto:rhaines@manchester.ac.uk
7
7
  URL:: http://taverna.sourceforge.net/
@@ -34,17 +34,22 @@ There are two entry points for the T2Server API:
34
34
  * T2Server::Server - Use this if you are providing a web interface to one or
35
35
  more Taverna 2 Server instances.
36
36
 
37
+ In both cases the gem should be initialized by requiring the top level ruby
38
+ file:
39
+ require 't2-server.rb'
40
+
37
41
  See the rdoc for more information.
38
42
 
39
43
  As well as rdoc there are also a couple of example scripts which
40
44
  demonstrate good use of the T2Server API. These are available in the
41
- <tt>bin</tt> directory:
42
- * run_workflow
43
- * server_info
44
- * delete_all_runs
45
+ <tt>bin</tt> directory but are also installed with the library code when the
46
+ gem is installed:
47
+ * t2-run-workflow
48
+ * t2-server-info
49
+ * t2-delete-runs
45
50
  Running any of these scripts with a <tt>-h</tt> or <tt>--help</tt>
46
51
  switch will show how to use them, e.g.:
47
- run_workflow --help
52
+ t2-run-workflow --help
48
53
 
49
54
  == Support
50
55
 
@@ -31,16 +31,21 @@
31
31
  #
32
32
  # Author: Robert Haines
33
33
 
34
- require 't2server'
34
+ require 'rubygems'
35
+ require 't2-server'
35
36
  require 'optparse'
36
37
 
37
38
  # set up options
39
+ delete_all = false
38
40
  opts = OptionParser.new do |opt|
39
- opt.banner = "Usage: delete_all_runs [options] server-address"
41
+ opt.banner = "Usage: t2-delete-runs [options] server-address [run-ids...]"
40
42
  opt.separator ""
41
- opt.separator " Where server-address is the full URI of the server to"
42
- opt.separator " connect to, e.g.: http://example.com:8080/taverna"
43
- opt.separator " and [options] can be:"
43
+ opt.separator " Where server-address is the full URI of the server to connect to,"
44
+ opt.separator " e.g.: http://example.com:8080/taverna, run-ids are the id numbers"
45
+ opt.separator " of the runs you want to delete and [options] can be:"
46
+ opt.on("--all", "Delete all runs on the server") do
47
+ delete_all = true
48
+ end
44
49
  opt.on_tail("-h", "-?", "--help", "Show this message") do
45
50
  puts opt
46
51
  exit
@@ -55,16 +60,53 @@ end
55
60
  # parse options
56
61
  opts.parse!
57
62
 
58
- # check for server address
59
- if ARGV[0] == nil
63
+ # get runs and server address from the arguments
64
+ runs = []
65
+ address = ""
66
+ for arg in ARGV
67
+ if arg.match(/https?:\/\//) == nil
68
+ runs << arg
69
+ else
70
+ address = arg
71
+ end
72
+ end
73
+
74
+ # can't do anything without a server address
75
+ if address == ""
60
76
  puts opts
61
77
  exit 1
62
78
  end
63
79
 
64
- # connect and delete them all!
80
+ # connect...
65
81
  begin
66
- T2Server::Server.connect(ARGV[0]).delete_all_runs
82
+ server = T2Server::Server.connect(ARGV[0])
67
83
  rescue T2Server::T2ServerError => e
68
84
  puts e
69
85
  exit 1
70
86
  end
87
+
88
+ # ...and delete them!
89
+ if delete_all
90
+ begin
91
+ server.delete_all_runs
92
+ rescue T2Server::AuthorizationError => ae
93
+ puts "You are not authorized to delete runs on this server."
94
+ rescue T2Server::T2ServerError => e
95
+ puts "There was a problem while deleting runs. Some may remain on the server."
96
+ end
97
+ else
98
+ for run in runs
99
+ begin
100
+ server.delete_run(run)
101
+ rescue T2Server::RunNotFoundError => rnf
102
+ puts "Run '#{run}' not found - skipping."
103
+ next
104
+ rescue T2Server::AuthorizationError => ae
105
+ puts "You are not authorized to delete run '#{run}' - skipping."
106
+ next
107
+ rescue T2Server::T2ServerError => e
108
+ puts "There was a problem while deleting run '#{run}' - skipping."
109
+ next
110
+ end
111
+ end
112
+ end
@@ -31,39 +31,39 @@
31
31
  #
32
32
  # Author: Robert Haines
33
33
 
34
- require 't2server'
34
+ require 'rubygems'
35
+ require 't2-server'
35
36
  require 'optparse'
36
37
 
37
38
  # go through the outputs and either print the contents
38
39
  # out or save them to a file.
39
40
  # if the output is a list, it appears as a directory so
40
41
  # all the individual entries must be grabbed from there.
41
- def get_outputs(run, pout, dir="")
42
- dirs, files = run.ls("out#{dir}")
43
- dirs.each {|d| get_outputs(run, pout, "#{dir}/#{d}")}
44
- files.each do |out|
45
- print " #{dir}/#{out} -> "
46
- data = run.get_output("#{dir}/#{out}")
47
- if pout
48
- p data
49
- else
50
- # strip path and convert remove /'s for file output
51
- filename = "#{dir.strip_path}/#{out}".gsub('/', '-')
52
- File.open(filename, "w") do |file|
53
- file.syswrite(data)
54
- end
55
- puts "written to file: #{filename}"
56
- end
42
+ def get_outputs(run, refs)
43
+ # get a list of the outputs
44
+ lists, items = run.ls("out")
45
+
46
+ # go through the lists
47
+ lists.each do |l|
48
+ print " #{l} -> "
49
+ p run.get_output("#{l}", refs)
50
+ end
51
+
52
+ # go through the singletons
53
+ items.each do |i|
54
+ print " #{i} -> "
55
+ p run.get_output("#{i}", refs)
57
56
  end
58
- end
57
+ end
59
58
 
60
59
  # set up options
61
60
  inputs = {}
62
61
  wkf_file = ""
63
- print_output = true
62
+ output_refs = false
64
63
  baclava_out = ""
64
+ delete_run = false
65
65
  opts = OptionParser.new do |opt|
66
- opt.banner = "Usage: run_workflow [options] server-address"
66
+ opt.banner = "Usage: t2-run-workflow [options] server-address"
67
67
  opt.separator ""
68
68
  opt.separator " Where server-address is the full URI of the server to"
69
69
  opt.separator " connect to, e.g.: http://example.com:8080/taverna"
@@ -73,7 +73,7 @@ opts = OptionParser.new do |opt|
73
73
  wkf_file = val
74
74
  end
75
75
  opt.on("-i INPUT:VALUE", "--input=INPUT:VALUE", "Set input port INPUT to VALUE") do |val|
76
- input, value = val.chomp.split(':')
76
+ input, value = val.chomp.split(':', 2)
77
77
  inputs[input] = value
78
78
  end
79
79
  opt.on("-b BACLAVA", "--baclava-in=BACLAVA", "Set baclava file for input port values") do |val|
@@ -87,9 +87,14 @@ opts = OptionParser.new do |opt|
87
87
  baclava_out = "out.xml"
88
88
  end
89
89
  end
90
- opt.on("-p", "--[no-]print", "Print outputs to the console. On by default " +
91
- "unless baclava output is selected") do |val|
92
- print_output = val
90
+ opt.on("-r", "--output-refs", "Return URIs that point to the data items " +
91
+ "of the output rather than the data items themselves.") do |val|
92
+ output_refs = val
93
+ end
94
+ opt.on("-D", "--delete", "Delete the run from the server when it is " +
95
+ "complete. By default the run and its results are preserved. Note that " +
96
+ "the run will still be deleted when its expiry time is reached") do |val|
97
+ delete_run = val
93
98
  end
94
99
  opt.on_tail("-h", "-?", "--help", "Show this message") do
95
100
  puts opt
@@ -166,10 +171,12 @@ if exitcd == 0
166
171
  puts "Baclava file written to '#{baclava_out}'"
167
172
  else
168
173
  puts "Outputs:"
169
- get_outputs(run, print_output)
174
+ get_outputs(run, output_refs)
170
175
  end
171
176
  end
172
177
 
173
- # delete run
174
- run.delete
175
- puts "Finished and deleted run"
178
+ # delete run?
179
+ if delete_run
180
+ run.delete
181
+ puts "Run deleted"
182
+ end
@@ -31,11 +31,12 @@
31
31
  #
32
32
  # Author: Robert Haines
33
33
 
34
- require 't2server'
34
+ require 'rubygems'
35
+ require 't2-server'
35
36
  require 'optparse'
36
37
 
37
38
  opts = OptionParser.new do |opt|
38
- opt.banner = "Usage: server_info [options] server-address"
39
+ opt.banner = "Usage: t2-server-info [options] server-address"
39
40
  opt.separator ""
40
41
  opt.separator " Where server-address is the full URI of the server to"
41
42
  opt.separator " connect to, e.g.: http://example.com:8080/taverna"
@@ -0,0 +1,71 @@
1
+ # Copyright (c) 2010, The University of Manchester, UK.
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # * Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # * Neither the names of The University of Manchester nor the names of its
16
+ # contributors may be used to endorse or promote products derived from this
17
+ # software without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ # POSSIBILITY OF SUCH DAMAGE.
30
+ #
31
+ # Author: Robert Haines
32
+
33
+ require 't2-server/xml'
34
+ require 't2-server/exceptions'
35
+ require 't2-server/server'
36
+ require 't2-server/run'
37
+
38
+ # This is a Ruby library to interface with the Taverna 2 Server REST API.
39
+ #
40
+ # There are two API entry points:
41
+ # * T2Server::Run - Use this for running single jobs on a server.
42
+ # * T2Server::Server - Use this if you are providing a web interface to a
43
+ # Taverna 2 Server instance.
44
+ module T2Server
45
+ # The version of this library
46
+ GEM_VERSION = "0.5.0"
47
+ # The version of the Taverna 2 Server API that this library can interface with
48
+ API_VERSION = "2.2a1"
49
+ end
50
+
51
+ # Add methods to the String class to operate on file paths.
52
+ class String
53
+ # :call-seq:
54
+ # str.strip_path -> string
55
+ #
56
+ # Returns a new String with one leading and one trailing slash
57
+ # removed from the ends of _str_ (if present).
58
+ def strip_path
59
+ self.gsub(/^\//, "").chomp("/")
60
+ end
61
+
62
+ # :call-seq:
63
+ # str.strip_path! -> str or nil
64
+ #
65
+ # Modifies _str_ in place as described for String#strip_path,
66
+ # returning _str_, or returning +nil+ if no modifications were made.
67
+ def strip_path!
68
+ g = self.gsub!(/^\//, "")
69
+ self.chomp!("/") || g
70
+ end
71
+ end
@@ -33,11 +33,12 @@
33
33
  require 'net/http'
34
34
 
35
35
  module T2Server
36
+ # :stopdoc:
36
37
  # An internal module to collect all the exceptions that we
37
38
  # can't really do anything about ourselves, such as
38
39
  # timeouts and lost connections. This is further wrapped
39
40
  # and exposed in the API as T2Server::ConnectionError below.
40
- module InternalHTTPError #:nodoc:
41
+ module InternalHTTPError
41
42
  end
42
43
 
43
44
  # These are the HTTP errors we want to catch.
@@ -55,6 +56,7 @@ module T2Server
55
56
  Net::ProtocolError
56
57
  ].each {|err| err.send(:include, InternalHTTPError)}
57
58
 
59
+ # :startdoc:
58
60
  # This is a superclass for all T2Server exceptions. It is provided as a
59
61
  # useful catch-all for all the internally raised/thrown exceptions.
60
62
  class T2ServerError < RuntimeError
@@ -30,7 +30,8 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 'rexml/document'
33
+ require 'rubygems'
34
+ require 'libxml'
34
35
 
35
36
  module T2Server
36
37
 
@@ -44,6 +45,8 @@ module T2Server
44
45
  # * Finished: The run has finished running and its outputs are available for
45
46
  # download.
46
47
  class Run
48
+ include LibXML
49
+
47
50
  private_class_method :new
48
51
 
49
52
  # The identifier of this run on the server. It is currently a UUID
@@ -134,18 +137,53 @@ module T2Server
134
137
  end
135
138
 
136
139
  # :call-seq:
137
- # run.get_output(output) -> string
138
- #
139
- # Return the value of the workflow output port _output_.
140
- #
141
- # Raises RunStateError if the run is not in the +Finished+ state.
142
- def get_output(output, type="text/plain")
143
- state = status
144
- raise RunStateError.new(state, STATE[:finished]) if state != STATE[:finished]
145
-
140
+ # run.get_output(output, refs=false) -> list
141
+ #
142
+ # Return the values of the workflow output port _output_. These are
143
+ # returned as a list of strings. If the output port represents a singleton
144
+ # output then a one item list is returned. By default this method returns
145
+ # the actual data from the output port but if _refs_ is set to true then
146
+ # it will instead return URIs to the actual data in the same list format.
147
+ # See also Run#get_output_refs.
148
+ def get_output(output, refs=false)
146
149
  output.strip_path!
147
- doc = @server.get_run_attribute(@uuid, "#{@links[:wdir]}/out/#{output}")
148
- doc
150
+ result = []
151
+
152
+ # look at the contents of the output port
153
+ lists, items = ls("out/#{output}")
154
+
155
+ # if lists and items are empty then it's a single value
156
+ if lists == [] and items == []
157
+ if refs
158
+ result << "#{@server.uri}/rest/runs/#{@uuid}/#{@links[:wdir]}/out/#{output}"
159
+ else
160
+ result << @server.get_run_attribute(@uuid, "#{@links[:wdir]}/out/#{output}")
161
+ end
162
+ end
163
+
164
+ # for each list recurse into it and add the items to the result
165
+ lists.each {|list| result << get_output("#{output}/#{list}", refs)}
166
+
167
+ # for each item, add it to the output list
168
+ items.each do |item|
169
+ if refs
170
+ result << "#{@server.uri}/rest/runs/#{@uuid}/#{@links[:wdir]}/out/#{output}/#{item}"
171
+ else
172
+ result << @server.get_run_attribute(@uuid, "#{@links[:wdir]}/out/#{output}/#{item}")
173
+ end
174
+ end
175
+
176
+ result
177
+ end
178
+
179
+ # :call-seq:
180
+ # run.get_output_refs(output) -> list
181
+ #
182
+ # Return references (URIs) to the values of the workflow output port
183
+ # _output_. These are returned as a list of URIs. If the output port
184
+ # represents a singleton output then a one item list is returned.
185
+ def get_output_refs(output)
186
+ get_output(output, true)
149
187
  end
150
188
 
151
189
  # :call-seq:
@@ -355,19 +393,37 @@ module T2Server
355
393
  # run.ls(dir="") -> [[dirs], [objects]]
356
394
  #
357
395
  # List a directory in the run's workspace on the server. If _dir_ is left
358
- # blank then / is listed. The contents of a directory are returned as a
359
- # list of two lists, directories and "objects" respectively.
396
+ # blank then / is listed. As there is no concept of changing into a
397
+ # directory (_cd_) in Taverna Server then all paths passed into _ls_
398
+ # should be full paths starting at "root". The contents of a directory are
399
+ # returned as a list of two lists, "directories" and "objects"
400
+ # respectively. In the case of listing the contents of the "out"
401
+ # directory, the "directories" returned by _ls_ are actually output port
402
+ # names and their contents are the values held by these ports. If there
403
+ # are multiple values listed then that port represents a list. If there
404
+ # are further directories below a port name then it is a list of lists.
360
405
  def ls(dir="")
361
406
  dir.strip_path!
362
407
  dir_list = @server.get_run_attribute(@uuid, "#{@links[:wdir]}/#{dir}")
363
- doc = REXML::Document.new(dir_list)
364
408
 
365
409
  # compile a list of directory entries stripping the
366
410
  # directory name from the front of each filename
367
411
  dirs = []
368
412
  files = []
369
- REXML::XPath.each(doc, "//nss:dir", Namespaces::MAP) {|e| dirs << e.text.split('/')[-1]}
370
- REXML::XPath.each(doc, "//nss:file", Namespaces::MAP) {|e| files << e.text.split('/')[-1]}
413
+
414
+ begin
415
+ doc = XML::Document.string(dir_list)
416
+
417
+ doc.find(XPaths::DIR, Namespaces::MAP).each {|e| dirs << e.content.split('/')[-1]}
418
+ doc.find(XPaths::FILE, Namespaces::MAP).each {|e| files << e.content.split('/')[-1]}
419
+ rescue XML::Error => xmle
420
+ # We expect to get a DOCUMENT_EMPTY error in some cases. All others
421
+ # should be re-raised.
422
+ if xmle.code != XML::Error::DOCUMENT_EMPTY
423
+ raise xmle
424
+ end
425
+ end
426
+
371
427
  [dirs, files]
372
428
  end
373
429
 
@@ -426,9 +482,9 @@ module T2Server
426
482
 
427
483
  # get inputs
428
484
  inputs = @server.get_run_attribute(@uuid, links[:inputs])
429
- doc = REXML::Document.new(inputs)
485
+ doc = XML::Document.string(inputs)
430
486
  nsmap = Namespaces::MAP
431
- links[:baclava] = "#{links[:inputs]}/" + REXML::XPath.first(doc, "//nsr:baclava", nsmap).attributes["href"].split('/')[-1]
487
+ links[:baclava] = "#{links[:inputs]}/" + doc.find_first(XPaths::BACLAVA, nsmap).attributes["href"].split('/')[-1]
432
488
 
433
489
  # set io properties
434
490
  links[:io] = "#{links[:listeners]}/io"
@@ -440,20 +496,20 @@ module T2Server
440
496
  end
441
497
 
442
498
  def parse_description(desc)
443
- doc = REXML::Document.new(desc)
499
+ doc = XML::Document.string(desc)
444
500
  nsmap = Namespaces::MAP
445
501
  {
446
- :expiry => REXML::XPath.first(doc, "//nsr:expiry", nsmap).attributes["href"].split('/')[-1],
447
- :workflow => REXML::XPath.first(doc, "//nsr:creationWorkflow", nsmap).attributes["href"].split('/')[-1],
448
- :status => REXML::XPath.first(doc, "//nsr:status", nsmap).attributes["href"].split('/')[-1],
449
- :createtime => REXML::XPath.first(doc, "//nsr:createTime", nsmap).attributes["href"].split('/')[-1],
450
- :starttime => REXML::XPath.first(doc, "//nsr:startTime", nsmap).attributes["href"].split('/')[-1],
451
- :finishtime => REXML::XPath.first(doc, "//nsr:finishTime", nsmap).attributes["href"].split('/')[-1],
452
- :wdir => REXML::XPath.first(doc, "//nsr:workingDirectory", nsmap).attributes["href"].split('/')[-1],
453
- :inputs => REXML::XPath.first(doc, "//nsr:inputs", nsmap).attributes["href"].split('/')[-1],
454
- :output => REXML::XPath.first(doc, "//nsr:output", nsmap).attributes["href"].split('/')[-1],
455
- :securectx => REXML::XPath.first(doc, "//nsr:securityContext", nsmap).attributes["href"].split('/')[-1],
456
- :listeners => REXML::XPath.first(doc, "//nsr:listeners", nsmap).attributes["href"].split('/')[-1]
502
+ :expiry => doc.find_first(XPaths::EXPIRY, nsmap).attributes["href"].split('/')[-1],
503
+ :workflow => doc.find_first(XPaths::WORKFLOW, nsmap).attributes["href"].split('/')[-1],
504
+ :status => doc.find_first(XPaths::STATUS, nsmap).attributes["href"].split('/')[-1],
505
+ :createtime => doc.find_first(XPaths::CREATETIME, nsmap).attributes["href"].split('/')[-1],
506
+ :starttime => doc.find_first(XPaths::STARTTIME, nsmap).attributes["href"].split('/')[-1],
507
+ :finishtime => doc.find_first(XPaths::FINISHTIME, nsmap).attributes["href"].split('/')[-1],
508
+ :wdir => doc.find_first(XPaths::WDIR, nsmap).attributes["href"].split('/')[-1],
509
+ :inputs => doc.find_first(XPaths::INPUTS, nsmap).attributes["href"].split('/')[-1],
510
+ :output => doc.find_first(XPaths::OUTPUT, nsmap).attributes["href"].split('/')[-1],
511
+ :securectx => doc.find_first(XPaths::SECURECTX, nsmap).attributes["href"].split('/')[-1],
512
+ :listeners => doc.find_first(XPaths::LISTENERS, nsmap).attributes["href"].split('/')[-1]
457
513
  }
458
514
  end
459
515
  end
@@ -30,16 +30,19 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
+ require 'rubygems'
33
34
  require 'base64'
34
35
  require 'uri'
35
36
  require 'net/https'
36
- require 'rexml/document'
37
+ require 'libxml'
37
38
 
38
39
  module T2Server
39
40
 
40
41
  # An interface for directly communicating with one or more Taverna 2 Server
41
42
  # instances.
42
43
  class Server
44
+ include LibXML
45
+
43
46
  private_class_method :new
44
47
 
45
48
  # The URI of this server instance as a String.
@@ -174,11 +177,17 @@ module T2Server
174
177
  end
175
178
 
176
179
  # :call-seq:
177
- # server.delete_run(uuid) -> bool
180
+ # server.delete_run(run) -> bool
178
181
  #
179
182
  # Delete the specified run from the server, discarding all of its state.
180
- def delete_run(uuid)
181
- request = Net::HTTP::Delete.new("#{@links[:runs]}/#{uuid}")
183
+ # _run_ can be either a Run instance or a UUID.
184
+ def delete_run(run)
185
+ # get the uuid from the run if that is what is passed in
186
+ if run.instance_of? Run
187
+ run = run.uuid
188
+ end
189
+
190
+ request = Net::HTTP::Delete.new("#{@links[:runs]}/#{run}")
182
191
  if ssl?
183
192
  request.basic_auth @username, @password
184
193
  end
@@ -191,12 +200,12 @@ module T2Server
191
200
  case response
192
201
  when Net::HTTPNoContent
193
202
  # Success, carry on...
194
- @runs.delete(uuid)
203
+ @runs.delete(run)
195
204
  true
196
205
  when Net::HTTPNotFound
197
- raise RunNotFoundError.new(uuid)
206
+ raise RunNotFoundError.new(run)
198
207
  when Net::HTTPForbidden
199
- raise AccessForbiddenError.new("run #{uuid}")
208
+ raise AccessForbiddenError.new("run #{run}")
200
209
  when Net::HTTPUnauthorized
201
210
  raise AuthorizationError.new(@username)
202
211
  else
@@ -216,15 +225,21 @@ module T2Server
216
225
  # :call-seq:
217
226
  # server.set_run_input(run, input, value) -> bool
218
227
  #
219
- # Set the workflow input port _input_ on run _run_ to _value_.
228
+ # Set the workflow input port _input_ on run _run_ to _value_. _run_ can
229
+ # be either a Run instance or a UUID.
220
230
  def set_run_input(run, input, value)
221
- path = "#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}"
231
+ # get the run from the uuid if that is what is passed in
232
+ if not run.instance_of? Run
233
+ run = run(run)
234
+ end
235
+
236
+ path = "#{@links[:runs]}/#{run.run}/#{run.inputs}/input/#{input}"
222
237
  set_attribute(path, Fragments::RUNINPUTVALUE % value, "application/xml")
223
238
  rescue AttributeNotFoundError => e
224
- if get_runs.has_key? uuid
239
+ if get_runs.has_key? run.uuid
225
240
  raise e
226
241
  else
227
- raise RunNotFoundError.new(uuid)
242
+ raise RunNotFoundError.new(run.uuid)
228
243
  end
229
244
  end
230
245
 
@@ -232,26 +247,36 @@ module T2Server
232
247
  # server.set_run_input_file(run, input, filename) -> bool
233
248
  #
234
249
  # Set the workflow input port _input_ on run _run_ to use the file at
235
- # _filename_ for its input.
250
+ # _filename_ for its input. _run_ can be either a Run instance or a UUID.
236
251
  def set_run_input_file(run, input, filename)
252
+ # get the run from the uuid if that is what is passed in
253
+ if not run.instance_of? Run
254
+ run = run(run)
255
+ end
256
+
237
257
  path = "#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}"
238
258
  set_attribute(path, Fragments::RUNINPUTFILE % filename, "application/xml")
239
259
  rescue AttributeNotFoundError => e
240
- if get_runs.has_key? uuid
260
+ if get_runs.has_key? run.uuid
241
261
  raise e
242
262
  else
243
- raise RunNotFoundError.new(uuid)
263
+ raise RunNotFoundError.new(run.uuid)
244
264
  end
245
265
  end
246
266
 
247
267
  # :call-seq:
248
- # server.make_run_dir(uuid, root, dir) -> bool
268
+ # server.make_run_dir(run, root, dir) -> bool
249
269
  #
250
- # Create a directory _dir_ within the directory _root_ on the run with
251
- # identifier _uuid_. This is mainly for use by Run#mkdir.
252
- def make_run_dir(uuid, root, dir)
270
+ # Create a directory _dir_ within the directory _root_ on _run_. _run_ can
271
+ # be either a Run instance or a UUID. This is mainly for use by Run#mkdir.
272
+ def make_run_dir(run, root, dir)
273
+ # get the uuid from the run if that is what is passed in
274
+ if run.instance_of? Run
275
+ run = run.uuid
276
+ end
277
+
253
278
  raise AccessForbiddenError.new("subdirectories (#{dir})") if dir.include? ?/
254
- request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{root}")
279
+ request = Net::HTTP::Post.new("#{@links[:runs]}/#{run}/#{root}")
255
280
  request.content_type = "application/xml"
256
281
  if ssl?
257
282
  request.basic_auth @username, @password
@@ -267,9 +292,9 @@ module T2Server
267
292
  # OK, carry on...
268
293
  true
269
294
  when Net::HTTPNotFound
270
- raise RunNotFoundError.new(uuid)
295
+ raise RunNotFoundError.new(run)
271
296
  when Net::HTTPForbidden
272
- raise AccessForbiddenError.new("#{dir} on run #{uuid}")
297
+ raise AccessForbiddenError.new("#{dir} on run #{run}")
273
298
  when Net::HTTPUnauthorized
274
299
  raise AuthorizationError.new(@username)
275
300
  else
@@ -278,14 +303,19 @@ module T2Server
278
303
  end
279
304
 
280
305
  # :call-seq:
281
- # server.upload_run_file(uuid, filename, location, rename) -> string
306
+ # server.upload_run_file(run, filename, location, rename) -> string
282
307
  #
283
- # Upload a file to the run with identifier _uuid_. Mainly for internal use
284
- # by Run#upload_file.
285
- def upload_run_file(uuid, filename, location, rename)
308
+ # Upload a file to _run_. _run_ can be either a Run instance or a UUID.
309
+ # Mainly for internal use by Run#upload_file.
310
+ def upload_run_file(run, filename, location, rename)
311
+ # get the uuid from the run if that is what is passed in
312
+ if run.instance_of? Run
313
+ run = run.uuid
314
+ end
315
+
286
316
  contents = Base64.encode64(IO.read(filename))
287
317
  rename = filename.split('/')[-1] if rename == ""
288
- request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{location}")
318
+ request = Net::HTTP::Post.new("#{@links[:runs]}/#{run}/#{location}")
289
319
  request.content_type = "application/xml"
290
320
  if ssl?
291
321
  request.basic_auth @username, @password
@@ -301,9 +331,9 @@ module T2Server
301
331
  # Success, return remote name of uploaded file
302
332
  rename
303
333
  when Net::HTTPNotFound
304
- raise RunNotFoundError.new(uuid)
334
+ raise RunNotFoundError.new(run)
305
335
  when Net::HTTPForbidden
306
- raise AccessForbiddenError.new("run #{uuid}")
336
+ raise AccessForbiddenError.new("run #{run}")
307
337
  when Net::HTTPUnauthorized
308
338
  raise AuthorizationError.new(@username)
309
339
  else
@@ -312,30 +342,42 @@ module T2Server
312
342
  end
313
343
 
314
344
  # :call-seq:
315
- # server.get_run_attribute(uuid, path) -> string
345
+ # server.get_run_attribute(run, path) -> string
316
346
  #
317
- # Get the attribute at _path_ in the run with identifier _uuid_.
318
- def get_run_attribute(uuid, path)
319
- get_attribute("#{@links[:runs]}/#{uuid}/#{path}")
347
+ # Get the attribute at _path_ in _run_. _run_ can be either a Run instance
348
+ # or a UUID.
349
+ def get_run_attribute(run, path)
350
+ # get the uuid from the run if that is what is passed in
351
+ if run.instance_of? Run
352
+ run = run.uuid
353
+ end
354
+
355
+ get_attribute("#{@links[:runs]}/#{run}/#{path}")
320
356
  rescue AttributeNotFoundError => e
321
- if get_runs.has_key? uuid
357
+ if get_runs.has_key? run
322
358
  raise e
323
359
  else
324
- raise RunNotFoundError.new(uuid)
360
+ raise RunNotFoundError.new(run)
325
361
  end
326
362
  end
327
363
 
328
364
  # :call-seq:
329
- # server.set_run_attribute(uuid, path, value) -> bool
365
+ # server.set_run_attribute(run, path, value) -> bool
330
366
  #
331
- # Set the attribute at _path_ in the run with identifier _uuid_ to _value_.
332
- def set_run_attribute(uuid, path, value)
333
- set_attribute("#{@links[:runs]}/#{uuid}/#{path}", value, "text/plain")
367
+ # Set the attribute at _path_ in _run_ to _value_. _run_ can be either a
368
+ # Run instance or a UUID.
369
+ def set_run_attribute(run, path, value)
370
+ # get the uuid from the run if that is what is passed in
371
+ if run.instance_of? Run
372
+ run = run.uuid
373
+ end
374
+
375
+ set_attribute("#{@links[:runs]}/#{run}/#{path}", value, "text/plain")
334
376
  rescue AttributeNotFoundError => e
335
- if get_runs.has_key? uuid
377
+ if get_runs.has_key? run
336
378
  raise e
337
379
  else
338
- raise RunNotFoundError.new(uuid)
380
+ raise RunNotFoundError.new(run)
339
381
  end
340
382
  end
341
383
 
@@ -393,24 +435,24 @@ module T2Server
393
435
  end
394
436
 
395
437
  def parse_description(desc)
396
- doc = REXML::Document.new(desc)
438
+ doc = XML::Document.string(desc)
397
439
  nsmap = Namespaces::MAP
398
440
  {
399
- :runs => URI.parse(REXML::XPath.first(doc, "//nsr:runs", nsmap).attributes["href"]).path,
400
- :runlimit => URI.parse(REXML::XPath.first(doc, "//nsr:runLimit", nsmap).attributes["href"]).path,
401
- :permworkflows => URI.parse(REXML::XPath.first(doc, "//nsr:permittedWorkflows", nsmap).attributes["href"]).path,
402
- :permlisteners => URI.parse(REXML::XPath.first(doc, "//nsr:permittedListeners", nsmap).attributes["href"]).path
441
+ :runs => URI.parse(doc.find_first(XPaths::RUNS, nsmap).attributes["href"]).path,
442
+ :runlimit => URI.parse(doc.find_first(XPaths::RUNLIMIT, nsmap).attributes["href"]).path,
443
+ :permworkflows => URI.parse(doc.find_first(XPaths::PERMWKF, nsmap).attributes["href"]).path,
444
+ :permlisteners => URI.parse(doc.find_first(XPaths::PERMLSTN, nsmap).attributes["href"]).path
403
445
  }
404
446
  end
405
447
 
406
448
  def get_runs
407
449
  run_list = get_attribute("#{@links[:runs]}")
408
450
 
409
- doc = REXML::Document.new(run_list)
451
+ doc = XML::Document.string(run_list)
410
452
 
411
453
  # get list of run uuids
412
454
  uuids = []
413
- REXML::XPath.each(doc, "//nsr:run", Namespaces::MAP) do |run|
455
+ doc.find(XPaths::RUN, Namespaces::MAP).each do |run|
414
456
  uuids << run.attributes["href"].split('/')[-1]
415
457
  end
416
458
 
@@ -30,7 +30,11 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
+ require 'rubygems'
34
+ require 'libxml'
35
+
33
36
  module T2Server
37
+ # :stopdoc:
34
38
  module Namespaces
35
39
  SERVER = "http://ns.taverna.org.uk/2010/xml/server/"
36
40
  REST = SERVER + "rest/"
@@ -39,7 +43,7 @@ module T2Server
39
43
  "nsr" => Namespaces::REST
40
44
  }
41
45
  end
42
-
46
+
43
47
  module Fragments
44
48
  WORKFLOW = "<t2s:workflow xmlns:t2s=\"#{Namespaces::SERVER}\">\n %s\n</t2s:workflow>"
45
49
  RUNINPUT = "<t2sr:runInput xmlns:t2sr=\"#{Namespaces::REST}\">\n %s\n</t2sr:runInput>"
@@ -48,4 +52,35 @@ module T2Server
48
52
  UPLOAD = "<t2sr:upload xmlns:t2sr=\"#{Namespaces::REST}\" t2sr:name=\"%s\">\n %s\n</t2sr:upload>"
49
53
  MKDIR = "<t2sr:mkdir xmlns:t2sr=\"#{Namespaces::REST}\" t2sr:name=\"%s\" />"
50
54
  end
55
+
56
+ module XPaths
57
+ include LibXML
58
+
59
+ # Shut the libxml error handler up
60
+ XML::Error.set_handler(&XML::Error::QUIET_HANDLER)
61
+
62
+ # Server XPath queries
63
+ RUN = XML::XPath::Expression.new("//nsr:run")
64
+ RUNS = XML::XPath::Expression.new("//nsr:runs")
65
+ RUNLIMIT = XML::XPath::Expression.new("//nsr:runLimit")
66
+ PERMWKF = XML::XPath::Expression.new("//nsr:permittedWorkflows")
67
+ PERMLSTN = XML::XPath::Expression.new("//nsr:permittedListeners")
68
+
69
+ # Run XPath queries
70
+ DIR = XML::XPath::Expression.new("//nss:dir")
71
+ FILE = XML::XPath::Expression.new("//nss:file")
72
+ EXPIRY = XML::XPath::Expression.new("//nsr:expiry")
73
+ WORKFLOW = XML::XPath::Expression.new("//nsr:creationWorkflow")
74
+ STATUS = XML::XPath::Expression.new("//nsr:status")
75
+ CREATETIME = XML::XPath::Expression.new("//nsr:createTime")
76
+ STARTTIME = XML::XPath::Expression.new("//nsr:startTime")
77
+ FINISHTIME = XML::XPath::Expression.new("//nsr:finishTime")
78
+ WDIR = XML::XPath::Expression.new("//nsr:workingDirectory")
79
+ INPUTS = XML::XPath::Expression.new("//nsr:inputs")
80
+ OUTPUT = XML::XPath::Expression.new("//nsr:output")
81
+ SECURECTX = XML::XPath::Expression.new("//nsr:securityContext")
82
+ LISTENERS = XML::XPath::Expression.new("//nsr:listeners")
83
+ BACLAVA = XML::XPath::Expression.new("//nsr:baclava")
84
+ end
85
+ # :startdoc:
51
86
  end
@@ -30,42 +30,6 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 't2server/xml'
34
- require 't2server/exceptions'
35
- require 't2server/server'
36
- require 't2server/run'
37
-
38
- # This is a Ruby library to interface with the Taverna 2 Server REST API.
39
- #
40
- # There are two API entry points:
41
- # * T2Server::Run - Use this for running single jobs on a server.
42
- # * T2Server::Server - Use this if you are providing a web interface to a
43
- # Taverna 2 Server instance.
44
- module T2Server
45
- # The version of this library
46
- GEM_VERSION = "0.2.1"
47
- # The version of the Taverna 2 Server API that this library can interface with
48
- API_VERSION = "2.2a1"
49
- end
50
-
51
- # Add methods to the String class to operate on file paths.
52
- class String
53
- # :call-seq:
54
- # str.strip_path -> string
55
- #
56
- # Returns a new String with one leading and one trailing slash
57
- # removed from the ends of _str_ (if present).
58
- def strip_path
59
- self.gsub(/^\//, "").chomp("/")
60
- end
61
-
62
- # :call-seq:
63
- # str.strip_path! -> str or nil
64
- #
65
- # Modifies _str_ in place as described for String#strip_path,
66
- # returning _str_, or returning +nil+ if no modifications were made.
67
- def strip_path!
68
- g = self.gsub!(/^\//, "")
69
- self.chomp!("/") || g
70
- end
71
- end
33
+ # This is simply here to provide backwards compatibility. Old versions had an
34
+ # inconsistancy between the gem name and the file to require to use it.
35
+ require 't2-server'
@@ -30,7 +30,7 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 't2server'
33
+ require 't2-server'
34
34
  require 'test/unit'
35
35
 
36
36
  class TestFilePaths < Test::Unit::TestCase
@@ -30,7 +30,7 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 't2server'
33
+ require 't2-server'
34
34
 
35
35
  class TestRun < Test::Unit::TestCase
36
36
 
@@ -41,9 +41,6 @@ class TestRun < Test::Unit::TestCase
41
41
  end
42
42
 
43
43
  # test bad state code
44
- assert_raise(T2Server::RunStateError) do
45
- @run.get_output("out")
46
- end
47
44
  assert_raise(T2Server::RunStateError) do
48
45
  @run.wait
49
46
  end
@@ -61,7 +58,7 @@ class TestRun < Test::Unit::TestCase
61
58
 
62
59
  # exitcode and output
63
60
  assert_instance_of(Fixnum, @run.exitcode)
64
- assert_equal(@run.get_output("Message"), "Hello, World!")
61
+ assert_equal(@run.get_output("Message"), ["Hello, World!"])
65
62
  assert_raise(T2Server::AccessForbiddenError) do
66
63
  @run.get_output("wrong!")
67
64
  end
@@ -30,7 +30,7 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 't2server'
33
+ require 't2-server'
34
34
 
35
35
  class TestServer < Test::Unit::TestCase
36
36
 
@@ -31,10 +31,13 @@
31
31
  # Author: Robert Haines
32
32
 
33
33
  require 'test/unit'
34
- require 't2server'
34
+ require 't2-server'
35
35
 
36
36
  # get a server address to test - 30 second timeout
37
- print "\nPlease supply a valid Taverna 2 Server address (leave blank to skip tests): "
37
+ print "\nPlease supply a valid Taverna 2 Server address.\n\nNOTE that these " +
38
+ "tests will fully load the server and then delete all the runs that it " +
39
+ "has permission to do so - if you are not using security ALL runs will be " +
40
+ "deleted!\n(leave blank to skip tests): "
38
41
  $stdout.flush
39
42
  if select([$stdin], [], [], 30)
40
43
  $address = $stdin.gets.chomp
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: t2-server
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Robert Haines
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-23 00:00:00 +01:00
18
+ date: 2010-10-22 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -34,24 +34,43 @@ dependencies:
34
34
  version: 0.8.7
35
35
  type: :development
36
36
  version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: libxml-ruby
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 27
46
+ segments:
47
+ - 1
48
+ - 1
49
+ - 4
50
+ version: 1.1.4
51
+ type: :runtime
52
+ version_requirements: *id002
37
53
  description: This gem provides access to the Taverna 2 Server REST interface from Ruby.
38
54
  email: rhaines@manchester.ac.uk
39
- executables: []
40
-
55
+ executables:
56
+ - t2-delete-runs
57
+ - t2-run-workflow
58
+ - t2-server-info
41
59
  extensions: []
42
60
 
43
61
  extra_rdoc_files:
44
62
  - README.rdoc
45
63
  - LICENCE
46
64
  files:
47
- - bin/server_info
48
- - bin/run_workflow
49
- - bin/delete_all_runs
65
+ - bin/t2-server-info
66
+ - bin/t2-delete-runs
67
+ - bin/t2-run-workflow
50
68
  - lib/t2server.rb
51
- - lib/t2server/server.rb
52
- - lib/t2server/exceptions.rb
53
- - lib/t2server/xml.rb
54
- - lib/t2server/run.rb
69
+ - lib/t2-server/server.rb
70
+ - lib/t2-server/exceptions.rb
71
+ - lib/t2-server/xml.rb
72
+ - lib/t2-server/run.rb
73
+ - lib/t2-server.rb
55
74
  - test/tc_server.rb
56
75
  - test/tc_paths.rb
57
76
  - test/tc_run.rb
@@ -64,8 +83,10 @@ homepage: http://www.taverna.org.uk/
64
83
  licenses: []
65
84
 
66
85
  post_install_message:
67
- rdoc_options: []
68
-
86
+ rdoc_options:
87
+ - -N
88
+ - --tab-width=2
89
+ - --main=README.rdoc
69
90
  require_paths:
70
91
  - lib
71
92
  required_ruby_version: !ruby/object:Gem::Requirement