t2-server 0.9.3 → 1.0.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.
@@ -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
  #
@@ -30,23 +30,33 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
+ # :stopdoc:
34
+ # This comment is needed to stop the above licence from being included in the
35
+ # documentation multiple times. Sigh.
33
36
  module T2Server
37
+ # :startdoc:
34
38
 
35
39
  # This class serves as a base class for concrete HTTP credential systems.
40
+ #
41
+ # Instances of this class cannot be used to authenticate a connection;
42
+ # please use HttpBasic instead.
36
43
  class HttpCredentials
37
44
  # The username held by these credentials.
38
45
  attr_reader :username
39
46
 
47
+ # :stopdoc:
40
48
  # Create a set of credentials with the supplied username and password.
41
49
  def initialize(username, password)
42
50
  @username = username
43
51
  @password = password
44
52
  end
53
+ # :startdoc:
45
54
 
46
55
  # :call-seq:
47
- # to_s
56
+ # to_s -> string
48
57
  #
49
- # Return the username held by these credentials.
58
+ # Return a String representation of these credentials. Just the username
59
+ # is returned; the password is kept hidden.
50
60
  def to_s
51
61
  @username
52
62
  end
@@ -55,7 +65,7 @@ module T2Server
55
65
  @@to_s = Kernel.instance_method(:to_s)
56
66
 
57
67
  # :call-seq:
58
- # inspect
68
+ # inspect -> string
59
69
  #
60
70
  # Override the Kernel#inspect method so that the password is not exposed
61
71
  # when it is called.
@@ -64,21 +74,27 @@ module T2Server
64
74
  end
65
75
  end
66
76
 
67
- # A class representing HTTP Basic credentials.
77
+ # A class representing HTTP Basic credentials. Use this class to
78
+ # authenticate operations on a Taverna Server that require it.
79
+ #
80
+ # See also Util.strip_uri_credentials.
68
81
  class HttpBasic < HttpCredentials
69
82
 
70
- # Create a set of credentials with the supplied username and password.
83
+ # :call-seq:
84
+ # new(username, password) -> HttpBasic
85
+ #
86
+ # Create a set of basic credentials using the supplied username and
87
+ # password.
71
88
  def initialize(username, password)
72
89
  super(username, password)
73
90
  end
74
91
 
75
- # :call-seq:
76
- # authenticate(request)
77
- #
92
+ # :stopdoc:
78
93
  # Authenticate the supplied HTTP request with the credentials held within
79
94
  # this class.
80
95
  def authenticate(request)
81
96
  request.basic_auth @username, @password
82
97
  end
98
+ # :startdoc:
83
99
  end
84
100
  end
@@ -39,11 +39,14 @@ module T2Server
39
39
  # stored.
40
40
  #
41
41
  # The parameters that can be set are:
42
- # * :ca_file
43
- # * :ca_path
44
- # * :verify_peer
45
- # * :client_certificate
46
- # * :client_password
42
+ # * :ca_file - A file with the correct CA chain to verify the remote server.
43
+ # * :ca_path - A directory containing the CA files for server verification.
44
+ # * :verify_peer - Use peer verification? (true or false).
45
+ # * :client_certificate - File with the client's certificate and private key.
46
+ # * :client_password - The password to unlock the client's private key.
47
+ # * :ssl_version - The TLS/SSL version to use (:TLSv1, :SSLv23 or :SSLv3).
48
+ # * :open_timeout - The number of seconds to wait while opening a connection.
49
+ # * :read_timeout - The number of seconds to wait while reading from a connection.
47
50
  # All others will be ignored. Any parameters not set will return +nil+ when
48
51
  # queried.
49
52
  class ConnectionParameters
@@ -53,7 +56,10 @@ module T2Server
53
56
  :ca_path,
54
57
  :verify_peer,
55
58
  :client_certificate,
56
- :client_password
59
+ :client_password,
60
+ :ssl_version,
61
+ :open_timeout,
62
+ :read_timeout
57
63
  ]
58
64
  # :startdoc:
59
65
 
@@ -98,6 +104,15 @@ module T2Server
98
104
  end
99
105
  end
100
106
 
107
+ # Connection parameters that specify the use of SSL version 3.
108
+ class SSL3ConnectionParameters < DefaultConnectionParameters
109
+ # Create connection parameters that specify the use of SSL version 3.
110
+ def initialize
111
+ super
112
+ self[:ssl_version] = :SSLv3
113
+ end
114
+ end
115
+
101
116
  # Connection parameters that simplify setting up verification of servers with
102
117
  # "self-signed" or non-standard certificates.
103
118
  class CustomCASSLConnectionParameters < DefaultConnectionParameters
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010-2012 The University of Manchester, UK.
1
+ # Copyright (c) 2010-2013 The University of Manchester, UK.
2
2
  #
3
3
  # All rights reserved.
4
4
  #
@@ -30,7 +30,11 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
+ # :stopdoc:
34
+ # This comment is needed to stop the above licence from being included in the
35
+ # documentation multiple times. Sigh.
33
36
  module T2Server
37
+ # :startdoc:
34
38
 
35
39
  # Base class of InputPort and OutputPort
36
40
  class Port
@@ -91,7 +95,7 @@ module T2Server
91
95
  end
92
96
 
93
97
  # :call-seq:
94
- # file? -> bool
98
+ # file? -> true or false
95
99
  #
96
100
  # Is this port's data being supplied by a file? The file could be local or
97
101
  # remote (already on the server) for this to return true.
@@ -100,7 +104,7 @@ module T2Server
100
104
  end
101
105
 
102
106
  # :call-seq:
103
- # remote_file? -> bool
107
+ # remote_file? -> true or false
104
108
  #
105
109
  # Is this port's data being supplied by a remote (one that is already on
106
110
  # the server) file?
@@ -135,7 +139,7 @@ module T2Server
135
139
  end
136
140
 
137
141
  # :call-seq:
138
- # baclava? -> bool
142
+ # baclava? -> true or false
139
143
  #
140
144
  # Has this port been set via a baclava document?
141
145
  def baclava?
@@ -143,11 +147,11 @@ module T2Server
143
147
  end
144
148
 
145
149
  # :call-seq:
146
- # set? -> bool
150
+ # set? -> true or false
147
151
  #
148
152
  # Has this port been set?
149
153
  def set?
150
- !value.nil? or file? or baclava?
154
+ !value.nil? || file? || baclava?
151
155
  end
152
156
  end
153
157
 
@@ -173,13 +177,28 @@ module T2Server
173
177
  # :startdoc:
174
178
 
175
179
  # :call-seq:
176
- # error? -> bool
180
+ # error? -> true or false
177
181
  #
178
182
  # Is there an error associated with this output port?
179
183
  def error?
180
184
  @error
181
185
  end
182
186
 
187
+ # :call-seq:
188
+ # empty? -> true or false
189
+ #
190
+ # Is this output port empty?
191
+ #
192
+ # Note that if the output port holds a list then it is not considered
193
+ # empty, even if that list is empty. This is because the port itself is
194
+ # not empty, there is a list there! A separate test should be performed to
195
+ # see if that list is empty or not.
196
+ def empty?
197
+ # Funnily enough, an empty list does *not* make a port empty!
198
+ return false if @structure.instance_of? Array
199
+ @structure.empty?
200
+ end
201
+
183
202
  # :call-seq:
184
203
  # [int] -> obj
185
204
  #
@@ -195,20 +214,27 @@ module T2Server
195
214
  end
196
215
 
197
216
  # :call-seq:
198
- # value -> obj
199
- # value(range) -> obj
200
- # value -> Array
201
- #
202
- # For singleton outputs get the value (or part of it). For list outputs
203
- # get all the values in an Array structure that mirrors the structure of
204
- # the output port. To get part of a value from a list use
205
- # 'port[].value(range)'.
206
- def value(range = nil)
217
+ # value -> binary blob
218
+ # value(range) -> binary blob
219
+ # value {|chunk| ...}
220
+ # value(range) {|chunk| ...}
221
+ # value -> array
222
+ #
223
+ # For singleton outputs download or stream the data (or part of it) held
224
+ # by the output port. Please see the documentation for PortValue#value for
225
+ # full details.
226
+ #
227
+ # For list outputs all data values are downloaded into memory and returned
228
+ # in an Array structure that mirrors the structure of the output port. Do
229
+ # not use this form if the output port has large amounts of data! To get
230
+ # part of a value from a list use something like:
231
+ # run.output_port("port_name")[0].value(0..100)
232
+ def value(range = nil, &block)
207
233
  if depth == 0
208
234
  if range.nil?
209
- @structure.value
235
+ @structure.value(&block)
210
236
  else
211
- @structure.value(range)
237
+ @structure.value(range, &block)
212
238
  end
213
239
  else
214
240
  @values = strip(:value) if @values.nil?
@@ -217,8 +243,55 @@ module T2Server
217
243
  end
218
244
 
219
245
  # :call-seq:
220
- # reference -> String
221
- # reference -> Array
246
+ # stream_value(stream) -> fixnum
247
+ # stream_value(stream, range) -> fixnum
248
+ #
249
+ # Stream a singleton port value directly to another stream and return the
250
+ # number of bytes written. If a range is supplied then only that range of
251
+ # data is streamed from the server. The stream passed in may be anything
252
+ # that provides a +write+ method; instances of IO and File, for example.
253
+ # No data is cached by this method.
254
+ #
255
+ # To stream parts of a list port, use PortValue#stream_value on the list
256
+ # item directly:
257
+ # run.output_port("port_name")[0].stream_value(stream)
258
+ def stream_value(stream, range = nil)
259
+ return 0 unless depth == 0
260
+ raise ArgumentError,
261
+ "Stream passed in must provide a write method" unless
262
+ stream.respond_to? :write
263
+
264
+ if range.nil?
265
+ @structure.stream_value(stream)
266
+ else
267
+ @structure.stream_value(stream, range)
268
+ end
269
+ end
270
+
271
+ # :call-seq:
272
+ # write_value_to_file(filename) -> fixnum
273
+ # write_value_to_file(filename, range) -> fixnum
274
+ #
275
+ # Stream a singleton port value to a file and return the number of bytes
276
+ # written. If a range is supplied then only that range of data is
277
+ # downloaded from the server.
278
+ #
279
+ # To save parts of a list port to a file, use
280
+ # PortValue#write_value_to_file on the list item directly:
281
+ # run.output_port("port_name")[0].write_value_to_file
282
+ def write_value_to_file(filename, range = nil)
283
+ return 0 unless depth == 0
284
+
285
+ if range.nil?
286
+ @structure.write_value_to_file(filename)
287
+ else
288
+ @structure.write_value_to_file(filename, range)
289
+ end
290
+ end
291
+
292
+ # :call-seq:
293
+ # reference -> string
294
+ # reference -> array
222
295
  #
223
296
  # Get URI references to the data values of this output port as strings.
224
297
  #
@@ -226,50 +299,35 @@ module T2Server
226
299
  # uris is returned. For an individual reference from a list use
227
300
  # 'port[].reference'.
228
301
  def reference
229
- @refs = strip(:reference) if @refs.nil?
230
- @refs
302
+ @refs ||= strip(:reference)
231
303
  end
232
304
 
233
305
  # :call-seq:
234
- # type -> String
235
- # type -> Array
306
+ # type -> string
307
+ # type -> array
236
308
  #
237
309
  # Get the mime type of the data value in this output port.
238
310
  #
239
311
  # For a singleton output a single type is returned. For lists an array of
240
312
  # types is returned. For an individual type from a list use 'port[].type'.
241
313
  def type
242
- @types = strip(:type) if @types.nil?
243
- @types
314
+ @types ||= strip(:type)
244
315
  end
245
316
 
246
317
  # :call-seq:
247
- # size -> int
248
- # size -> Array
318
+ # size -> fixnum
319
+ # size -> array
249
320
  #
250
321
  # Get the data size of the data value in this output port.
251
322
  #
252
323
  # For a singleton output a single size is returned. For lists an array of
253
324
  # sizes is returned. For an individual size from a list use 'port[].size'.
254
325
  def size
255
- @sizes = strip(:size) if @sizes.nil?
256
- @sizes
257
- end
258
-
259
- # :call-seq:
260
- # error -> String
261
- #
262
- # Get the error message (if there is one) of this output port.
263
- #
264
- # This method is only for use on outputs of depth 0. For other depths use
265
- # 'port[].error'
266
- def error
267
- return nil unless depth == 0
268
- @structure.error
326
+ @sizes ||= strip(:size)
269
327
  end
270
328
 
271
329
  # :call-seq:
272
- # total_size -> int
330
+ # total_size -> fixnum
273
331
  #
274
332
  # Return the total data size of all the data in this output port.
275
333
  def total_size
@@ -282,9 +340,42 @@ module T2Server
282
340
  end
283
341
  end
284
342
 
343
+ # :call-seq:
344
+ # zip -> binary blob
345
+ # zip(filename) -> fixnum
346
+ # zip(stream) -> fixnum
347
+ # zip {|chunk| ...}
348
+ #
349
+ # Get the data in this output port directly from the server in zip format.
350
+ #
351
+ # This method does not work with singleton ports. Taverna Server cannot
352
+ # currently return zip files of singleton ports on their own. If you wish
353
+ # to get a singleton port in a zip file then you can use Run#zip_output
354
+ # which will return all outputs in a single file.
355
+ #
356
+ # If this method is called on a singleton port it will return +nil+ and
357
+ # streaming from it will return nothing.
358
+ #
359
+ # Calling this method with no parameters will simply return a blob of
360
+ # zipped data. Providing a filename will stream the data directly to that
361
+ # file and return the number of bytes written. Passing in an object that
362
+ # has a +write+ method (for example, an instance of File or IO) will
363
+ # stream the zip data directly to that object and return the number of
364
+ # bytes that were streamed. Passing in a block will allow access to the
365
+ # underlying data stream:
366
+ # port.zip do |chunk|
367
+ # print chunk
368
+ # end
369
+ #
370
+ # Raises RunStateError if the run has not finished running.
371
+ def zip(param = nil, &block)
372
+ return nil if depth == 0
373
+ @run.zip_output(param, name, &block)
374
+ end
375
+
285
376
  # :stopdoc:
286
- def download(uri, range = nil)
287
- @run.download_output_data(uri, range)
377
+ def download(uri, range = nil, &block)
378
+ @run.download_output_data(uri, range, &block)
288
379
  end
289
380
  # :startdoc:
290
381
 
@@ -301,14 +392,15 @@ module T2Server
301
392
  return data
302
393
  when 'value'
303
394
  return PortValue.new(self, xml_node_attribute(node, 'href'), false,
304
- xml_node_attribute(node, 'contentType'),
305
- xml_node_attribute(node, 'contentByteLength').to_i)
395
+ xml_node_attribute(node, 'contentByteLength').to_i,
396
+ xml_node_attribute(node, 'contentType'))
306
397
  when 'error'
307
398
  @error = true
308
- return PortValue.new(self, xml_node_attribute(node, 'href'), true)
399
+ return PortValue.new(self, xml_node_attribute(node, 'href'), true,
400
+ xml_node_attribute(node, 'errorByteLength').to_i)
309
401
  when 'absent'
310
402
  if current_depth == @depth
311
- return PortValue.new(self, "", false, "application/x-empty")
403
+ return PortValue.new(self, "", false, 0, "application/x-empty")
312
404
  else
313
405
  return []
314
406
  end
@@ -345,95 +437,116 @@ module T2Server
345
437
  # The size (in bytes) of the port value.
346
438
  attr_reader :size
347
439
 
440
+ # The mime-type we use for an error value.
441
+ ERROR_TYPE = "application/x-error"
442
+
443
+ # The mime-type we use for an empty value. Note that an empty value is not
444
+ # simply an empty string. It is the complete absence of a value.
445
+ EMPTY_TYPE = "application/x-empty"
446
+
348
447
  # :stopdoc:
349
- def initialize(port, ref, error, type = "", size = 0)
448
+ def initialize(port, ref, error, size, type = "")
350
449
  @port = port
351
450
  @reference = URI.parse(ref)
352
- @type = type
451
+ @type = (error ? ERROR_TYPE : type)
353
452
  @size = size
354
- @value = nil
355
- @vgot = nil
356
- @error = nil
357
-
358
- if error
359
- @error = @port.download(@reference)
360
- @type = "error"
361
- end
453
+ @error = error
362
454
  end
363
455
  # :startdoc:
364
456
 
365
457
  # :call-seq:
366
- # value -> obj
367
- # value(range) -> obj
368
- #
369
- # Return the value of this port. It is downloaded from the server if it
370
- # has not already been retrieved. If a range is specified then just that
371
- # portion of the value is downloaded and returned. If no range is specified
372
- # then the whole value is downloaded and returned.
373
- #
374
- # All downloaded data is cached and not downloaded a second time if the
375
- # same or similar ranges are requested.
376
- def value(range = 0...@size)
377
- return nil if error?
378
- return "" if @type == "application/x-empty"
379
- return @value if range == :debug
380
-
381
- # check that the range provided is sensible
458
+ # value -> binary blob
459
+ # value(range) -> binary blob
460
+ # value {|chunk| ...}
461
+ # value(range) {|chunk| ...}
462
+ #
463
+ # Get the value of this port from the server.
464
+ #
465
+ # If no parameters are supplied then this method will simply download and
466
+ # return all the data.
467
+ #
468
+ # Passing in a block will allow access to the underlying data stream so
469
+ # the data is not stored in memory:
470
+ # run.output_port("port") do |chunk|
471
+ # print chunk
472
+ # end
473
+ #
474
+ # In both cases supplying a Range will download and return the data in
475
+ # that range.
476
+ #
477
+ # This method does not cache any data.
478
+ #
479
+ # If this port is an error then this value will be the error message.
480
+ def value(range = 0...@size, &block)
481
+ # The following block is a workaround for Taverna Server versions prior
482
+ # to 2.4.1 and can be removed when support for those versions is no
483
+ # longer required.
484
+ if error? && @size == 0
485
+ value = @port.download(@reference)
486
+ @size = value.size
487
+ range = 0...@size if range.min.nil?
488
+ return value[range]
489
+ end
490
+
491
+ return "" if @type == EMPTY_TYPE
492
+
493
+ # Check that the range provided is sensible
382
494
  range = 0..range.max if range.min < 0
383
495
  range = range.min...@size if range.max >= @size
384
496
 
385
- need = fill(@vgot, range)
386
- case need.length
387
- when 0
388
- # we already have all the data we need, just return the right bit.
389
- # @vgot cannot be nil here and must fully encompass range.
390
- ret_range = (range.min - @vgot.min)..(range.max - @vgot.min)
391
- @value[ret_range]
392
- when 1
393
- # we either have some data, at one end of range or either side of it,
394
- # or none. @vgot can be nil here.
395
- # In both cases we download what we need.
396
- new_data = @port.download(@reference, need[0])
397
- if @vgot.nil?
398
- # this is the only data we have, return it all.
399
- @vgot = range
400
- @value = new_data
401
- else
402
- # add the new data to the correct end of the data we have, then
403
- # return the range requested.
404
- if range.max <= @vgot.max
405
- @vgot = range.min..@vgot.max
406
- @value = new_data + @value
407
- @value[0..range.max]
408
- else
409
- @vgot = @vgot.min..range.max
410
- @value = @value + new_data
411
- @value[(range.min - @vgot.min)..@vgot.max]
412
- end
413
- end
414
- when 2
415
- # we definitely have some data and it is in the middle of the
416
- # range requested. @vgot cannot be nil here.
417
- @vgot = range
418
- @value = @port.download(@reference, need[0]) + @value +
419
- @port.download(@reference, need[1])
497
+ @port.download(@reference, range, &block)
498
+ end
499
+
500
+ # :call-seq:
501
+ # stream_value(stream) -> fixnum
502
+ # stream_value(stream, range) -> fixnum
503
+ #
504
+ # Stream this port value directly into another stream. The stream passed
505
+ # in may be anything that provides a +write+ method; instances of IO and
506
+ # File, for example. No data is cached by this method.
507
+ #
508
+ # The number of bytes written to the stream is returned.
509
+ def stream_value(stream, range = 0...@size)
510
+ raise ArgumentError,
511
+ "Stream passed in must provide a write method" unless
512
+ stream.respond_to? :write
513
+
514
+ bytes = 0
515
+
516
+ value(range) do |chunk|
517
+ bytes += stream.write(chunk)
420
518
  end
519
+
520
+ bytes
421
521
  end
422
522
 
423
523
  # :call-seq:
424
- # error? -> bool
524
+ # write_value_to_file(filename) -> fixnum
525
+ # write_value_to_file(filename, range) -> fixnum
526
+ #
527
+ # Stream this port value directly to a file. If a range is supplied then
528
+ # just that range of data is downloaded from the server. No data is cached
529
+ # by this method.
530
+ def write_value_to_file(filename, range = 0...@size)
531
+ File.open(filename, "wb") do |file|
532
+ stream_value(file, range)
533
+ end
534
+ end
535
+
536
+ # :call-seq:
537
+ # error? -> true or false
425
538
  #
426
539
  # Does this port represent an error?
427
540
  def error?
428
- !@error.nil?
541
+ @error
429
542
  end
430
543
 
431
544
  # :call-seq:
432
- # error -> string
545
+ # empty? -> true or false
433
546
  #
434
- # Return the error message for this value, or _nil_ if it is not an error.
435
- def error
436
- @error
547
+ # Is this port value empty?
548
+ def empty?
549
+ @type == EMPTY_TYPE
437
550
  end
438
551
 
439
552
  # Used within #inspect, below to help override the built in version.
@@ -450,29 +563,5 @@ module T2Server
450
563
  }
451
564
  end
452
565
 
453
- private
454
- def fill(got, want)
455
- return [want] if got.nil?
456
-
457
- if got.member? want.min
458
- if got.member? want.max
459
- return []
460
- else
461
- return [(got.max + 1)..want.max]
462
- end
463
- else
464
- if got.member? want.max
465
- return [want.min..(got.min - 1)]
466
- else
467
- if want.max < got.min
468
- return [want.min..(got.min - 1)]
469
- elsif want.min > got.max
470
- return [(got.max + 1)..want.max]
471
- else
472
- return [want.min..(got.min - 1), (got.max + 1)..want.max]
473
- end
474
- end
475
- end
476
- end
477
566
  end
478
567
  end