t2-server 0.9.1 → 0.9.2

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.
@@ -40,10 +40,11 @@ module T2Server
40
40
  class Server
41
41
  include XML::Methods
42
42
 
43
- # The version of the remote Taverna Server instance.
44
- attr_reader :version
45
-
46
43
  # :stopdoc:
44
+ # Internal references to the main rest and admin top-level resource
45
+ # endpoints.
46
+ REST_ENDPOINT = "rest/"
47
+
47
48
  XPaths = {
48
49
  # Server top-level XPath queries
49
50
  :server => XML::Methods.xpath_compile("//nsr:serverDescription"),
@@ -85,13 +86,12 @@ module T2Server
85
86
  # setup connection
86
87
  @connection = ConnectionFactory.connect(uri, params)
87
88
 
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"
89
+ # The following four fields hold cached data about the server that is
90
+ # only downloaded the first time it is requested.
91
+ @server_doc = nil
92
+ @version = nil
93
+ @version_components = nil
94
+ @links = nil
95
95
 
96
96
  # initialize run object cache
97
97
  @runs = {}
@@ -149,8 +149,34 @@ module T2Server
149
149
  user = credentials.nil? ? :all : credentials.username
150
150
  @runs[user] = {} unless @runs[user]
151
151
 
152
- @connection.POST_run("#{@links[:runs]}",
153
- XML::Fragments::WORKFLOW % workflow, credentials)
152
+ @connection.POST(links[:runs], workflow,
153
+ "application/vnd.taverna.t2flow+xml", credentials)
154
+ end
155
+
156
+ # :call-seq:
157
+ # version -> String
158
+ #
159
+ # The version string of the remote Taverna Server.
160
+ def version
161
+ if @version.nil?
162
+ @version = _get_version
163
+ end
164
+
165
+ @version
166
+ end
167
+
168
+ # :call-seq:
169
+ # version_components -> Array
170
+ #
171
+ # An array of the major, minor and patch version components of the remote
172
+ # Taverna Server.
173
+ def version_components
174
+ if @version_components.nil?
175
+ comps = version.split(".")
176
+ @version_components = comps.map { |v| v.to_i }
177
+ end
178
+
179
+ @version_components
154
180
  end
155
181
 
156
182
  # :call-seq:
@@ -168,7 +194,7 @@ module T2Server
168
194
  # Runs in any state (+Initialized+, +Running+ and +Finished+) are counted
169
195
  # against this maximum.
170
196
  def run_limit(credentials = nil)
171
- get_attribute(@links[:runlimit], "text/plain", credentials).to_i
197
+ read(links[:runlimit], "text/plain", credentials).to_i
172
198
  end
173
199
 
174
200
  # :call-seq:
@@ -187,29 +213,32 @@ module T2Server
187
213
  get_runs(credentials)[identifier]
188
214
  end
189
215
 
190
- # :call-seq:
191
- # delete_run(run, credentials = nil) -> bool
192
- #
193
- # Delete the specified run from the server, discarding all of its state.
194
- # _run_ can be either a Run instance or a identifier.
216
+ # :stopdoc:
195
217
  def delete_run(run, credentials = nil)
218
+ warn "[DEPRECATION] 'Server#delete_run' is deprecated and will be " +
219
+ "removed in 1.0. Please use 'Run#delete' to delete a run."
220
+
196
221
  # get the identifier from the run if that is what is passed in
197
222
  if run.instance_of? Run
198
223
  run = run.identifier
199
224
  end
200
225
 
201
- if delete_attribute("#{@links[:runs]}/#{run}", credentials)
226
+ run_uri = Util.append_to_uri_path(links[:runs], run)
227
+ if delete(run_uri, credentials)
202
228
  # delete cached run object - this must be done per user
203
229
  user = credentials.nil? ? :all : credentials.username
204
230
  @runs[user].delete(run) if @runs[user]
205
231
  true
206
232
  end
207
233
  end
234
+ # :startdoc:
208
235
 
209
236
  # :call-seq:
210
237
  # delete_all_runs(credentials = nil)
211
238
  #
212
- # Delete all runs on this server, discarding all of their state.
239
+ # Delete all runs on this server, discarding all of their state. Note that
240
+ # only those runs that the provided credentials have permission to delete
241
+ # will be deleted.
213
242
  def delete_all_runs(credentials = nil)
214
243
  # first refresh run list
215
244
  runs(credentials).each {|run| run.delete}
@@ -244,15 +273,9 @@ module T2Server
244
273
  run.input_port(input).remote_file = filename
245
274
  end
246
275
 
247
- def create_dir(run, root, dir, credentials = nil)
248
- # get the identifier from the run if that is what is passed in
249
- if run.instance_of? Run
250
- run = run.identifier
251
- end
252
-
253
- raise AccessForbiddenError.new("subdirectories (#{dir})") if dir.include? ?/
254
- @connection.POST_dir("#{@links[:runs]}/#{run}/#{root}",
255
- XML::Fragments::MKDIR % dir, run, dir, credentials)
276
+ def mkdir(uri, dir, credentials = nil)
277
+ @connection.POST(uri, XML::Fragments::MKDIR % dir, "application/xml",
278
+ credentials)
256
279
  end
257
280
 
258
281
  def make_run_dir(run, root, dir, credentials = nil)
@@ -262,25 +285,26 @@ module T2Server
262
285
  create_dir(run, root, dir, credentials)
263
286
  end
264
287
 
265
- def upload_file(run, filename, location, rename, credentials = nil)
288
+ def upload_file(filename, uri, remote_name, credentials = nil)
266
289
  contents = IO.read(filename)
267
- rename = filename.split('/')[-1] if rename == ""
290
+ remote_name = filename.split('/')[-1] if remote_name == ""
268
291
 
269
- if upload_data(run, contents, rename, location, credentials)
270
- rename
271
- end
292
+ upload_data(contents, remote_name, uri, credentials)
272
293
  end
273
294
 
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
278
- end
295
+ def upload_data(data, remote_name, uri, credentials = nil)
296
+ # Different Server versions support different upload methods
297
+ (major, minor, patch) = version_components
279
298
 
280
- contents = Base64.encode64(data)
281
-
282
- @connection.POST_file("#{@links[:runs]}/#{run}/#{location}",
283
- XML::Fragments::UPLOAD % [remote_name, contents], run, credentials)
299
+ if minor == 4 && patch >= 1
300
+ put_uri = Util.append_to_uri_path(uri, remote_name)
301
+ @connection.PUT(put_uri, data, "application/octet-stream", credentials)
302
+ else
303
+ contents = Base64.encode64(data)
304
+ @connection.POST(uri,
305
+ XML::Fragments::UPLOAD % [remote_name, contents], "application/xml",
306
+ credentials)
307
+ end
284
308
  end
285
309
 
286
310
  def upload_run_file(run, filename, location, rename, credentials = nil)
@@ -291,105 +315,16 @@ module T2Server
291
315
  upload_file(run, filename, location, rename, credentials)
292
316
  end
293
317
 
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
296
- if run.instance_of? Run
297
- run = run.identifier
298
- end
299
-
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)
307
- end
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
314
- end
315
-
316
- get_attribute("#{@links[:runs]}/#{run}/#{path}", type, credentials)
317
- rescue AttributeNotFoundError => e
318
- if get_runs(credentials).has_key? run
319
- raise e
320
- else
321
- raise RunNotFoundError.new(run)
322
- end
323
- end
324
-
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
327
- if run.instance_of? Run
328
- run = run.identifier
329
- end
330
-
331
- set_attribute("#{@links[:runs]}/#{run}/#{path}", value, type,
332
- credentials)
333
- rescue AttributeNotFoundError => e
334
- if get_runs(credentials).has_key? run
335
- raise e
336
- else
337
- raise RunNotFoundError.new(run)
338
- end
339
- end
340
-
341
- def delete_run_attribute(run, path, credentials = nil)
342
- # get the identifier from the run if that is what is passed in
343
- if run.instance_of? Run
344
- run = run.identifier
345
- end
346
-
347
- delete_attribute("#{@links[:runs]}/#{run}/#{path}", credentials)
348
- rescue AttributeNotFoundError => e
349
- if get_runs(credentials).has_key? run
350
- raise e
351
- else
352
- raise RunNotFoundError.new(run)
353
- end
354
- end
355
-
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
360
- end
361
-
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
367
- else
368
- raise RunNotFoundError.new(run)
369
- end
370
- end
371
-
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)
318
+ def is_resource_writable?(uri, credentials = nil)
319
+ headers = @connection.OPTIONS(uri, credentials)
383
320
  headers["allow"][0].split(",").include? "PUT"
384
321
  end
385
- # :startdoc:
386
322
 
387
- private
388
- def create_attribute(path, value, type, credentials = nil)
389
- @connection.POST(path, value, type, credentials)
323
+ def create(uri, value, type, credentials = nil)
324
+ @connection.POST(uri, value, type, credentials)
390
325
  end
391
326
 
392
- def get_attribute(path, type, *rest)
327
+ def read(uri, type, *rest)
393
328
  credentials = nil
394
329
  range = nil
395
330
 
@@ -405,77 +340,111 @@ module T2Server
405
340
  end
406
341
 
407
342
  begin
408
- @connection.GET(path, type, range, credentials)
343
+ @connection.GET(uri, type, range, credentials)
409
344
  rescue ConnectionRedirectError => cre
345
+ # We've been redirected so save the new connection object with the new
346
+ # server URI and try again with the new URI.
410
347
  @connection = cre.redirect
348
+ uri = Util.replace_uri_path(@connection.uri, uri.path)
411
349
  retry
412
350
  end
413
351
  end
414
352
 
415
- def set_attribute(path, value, type, credentials = nil)
416
- @connection.PUT(path, value, type, credentials)
353
+ def update(uri, value, type, credentials = nil)
354
+ @connection.PUT(uri, value, type, credentials)
417
355
  end
418
356
 
419
- def delete_attribute(path, credentials = nil)
420
- @connection.DELETE(path, credentials)
357
+ def delete(uri, credentials = nil)
358
+ @connection.DELETE(uri, credentials)
421
359
  end
360
+ # :startdoc:
361
+
362
+ private
422
363
 
423
- def get_version(doc)
364
+ def links
365
+ @links = _get_server_links if @links.nil?
366
+
367
+ @links
368
+ end
369
+
370
+ def _get_server_description
371
+ if @server_doc.nil?
372
+ rest_uri = Util.append_to_uri_path(uri, REST_ENDPOINT)
373
+ @server_doc = xml_document(read(rest_uri, "application/xml"))
374
+ end
375
+
376
+ @server_doc
377
+ end
378
+
379
+ def _get_version
380
+ doc = _get_server_description
424
381
  version = xpath_attr(doc, XPaths[:server], "serverVersion")
425
382
  if version == nil
426
383
  raise RuntimeError.new("Taverna Servers prior to version 2.3 " +
427
384
  "are no longer supported.")
428
385
  else
429
- return version.to_f
386
+ # Remove SNAPSHOT tag if it's there.
387
+ if version.end_with? "-SNAPSHOT"
388
+ version.gsub!("-SNAPSHOT", "")
389
+ end
390
+
391
+ # Add .0 if we only have a major and minor component.
392
+ if version.split(".").length == 2
393
+ version += ".0"
394
+ end
395
+
396
+ return version
430
397
  end
431
398
  end
432
399
 
433
- def get_description(doc)
400
+ def _get_server_links
401
+ doc = _get_server_description
434
402
  links = {}
435
- links[:runs] = URI.parse(xpath_attr(doc, XPaths[:runs], "href")).path
403
+ links[:runs] = URI.parse(xpath_attr(doc, XPaths[:runs], "href"))
436
404
 
437
- links[:policy] = URI.parse(xpath_attr(doc, XPaths[:policy], "href")).path
438
- doc = xml_document(get_attribute(links[:policy], "application/xml"))
405
+ links[:policy] = URI.parse(xpath_attr(doc, XPaths[:policy], "href"))
406
+ doc = xml_document(read(links[:policy], "application/xml"))
439
407
 
440
408
  links[:permlisteners] =
441
- URI.parse(xpath_attr(doc, XPaths[:permlstt], "href")).path
409
+ URI.parse(xpath_attr(doc, XPaths[:permlstt], "href"))
442
410
  links[:notifications] =
443
- URI.parse(xpath_attr(doc, XPaths[:notify], "href")).path
411
+ URI.parse(xpath_attr(doc, XPaths[:notify], "href"))
444
412
 
445
413
  links[:runlimit] =
446
- URI.parse(xpath_attr(doc, XPaths[:runlimit], "href")).path
414
+ URI.parse(xpath_attr(doc, XPaths[:runlimit], "href"))
447
415
  links[:permworkflows] =
448
- URI.parse(xpath_attr(doc, XPaths[:permwkf], "href")).path
416
+ URI.parse(xpath_attr(doc, XPaths[:permwkf], "href"))
449
417
 
450
418
  links
451
419
  end
452
420
 
453
421
  def get_runs(credentials = nil)
454
- run_list = get_attribute("#{@links[:runs]}", "application/xml",
455
- credentials)
422
+ run_list = read(links[:runs], "application/xml", credentials)
456
423
 
457
424
  doc = xml_document(run_list)
458
425
 
459
426
  # get list of run identifiers
460
- ids = []
427
+ run_list = {}
461
428
  xpath_find(doc, XPaths[:run]).each do |run|
462
- ids << xml_node_attribute(run, "href").split('/')[-1]
429
+ uri = URI.parse(xml_node_attribute(run, "href"))
430
+ id = xml_node_content(run)
431
+ run_list[id] = uri
463
432
  end
464
433
 
465
434
  # cache run objects - this must be done per user
466
435
  user = credentials.nil? ? :all : credentials.username
467
436
  @runs[user] = {} unless @runs[user]
468
437
 
469
- # add new runs
470
- ids.each do |id|
438
+ # add new runs to the user cache
439
+ run_list.each_key do |id|
471
440
  if !@runs[user].has_key? id
472
- @runs[user][id] = Run.create(self, "", credentials, id)
441
+ @runs[user][id] = Run.create(self, "", credentials, run_list[id])
473
442
  end
474
443
  end
475
444
 
476
445
  # clear out the expired runs
477
- if @runs[user].length > ids.length
478
- @runs[user].delete_if {|key, val| !ids.member? key}
446
+ if @runs[user].length > run_list.length
447
+ @runs[user].delete_if {|key, val| !run_list.member? key}
479
448
  end
480
449
 
481
450
  @runs[user]
@@ -31,6 +31,9 @@
31
31
  # Author: Robert Haines
32
32
 
33
33
  module T2Server
34
+
35
+ # This module contains various utility methods that the library uses
36
+ # internally.
34
37
  module Util
35
38
 
36
39
  # :call-seq:
@@ -67,5 +70,41 @@ module T2Server
67
70
  path.gsub(/^\//, "").chomp("/")
68
71
  end
69
72
 
73
+ # :call-seq:
74
+ # Util.append_to_uri_path(uri, path) -> URI
75
+ #
76
+ # Appends a path to the end of the path of the given URI.
77
+ def self.append_to_uri_path(uri, path)
78
+ return uri if path == ""
79
+
80
+ new_uri = uri.clone
81
+ new_uri.path = "#{uri.path}/#{path}"
82
+
83
+ new_uri
84
+ end
85
+
86
+ # :call-seq:
87
+ # Util.replace_uri_path(uri, path) -> URI
88
+ #
89
+ # Replace the given URI's path with a new one. The new path must be an
90
+ # absolute path (start with a slash).
91
+ def self.replace_uri_path(uri, path)
92
+ new_uri = uri.clone
93
+ new_uri.path = path
94
+
95
+ new_uri
96
+ end
97
+
98
+ # :call_seq:
99
+ # Util.get_path_leaf_from_uri(uri) -> String
100
+ #
101
+ # Get the final component from the path of a URI. This method returns the
102
+ # empty string (not _nil_ ) if the URI does not have a path.
103
+ def self.get_path_leaf_from_uri(uri)
104
+ path = uri.path.split("/")[-1]
105
+
106
+ path.nil? ? "" : path
107
+ end
108
+
70
109
  end
71
110
  end