t2-server 0.9.1 → 0.9.2

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