t2-server 0.2.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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