From cbc5f08e92c92601009f0c11c8ec67ede2894858 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Fri, 2 Mar 2012 09:33:44 +0000 Subject: error report fixed for old task services --- lib/error.rb | 160 +++++++++++++++++++++++---------------------- lib/opentox.rb | 8 ++- lib/rest-client-wrapper.rb | 28 ++++---- lib/task.rb | 25 ++++--- test/task.rb | 25 +++++-- 5 files changed, 137 insertions(+), 109 deletions(-) diff --git a/lib/error.rb b/lib/error.rb index 88c8be8..8368404 100644 --- a/lib/error.rb +++ b/lib/error.rb @@ -1,101 +1,107 @@ # adding additional fields to Exception class to format errors according to OT-API class RuntimeError - attr_accessor :http_code - @http_code = 500 + attr_accessor :report, :http_code + def initialize message + super message + @http_code ||= 500 + @report = OpenTox::ErrorReport.create self + $logger.error "\n"+@report.to_ntriples + end end module OpenTox - # Errors received from RestClientWrapper calls - class RestError < RuntimeError - attr_accessor :request, :response, :cause - def initialize args - @request = args[:request] - @response = args[:response] - args[:http_code] ? @http_code = args[:http_code] : @http_code = @response.code if @response - @cause = args[:cause] - msg = args.to_yaml - $logger.error msg - super msg + class Error < RuntimeError + def initialize code, message + @http_code = code + super message end end - # Errors rescued from task blocks - class TaskError < RuntimeError - attr_reader :error, :actor, :report - def initialize error, actor=nil - @error = error - @actor = actor - @report = ErrorReport.create error, actor - # TODO avoid error log duplication, improve output - msg = "\nActor: \"#{actor}\"\n" - msg += "\nCode: #{@report.http_code}" - msg += "\nerrorCause: #{@report.errorCause}\n" - msg += @report.message - $logger.error msg - super msg + # create error classes dynamically + { + "BadRequestError" => 400, + "NotAuthorizedError" => 401, + "NotFoundError" => 404, + "ServiceUnavailableError" => 503, + "TimeOutError" => 504, + }.each do |klass,code| + c = Class.new Error do + define_method :initialize do |message| + super code, message + end + end + OpenTox.const_set klass,c + end + + # Errors received from RestClientWrapper calls + class RestCallError < Error + attr_accessor :request, :response + def initialize request, response, message + @request = request + @response = response + super 502, message end end class ErrorReport - # TODO replace params with URIs (errorCause -> OT.errorCause) - attr_reader :message, :actor, :errorCause, :http_code, :errorDetails, :errorType + attr_accessor :rdf # RDF Graph + attr_accessor :http_code # TODO: remove when task service is fixed - private - def initialize( http_code, erroType, message, actor, errorCause, rest_params=nil, backtrace=nil ) - @http_code = http_code - @errorType = erroType - @message = message - @actor = actor - @errorCause = errorCause - @rest_params = rest_params - @backtrace = backtrace + def initialize + @rdf = RDF::Graph.new end - - public + # 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 self.create( error, actor ) - rest_params = error.request if error.respond_to? :request - backtrace = error.backtrace.short_backtrace if error.respond_to? :backtrace and error.backtrace #if CONFIG[:backtrace] - error.respond_to?(:http_code) ? http_code = error.http_code : http_code = 500 - error.respond_to?(:cause) ? cause = error.cause : cause = 'Unknown' - ErrorReport.new( http_code, error.class.to_s, error.message, actor, cause, rest_params, backtrace ) - end - - def self.from_rdf(rdf) - metadata = OpenTox::Parser::Owl.from_rdf( rdf, OT.ErrorReport ).metadata - ErrorReport.new(metadata[OT.statusCode], metadata[OT.errorCode], metadata[OT.message], metadata[OT.actor], metadata[OT.errorCause]) - end - - # overwrite sorting to make easier readable - def to_yaml_properties - p = super - p = ( p - ["@backtrace"]) + ["@backtrace"] if @backtrace - p = ( p - ["@errorCause"]) + ["@errorCause"] if @errorCause - p - end - - def rdf_content() - c = { - RDF.type => [OT.ErrorReport], - OT.statusCode => @http_code, - OT.message => @message, - OT.actor => @actor, - OT.errorCode => @errorType, - } - c[OT.errorCause] = @errorCause.rdf_content if @errorCause - c + def self.create error + report = ErrorReport.new + subject = RDF::Node.new + report.rdf << [subject, RDF.type, RDF::OT.ErrorReport] + message = error.message + errorDetails = "" + if error.respond_to? :request + report.rdf << [subject, RDF::OT.actor, error.request.url ] + errorDetails += "REST paramenters:\n#{error.request.args.inspect}" + end + error.respond_to?(:http_code) ? statusCode = error.http_code : statusCode = 500 + if error.respond_to? :response + statusCode = error.response.code + message = error.body + end + statusCode = error.http_code if error.respond_to? :http_code + report.rdf << [subject, RDF::OT.statusCode, statusCode ] + report.rdf << [subject, RDF::OT.errorCode, error.class.to_s ] + # TODO: remove kludge for old task services + report.http_code = statusCode + report.rdf << [subject, RDF::OT.message , message ] + + errorDetails += "\nBacktrace:\n" + error.backtrace.short_backtrace if error.respond_to?(:backtrace) and error.backtrace + report.rdf << [subject, RDF::OT.errorDetails, errorDetails ] + # TODO Error cause + #report.rdf << [subject, OT.errorCause, error.report] if error.respond_to?(:report) and !error.report.empty? + report end - # TODO: use rdf.rb - def to_rdfxml - s = Serializer::Owl.new - s.add_resource(CONFIG[:services]["opentox-task"]+"/tmpId/ErrorReport/tmpId", OT.errorReport, rdf_content) - s.to_rdfxml + # define to_ and self.from_ methods for various rdf formats + [:rdfxml,:ntriples].each do |format| + + define_singleton_method ("from_#{format}").to_sym do |rdf| + report = ErrorReport.new + RDF::Reader.for(format).new(rdf) do |reader| + reader.each_statement{ |statement| report.rdf << statement } + end + report + end + + send :define_method, ("to_#{format}").to_sym do + rdfxml = RDF::Writer.for(format).buffer do |writer| + @rdf.each{|statement| writer << statement} + end + rdfxml + end end end diff --git a/lib/opentox.rb b/lib/opentox.rb index 4c2a668..10c7895 100644 --- a/lib/opentox.rb +++ b/lib/opentox.rb @@ -24,7 +24,7 @@ module OpenTox if reload or @metadata.empty? @metadata = {} kind_of?(OpenTox::Dataset) ? uri = File.join(@uri,"metadata") : uri = @uri - RDF::Reader.for(:rdfxml).new( RestClientWrapper.get(@uri) ) do |reader| + 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 @@ -34,10 +34,14 @@ module OpenTox end def save + post self.to_rdfxml, { :content_type => 'application/rdf+xml'} + end + + def to_rdfxml rdf = RDF::Writer.for(:rdfxml).buffer do |writer| @metadata.each { |p,o| writer << RDF::Statement.new(RDF::URI.new(@uri), p, o) } end - post rdf, { :content_type => 'application/rdf+xml'} + rdf end # REST API diff --git a/lib/rest-client-wrapper.rb b/lib/rest-client-wrapper.rb index 1e871b0..0780dd5 100644 --- a/lib/rest-client-wrapper.rb +++ b/lib/rest-client-wrapper.rb @@ -27,29 +27,25 @@ module OpenTox args[:headers] = headers @request = RestClient::Request.new(args) - # catch input errors - rest_error "Invalid URI: '#{uri}'" unless URI.valid? uri - rest_error "Unreachable URI: '#{uri}'" unless URI.accessible? uri - rest_error "Headers are not a hash: #{headers.inspect}" unless headers==nil or headers.is_a?(Hash) - # make sure that no header parameters are set in payload + # check input + raise OpenTox::Error.new 400, "Invalid URI: '#{uri}'" unless URI.valid? uri + raise OpenTox::Error.new 400, "Unreachable URI: '#{uri}'" unless URI.accessible? uri + raise OpenTox::Error.new 400, "Headers are not a hash: #{headers.inspect}" unless headers==nil or headers.is_a?(Hash) + # make sure that no header parameters are set in the payload [:accept,:content_type,:subjectid].each do |header| - rest_error "#{header} should be submitted in the headers" if payload and payload.is_a?(Hash) and payload[header] + raise OpenTox::Error.new 400, "#{header} should be submitted in the headers" if payload and payload.is_a?(Hash) and payload[header] end - rest_error "waiting_task is not 'nil', OpenTox::SubTask or OpenTox::Task: #{waiting_task.class}" unless waiting_task.nil? or waiting_task.is_a?(OpenTox::Task) or waiting_task.is_a?(OpenTox::SubTask) + raise OpenTox::Error.new 400, "waiting_task is not 'nil', OpenTox::SubTask or OpenTox::Task: #{waiting_task.class}" unless waiting_task.nil? or waiting_task.is_a?(OpenTox::Task) or waiting_task.is_a?(OpenTox::SubTask) begin @response = @request.execute do |response, request, result| # ignore error codes from Task services (may contain eg 500 which causes exceptions in RestClient and RDF::Reader - rest_error unless response.code < 400 or URI.task? uri + rest_error "Response code is #{response.code}" unless response.code < 400 or URI.task? uri return response end - - # TODO: tests for workarounds - # PENDING NTUA does return errors with 200 - #raise RestClient::ExceptionWithResponse.new(@response) if uri=~/ntua/ and @response.body =~ /about.*http:\/\/anonymous.org\/error/ - return @response if @response.code==200 or wait.false? + return @response if @response.code==200 or !wait # wait for task while @response.code==201 or @response.code==202 @@ -98,7 +94,7 @@ module OpenTox rest_error "Uri list has more than one entry, should be a single task" if @response.split("\n").size > 1 #if uri list contains more then one uri, its not a task task = OpenTox::Task.new(@response.to_s.chomp) if URI.available? @response.to_s else - rest_error @response, "Unknown content-type for task : '"+@response.headers[:content_type].to_s+"'"+" base-uri: "+base_uri.to_s+" content: "+@response[0..200].to_s + rest_error "Unknown content-type for task : '"+@response.headers[:content_type].to_s+"'"+" base-uri: "+base_uri.to_s+" content: "+@response[0..200].to_s end #LOGGER.debug "result is a task '"+task.uri.to_s+"', wait for completion" @@ -107,7 +103,7 @@ module OpenTox if task.errorReport received_error task.errorReport, task.http_code, nil, {:rest_uri => task.uri, :rest_code => task.http_code} else - rest_call_error "Status of task '"+task.uri.to_s+"' is no longer running (hasStatus is '"+task.status+ + rest_error "Status of task '"+task.uri.to_s+"' is no longer running (hasStatus is '"+task.status+ "'), but it is neither completed nor has an errorReport" end end @@ -115,7 +111,7 @@ module OpenTox end def self.rest_error message - raise OpenTox::RestError.new :request => @request, :response => @response, :cause => message + raise OpenTox::RestCallError.new @request, @response, message end =begin diff --git a/lib/task.rb b/lib/task.rb index d3b6312..f75f87d 100644 --- a/lib/task.rb +++ b/lib/task.rb @@ -17,7 +17,7 @@ module OpenTox if URI.accessible?(result_uri) task.completed result_uri else - raise "\"#{result_uri}\" is not a valid result URI" + raise NotFoundError.new "\"#{result_uri}\" is not a valid result URI" #task.error OpenTox::RestError.new :http_code => 404, :cause => "#{result_uri} is not a valid URI", :actor => params[:creator] end rescue @@ -53,7 +53,7 @@ module OpenTox end def creator - metadata[RDF::DC.creator] + metadata[RDF::DC.creator] end def cancel @@ -63,13 +63,16 @@ module OpenTox def completed(uri) #error OpenTox::RestError.new :http_code => 404, :cause => "\"#{uri}\" does not exist.", :actor => creator unless URI.accessible? uri - raise "Result URI \"#{uri}\" does not exist." unless URI.accessible? uri + raise NotFoundError.new "Result URI \"#{uri}\" does not exist." unless URI.accessible? uri RestClientWrapper.put(File.join(@uri,'Completed'),{:resultURI => uri}) end def error error - error = OpenTox::TaskError.new error, creator - RestClientWrapper.put(File.join(@uri,'Error'),{:errorReport => error.report}) + # TODO: switch task service to rdf + #RestClientWrapper.put(File.join(@uri,'Error'),{:errorReport => error.report.to_rdfxml}) + # create report for non-runtime errors + error.respond_to?(:reporti) ? report = error.report : report = OpenTox::ErrorReport.create(error) + RestClientWrapper.put(File.join(@uri,'Error'),{:errorReport => report.to_yaml}) kill raise error end @@ -80,7 +83,7 @@ module OpenTox due_to_time = Time.new + DEFAULT_TASK_MAX_DURATION while running? sleep dur - raise "max wait time exceeded ("+DEFAULT_TASK_MAX_DURATION.to_s+"sec), task: '"+@uri.to_s+"'" if (Time.new > due_to_time) + raise TimeOutError.new "max wait time exceeded ("+DEFAULT_TASK_MAX_DURATION.to_s+"sec), task: '"+@uri.to_s+"'" if (Time.new > due_to_time) end end @@ -100,7 +103,8 @@ module OpenTox end def error? - RestClientWrapper.head(@uri).code == 500 + code = RestClientWrapper.head(@uri).code + code >= 400 and code != 503 end def method_missing(method,*args) @@ -114,14 +118,15 @@ module OpenTox response = metadata[RDF::OT[method]].to_s response = metadata[RDF::OT1[method]].to_s #if response.empty? # API 1.1 compatibility if response.empty? - $logger.error "No #{method} metadata for #{@uri} " - raise "No #{method} metadata for #{@uri} " + raise NotFoundError.new "No #{method} metadata for #{@uri} " end return response end + rescue OpenTox::Error + raise $! rescue $logger.error "Unknown #{self.class} method #{method}" - #super + super end end diff --git a/test/task.rb b/test/task.rb index 7ea30b9..d49871f 100644 --- a/test/task.rb +++ b/test/task.rb @@ -10,6 +10,8 @@ TASK_SERVICE_URI = "http://ot-dev.in-silico.ch/task" class TaskTest < Test::Unit::TestCase +=begin +=end def test_all all = OpenTox::Task.all(TASK_SERVICE_URI) assert_equal Array, all.class @@ -44,8 +46,20 @@ class TaskTest < Test::Unit::TestCase def test_create_and_fail task = OpenTox::Task.create TASK_SERVICE_URI, :description => "test failure", :creator => "http://test.org/fake_creator" do - sleep 1 - raise "an unexpected error occured" + sleep 0.5 + raise "A runtime error occured" + end + assert task.running? + assert_equal "Running", task.hasStatus + task.wait + assert task.error? + assert_equal "Error", task.hasStatus + end + + def test_create_and_fail_with_opentox_error + task = OpenTox::Task.create TASK_SERVICE_URI, :description => "test failure", :creator => "http://test.org/fake_creator" do + sleep 0.5 + raise OpenTox::Error.new 500, "An OpenTox::Error occured" end assert task.running? assert_equal "Running", task.hasStatus @@ -56,14 +70,17 @@ class TaskTest < Test::Unit::TestCase def test_wrong_result_uri task = OpenTox::Task.create TASK_SERVICE_URI, :description => "test wrong result uri", :creator => "http://test.org/fake_creator" do - sleep 1 + sleep 0.5 "Asasadasd" end assert task.running? assert_equal "Running", task.hasStatus + puts task.uri task.wait assert task.error? - assert_equal "Error", task.hasStatus + #assert_equal "Error", task.hasStatus end +=begin +=end end -- cgit v1.2.3