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.
@@ -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]}".gsub("//", "/"))
106
+ request = Net::HTTP::Post.new("#{@links[:runs]}")
85
107
  request.content_type = "application/xml"
86
- response = Net::HTTP.new(@host, @port).start do |http|
87
- http.request(request, Fragments::WORKFLOW % workflow)
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
- puts "Sorry, but the server is already running its configured limit of concurrent workflows."
97
- puts "Please try again later."
98
- ""
122
+ raise ServerAtCapacityError.new(@run_limit)
99
123
  else
100
- response_error(response)
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}".gsub("//", "/"))
115
- response = Net::HTTP.new(@host, @port).start {|http| http.request(request)}
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
- puts "Cannot find run #{run.uuid}."
124
- false
162
+ raise RunNotFoundError.new(uuid)
163
+ when Net::HTTPForbidden
164
+ raise AccessForbiddenError.new("run #{uuid}")
125
165
  else
126
- response_error(response)
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
- request = Net::HTTP::Put.new("#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}".gsub("//", "/"))
137
- request.content_type = "application/xml"
138
- response = Net::HTTP.new(@host, @port).start do |http|
139
- http.request(request, Fragments::RUNINPUTVALUE % value)
140
- end
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
- response_error(response)
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
- request = Net::HTTP::Put.new("#{@links[:runs]}/#{run.uuid}/#{run.inputs}/input/#{input}".gsub("//", "/"))
153
- request.content_type = "application/xml"
154
- response = Net::HTTP.new(@host, @port).start do |http|
155
- http.request(request, Fragments::RUNINPUTFILE % filename)
156
- end
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
- response_error(response)
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
- request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{root}".gsub("//", "/"))
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
- response = Net::HTTP.new(@host, @port).start do |http|
171
- http.request(request, Fragments::MKDIR % dir)
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
- puts "Cannot find run #{uuid}."
183
- false
232
+ raise RunNotFoundError.new(uuid)
233
+ when Net::HTTPForbidden
234
+ raise AccessForbiddenError.new("#{dir} on run #{uuid}")
184
235
  else
185
- response_error(response)
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}".gsub("//", "/"))
248
+ request = Net::HTTP::Post.new("#{@links[:runs]}/#{uuid}/#{location}")
193
249
  request.content_type = "application/xml"
194
- response = Net::HTTP.new(@host, @port).start do |http|
195
- http.request(request, Fragments::UPLOAD % [rename, contents])
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
- puts "Error!", response.body
265
+ raise AccessForbiddenError.new("run #{uuid}")
204
266
  else
205
- response_error(response)
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
- request = Net::HTTP::Put.new("#{@links[:runs]}/#{uuid}/#{path}".gsub("//", "/"))
215
- request.content_type = "text/plain"
216
- response = Net::HTTP.new(@host, @port).start {|http| http.request(request, value)}
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
- response_error(response)
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.gsub("//", "/"))
237
- response = Net::HTTP.new(@host, @port).start {|http| http.request(request)}
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
- puts "Cannot find attribute #{path}."
312
+ raise AttributeNotFoundError.new(path)
244
313
  when Net::HTTPForbidden
245
- puts "Verboten!"
314
+ raise AccessForbiddenError.new("attribute #{path}")
246
315
  else
247
- response_error(response)
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