From 9ed209b262e0b540af967e24e9b9845600a0669c Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 15 Feb 2012 18:06:46 +0100 Subject: tests fixed for new task api --- lib/error.rb | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/spork.rb | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/task.rb | 68 +++++++++++++++++------------------------ test/rest.rb | 4 +-- test/task.rb | 6 ++-- 5 files changed, 214 insertions(+), 46 deletions(-) create mode 100644 lib/error.rb create mode 100644 lib/spork.rb diff --git a/lib/error.rb b/lib/error.rb new file mode 100644 index 0000000..b92f2a4 --- /dev/null +++ b/lib/error.rb @@ -0,0 +1,99 @@ + +# adding additional fields to Exception class to format errors according to OT-API +class Exception + attr_accessor :errorCause + def http_code; 500; end +end + +module OpenTox + + class BadRequestError < RuntimeError + def http_code; 400; end + end + + class NotAuthorizedError < RuntimeError + def http_code; 401; end + end + + class NotFoundError < RuntimeError + def http_code; 404; end + end + + class ServiceUnavailableError < RuntimeError + def http_code; 503; end + end + + class RestCallError < RuntimeError + attr_accessor :rest_params + def http_code; 502; end + end + + class ErrorReport + + # TODO replace params with URIs (errorCause -> OT.errorCause) + attr_reader :message, :actor, :errorCause, :http_code, :errorDetails, :errorType + + 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 + 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.rest_params if error.is_a?(OpenTox::RestCallError) and error.rest_params + backtrace = error.backtrace.short_backtrace if CONFIG[:backtrace] + ErrorReport.new( error.http_code, error.class.to_s, error.message, actor, error.errorCause, 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 + end + + def to_rdfxml + s = Serializer::Owl.new + s.add_resource(CONFIG[:services]["opentox-task"]+"/tmpId/ErrorReport/tmpId", OT.errorReport, rdf_content) + s.to_rdfxml + end + end +end + +class Array + def short_backtrace + short = [] + each do |c| + break if c =~ /sinatra\/base/ + short << c + end + short.join("\n") + end +end diff --git a/lib/spork.rb b/lib/spork.rb new file mode 100644 index 0000000..c77b5b5 --- /dev/null +++ b/lib/spork.rb @@ -0,0 +1,83 @@ +# A way to cleanly handle process forking in Sinatra when using Passenger, aka "sporking some code". +# This will allow you to properly execute some code asynchronously, which otherwise does not work correctly. +# +# Written by Ron Evans +# More info at http://deadprogrammersociety.com +# +# Mostly lifted from the Spawn plugin for Rails (http://github.com/tra/spawn) +# but with all of the Rails stuff removed.... cause you are using Sinatra. If you are using Rails, Spawn is +# what you need. If you are using something else besides Sinatra that is Rack-based under Passenger, and you are having trouble with +# asynch processing, let me know if spork helped you. +# +module Spork + # things to close in child process + @@resources = [] + def self.resources + @@resources + end + + # set the resource to disconnect from in the child process (when forking) + def self.resource_to_close(resource) + @@resources << resource + end + + # close all the resources added by calls to resource_to_close + def self.close_resources + @@resources.each do |resource| + resource.close if resource && resource.respond_to?(:close) && !resource.closed? + end + @@resources = [] + end + + # actually perform the fork... er, spork + # valid options are: + # :priority => to set the process priority of the child + # :logger => a logger object to use from the child + # :no_detach => true if you want to keep the child process under the parent control. usually you do NOT want this + def self.spork(options={}) + logger = options[:logger] + logger.debug "spork> parent PID = #{Process.pid}" if logger + + child = fork do + begin + start = Time.now + logger.debug "spork> child PID = #{Process.pid}" if logger + + # set the nice priority if needed + Process.setpriority(Process::PRIO_PROCESS, 0, options[:priority]) if options[:priority] + + # disconnect from the rack + Spork.close_resources + + # run the block of code that takes so long + yield + + rescue => ex + #raise ex + logger.error "spork> Exception in child[#{Process.pid}] - #{ex.class}: #{ex.message}" if logger + ensure + logger.info "spork> child[#{Process.pid}] took #{Time.now - start} sec" if logger + # this form of exit doesn't call at_exit handlers + exit!(0) + end + end + + # detach from child process (parent may still wait for detached process if they wish) + Process.detach(child) unless options[:no_detach] + + return child + end + +end + +# Patch to work with passenger +if defined? Passenger::Rack::RequestHandler + class Passenger::Rack::RequestHandler + alias_method :orig_process_request, :process_request + def process_request(env, input, output) + Spork.resource_to_close(input) + Spork.resource_to_close(output) + orig_process_request(env, input, output) + end + end +end diff --git a/lib/task.rb b/lib/task.rb index 0adb7a0..aee6c62 100644 --- a/lib/task.rb +++ b/lib/task.rb @@ -1,62 +1,31 @@ +require File.join(File.dirname(__FILE__),'spork') DEFAULT_TASK_MAX_DURATION = 36000 module OpenTox # Class for handling asynchronous tasks class Task - - def self.create service_uri - Task.new RestClient.post(service_uri,{}).chomp - #eval("#{self}.new(\"#{uri}\", #{subjectid})") - end - - def http_code - get(@uri).code - end - - def status - metadata[RDF::OT.hasStatus].to_s - end - - def result_uri - metadata[RDF::OT.resultURI] + def self.create service_uri, params={} + task = Task.new RestClient.post(service_uri,params).chomp + pid = Spork.spork { yield } + task.pid = pid + task end def description metadata[RDF::DC.description] end - def errorReport - metadata[RDF::OT.errorReport] - end - def cancel - RestClient.put(File.join(@uri,'Cancelled'),{:cannot_be => "empty"}) + RestClient.put(File.join(@uri,'Cancelled'),{}) end def completed(uri) RestClient.put(File.join(@uri,'Completed'),{:resultURI => uri}) end - def error(error_report) - raise "no error report" unless error_report.is_a?(OpenTox::ErrorReport) - RestClient.put(File.join(@uri,'Error'),{:errorReport => error_report.to_yaml}) - end - - def pid=(pid) - RestClient.put(File.join(@uri,'pid'), {:pid => pid}) - end - - def running? - metadata[RDF::OT.hasStatus] == 'Running' - end - - def completed? - metadata[RDF::OT.hasStatus] == 'Completed' - end - - def error? - metadata[RDF::OT.hasStatus] == 'Error' + def error(error) + RestClient.put(File.join(@uri,'Error'),{:errorReport => OpenTox::Error.new(error)}) end # waits for a task, unless time exceeds or state is no longer running @@ -69,4 +38,23 @@ module OpenTox end end + def method_missing(method,*args) + method = method.to_s + begin + case method + when /=/ + res = RestClient.put(File.join(@uri,method.sub(/=/,'')),{}) + super unless res.code == 200 + when /\?/ + return metadata[RDF::OT.hasStatus] == method.sub(/\?/,'').capitalize + else + return metadata[RDF::OT[method]].to_s + end + rescue + super + end + end + + #TODO: subtasks + end diff --git a/test/rest.rb b/test/rest.rb index bb91b68..52ca55d 100644 --- a/test/rest.rb +++ b/test/rest.rb @@ -6,9 +6,7 @@ class RestTest < Test::Unit::TestCase def test_post_get_delete uri = "http://ot-dev.in-silico.ch/dataset" - dataset_service = OpenTox::Dataset.new uri - assert_match /#{uri}/, dataset_service.get - dataset = dataset_service.post + dataset = OpenTox::Dataset.create uri assert_match /#{uri}/, dataset.uri.to_s metadata = dataset.metadata assert_equal RDF::OT.Dataset, metadata[RDF.type] diff --git a/test/task.rb b/test/task.rb index 1f0c9c2..5828223 100644 --- a/test/task.rb +++ b/test/task.rb @@ -15,10 +15,10 @@ class TaskTest < Test::Unit::TestCase def test_create_and_complete task = OpenTox::Task.create TASK_SERVICE_URI - assert_equal "Running", task.status + assert_equal "Running", task.hasStatus task.completed "http://test.org" - assert_equal "Completed", task.status - assert_equal "http://test.org", task.result_uri + assert_equal "Completed", task.hasStatus + assert_equal "http://test.org", task.resultURI end -- cgit v1.2.3