t2-server 0.9.3 → 1.0.0

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