From 59dba52a30de35da0122cd6c25777573faa5ffc3 Mon Sep 17 00:00:00 2001 From: mguetlein Date: Mon, 24 Jan 2011 17:19:56 +0100 Subject: fix error handling --- lib/error.rb | 67 ++++++++++++++++-------------------------- lib/overwrite.rb | 18 ++++++++++-- lib/rest_client_wrapper.rb | 72 +++++++++++++++++++++++++++++----------------- lib/task.rb | 36 +++++++++++------------ 4 files changed, 102 insertions(+), 91 deletions(-) (limited to 'lib') diff --git a/lib/error.rb b/lib/error.rb index e47ad62..8a57bd0 100644 --- a/lib/error.rb +++ b/lib/error.rb @@ -1,69 +1,52 @@ # adding additional fields to Exception class to format errors according to OT-API class Exception - attr_accessor :creator, :errorCause, :id, :http_code + attr_accessor :errorCause def http_code; 500; end end module OpenTox - class BadRequestError < Exception + class BadRequestError < RuntimeError def http_code; 400; end end - class NotAuthorizedError < Exception + class NotAuthorizedError < RuntimeError def http_code; 401; end end - class NotFoundError < Exception + class NotFoundError < RuntimeError def http_code; 404; end end - class RestCallError < Exception - attr_accessor :rest_code, :rest_body, :rest_uri, :rest_payload, :rest_headers + class RestCallError < RuntimeError + attr_accessor :rest_params def http_code; 502; end end class ErrorReport - # formats error according to accept-header, yaml is default - # ( sets content-type in response accordingly ) - # @param [Exception] error - # @param |Sinatra::Request, optional] request - # @param [Sinatra::Response, optiona,] response, optional to set content-type - # @return [String] formated error - def self.format(error, request=nil, response=nil) - # sets current uri - error.creator = "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}" if request - # bit of a hack: set instance attribute in order to add it for the to_yaml conversion - error.http_code = error.http_code - - accept = request.env['HTTP_ACCEPT'].to_s if request - case accept - # when /rdf/ - # TODO add error to rdf - when /html/ - response['Content-Type'] = 'text/html' if response - OpenTox.text_to_html error.to_yaml - else - response['Content-Type'] = 'application/x-yaml' if response - error.to_yaml - end + # TODO replace params with URIs (errorCause -> OT.errorCause) + attr_reader :message, :actor, :errorCause, :http_code, :errorDetails, :errorType + + # creates a error report object, from an ruby-exception object + # @param [Exception] error + # @param [String] actor, URI of the call that cause the error + def initialize( error, actor ) + @http_code = error.http_code + @errorType = error.class.to_s + @message = error.message + @actor = actor + @errorCause = error.errorCause if error.errorCause + @rest_params = error.rest_params if error.is_a?(OpenTox::RestCallError) and error.rest_params + end + + def self.from_rdf(rdf) + raise "not yet implemented" end - # trys to parse error from text - # @return [Exception] Exception if parsing sucessfull, nil otherwise - def self.parse( body ) - begin - err = YAML.load(body) - if err and err.is_a?(Exception) - return err - else - return nil - end - rescue - return nil - end + def self.to_rdf + raise "not yet implemented" end end end \ No newline at end of file diff --git a/lib/overwrite.rb b/lib/overwrite.rb index 83d8099..4fa0829 100644 --- a/lib/overwrite.rb +++ b/lib/overwrite.rb @@ -19,8 +19,22 @@ error Exception do # log error to logfile LOGGER.error error.class.to_s+": "+error.message # log backtrace only if code is 500 -> unwanted (Runtime)Exceptions and internal errors (see error.rb) - LOGGER.error error.backtrace.join("\n") if error.http_code==500 - halt error.http_code,OpenTox::ErrorReport.format(error,request,response) + LOGGER.error ":\n"+error.backtrace.join("\n") if error.http_code==500 + + actor = "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}" + rep = OpenTox::ErrorReport.new(error, actor) + + case request.env['HTTP_ACCEPT'] + when /rdf/ + content_type 'application/rdf+xml' + halt error.http_code,rep.to_xml + when /html/ + content_type 'text/html' + halt error.http_code,(OpenTox.text_to_html rep.to_yaml) + else + content_type 'application/x-yaml' + halt error.http_code,rep.to_yaml + end end class String diff --git a/lib/rest_client_wrapper.rb b/lib/rest_client_wrapper.rb index 3d7b72e..7c2d719 100644 --- a/lib/rest_client_wrapper.rb +++ b/lib/rest_client_wrapper.rb @@ -50,16 +50,6 @@ module OpenTox execute( "delete", uri, headers, nil) end - # raises an Error message (rescued in overwrite.rb -> halt 502) - # usage: if the return value of a call is invalid - # @param [String] error_msg the error message - # @param [String] uri destination URI that is responsible for the error - # @param [optional,Hash] headers sent to the URI - # @param [optional,String] payload data sent to the URI - def self.raise_uri_error(error_msg, uri, headers=nil, payload=nil) - raise_ot_error( nil, error_msg, nil, uri, headers, payload ) - end - private def self.execute( rest_call, uri, headers, payload=nil, waiting_task=nil, wait=true ) @@ -70,7 +60,7 @@ module OpenTox headers.each{ |k,v| headers.delete(k) if v==nil } if headers #remove keys with empty values, as this can cause problems begin - #LOGGER.debug "RestCall: "+rest_call.to_s+" "+uri.to_s+" "+headers.inspect + #LOGGER.debug "RestCall: "+rest_call.to_s+" "+uri.to_s+" "+headers.inspect+" "+payload.inspect resource = RestClient::Resource.new(uri,{:timeout => 60}) if payload result = resource.send(rest_call, payload, headers) @@ -86,6 +76,7 @@ module OpenTox raise "content-type not set" unless res.content_type res.code = result.code + #LOGGER.debug "RestCall result: "+res.to_s+" "+res.code.to_s+" "+res.content_type.to_s # TODO: Ambit returns task representation with 200 instead of result URI return res if res.code==200 || !wait @@ -96,11 +87,16 @@ module OpenTox return res rescue RestClient::RequestTimeout => ex - raise_ot_error 408,ex.message,nil,ex.uri,headers,payload + received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers} rescue RestClient::ExceptionWithResponse => ex - raise_ot_error ex.http_code,ex.message,ex.http_body,uri,headers,payload + # 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} + rescue OpenTox::RestCallError => ex + # already a rest-error, probably comes from wait_for_task, just pass through + raise ex rescue => ex - raise_ot_error 500,ex.message,nil,uri,headers,payload + # some internal error occuring in rest_client_wrapper, just pass through + raise ex end end @@ -121,27 +117,49 @@ module OpenTox LOGGER.debug "result is a task '"+task.uri.to_s+"', wait for completion" task.wait_for_completion waiting_task - raise task.description unless task.completed? # maybe task was cancelled / error - + 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} + else + raise "task status: '"+task.status.to_s+"' but errorReport nil" + end + end + res = WrapperResult.new task.result_uri res.code = task.http_code res.content_type = "text/uri-list" return res end - def self.raise_ot_error( code, message, body, uri, headers, payload=nil ) - error = OpenTox::RestCallError.new("REST call returned error: '"+message.to_s+"'") - error.rest_code = code - error.rest_uri = uri - error.rest_headers = headers - error.rest_payload = payload - parsed = OpenTox::ErrorReport.parse(body) if body - if parsed - error.errorCause = parsed + def self.received_error( body, code, content_type=nil, params=nil ) + + # try to parse body + report = nil + if body.is_a?(OpenTox::ErrorReport) + report = body + else + case content_type + when /yaml/ + report = YAML.load(body) + when /rdf/ + report = OpenTox::ErrorReport.from_rdf(body) + end + end + + unless report + # parsing was not successfull + # raise 'plain' RestCallError + err = OpenTox::RestCallError.new("REST call returned error: '"+body.to_s+"'") + err.rest_params = params + raise err else - error.rest_body = body + # parsing sucessfull + # raise RestCallError with parsed report as error cause + err = OpenTox::RestCallError.new("REST call subsequent error") + err.errorCause = report + err.rest_params = params + raise err end - raise error end end end diff --git a/lib/task.rb b/lib/task.rb index d701c82..06d290f 100644 --- a/lib/task.rb +++ b/lib/task.rb @@ -56,26 +56,17 @@ module OpenTox #raise "Server too busy to start a new task" end - task_pid = Spork.spork(:logger => LOGGER) do LOGGER.debug "Task #{task.uri} started #{Time.now}" - begin - result = catch(:halt) do - yield task - end - # catching halt, set task state to error - if result && result.is_a?(Array) && result.size==2 && result[0]>202 - LOGGER.error "task was halted: "+result.inspect - task.error(result[1]) - return - end + result = yield task LOGGER.debug "Task #{task.uri} done #{Time.now} -> "+result.to_s task.completed(result) - rescue => ex - LOGGER.error "task failed: "+ex.message - LOGGER.error ": "+ex.backtrace.join("\n") - task.error(ex.message) + rescue => error + LOGGER.error "task failed: "+error.class.to_s+": "+error.message + # log backtrace only if code is 500 -> unwanted (Runtime)Exceptions and internal errors (see error.rb) + LOGGER.error ":\n"+error.backtrace.join("\n") if error.http_code==500 + task.error(OpenTox::ErrorReport.new(error, creator)) end end task.pid = task_pid @@ -127,6 +118,10 @@ module OpenTox @metadata[DC.description] end + def errorReport + @metadata[OT.errorReport] + end + def cancel RestClientWrapper.put(File.join(@uri,'Cancelled')) load_metadata @@ -137,8 +132,9 @@ module OpenTox load_metadata end - def error(description) - RestClientWrapper.put(File.join(@uri,'Error'),{:description => description.to_s[0..2000]}) + def error(error_report) + raise "no error report" unless error_report.is_a?(OpenTox::ErrorReport) + RestClientWrapper.put(File.join(@uri,'Error'),{:errorReport => error_report.to_yaml}) load_metadata end @@ -236,7 +232,7 @@ module OpenTox end end - LOGGER.debug "Task '"+@metadata[OT.hasStatus]+"': "+@uri.to_s+", Result: "+@metadata[OT.resultURI].to_s + LOGGER.debug "Task '"+@metadata[OT.hasStatus].to_s+"': "+@uri.to_s+", Result: "+@metadata[OT.resultURI].to_s end # updates percentageCompleted value (can only be increased) @@ -250,7 +246,7 @@ module OpenTox load_metadata end end - + private def check_state begin @@ -265,7 +261,7 @@ module OpenTox "'" unless @metadata[OT.resultURI] and @metadata[OT.resultURI].to_s.uri? end rescue => ex - RestClientWrapper.raise_uri_error(ex.message, @uri) + raise OpenTox::BadRequestError.new ex.message+" (task-uri:"+@uri+")" end end -- cgit v1.2.3