summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorChristoph Helma <helma@in-silico.ch>2012-02-28 17:13:20 +0000
committerChristoph Helma <helma@in-silico.ch>2012-02-28 17:13:20 +0000
commitf40871b9b60ae706c0668c9ac4cfbfff866ce5dc (patch)
treee63e0a08523ea8ee07b933af2023cfd5f47f15a8 /lib
parentfa9069e13fb6b1c8bb4ebcdf82f1cf1c04ad71ca (diff)
generic rest-client calls ignoring http errors from task services
Diffstat (limited to 'lib')
-rw-r--r--lib/error.rb2
-rw-r--r--lib/opentox-client.rb2
-rw-r--r--lib/opentox.rb16
-rw-r--r--lib/rest-client-wrapper.rb (renamed from lib/rest_client_wrapper.rb)116
-rw-r--r--lib/task.rb32
5 files changed, 130 insertions, 38 deletions
diff --git a/lib/error.rb b/lib/error.rb
index 6987f35..1866585 100644
--- a/lib/error.rb
+++ b/lib/error.rb
@@ -1,6 +1,6 @@
# adding additional fields to Exception class to format errors according to OT-API
class Exception
- attr_accessor :errorCause
+ attr_accessor :errorCause # is errorReport
def http_code; 500; end
end
diff --git a/lib/opentox-client.rb b/lib/opentox-client.rb
index 1a5e7c3..a587aa5 100644
--- a/lib/opentox-client.rb
+++ b/lib/opentox-client.rb
@@ -8,7 +8,7 @@ require 'yaml'
require 'logger'
require File.join(File.dirname(__FILE__),"overwrite.rb")
require File.join(File.dirname(__FILE__),"error.rb")
-require File.join(File.dirname(__FILE__),"rest_client_wrapper.rb")
+require File.join(File.dirname(__FILE__),"rest-client-wrapper.rb")
require File.join(File.dirname(__FILE__),"otlogger.rb") # avoid require conflicts with logger
require File.join(File.dirname(__FILE__),"opentox.rb")
require File.join(File.dirname(__FILE__),"task.rb")
diff --git a/lib/opentox.rb b/lib/opentox.rb
index f81ae10..145aeb6 100644
--- a/lib/opentox.rb
+++ b/lib/opentox.rb
@@ -20,24 +20,16 @@ module OpenTox
# Ruby interface
- # override to read all error codes
def metadata reload=true
if reload or @metadata.empty?
@metadata = {}
- # ignore error codes from Task services (may contain eg 500 which causes exceptions in RestClient and RDF::Reader
- # TODO: convert to RestClientWrapper
kind_of?(OpenTox::Dataset) ? uri = File.join(@uri,"metadata") : uri = @uri
- RestClient.get(uri) do |response, request, result|
- #response = RestClientWrapper.get(@uri) #do |response, request, result|
- $logger.warn "#{@uri} returned #{result}" unless response.code == 200 or response.code == 202 or URI.task? @uri
- RDF::Reader.for(:rdfxml).new(response) do |reader|
- reader.each_statement do |statement|
- @metadata[statement.predicate] = statement.object if statement.subject == @uri
- end
+ RDF::Reader.for(:rdfxml).new( RestClientWrapper.get(@uri) ) do |reader|
+ reader.each_statement do |statement|
+ @metadata[statement.predicate] = statement.object if statement.subject == @uri
end
end
end
- #puts @metadata.inspect
@metadata
end
@@ -52,7 +44,7 @@ module OpenTox
def get params={}
params[:subjectid] ||= @subjectid
params[:accept] ||= 'application/rdf+xml'
- @response = RestClientWrapper.get @uri, params
+ @response = RestClientWrapper.get @uri, {}, params
end
def post payload={}, params={}
diff --git a/lib/rest_client_wrapper.rb b/lib/rest-client-wrapper.rb
index 89de277..95aee8e 100644
--- a/lib/rest_client_wrapper.rb
+++ b/lib/rest-client-wrapper.rb
@@ -6,6 +6,89 @@ module OpenTox
class RestClientWrapper
+ # create REST class methods
+ [:head,:get,:post,:put,:dealete].each do |method|
+
+ #define_singleton_method method do |uri,args={},headers={},waiting_task=nil, wait=true|
+ define_singleton_method method do |uri,payload={},headers={},waiting_task=nil, wait=true|
+
+ args={}
+ args[:method] = method
+ args[:url] = uri
+ args[:timeout] = 600
+ args[:payload] = payload
+ args[:headers] = headers
+ #raise OpenTox::BadRequestError.new "Empty URI." unless uri # error raised at method call
+ raise OpenTox::BadRequestError.new "Invalid URI: '#{uri}'" unless URI.valid? uri
+ raise OpenTox::BadRequestError.new "Unreachable URI: '#{uri}'" unless URI.accessible? uri
+=begin
+ raise "headers are no hash: "+headers.inspect unless headers==nil or headers.is_a?(Hash)
+ # TODO: loop over accept, contant_type, subjectid
+ raise OpenTox::BadRequestError.new "accept should go into the headers" if payload and payload.is_a?(Hash) and payload[:accept]
+ raise OpenTox::BadRequestError.new "content_type should go into the headers" if payload and payload.is_a?(Hash) and payload[:content_type]
+ raise OpenTox::BadRequestError.new "subjectid should go into the headers" if payload and payload.is_a?(Hash) and payload[:subjectid]
+ raise "__waiting_task__ must be 'nil' or '(sub)task', is "+waiting_task.class.to_s if
+ waiting_task!=nil and !(waiting_task.is_a?(Task) || waiting_task.is_a?(SubTask))
+ headers.each{ |k,v| headers.delete(k) if v==nil } if headers #remove keys with empty values, as this can cause problems
+ ## PENDING partner services accept subjectid only in header
+ headers = {} unless headers
+ #headers[:subjectid] = payload.delete(:subjectid) if payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
+
+ # PENDING needed for NUTA, until we finally agree on how to send subjectid
+ #headers[:subjectid] = payload.delete(:subjectid) if uri=~/ntua/ and payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
+=end
+
+ begin
+ #$logger.debug "RestCall: "+method.to_s+" "+uri.to_s+" "+headers.inspect+" "+args.inspect
+ request = RestClient::Request.new(args)
+ result = request.execute do |response, request, result|
+ unless response.code < 400 or URI.task? uri
+ $logger.error "#{uri} returned #{result.inspect}"
+ raise OpenTox::RestCallError result.inspect
+ end
+ return response
+ end
+ # ignore error codes from Task services (may contain eg 500 which causes exceptions in RestClient and RDF::Reader
+ #LOGGER.debug "result body size: #{result.body.size}"
+
+ # PENDING NTUA does return errors with 200
+ raise RestClient::ExceptionWithResponse.new(result) if uri=~/ntua/ and result.body =~ /about.*http:\/\/anonymous.org\/error/
+
+ # result is a string, with the additional fields content_type and code
+ res = WrapperResult.new(result.body)
+ res.content_type = result.headers[:content_type]
+ raise "content-type not set" unless res.content_type
+ res.code = result.code
+
+ # TODO: Ambit returns task representation with 200 instead of result URI
+ return res if res.code==200 || !wait
+
+ while (res.code==201 || res.code==202)
+ res = wait_for_task(res, uri, waiting_task)
+ end
+ raise "illegal status code: '"+res.code.to_s+"'" unless res.code==200
+ return res
+
+ rescue RestClient::RequestTimeout => ex
+ received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue Errno::ETIMEDOUT => ex
+ received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue Errno::ECONNREFUSED => ex
+ received_error ex.message, 500, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue RestClient::ExceptionWithResponse => ex
+ # error comming from a different webservice,
+ received_error ex.http_body, ex.http_code, ex.response.net_http_res.content_type, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue OpenTox::RestCallError => ex
+ # already a rest-error, probably comes from wait_for_task, just pass through
+ raise ex
+ rescue => ex
+ # some internal error occuring in rest-client-wrapper, just pass through
+ raise ex
+ end
+ end
+ end
+
+=begin
# performs a GET REST call
# raises OpenTox::Error if call fails (rescued in overwrite.rb -> halt 502)
# per default: waits for Task to finish and returns result URI of Task
@@ -50,31 +133,45 @@ module OpenTox
execute( "delete", uri, nil, headers)
end
+ def self.head(uri)
+ execute("head",uri)
+ end
+
private
def self.execute( rest_call, uri, payload=nil, headers={}, waiting_task=nil, wait=true )
- raise OpenTox::BadRequestError.new "uri is null" unless uri
- raise OpenTox::BadRequestError.new "not a uri: "+uri.to_s unless URI.valid? uri.to_s
+ raise OpenTox::BadRequestError.new "Empty URI." unless uri
+ raise OpenTox::BadRequestError.new "Invalid URI: '#{uri}'" unless URI.valid? uri
+ raise OpenTox::BadRequestError.new "Unreachable URI: '#{uri}'" unless URI.accessible? uri
raise "headers are no hash: "+headers.inspect unless headers==nil or headers.is_a?(Hash)
+ # TODO: loop over accept, contant_type, subjectid
raise OpenTox::BadRequestError.new "accept should go into the headers" if payload and payload.is_a?(Hash) and payload[:accept]
raise OpenTox::BadRequestError.new "content_type should go into the headers" if payload and payload.is_a?(Hash) and payload[:content_type]
+ raise OpenTox::BadRequestError.new "subjectid should go into the headers" if payload and payload.is_a?(Hash) and payload[:subjectid]
raise "__waiting_task__ must be 'nil' or '(sub)task', is "+waiting_task.class.to_s if
waiting_task!=nil and !(waiting_task.is_a?(Task) || waiting_task.is_a?(SubTask))
headers.each{ |k,v| headers.delete(k) if v==nil } if headers #remove keys with empty values, as this can cause problems
## PENDING partner services accept subjectid only in header
headers = {} unless headers
- headers[:subjectid] = payload.delete(:subjectid) if payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
+ #headers[:subjectid] = payload.delete(:subjectid) if payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
# PENDING needed for NUTA, until we finally agree on how to send subjectid
- headers[:subjectid] = payload.delete(:subjectid) if uri=~/ntua/ and payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
+ #headers[:subjectid] = payload.delete(:subjectid) if uri=~/ntua/ and payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
begin
#LOGGER.debug "RestCall: "+rest_call.to_s+" "+uri.to_s+" "+headers.inspect+" "+payload.inspect
resource = RestClient::Resource.new(uri,{:timeout => 600})
if rest_call=="post" || rest_call=="put"
- result = resource.send(rest_call, payload, headers){|response, request, result| response }
+ result = resource.send(rest_call, payload, headers){|response, request, result| return response }
+ elsif rest_call == "head"
+ result = resource.send(rest_call){ |response, request, result| return response }
else
- result = resource.send(rest_call, headers){|response, request, result| response }
+ result = resource.send(rest_call, headers){|response, request, result| return response }
+ end
+ # ignore error codes from Task services (may contain eg 500 which causes exceptions in RestClient and RDF::Reader
+ unless result.code < 400 or URI.task? @uri
+ $logger.error "#{@uri} returned #{result}"
+ raise OpenTox::RestCallError result
end
#LOGGER.debug "result body size: #{result.body.size}"
@@ -109,10 +206,11 @@ module OpenTox
# already a rest-error, probably comes from wait_for_task, just pass through
raise ex
rescue => ex
- # some internal error occuring in rest_client_wrapper, just pass through
+ # some internal error occuring in rest-client-wrapper, just pass through
raise ex
end
end
+=end
def self.wait_for_task( res, base_uri, waiting_task=nil )
#TODO remove TUM hack
@@ -132,7 +230,7 @@ module OpenTox
end
#LOGGER.debug "result is a task '"+task.uri.to_s+"', wait for completion"
- task.wait_for_completion waiting_task
+ task.wait waiting_task
unless task.completed? # maybe task was cancelled / error
if task.errorReport
received_error task.errorReport, task.http_code, nil, {:rest_uri => task.uri, :rest_code => task.http_code}
@@ -144,7 +242,7 @@ module OpenTox
res = WrapperResult.new task.result_uri
res.code = task.http_code
- res.content_type = "text/uri-list"
+ res.content_type ="text/uri-list"
return res
end
diff --git a/lib/task.rb b/lib/task.rb
index 286e998..635584f 100644
--- a/lib/task.rb
+++ b/lib/task.rb
@@ -6,7 +6,9 @@ module OpenTox
class Task
attr_accessor :pid, :observer_pid
+
def self.create service_uri, params={}
+
task = Task.new RestClientWrapper.post(service_uri,params).chomp
pid = fork do
begin
@@ -17,19 +19,15 @@ module OpenTox
raise "#{result_uri} is not a valid URI"
end
rescue
- # TODO add service URI to Kernel.raise
- # serialize error and send to task service
- #task.error $!
task.error $!
- raise
end
end
Process.detach(pid)
task.pid = pid
- # watch if task has been cancelled
+ # watch if task has been cancelled
observer_pid = fork do
- task.wait_for_completion
+ task.wait
begin
Process.kill(9,task.pid) if task.cancelled?
rescue
@@ -39,6 +37,7 @@ module OpenTox
Process.detach(observer_pid)
task.observer_pid = observer_pid
task
+
end
def kill
@@ -50,6 +49,10 @@ module OpenTox
def description
metadata[RDF::DC.description]
end
+
+ def creator
+ metadata[RDF::DC.creator]
+ end
def cancel
kill
@@ -64,15 +67,16 @@ module OpenTox
end
def error error
- $logger.error self if $logger
- report = ErrorReport.create(error,"http://localhost")
+ $logger.error self
+ report = ErrorReport.create(error,self.creator)
RestClientWrapper.put(File.join(@uri,'Error'),{:errorReport => report})
kill
+ raise error
end
# waits for a task, unless time exceeds or state is no longer running
# @param [optional,Numeric] dur seconds pausing before checking again for completion
- def wait_for_completion(dur=0.3)
+ def wait(dur=0.3)
due_to_time = Time.new + DEFAULT_TASK_MAX_DURATION
while running?
sleep dur
@@ -84,19 +88,19 @@ module OpenTox
# get only header for ststus requests
def running?
- RestClient.head(@uri){ |response, request, result| result.code.to_i == 202 }
+ RestClientWrapper.head(@uri).code == 202
end
def cancelled?
- RestClient.head(@uri){ |response, request, result| result.code.to_i == 503 }
+ RestClientWrapper.head(@uri).code == 503
end
def completed?
- RestClient.head(@uri){ |response, request, result| result.code.to_i == 200 }
+ RestClientWrapper.head(@uri).code == 200
end
def error?
- RestClient.head(@uri){ |response, request, result| result.code.to_i == 500 }
+ RestClientWrapper.head(@uri).code == 500
end
def method_missing(method,*args)
@@ -106,8 +110,6 @@ module OpenTox
when /=/
res = RestClientWrapper.put(File.join(@uri,method.sub(/=/,'')),{})
super unless res.code == 200
- #when /\?/
- #return hasStatus == method.sub(/\?/,'').capitalize
else
response = metadata[RDF::OT[method]].to_s
response = metadata[RDF::OT1[method]].to_s #if response.empty? # API 1.1 compatibility