t2-server 0.6.1 → 0.9.0

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