t2-server 0.6.1 → 0.9.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.
Files changed (52) hide show
  1. data/.rvmrc +1 -0
  2. data/CHANGES.rdoc +48 -0
  3. data/LICENCE.rdoc +2 -2
  4. data/README.rdoc +245 -10
  5. data/Rakefile +108 -0
  6. data/bin/t2-delete-runs +21 -34
  7. data/bin/t2-get-output +134 -0
  8. data/bin/t2-run-workflow +121 -109
  9. data/bin/t2-server-admin +128 -0
  10. data/bin/t2-server-info +25 -38
  11. data/lib/t2-server-cli.rb +116 -0
  12. data/lib/t2-server.rb +16 -27
  13. data/lib/t2-server/admin.rb +147 -0
  14. data/lib/t2-server/connection-parameters.rb +144 -0
  15. data/lib/t2-server/connection.rb +352 -0
  16. data/lib/t2-server/credentials.rb +84 -0
  17. data/lib/t2-server/exceptions.rb +42 -21
  18. data/lib/t2-server/port.rb +472 -0
  19. data/lib/t2-server/run.rb +822 -227
  20. data/lib/t2-server/server.rb +313 -317
  21. data/lib/t2-server/util.rb +71 -0
  22. data/lib/t2-server/xml/libxml.rb +87 -0
  23. data/lib/t2-server/xml/nokogiri.rb +85 -0
  24. data/lib/t2-server/xml/rexml.rb +85 -0
  25. data/lib/t2-server/xml/xml.rb +111 -0
  26. data/lib/t2server.rb +4 -1
  27. data/t2-server.gemspec +112 -0
  28. data/test/tc_admin.rb +63 -0
  29. data/test/{tc_paths.rb → tc_params.rb} +11 -25
  30. data/test/tc_perms.rb +132 -0
  31. data/test/tc_run.rb +200 -67
  32. data/test/tc_secure.rb +191 -0
  33. data/test/tc_server.rb +25 -23
  34. data/test/tc_util.rb +74 -0
  35. data/test/ts_t2server.rb +57 -12
  36. data/test/workflows/always_fail.t2flow +69 -0
  37. data/test/workflows/list_and_value.t2flow +12 -0
  38. data/test/workflows/list_with_errors.t2flow +107 -0
  39. data/test/workflows/secure/basic-http.t2flow +74 -0
  40. data/test/workflows/secure/basic-https.t2flow +74 -0
  41. data/test/workflows/secure/client-https.t2flow +162 -0
  42. data/test/workflows/secure/digest-http.t2flow +129 -0
  43. data/test/workflows/secure/digest-https.t2flow +107 -0
  44. data/test/workflows/secure/heater-pk.pem +20 -0
  45. data/test/workflows/secure/user-cert.p12 +0 -0
  46. data/test/workflows/secure/ws-http.t2flow +180 -0
  47. data/test/workflows/secure/ws-https.t2flow +180 -0
  48. data/test/workflows/strings.txt +10 -0
  49. data/test/workflows/xml_xpath.t2flow +136 -136
  50. data/version.yml +4 -0
  51. metadata +132 -34
  52. data/lib/t2-server/xml.rb +0 -86
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010, 2011 The University of Manchester, UK.
1
+ # Copyright (c) 2010-2012 The University of Manchester, UK.
2
2
  #
3
3
  # All rights reserved.
4
4
  #
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # * Neither the names of The University of Manchester nor the names of its
16
16
  # contributors may be used to endorse or promote products derived from this
17
- # software without specific prior written permission.
17
+ # software without specific prior written permission.
18
18
  #
19
19
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
20
  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -30,459 +30,455 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 'rubygems'
34
33
  require 'base64'
35
34
  require 'uri'
36
- require 'net/https'
37
- require 'libxml'
38
35
 
39
36
  module T2Server
40
37
 
41
38
  # An interface for directly communicating with one or more Taverna 2 Server
42
39
  # instances.
43
40
  class Server
44
- include LibXML
41
+ include XML::Methods
45
42
 
46
- private_class_method :new
43
+ # The version of the remote Taverna Server instance.
44
+ attr_reader :version
47
45
 
48
- # The URI of this server instance.
49
- attr_reader :uri
50
-
51
- # The maximum number of runs that this server will allow at any one time.
52
- # Runs in any state (+Initialized+, +Running+ and +Finished+) are counted
53
- # against this maximum.
54
- attr_reader :run_limit
55
-
56
- # list of servers we know about
57
- @@servers = []
58
-
59
46
  # :stopdoc:
60
- # New is private but rdoc does not get it right! Hence :stopdoc: section.
61
- def initialize(uri, username, password)
62
- @uri = uri
63
- @host = @uri.host
64
- @port = @uri.port
65
- @base_path = @uri.path
66
- @rest_path = @uri.path + "/rest"
67
-
68
- # set up http connection
69
- @http = Net::HTTP.new(@host, @port)
70
-
71
- # use ssl?
72
- @ssl = uri.scheme == "https"
73
- if ssl?
74
- @username = username
75
- @password = password
76
-
77
- @http.use_ssl = true
78
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
79
- end
80
-
81
- # add a slash to the end of this address to work around this bug:
82
- # http://www.mygrid.org.uk/dev/issues/browse/TAVSERV-113
83
- @links = parse_description(get_attribute("#{@rest_path}/"))
84
- #@links.each {|key, val| puts "#{key}: #{val}"}
85
-
86
- # get max runs
87
- @run_limit = get_attribute(@links[:runlimit]).to_i
88
-
89
- # initialise run list
90
- @runs = {}
91
- @runs = get_runs
92
- end
47
+ XPaths = {
48
+ # Server top-level XPath queries
49
+ :server => XML::Methods.xpath_compile("//nsr:serverDescription"),
50
+ :policy => XML::Methods.xpath_compile("//nsr:policy"),
51
+ :run => XML::Methods.xpath_compile("//nsr:run"),
52
+ :runs => XML::Methods.xpath_compile("//nsr:runs"),
53
+
54
+ # Server policy XPath queries
55
+ :runlimit => XML::Methods.xpath_compile("//nsr:runLimit"),
56
+ :permwkf => XML::Methods.xpath_compile("//nsr:permittedWorkflows"),
57
+ :permlstn => XML::Methods.xpath_compile("//nsr:permittedListeners"),
58
+ :permlstt => XML::Methods.xpath_compile("//nsr:permittedListenerTypes"),
59
+ :notify =>
60
+ XML::Methods.xpath_compile("//nsr:enabledNotificationFabrics")
61
+ }
93
62
  # :startdoc:
94
63
 
95
64
  # :call-seq:
96
- # Server.connect(uri, username="", password="") -> server
65
+ # new(uri, connection_parameters = nil) -> Server
66
+ # new(uri, connection_parameters = nil) {|self| ...}
97
67
  #
98
- # Connect to the server specified by _uri_ which should be of the form:
99
- # http://example.com:8888/blah or https://user:pass@example.com:8888/blah
68
+ # Create a new Server instance that represents the real server at _uri_.
69
+ # If _connection_parameters_ are supplied they will be used to set up the
70
+ # network connection to the server.
100
71
  #
101
- # The username and password can also be passed in separately.
102
- # A Server instance is returned that represents the connection.
103
- def Server.connect(uri, username="", password="")
72
+ # It will _yield_ itself if a block is given.
73
+ def initialize(uri, params = nil)
104
74
  # we want to use URIs here but strings can be passed in
105
- if !uri.instance_of? URI
106
- uri = URI.parse(uri.strip_path);
75
+ unless uri.is_a? URI
76
+ uri = URI.parse(Util.strip_path_slashes(uri))
107
77
  end
108
-
78
+
109
79
  # strip username and password from the URI if present
110
- username = uri.user || username
111
- password = uri.password || password
112
- new_uri = URI::HTTP.new(uri.scheme, nil, uri.host, uri.port, nil,
80
+ if uri.user != nil
81
+ uri = URI::HTTP.new(uri.scheme, nil, uri.host, uri.port, nil,
113
82
  uri.path, nil, nil, nil);
114
-
115
- # see if we've already got this server
116
- server = @@servers.find {|s| s.uri == new_uri}
117
-
118
- if !server
119
- # no, so create new one and return it
120
- server = new(new_uri, username, password)
121
- @@servers << server
122
83
  end
123
-
124
- server
84
+
85
+ # setup connection
86
+ @connection = ConnectionFactory.connect(uri, params)
87
+
88
+ # add a slash to the end of this address to work around this bug:
89
+ # http://www.mygrid.org.uk/dev/issues/browse/TAVSERV-113
90
+ server_description = xml_document(get_attribute("#{uri.path}/rest/",
91
+ "application/xml"))
92
+ @version = get_version(server_description)
93
+ @links = get_description(server_description)
94
+ @links[:admin] = "#{uri.path}/admin"
95
+
96
+ # initialize run object cache
97
+ @runs = {}
98
+
99
+ yield(self) if block_given?
100
+ end
101
+
102
+ # :stopdoc:
103
+ def Server.connect(uri, username="", password="")
104
+ warn "[DEPRECATION] 'Server#connect' is deprecated and will be " +
105
+ "removed in 1.0."
106
+ new(uri)
107
+ end
108
+ # :startdoc:
109
+
110
+ # :call-seq:
111
+ # administrator(credentials = nil) -> Administrator
112
+ # administrator(credentials = nil) {|admin| ...}
113
+ #
114
+ # Return an instance of the Taverna Server administrator interface. This
115
+ # method will _yield_ the newly created administrator if a block is given.
116
+ def administrator(credentials = nil)
117
+ admin = Administrator.new(self, credentials)
118
+
119
+ yield(admin) if block_given?
120
+ admin
125
121
  end
126
122
 
127
123
  # :call-seq:
128
- # server.create_run(workflow) -> run
124
+ # create_run(workflow, credentials = nil) -> run
125
+ # create_run(workflow, credentials = nil) {|run| ...}
129
126
  #
130
127
  # Create a run on this server using the specified _workflow_.
131
- def create_run(workflow)
132
- uuid = initialize_run(workflow)
133
- @runs[uuid] = Run.create(self, "", uuid)
128
+ # This method will _yield_ the newly created Run if a block is given.
129
+ def create_run(workflow, credentials = nil)
130
+ id = initialize_run(workflow, credentials)
131
+ run = Run.create(self, "", credentials, id)
132
+
133
+ # cache newly created run object - this must be done per user
134
+ user = credentials.nil? ? :all : credentials.username
135
+ @runs[user] = {} unless @runs[user]
136
+ @runs[user][id] = run
137
+
138
+ yield(run) if block_given?
139
+ run
134
140
  end
135
141
 
136
142
  # :call-seq:
137
- # server.initialize_run(workflow) -> string
143
+ # initialize_run(workflow, credentials = nil) -> string
138
144
  #
139
145
  # Create a run on this server using the specified _workflow_ but do not
140
- # return it as a Run instance. Return its UUID instead.
141
- def initialize_run(workflow)
142
- request = Net::HTTP::Post.new("#{@links[:runs]}")
143
- request.content_type = "application/xml"
144
- if ssl?
145
- request.basic_auth @username, @password
146
- end
147
- begin
148
- response = @http.request(request, Fragments::WORKFLOW % workflow)
149
- rescue InternalHTTPError => e
150
- raise ConnectionError.new(e)
151
- end
152
-
153
- case response
154
- when Net::HTTPCreated
155
- # return the uuid of the newly created run
156
- epr = URI.parse(response['location'])
157
- epr.path[-36..-1]
158
- when Net::HTTPForbidden
159
- raise ServerAtCapacityError.new(@run_limit)
160
- when Net::HTTPUnauthorized
161
- raise AuthorizationError.new(@username)
162
- else
163
- raise UnexpectedServerResponse.new(response)
164
- end
146
+ # return it as a Run instance. Return its identifier instead.
147
+ def initialize_run(workflow, credentials = nil)
148
+ # set up the run object cache - this must be done per user
149
+ user = credentials.nil? ? :all : credentials.username
150
+ @runs[user] = {} unless @runs[user]
151
+
152
+ @connection.POST_run("#{@links[:runs]}",
153
+ XML::Fragments::WORKFLOW % workflow, credentials)
165
154
  end
166
155
 
167
156
  # :call-seq:
168
- # server.ssl? -> bool
157
+ # uri -> URI
169
158
  #
170
- # Is this server using SSL?
171
- def ssl?
172
- @ssl
159
+ # The URI of the connection to the remote Taverna Server.
160
+ def uri
161
+ @connection.uri
173
162
  end
174
163
 
175
164
  # :call-seq:
176
- # server.runs -> [runs]
165
+ # run_limit(credentials = nil) -> num
166
+ #
167
+ # The maximum number of runs that this server will allow at any one time.
168
+ # Runs in any state (+Initialized+, +Running+ and +Finished+) are counted
169
+ # against this maximum.
170
+ def run_limit(credentials = nil)
171
+ get_attribute(@links[:runlimit], "text/plain", credentials).to_i
172
+ end
173
+
174
+ # :call-seq:
175
+ # runs(credentials = nil) -> [runs]
177
176
  #
178
177
  # Return the set of runs on this server.
179
- def runs
180
- get_runs.values
178
+ def runs(credentials = nil)
179
+ get_runs(credentials).values
181
180
  end
182
181
 
183
182
  # :call-seq:
184
- # server.run(uuid) -> run
183
+ # run(identifier, credentials = nil) -> run
185
184
  #
186
185
  # Return the specified run.
187
- def run(uuid)
188
- get_runs[uuid]
186
+ def run(identifier, credentials = nil)
187
+ get_runs(credentials)[identifier]
189
188
  end
190
189
 
191
190
  # :call-seq:
192
- # server.delete_run(run) -> bool
191
+ # delete_run(run, credentials = nil) -> bool
193
192
  #
194
193
  # Delete the specified run from the server, discarding all of its state.
195
- # _run_ can be either a Run instance or a UUID.
196
- def delete_run(run)
197
- # get the uuid from the run if that is what is passed in
194
+ # _run_ can be either a Run instance or a identifier.
195
+ def delete_run(run, credentials = nil)
196
+ # get the identifier from the run if that is what is passed in
198
197
  if run.instance_of? Run
199
- run = run.uuid
198
+ run = run.identifier
200
199
  end
201
200
 
202
- request = Net::HTTP::Delete.new("#{@links[:runs]}/#{run}")
203
- if ssl?
204
- request.basic_auth @username, @password
205
- end
206
- begin
207
- response = @http.request(request)
208
- rescue InternalHTTPError => e
209
- raise ConnectionError.new(e)
210
- end
211
-
212
- case response
213
- when Net::HTTPNoContent
214
- # Success, carry on...
215
- @runs.delete(run)
201
+ if delete_attribute("#{@links[:runs]}/#{run}", credentials)
202
+ # delete cached run object - this must be done per user
203
+ user = credentials.nil? ? :all : credentials.username
204
+ @runs[user].delete(run) if @runs[user]
216
205
  true
217
- when Net::HTTPNotFound
218
- raise RunNotFoundError.new(run)
219
- when Net::HTTPForbidden
220
- raise AccessForbiddenError.new("run #{run}")
221
- when Net::HTTPUnauthorized
222
- raise AuthorizationError.new(@username)
223
- else
224
- raise UnexpectedServerResponse.new(response)
225
206
  end
226
207
  end
227
208
 
228
209
  # :call-seq:
229
- # server.delete_all_runs
210
+ # delete_all_runs(credentials = nil)
230
211
  #
231
212
  # Delete all runs on this server, discarding all of their state.
232
- def delete_all_runs
213
+ def delete_all_runs(credentials = nil)
233
214
  # first refresh run list
234
- runs.each {|run| run.delete}
215
+ runs(credentials).each {|run| run.delete}
235
216
  end
236
217
 
237
- # :call-seq:
238
- # server.set_run_input(run, input, value) -> bool
239
- #
240
- # Set the workflow input port _input_ on run _run_ to _value_. _run_ can
241
- # be either a Run instance or a UUID.
242
- def set_run_input(run, input, value)
243
- # get the run from the uuid if that is what is passed in
218
+ # :stopdoc:
219
+ def set_run_input(run, input, value, credentials = nil)
220
+ warn "[DEPRECATION] 'Server#set_run_input' is deprecated and will be " +
221
+ "removed in 1.0. Input ports are set directly instead. The most " +
222
+ "direct replacement for this method is: " +
223
+ "'Run#input_port(input).value = value'"
224
+
225
+ # get the run from the identifier if that is what is passed in
244
226
  if not run.instance_of? Run
245
- run = run(run)
227
+ run = run(run, credentials)
246
228
  end
247
229
 
248
- xml_value = XML::Node.new_text(value)
249
- path = "#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}"
250
- set_attribute(path, Fragments::RUNINPUTVALUE % xml_value, "application/xml")
251
- rescue AttributeNotFoundError => e
252
- if get_runs.has_key? run.uuid
253
- raise e
254
- else
255
- raise RunNotFoundError.new(run.uuid)
256
- end
230
+ run.input_port(input).value = value
257
231
  end
258
232
 
259
- # :call-seq:
260
- # server.set_run_input_file(run, input, filename) -> bool
261
- #
262
- # Set the workflow input port _input_ on run _run_ to use the file at
263
- # _filename_ for its input. _run_ can be either a Run instance or a UUID.
264
- def set_run_input_file(run, input, filename)
265
- # get the run from the uuid if that is what is passed in
233
+ def set_run_input_file(run, input, filename, credentials = nil)
234
+ warn "[DEPRECATION] 'Server#set_run_input_file' is deprecated and " +
235
+ "will be removed in 1.0. Input ports are set directly instead. The " +
236
+ "most direct replacement for this method is: " +
237
+ "'Run#input_port(input).remote_file = filename'"
238
+
239
+ # get the run from the identifier if that is what is passed in
266
240
  if not run.instance_of? Run
267
- run = run(run)
241
+ run = run(run, credentials)
268
242
  end
269
243
 
270
- xml_value = XML::Node.new_text(filename)
271
- path = "#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}"
272
- set_attribute(path, Fragments::RUNINPUTFILE % xml_value, "application/xml")
273
- rescue AttributeNotFoundError => e
274
- if get_runs.has_key? run.uuid
275
- raise e
276
- else
277
- raise RunNotFoundError.new(run.uuid)
278
- end
244
+ run.input_port(input).remote_file = filename
279
245
  end
280
246
 
281
- # :call-seq:
282
- # server.make_run_dir(run, root, dir) -> bool
283
- #
284
- # Create a directory _dir_ within the directory _root_ on _run_. _run_ can
285
- # be either a Run instance or a UUID. This is mainly for use by Run#mkdir.
286
- def make_run_dir(run, root, dir)
287
- # get the uuid from the run if that is what is passed in
247
+ def create_dir(run, root, dir, credentials = nil)
248
+ # get the identifier from the run if that is what is passed in
288
249
  if run.instance_of? Run
289
- run = run.uuid
250
+ run = run.identifier
290
251
  end
291
252
 
292
253
  raise AccessForbiddenError.new("subdirectories (#{dir})") if dir.include? ?/
293
- request = Net::HTTP::Post.new("#{@links[:runs]}/#{run}/#{root}")
294
- request.content_type = "application/xml"
295
- if ssl?
296
- request.basic_auth @username, @password
297
- end
298
- begin
299
- response = @http.request(request, Fragments::MKDIR % dir)
300
- rescue InternalHTTPError => e
301
- raise ConnectionError.new(e)
254
+ @connection.POST_dir("#{@links[:runs]}/#{run}/#{root}",
255
+ XML::Fragments::MKDIR % dir, run, dir, credentials)
256
+ end
257
+
258
+ def make_run_dir(run, root, dir, credentials = nil)
259
+ warn "[DEPRECATION] 'Server#make_run_dir' is deprecated and will be " +
260
+ "removed in 1.0. Please use 'Run#mkdir' instead."
261
+
262
+ create_dir(run, root, dir, credentials)
263
+ end
264
+
265
+ def upload_file(run, filename, location, rename, credentials = nil)
266
+ contents = IO.read(filename)
267
+ rename = filename.split('/')[-1] if rename == ""
268
+
269
+ if upload_data(run, contents, rename, location, credentials)
270
+ rename
302
271
  end
272
+ end
303
273
 
304
- case response
305
- when Net::HTTPCreated
306
- # OK, carry on...
307
- true
308
- when Net::HTTPNotFound
309
- raise RunNotFoundError.new(run)
310
- when Net::HTTPForbidden
311
- raise AccessForbiddenError.new("#{dir} on run #{run}")
312
- when Net::HTTPUnauthorized
313
- raise AuthorizationError.new(@username)
314
- else
315
- raise UnexpectedServerResponse.new(response)
274
+ def upload_data(run, data, remote_name, location, credentials = nil)
275
+ # get the identifier from the run if that is what is passed in
276
+ if run.instance_of? Run
277
+ run = run.identifier
316
278
  end
279
+
280
+ contents = Base64.encode64(data)
281
+
282
+ @connection.POST_file("#{@links[:runs]}/#{run}/#{location}",
283
+ XML::Fragments::UPLOAD % [remote_name, contents], run, credentials)
317
284
  end
318
285
 
319
- # :call-seq:
320
- # server.upload_run_file(run, filename, location, rename) -> string
321
- #
322
- # Upload a file to _run_. _run_ can be either a Run instance or a UUID.
323
- # Mainly for internal use by Run#upload_file.
324
- def upload_run_file(run, filename, location, rename)
325
- # get the uuid from the run if that is what is passed in
286
+ def upload_run_file(run, filename, location, rename, credentials = nil)
287
+ warn "[DEPRECATION] 'Server#upload_run_file' is deprecated and will " +
288
+ "be removed in 1.0. Please use 'Run#upload_file' or " +
289
+ "'Run#input_port(input).file = filename' instead."
290
+
291
+ upload_file(run, filename, location, rename, credentials)
292
+ end
293
+
294
+ def create_run_attribute(run, path, value, type, credentials = nil)
295
+ # get the identifier from the run if that is what is passed in
326
296
  if run.instance_of? Run
327
- run = run.uuid
297
+ run = run.identifier
328
298
  end
329
299
 
330
- contents = Base64.encode64(IO.read(filename))
331
- rename = filename.split('/')[-1] if rename == ""
332
- request = Net::HTTP::Post.new("#{@links[:runs]}/#{run}/#{location}")
333
- request.content_type = "application/xml"
334
- if ssl?
335
- request.basic_auth @username, @password
300
+ create_attribute("#{@links[:runs]}/#{run}/#{path}", value, type,
301
+ credentials)
302
+ rescue AttributeNotFoundError => e
303
+ if get_runs(credentials).has_key? run
304
+ raise e
305
+ else
306
+ raise RunNotFoundError.new(run)
336
307
  end
337
- begin
338
- response = @http.request(request, Fragments::UPLOAD % [rename, contents])
339
- rescue InternalHTTPError => e
340
- raise ConnectionError.new(e)
308
+ end
309
+
310
+ def get_run_attribute(run, path, type, credentials = nil)
311
+ # get the identifier from the run if that is what is passed in
312
+ if run.instance_of? Run
313
+ run = run.identifier
341
314
  end
342
315
 
343
- case response
344
- when Net::HTTPCreated
345
- # Success, return remote name of uploaded file
346
- rename
347
- when Net::HTTPNotFound
348
- raise RunNotFoundError.new(run)
349
- when Net::HTTPForbidden
350
- raise AccessForbiddenError.new("run #{run}")
351
- when Net::HTTPUnauthorized
352
- raise AuthorizationError.new(@username)
316
+ get_attribute("#{@links[:runs]}/#{run}/#{path}", type, credentials)
317
+ rescue AttributeNotFoundError => e
318
+ if get_runs(credentials).has_key? run
319
+ raise e
353
320
  else
354
- raise UnexpectedServerResponse.new(response)
321
+ raise RunNotFoundError.new(run)
355
322
  end
356
323
  end
357
324
 
358
- # :call-seq:
359
- # server.get_run_attribute(run, path) -> string
360
- #
361
- # Get the attribute at _path_ in _run_. _run_ can be either a Run instance
362
- # or a UUID.
363
- def get_run_attribute(run, path)
364
- # get the uuid from the run if that is what is passed in
325
+ def set_run_attribute(run, path, value, type, credentials = nil)
326
+ # get the identifier from the run if that is what is passed in
365
327
  if run.instance_of? Run
366
- run = run.uuid
328
+ run = run.identifier
367
329
  end
368
330
 
369
- get_attribute("#{@links[:runs]}/#{run}/#{path}")
331
+ set_attribute("#{@links[:runs]}/#{run}/#{path}", value, type,
332
+ credentials)
370
333
  rescue AttributeNotFoundError => e
371
- if get_runs.has_key? run
334
+ if get_runs(credentials).has_key? run
372
335
  raise e
373
336
  else
374
337
  raise RunNotFoundError.new(run)
375
338
  end
376
339
  end
377
340
 
378
- # :call-seq:
379
- # server.set_run_attribute(run, path, value) -> bool
380
- #
381
- # Set the attribute at _path_ in _run_ to _value_. _run_ can be either a
382
- # Run instance or a UUID.
383
- def set_run_attribute(run, path, value)
384
- # get the uuid from the run if that is what is passed in
341
+ def delete_run_attribute(run, path, credentials = nil)
342
+ # get the identifier from the run if that is what is passed in
385
343
  if run.instance_of? Run
386
- run = run.uuid
344
+ run = run.identifier
387
345
  end
388
346
 
389
- set_attribute("#{@links[:runs]}/#{run}/#{path}", value, "text/plain")
347
+ delete_attribute("#{@links[:runs]}/#{run}/#{path}", credentials)
390
348
  rescue AttributeNotFoundError => e
391
- if get_runs.has_key? run
349
+ if get_runs(credentials).has_key? run
392
350
  raise e
393
351
  else
394
352
  raise RunNotFoundError.new(run)
395
353
  end
396
354
  end
397
355
 
398
- private
399
- def get_attribute(path)
400
- request = Net::HTTP::Get.new(path)
401
- if ssl?
402
- request.basic_auth @username, @password
403
- end
404
- begin
405
- response = @http.request(request)
406
- rescue InternalHTTPError => e
407
- raise ConnectionError.new(e)
356
+ def download_run_file(run, path, range, credentials = nil)
357
+ # get the identifier from the run if that is what is passed in
358
+ if run.instance_of? Run
359
+ run = run.identifier
408
360
  end
409
361
 
410
- case response
411
- when Net::HTTPOK
412
- return response.body
413
- when Net::HTTPNotFound
414
- raise AttributeNotFoundError.new(path)
415
- when Net::HTTPForbidden
416
- raise AccessForbiddenError.new("attribute #{path}")
417
- when Net::HTTPUnauthorized
418
- raise AuthorizationError.new(@username)
362
+ get_attribute("#{@links[:runs]}/#{run}/#{path}",
363
+ "application/octet-stream", range, credentials)
364
+ rescue AttributeNotFoundError => e
365
+ if get_runs(credentials).has_key? run
366
+ raise e
419
367
  else
420
- raise UnexpectedServerResponse.new(response)
368
+ raise RunNotFoundError.new(run)
421
369
  end
422
370
  end
423
371
 
424
- def set_attribute(path, value, type)
425
- request = Net::HTTP::Put.new(path)
426
- request.content_type = type
427
- if ssl?
428
- request.basic_auth @username, @password
372
+ def get_admin_attribute(path, credentials = nil)
373
+ get_attribute("#{@links[:admin]}/#{path}", "*/*", credentials)
374
+ end
375
+
376
+ def set_admin_attribute(path, value, credentials = nil)
377
+ set_attribute("#{@links[:admin]}/#{path}", value, "text/plain",
378
+ credentials)
379
+ end
380
+
381
+ def admin_resource_writable?(path, credentials = nil)
382
+ headers = @connection.OPTIONS("#{@links[:admin]}/#{path}", credentials)
383
+ headers["allow"][0].split(",").include? "PUT"
384
+ end
385
+ # :startdoc:
386
+
387
+ private
388
+ def create_attribute(path, value, type, credentials = nil)
389
+ @connection.POST(path, value, type, credentials)
390
+ end
391
+
392
+ def get_attribute(path, type, *rest)
393
+ credentials = nil
394
+ range = nil
395
+
396
+ rest.each do |param|
397
+ case param
398
+ when HttpCredentials
399
+ credentials = param
400
+ when Range
401
+ range = param
402
+ when Array
403
+ range = param[0]..param[1]
404
+ end
429
405
  end
406
+
430
407
  begin
431
- response = @http.request(request, value)
432
- rescue InternalHTTPError => e
433
- raise ConnectionError.new(e)
408
+ @connection.GET(path, type, range, credentials)
409
+ rescue ConnectionRedirectError => cre
410
+ @connection = cre.redirect
411
+ retry
434
412
  end
413
+ end
435
414
 
436
- case response
437
- when Net::HTTPOK
438
- # OK, so carry on
439
- true
440
- when Net::HTTPNotFound
441
- raise AttributeNotFoundError.new(path)
442
- when Net::HTTPForbidden
443
- raise AccessForbiddenError.new("attribute #{path}")
444
- when Net::HTTPUnauthorized
445
- raise AuthorizationError.new(@username)
415
+ def set_attribute(path, value, type, credentials = nil)
416
+ @connection.PUT(path, value, type, credentials)
417
+ end
418
+
419
+ def delete_attribute(path, credentials = nil)
420
+ @connection.DELETE(path, credentials)
421
+ end
422
+
423
+ def get_version(doc)
424
+ version = xpath_attr(doc, XPaths[:server], "serverVersion")
425
+ if version == nil
426
+ raise RuntimeError.new("Taverna Servers prior to version 2.3 " +
427
+ "are no longer supported.")
446
428
  else
447
- raise UnexpectedServerResponse.new(response)
429
+ return version.to_f
448
430
  end
449
431
  end
450
432
 
451
- def parse_description(desc)
452
- doc = XML::Document.string(desc)
453
- nsmap = Namespaces::MAP
454
- {
455
- :runs => URI.parse(doc.find_first(XPaths::RUNS, nsmap).attributes["href"]).path,
456
- :runlimit => URI.parse(doc.find_first(XPaths::RUNLIMIT, nsmap).attributes["href"]).path,
457
- :permworkflows => URI.parse(doc.find_first(XPaths::PERMWKF, nsmap).attributes["href"]).path,
458
- :permlisteners => URI.parse(doc.find_first(XPaths::PERMLSTN, nsmap).attributes["href"]).path
459
- }
433
+ def get_description(doc)
434
+ links = {}
435
+ links[:runs] = URI.parse(xpath_attr(doc, XPaths[:runs], "href")).path
436
+
437
+ links[:policy] = URI.parse(xpath_attr(doc, XPaths[:policy], "href")).path
438
+ doc = xml_document(get_attribute(links[:policy], "application/xml"))
439
+
440
+ links[:permlisteners] =
441
+ URI.parse(xpath_attr(doc, XPaths[:permlstt], "href")).path
442
+ links[:notifications] =
443
+ URI.parse(xpath_attr(doc, XPaths[:notify], "href")).path
444
+
445
+ links[:runlimit] =
446
+ URI.parse(xpath_attr(doc, XPaths[:runlimit], "href")).path
447
+ links[:permworkflows] =
448
+ URI.parse(xpath_attr(doc, XPaths[:permwkf], "href")).path
449
+
450
+ links
460
451
  end
461
452
 
462
- def get_runs
463
- run_list = get_attribute("#{@links[:runs]}")
453
+ def get_runs(credentials = nil)
454
+ run_list = get_attribute("#{@links[:runs]}", "application/xml",
455
+ credentials)
464
456
 
465
- doc = XML::Document.string(run_list)
457
+ doc = xml_document(run_list)
466
458
 
467
- # get list of run uuids
468
- uuids = []
469
- doc.find(XPaths::RUN, Namespaces::MAP).each do |run|
470
- uuids << run.attributes["href"].split('/')[-1]
459
+ # get list of run identifiers
460
+ ids = []
461
+ xpath_find(doc, XPaths[:run]).each do |run|
462
+ ids << xml_node_attribute(run, "href").split('/')[-1]
471
463
  end
472
464
 
465
+ # cache run objects - this must be done per user
466
+ user = credentials.nil? ? :all : credentials.username
467
+ @runs[user] = {} unless @runs[user]
468
+
473
469
  # add new runs
474
- uuids.each do |uuid|
475
- if !@runs.has_key? uuid
476
- @runs[uuid] = Run.create(self, "", uuid)
470
+ ids.each do |id|
471
+ if !@runs[user].has_key? id
472
+ @runs[user][id] = Run.create(self, "", credentials, id)
477
473
  end
478
474
  end
479
475
 
480
476
  # clear out the expired runs
481
- if @runs.length > @run_limit
482
- @runs.delete_if {|key, val| !uuids.member? key}
477
+ if @runs[user].length > ids.length
478
+ @runs[user].delete_if {|key, val| !ids.member? key}
483
479
  end
484
480
 
485
- @runs
481
+ @runs[user]
486
482
  end
487
- end
483
+ end
488
484
  end