From 9d06bd3024139f2bfee4722c7536ee4ffa99fe32 Mon Sep 17 00:00:00 2001 From: mguetlein Date: Thu, 20 Jan 2011 11:29:53 +0100 Subject: implemented new error handling, still TODO rdf-support, replace halts --- lib/error.rb | 60 +++++++++++++++++++++++++++--- lib/opentox-ruby.rb | 4 +- lib/overwrite.rb | 44 +++++++++++----------- lib/rest_client_wrapper.rb | 91 ++++++++++------------------------------------ 4 files changed, 99 insertions(+), 100 deletions(-) diff --git a/lib/error.rb b/lib/error.rb index 87e1a5d..b72ce7e 100644 --- a/lib/error.rb +++ b/lib/error.rb @@ -1,11 +1,61 @@ -module OpenTox - class NotFoundError < RuntimeError - +# adding additional fields to Exception class to format errors according to OT-API +class Exception + attr_accessor :creator, :errorCause, :id +end + +module OpenTox + + class NotAuthorizedError < Exception end - class BadRequestError < RuntimeError - + class NotFoundError < Exception end + class BadRequestError < Exception + end + + class RestCallError < Exception + attr_accessor :code, :body, :uri, :payload, :headers + 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 + 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 + 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 + end + end end \ No newline at end of file diff --git a/lib/opentox-ruby.rb b/lib/opentox-ruby.rb index fc1732d..735b845 100644 --- a/lib/opentox-ruby.rb +++ b/lib/opentox-ruby.rb @@ -1,4 +1,4 @@ -['rubygems', 'sinatra', 'sinatra/url_for', 'rest_client', 'yaml', 'cgi', 'spork', 'overwrite', 'environment'].each do |lib| +['rubygems', 'sinatra', 'sinatra/url_for', 'rest_client', 'yaml', 'cgi', 'spork', 'error', 'overwrite', 'environment'].each do |lib| require lib end @@ -9,6 +9,6 @@ rescue LoadError end ['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', - 'rest_client_wrapper', 'authorization', 'policy', 'helper', 'to-html', 'error' ].each do |lib| + 'rest_client_wrapper', 'authorization', 'policy', 'helper', 'to-html' ].each do |lib| require lib end diff --git a/lib/overwrite.rb b/lib/overwrite.rb index 720ed77..2f9fabd 100644 --- a/lib/overwrite.rb +++ b/lib/overwrite.rb @@ -10,30 +10,30 @@ before { request.env['HTTP_ACCEPT'] += ";text/html" if request.env["HTTP_USER_AGENT"]=~/MSIE/ } -# handle errors manually -# this is to return 502, when an error occurs during a rest-call (see rest_client_wrapper.rb) -set :raise_errors, Proc.new { false } -set :show_exceptions, false -error do - # try if the error is an OpenTox::Error - if OpenTox::Error.parse(request.env['sinatra.error'].to_s) - # if true, this error comes from rest_client_wrapper, halt with 502 - # (502 is defined in OT API as Error coming from other service) - halt 502,request.env['sinatra.error'] +# Error handling +# Errors are logged as error and formated according to acccept-header +# Non OpenTox::Errors (defined in error.rb) are handled as internal error (500), stacktrace is logged +# IMPT: set sinatra settings :show_exceptions + :raise_errors to false in config.ru, otherwise Rack::Showexceptions takes over +error Exception do + error = request.env['sinatra.error'] + # log error to logfile + LOGGER.error error.class.to_s+": "+error.message + case error.class + when OpenTox::BadRequestError + code = 400 + when OpenTox::NotAuthorizedError + code = 401 + when OpenTox::NotFoundError + code = 404 + when OpenTox::RestCallError + code = 502 else - # else, raise exception, this will return 500 = internal error - raise request.env['sinatra.error'] - end -end - -class Sinatra::Base - # overwriting halt to log halts (!= 202) - def halt(*response) - LOGGER.error "halt "+response.first.to_s+" "+(response.size>1 ? response[1].to_s : "") if response and response.first and response.first >= 300 - # orig sinatra code: - response = response.first if response.length == 1 - throw :halt, response + # (unwanted RuntimeExceptions as well as sth. like 'raise "invalid state"' is handled here) + code = 500 + # log backtrace for debugging + LOGGER.error error.backtrace.join("\n") end + halt code,OpenTox::ErrorReport.format(error,request,response) end class String diff --git a/lib/rest_client_wrapper.rb b/lib/rest_client_wrapper.rb index 920a828..5bc8072 100644 --- a/lib/rest_client_wrapper.rb +++ b/lib/rest_client_wrapper.rb @@ -1,32 +1,4 @@ module OpenTox - - #PENDING: implement ot error api, move to own file - class Error - - attr_accessor :code, :body, :uri, :payload, :headers - - def initialize(code, body, uri, payload, headers) - self.code = code - self.body = body.to_s[0..1000] - self.uri = uri - self.payload = payload - self.headers = headers - end - - def self.parse(error_array_string) - begin - err = YAML.load(error_array_string) - if err and err.is_a?(Array) and err.size>0 and err[0].is_a?(Error) - return err - else - return nil - end - rescue - return nil - end - end - - end class WrapperResult < String attr_accessor :content_type, :code @@ -85,16 +57,16 @@ module OpenTox # @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( "-", error_msg, uri, headers, payload ) + 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 ) - raise_ot_error 400,"uri is null",uri,headers,payload unless uri - raise_ot_error 400,"not a uri",uri,headers,payload unless uri.to_s.uri? - raise_ot_error 400,"headers are no hash",uri,headers,payload unless headers==nil or headers.is_a?(Hash) - raise_ot_error 400,"nil headers for post not allowed, use {}",uri,headers,payload if rest_call=="post" and headers==nil + raise OpenTox::BadRequestError.new "uri is null" unless uri + raise OpenTox::BadRequestError.new "not a uri: "+uri.to_s unless uri.to_s.uri? + raise OpenTox::BadRequestError.new "headers are no hash: "+headers.inspect unless headers==nil or headers.is_a?(Hash) + raise OpenTox::BadRequestError.new "nil headers for post not allowed, use {}" if rest_call=="post" and headers==nil headers.each{ |k,v| headers.delete(k) if v==nil } if headers #remove keys with empty values, as this can cause problems begin @@ -124,18 +96,11 @@ module OpenTox return res rescue RestClient::RequestTimeout => ex - raise_ot_error 408,ex.message,uri,headers,payload + raise_ot_error 408,ex.message,nil,ex.uri,headers,payload + rescue RestClient::ExceptionWithResponse => ex + raise_ot_error ex.http_code,ex.message,ex.http_body,uri,headers,payload rescue => ex - #raise ex - #raise "'"+ex.message+"' uri: "+uri.to_s - begin - code = ex.http_code - msg = ex.http_body - rescue - code = 500 - msg = ex.to_s - end - raise_ot_error code,msg,uri,headers,payload + raise_ot_error 500,ex.message,nil,uri,headers,payload end end @@ -164,35 +129,19 @@ module OpenTox return res end - def self.raise_ot_error( code, body, uri, headers, payload=nil ) - - #build error - causing_errors = Error.parse(body) - if causing_errors - error = causing_errors + [Error.new(code, "subsequent error", uri, payload, headers)] + def self.raise_ot_error( code, message, body, uri, headers, payload=nil ) + error = OpenTox::RestCallError.new("REST call returned error: '"+message.to_s+"'") + error.code = code + error.uri = uri + error.headers = headers + error.payload = payload + parsed = OpenTox::ErrorReport.parse(body) if body + if parsed + error.errorCause = parsed else - error = [Error.new(code, body, uri, payload, headers)] + error.body = body end - - #debug utility: write error to file - error_dir = "/tmp/ot_errors" - FileUtils.mkdir(error_dir) unless File.exist?(error_dir) - raise "could not create error dir" unless File.exist?(error_dir) and File.directory?(error_dir) - file_name = "error" - time=Time.now.strftime("%m.%d.%Y-%H:%M:%S") - count = 1 - count+=1 while File.exist?(File.join(error_dir,file_name+"_"+time+"_"+count.to_s)) - File.new(File.join(error_dir,file_name+"_"+time+"_"+count.to_s),"w").puts(body) - - # handle error - # PENDING: always return yaml for now - - # raising OpenTox::Error - # to handle the error yourself, put rest-call in begin, rescue block - # if the error is not caught: - # if we are in a task, the error is caught, logged, and task state is set to error in Task.as_task - # if we are in a default call, the error is handled in overwrite.rb to return 502 (according to OT API) - raise error.to_yaml + raise error end end end -- cgit v1.2.3