t2-server 0.0.4 → 0.1.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.
- data/README.rdoc +9 -2
- data/bin/delete_all_runs +6 -1
- data/bin/run_workflow +18 -9
- data/bin/server_info +14 -9
- data/lib/t2server.rb +31 -0
- data/lib/t2server/exceptions.rb +153 -0
- data/lib/t2server/run.rb +215 -31
- data/lib/t2server/server.rb +175 -95
- data/test/tc_paths.rb +60 -0
- data/test/tc_run.rb +72 -0
- data/test/tc_server.rb +71 -0
- data/test/ts_t2server.rb +52 -0
- data/test/workflows/hello.t2flow +21 -0
- metadata +27 -8
data/lib/t2server/server.rb
CHANGED
@@ -37,6 +37,9 @@ require 'rexml/document'
|
|
37
37
|
include REXML
|
38
38
|
|
39
39
|
module T2Server
|
40
|
+
|
41
|
+
# An interface for directly communicating with one or more Taverna 2 Server
|
42
|
+
# instances.
|
40
43
|
class Server
|
41
44
|
private_class_method :new
|
42
45
|
attr_reader :uri, :run_limit
|
@@ -44,9 +47,11 @@ module T2Server
|
|
44
47
|
# list of servers we know about
|
45
48
|
@@servers = []
|
46
49
|
|
50
|
+
# :stopdoc:
|
51
|
+
# New is private but rdoc does not get it right! Hence :stopdoc: section.
|
47
52
|
def initialize(uri)
|
48
|
-
@uri = uri
|
49
|
-
uri = URI.parse(uri)
|
53
|
+
@uri = uri.strip_path
|
54
|
+
uri = URI.parse(@uri)
|
50
55
|
@host = uri.host
|
51
56
|
@port = uri.port
|
52
57
|
@base_path = uri.path
|
@@ -61,7 +66,15 @@ module T2Server
|
|
61
66
|
@runs = {}
|
62
67
|
@runs = get_runs
|
63
68
|
end
|
69
|
+
# :startdoc:
|
64
70
|
|
71
|
+
# :call-seq:
|
72
|
+
# Server.connect(uri) -> server
|
73
|
+
#
|
74
|
+
# Connect to the server specified by _uri_ which should be of the form:
|
75
|
+
# http://example.com:8888/blah
|
76
|
+
#
|
77
|
+
# A Server instance is returned that represents the connection.
|
65
78
|
def Server.connect(uri)
|
66
79
|
# see if we've already got this server
|
67
80
|
server = @@servers.find {|s| s.uri == uri}
|
@@ -74,17 +87,30 @@ module T2Server
|
|
74
87
|
|
75
88
|
server
|
76
89
|
end
|
77
|
-
|
90
|
+
|
91
|
+
# :call-seq:
|
92
|
+
# server.create_run(workflow) -> run
|
93
|
+
#
|
94
|
+
# Create a run on this server using the specified _workflow_.
|
78
95
|
def create_run(workflow)
|
79
96
|
uuid = initialize_run(workflow)
|
80
97
|
@runs[uuid] = Run.create(self, "", uuid)
|
81
98
|
end
|
82
|
-
|
99
|
+
|
100
|
+
# :call-seq:
|
101
|
+
# server.initialize_run(workflow) -> string
|
102
|
+
#
|
103
|
+
# Create a run on this server using the specified _workflow_ but do not
|
104
|
+
# return it as a Run instance. Return its UUID instead.
|
83
105
|
def initialize_run(workflow)
|
84
|
-
request = Net::HTTP::Post.new("#{@links[:runs]}"
|
106
|
+
request = Net::HTTP::Post.new("#{@links[:runs]}")
|
85
107
|
request.content_type = "application/xml"
|
86
|
-
|
87
|
-
|
108
|
+
begin
|
109
|
+
response = Net::HTTP.new(@host, @port).start do |http|
|
110
|
+
http.request(request, Fragments::WORKFLOW % workflow)
|
111
|
+
end
|
112
|
+
rescue InternalHTTPError => e
|
113
|
+
raise ConnectionError.new(e)
|
88
114
|
end
|
89
115
|
|
90
116
|
case response
|
@@ -93,26 +119,39 @@ module T2Server
|
|
93
119
|
epr = URI.parse(response['location'])
|
94
120
|
epr.path[-36..-1]
|
95
121
|
when Net::HTTPForbidden
|
96
|
-
|
97
|
-
puts "Please try again later."
|
98
|
-
""
|
122
|
+
raise ServerAtCapacityError.new(@run_limit)
|
99
123
|
else
|
100
|
-
|
101
|
-
""
|
124
|
+
raise UnexpectedServerResponse.new(response)
|
102
125
|
end
|
103
126
|
end
|
104
|
-
|
127
|
+
|
128
|
+
# :call-seq:
|
129
|
+
# server.runs -> [runs]
|
130
|
+
#
|
131
|
+
# Return the set of runs on this server.
|
105
132
|
def runs
|
106
133
|
get_runs.values
|
107
134
|
end
|
108
|
-
|
135
|
+
|
136
|
+
# :call-seq:
|
137
|
+
# server.run(uuid) -> run
|
138
|
+
#
|
139
|
+
# Return the specified run.
|
109
140
|
def run(uuid)
|
110
141
|
get_runs[uuid]
|
111
142
|
end
|
112
143
|
|
144
|
+
# :call-seq:
|
145
|
+
# server.delete_run(uuid) -> bool
|
146
|
+
#
|
147
|
+
# Delete the specified run from the server, discarding all of its state.
|
113
148
|
def delete_run(uuid)
|
114
|
-
request = Net::HTTP::Delete.new("#{@links[:runs]}/#{uuid}"
|
115
|
-
|
149
|
+
request = Net::HTTP::Delete.new("#{@links[:runs]}/#{uuid}")
|
150
|
+
begin
|
151
|
+
response = Net::HTTP.new(@host, @port).start {|http| http.request(request)}
|
152
|
+
rescue InternalHTTPError => e
|
153
|
+
raise ConnectionError.new(e)
|
154
|
+
end
|
116
155
|
|
117
156
|
case response
|
118
157
|
when Net::HTTPNoContent
|
@@ -120,131 +159,183 @@ module T2Server
|
|
120
159
|
@runs.delete(uuid)
|
121
160
|
true
|
122
161
|
when Net::HTTPNotFound
|
123
|
-
|
124
|
-
|
162
|
+
raise RunNotFoundError.new(uuid)
|
163
|
+
when Net::HTTPForbidden
|
164
|
+
raise AccessForbiddenError.new("run #{uuid}")
|
125
165
|
else
|
126
|
-
|
166
|
+
raise UnexpectedServerResponse.new(response)
|
127
167
|
end
|
128
168
|
end
|
129
|
-
|
169
|
+
|
170
|
+
# :call-seq:
|
171
|
+
# server.delete_all_runs
|
172
|
+
#
|
173
|
+
# Delete all runs on this server, discarding all of their state.
|
130
174
|
def delete_all_runs
|
131
175
|
# first refresh run list
|
132
176
|
runs.each {|run| run.delete}
|
133
177
|
end
|
134
|
-
|
178
|
+
|
179
|
+
# :call-seq:
|
180
|
+
# server.set_run_input(run, input, value) -> bool
|
181
|
+
#
|
182
|
+
# Set the workflow input port _input_ on run _run_ to _value_.
|
135
183
|
def set_run_input(run, input, value)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
case response
|
143
|
-
when Net::HTTPOK
|
144
|
-
# Yay!
|
145
|
-
true
|
184
|
+
path = "#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}"
|
185
|
+
set_attribute(path, Fragments::RUNINPUTVALUE % value, "application/xml")
|
186
|
+
rescue AttributeNotFoundError => e
|
187
|
+
if get_runs.has_key? uuid
|
188
|
+
raise e
|
146
189
|
else
|
147
|
-
|
190
|
+
raise RunNotFoundError.new(uuid)
|
148
191
|
end
|
149
192
|
end
|
150
193
|
|
194
|
+
# :call-seq:
|
195
|
+
# server.set_run_input_file(run, input, filename) -> bool
|
196
|
+
#
|
197
|
+
# Set the workflow input port _input_ on run _run_ to use the file at
|
198
|
+
# _filename_ for its input.
|
151
199
|
def set_run_input_file(run, input, filename)
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
case response
|
159
|
-
when Net::HTTPOK
|
160
|
-
# Yay!
|
161
|
-
true
|
200
|
+
path = "#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}"
|
201
|
+
set_attribute(path, Fragments::RUNINPUTFILE % filename, "application/xml")
|
202
|
+
rescue AttributeNotFoundError => e
|
203
|
+
if get_runs.has_key? uuid
|
204
|
+
raise e
|
162
205
|
else
|
163
|
-
|
206
|
+
raise RunNotFoundError.new(uuid)
|
164
207
|
end
|
165
208
|
end
|
166
209
|
|
210
|
+
# :call-seq:
|
211
|
+
# server.make_run_dir(uuid, root, dir) -> bool
|
212
|
+
#
|
213
|
+
# Create a directory _dir_ within the directory _root_ on the run with
|
214
|
+
# identifier _uuid_. This is mainly for use by Run#mkdir.
|
167
215
|
def make_run_dir(uuid, root, dir)
|
168
|
-
|
216
|
+
raise AccessForbiddenError.new("subdirectories (#{dir})") if dir.include? ?/
|
217
|
+
request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{root}")
|
169
218
|
request.content_type = "application/xml"
|
170
|
-
|
171
|
-
|
219
|
+
begin
|
220
|
+
response = Net::HTTP.new(@host, @port).start do |http|
|
221
|
+
http.request(request, Fragments::MKDIR % dir)
|
222
|
+
end
|
223
|
+
rescue InternalHTTPError => e
|
224
|
+
raise ConnectionError.new(e)
|
172
225
|
end
|
173
|
-
|
226
|
+
|
174
227
|
case response
|
175
228
|
when Net::HTTPCreated
|
176
229
|
# OK, carry on...
|
177
230
|
true
|
178
|
-
when Net::HTTPForbidden
|
179
|
-
puts "Error!", response.body
|
180
|
-
false
|
181
231
|
when Net::HTTPNotFound
|
182
|
-
|
183
|
-
|
232
|
+
raise RunNotFoundError.new(uuid)
|
233
|
+
when Net::HTTPForbidden
|
234
|
+
raise AccessForbiddenError.new("#{dir} on run #{uuid}")
|
184
235
|
else
|
185
|
-
|
236
|
+
raise UnexpectedServerResponse.new(response)
|
186
237
|
end
|
187
238
|
end
|
188
|
-
|
239
|
+
|
240
|
+
# :call-seq:
|
241
|
+
# server.upload_run_file(uuid, filename, location, rename) -> string
|
242
|
+
#
|
243
|
+
# Upload a file to the run with identifier _uuid_. Mainly for internal use
|
244
|
+
# by Run#upload_file.
|
189
245
|
def upload_run_file(uuid, filename, location, rename)
|
190
246
|
contents = Base64.encode64(IO.read(filename))
|
191
247
|
rename = filename.split('/')[-1] if rename == ""
|
192
|
-
request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{location}"
|
248
|
+
request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{location}")
|
193
249
|
request.content_type = "application/xml"
|
194
|
-
|
195
|
-
|
250
|
+
begin
|
251
|
+
response = Net::HTTP.new(@host, @port).start do |http|
|
252
|
+
http.request(request, Fragments::UPLOAD % [rename, contents])
|
253
|
+
end
|
254
|
+
rescue InternalHTTPError => e
|
255
|
+
raise ConnectionError.new(e)
|
196
256
|
end
|
197
|
-
|
257
|
+
|
198
258
|
case response
|
199
259
|
when Net::HTTPCreated
|
200
260
|
# Success, return remote name of uploaded file
|
201
261
|
rename
|
262
|
+
when Net::HTTPNotFound
|
263
|
+
raise RunNotFoundError.new(uuid)
|
202
264
|
when Net::HTTPForbidden
|
203
|
-
|
265
|
+
raise AccessForbiddenError.new("run #{uuid}")
|
204
266
|
else
|
205
|
-
|
267
|
+
raise UnexpectedServerResponse.new(response)
|
206
268
|
end
|
207
269
|
end
|
208
270
|
|
271
|
+
# :call-seq:
|
272
|
+
# server.get_run_attribute(uuid, path) -> string
|
273
|
+
#
|
274
|
+
# Get the attribute at _path_ in the run with identifier _uuid_.
|
209
275
|
def get_run_attribute(uuid, path)
|
210
276
|
get_attribute("#{@links[:runs]}/#{uuid}/#{path}")
|
277
|
+
rescue AttributeNotFoundError => e
|
278
|
+
if get_runs.has_key? uuid
|
279
|
+
raise e
|
280
|
+
else
|
281
|
+
raise RunNotFoundError.new(uuid)
|
282
|
+
end
|
211
283
|
end
|
212
284
|
|
285
|
+
# :call-seq:
|
286
|
+
# server.set_run_attribute(uuid, path, value) -> bool
|
287
|
+
#
|
288
|
+
# Set the attribute at _path_ in the run with identifier _uuid_ to _value_.
|
213
289
|
def set_run_attribute(uuid, path, value)
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
case response
|
219
|
-
when Net::HTTPOK
|
220
|
-
# OK, so carry on
|
221
|
-
true
|
222
|
-
when Net::HTTPForbidden
|
223
|
-
puts "Error!"
|
224
|
-
puts response.body
|
225
|
-
false
|
226
|
-
when Net::HTTPNotFound
|
227
|
-
puts "Cannot find run #{uuid}."
|
228
|
-
false
|
290
|
+
set_attribute("#{@links[:runs]}/#{uuid}/#{path}", value, "text/plain")
|
291
|
+
rescue AttributeNotFoundError => e
|
292
|
+
if get_runs.has_key? uuid
|
293
|
+
raise e
|
229
294
|
else
|
230
|
-
|
295
|
+
raise RunNotFoundError.new(uuid)
|
231
296
|
end
|
232
297
|
end
|
233
|
-
|
298
|
+
|
234
299
|
private
|
235
300
|
def get_attribute(path)
|
236
|
-
request = Net::HTTP::Get.new(path
|
237
|
-
|
238
|
-
|
301
|
+
request = Net::HTTP::Get.new(path)
|
302
|
+
begin
|
303
|
+
response = Net::HTTP.new(@host, @port).start {|http| http.request(request)}
|
304
|
+
rescue InternalHTTPError => e
|
305
|
+
raise ConnectionError.new(e)
|
306
|
+
end
|
307
|
+
|
239
308
|
case response
|
240
309
|
when Net::HTTPOK
|
241
310
|
return response.body
|
242
311
|
when Net::HTTPNotFound
|
243
|
-
|
312
|
+
raise AttributeNotFoundError.new(path)
|
244
313
|
when Net::HTTPForbidden
|
245
|
-
|
314
|
+
raise AccessForbiddenError.new("attribute #{path}")
|
246
315
|
else
|
247
|
-
|
316
|
+
raise UnexpectedServerResponse.new(response)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def set_attribute(path, value, type)
|
321
|
+
request = Net::HTTP::Put.new(path)
|
322
|
+
request.content_type = type
|
323
|
+
begin
|
324
|
+
response = Net::HTTP.new(@host, @port).start {|http| http.request(request, value)}
|
325
|
+
rescue InternalHTTPError => e
|
326
|
+
raise ConnectionError.new(e)
|
327
|
+
end
|
328
|
+
|
329
|
+
case response
|
330
|
+
when Net::HTTPOK
|
331
|
+
# OK, so carry on
|
332
|
+
true
|
333
|
+
when Net::HTTPNotFound
|
334
|
+
raise AttributeNotFoundError.new(path)
|
335
|
+
when Net::HTTPForbidden
|
336
|
+
raise AccessForbiddenError.new("attribute #{path}")
|
337
|
+
else
|
338
|
+
raise UnexpectedServerResponse.new(response)
|
248
339
|
end
|
249
340
|
end
|
250
341
|
|
@@ -258,18 +349,7 @@ module T2Server
|
|
258
349
|
:permlisteners => URI.parse(XPath.first(doc, "//nsr:permittedListeners", nsmap).attributes["href"]).path
|
259
350
|
}
|
260
351
|
end
|
261
|
-
|
262
|
-
def response_error(response)
|
263
|
-
puts "Unnexpected response from Taverna Server!"
|
264
|
-
puts "Server is: #{@host}:#{@port}#{@base_path}"
|
265
|
-
puts "Response code is: #{response.code}"
|
266
|
-
if response.body
|
267
|
-
puts "Response body is: \n#{response.body}"
|
268
|
-
end
|
269
|
-
puts "\nRaw error is: \n#{response.error!}"
|
270
|
-
false
|
271
|
-
end
|
272
|
-
|
352
|
+
|
273
353
|
def get_runs
|
274
354
|
run_list = get_attribute("#{@links[:runs]}")
|
275
355
|
|
data/test/tc_paths.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright (c) 2010, The University of Manchester, UK.
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
13
|
+
# and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# * Neither the names of The University of Manchester nor the names of its
|
16
|
+
# contributors may be used to endorse or promote products derived from this
|
17
|
+
# software without specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
#
|
31
|
+
# Author: Robert Haines
|
32
|
+
|
33
|
+
require 't2server'
|
34
|
+
require 'test/unit'
|
35
|
+
|
36
|
+
class TestFilePaths < Test::Unit::TestCase
|
37
|
+
def test_standard
|
38
|
+
assert_equal("dir/with/child", "dir/with/child".strip_path)
|
39
|
+
assert_equal("dir/with/child", "/dir/with/child".strip_path)
|
40
|
+
assert_equal("dir/with/child", "dir/with/child/".strip_path)
|
41
|
+
assert_equal("dir/with/child", "/dir/with/child/".strip_path)
|
42
|
+
|
43
|
+
# make sure it is not stripping in place
|
44
|
+
dir = "/dir/with/child/"
|
45
|
+
dir.strip_path
|
46
|
+
assert_equal("/dir/with/child/", dir)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_inplace
|
50
|
+
assert_nil("dir/with/child".strip_path!)
|
51
|
+
assert_not_nil("/dir/with/child".strip_path!)
|
52
|
+
assert_not_nil("dir/with/child/".strip_path!)
|
53
|
+
assert_not_nil("/dir/with/child/".strip_path!)
|
54
|
+
|
55
|
+
# make sure it is stripping in place
|
56
|
+
dir = "/dir/with/child/"
|
57
|
+
dir.strip_path!
|
58
|
+
assert_equal("dir/with/child", dir)
|
59
|
+
end
|
60
|
+
end
|