t2-server 0.0.4 → 0.1.0

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