From 723d731bc08d90a82f22474c9fafcc504eb424d3 Mon Sep 17 00:00:00 2001 From: ch Date: Wed, 8 Jul 2015 10:58:24 +0200 Subject: feature, error and task (almost) working --- lib/error.rb | 31 ++++++++------- lib/opentox.rb | 95 ++++++++++++++++++++-------------------------- lib/overwrite.rb | 4 +- lib/rest-client-wrapper.rb | 12 +++--- lib/task.rb | 48 +++++++++-------------- 5 files changed, 85 insertions(+), 105 deletions(-) diff --git a/lib/error.rb b/lib/error.rb index 506c542..878ed28 100644 --- a/lib/error.rb +++ b/lib/error.rb @@ -8,27 +8,27 @@ module OpenToxError @error_cause = cause ? OpenToxError::cut_backtrace(cause) : short_backtrace super message - #unless self.is_a? Errno::EAGAIN # avoid "Resource temporarily unavailable" errors @uri = uri.to_s.sub(%r{//.*:.*@},'//') # remove credentials from uri @http_code ||= 500 - @rdf = RDF::Graph.new - subject = RDF::Node.new - @rdf << [subject, RDF.type, RDF::OT.ErrorReport] - @rdf << [subject, RDF::OT.actor, @uri] - @rdf << [subject, RDF::OT.message, message.to_s] - @rdf << [subject, RDF::OT.statusCode, @http_code] - @rdf << [subject, RDF::OT.errorCode, self.class.to_s] - @rdf << [subject, RDF::OT.errorCause, @error_cause] - $logger.error("\n"+self.to_yaml) - #end + @metadata = { + :type => "ErrorReport", + :actor => @uri, + :message => message.to_s, + :statusCode => @http_code, + :errorCode => self.class.to_s, + :errorCause => @error_cause, + } + $logger.error("\n"+JSON.pretty_generate(@metadata)) end +=begin # this method defines what is used for to_yaml (override to skip large @rdf graph) def encode_with coder @rdf.each do |statement| coder[statement.predicate.fragment.to_s] = statement.object.to_s end end +=end def self.cut_backtrace(trace) if trace.is_a?(Array) @@ -36,7 +36,7 @@ module OpenToxError cut_index ||= trace.size cut_index -= 1 cut_index = trace.size-1 if cut_index < 0 - trace[0..cut_index].join("\n") + trace[0..cut_index] else trace end @@ -47,6 +47,7 @@ module OpenToxError OpenToxError::cut_backtrace(backtrace) end +=begin RDF_FORMATS.each do |format| # rdf serialization methods for all formats e.g. to_rdfxml send :define_method, "to_#{format}".to_sym do @@ -63,11 +64,15 @@ module OpenToxError @rdf.each{|statement| writer << statement} if @rdf end end +=end + + def to_json + @metadata.to_json + end end class RuntimeError -#class StandardError include OpenToxError end diff --git a/lib/opentox.rb b/lib/opentox.rb index 9564e9c..850ef4b 100644 --- a/lib/opentox.rb +++ b/lib/opentox.rb @@ -3,7 +3,6 @@ $logger = OTLogger.new(STDERR) $logger.level = Logger::DEBUG module OpenTox - #include RDF CH: leads to namespace clashes with URI class attr_reader :uri attr_writer :metadata, :parameters @@ -14,20 +13,16 @@ module OpenTox # @param uri [optional,String] URI # @return [OpenTox] OpenTox object def initialize uri=nil - @rdf = RDF::Graph.new @metadata = {} - @parameters = [] + @metadata[:type] = self.class.to_s.split(/::/).last + #@parameters = [] uri ? @uri = uri.to_s.chomp : @uri = File.join(service_uri, SecureRandom.uuid) end # Object metadata (lazy loading) # @return [Hash] Object metadata def metadata force_update=false - if (@metadata.nil? or @metadata.empty? or force_update) and URI.accessible? @uri - get if @rdf.nil? or @rdf.empty? or force_update - # return values as plain strings instead of RDF objects - @metadata = @rdf.to_hash[RDF::URI.new(@uri)].inject({}) { |h, (predicate, values)| h[predicate] = values.collect{|v| v.to_s}; h } - end + get #if (@metadata.nil? or @metadata.empty? or force_update) and URI.accessible? @uri @metadata end @@ -35,6 +30,7 @@ module OpenTox # @param predicate [String] Predicate URI # @return [Array, String] Predicate value(s) def [](predicate) + predicate = predicate.to_s return nil if metadata[predicate].nil? metadata[predicate].size == 1 ? metadata[predicate].first : metadata[predicate] end @@ -43,15 +39,17 @@ module OpenTox # @param predicate [String] Predicate URI # @param values [Array, String] Predicate value(s) def []=(predicate,values) + predicate = predicate.to_s @metadata[predicate] = [values].flatten end +=begin # Object parameters (lazy loading) # {http://opentox.org/dev/apis/api-1.2/interfaces OpenTox API} # @return [Hash] Object parameters def parameters force_update=false if (@parameters.empty? or force_update) and URI.accessible? @uri - get if @rdf.empty? or force_update + get #if @rdf.empty? or force_update params = {} query = RDF::Query.new({ :parameter => { @@ -74,20 +72,28 @@ module OpenTox def parameter_value title @parameters.collect{|p| p[RDF::OT.paramValue] if p[RDF::DC.title] == title}.compact.first end +=end # Get object from webservice # @param [String,optional] mime_type - def get mime_type="text/plain" - bad_request_error "Mime type #{mime_type} is not supported. Please use 'text/plain' (default) or 'application/rdf+xml'." unless mime_type == "text/plain" or mime_type == "application/rdf+xml" + def get mime_type="application/json" + bad_request_error "Mime type #{mime_type} is not supported. Please use 'application/json' (default), 'text/plain' (ntriples) or mime_type == 'application/rdf+xml'." unless mime_type == "application/json" or mime_type == "text/plain" or mime_type == "application/rdf+xml" response = RestClientWrapper.get(@uri,{},{:accept => mime_type}) if URI.task?(response) uri = wait_for_task response response = RestClientWrapper.get(uri,{},{:accept => mime_type}) end - parse_ntriples response if mime_type == "text/plain" - parse_rdfxml response if mime_type == "application/rdf+xml" + case mime_type + when 'application/json' + @metadata = JSON.parse(response) + when "text/plain" + parse_ntriples response + when "application/rdf+xml" + parse_rdfxml response + end end +=begin # Post object to webservice (append to object), rarely useful and deprecated # @deprecated def post wait=true, mime_type="text/plain" @@ -102,17 +108,20 @@ module OpenTox uri = RestClientWrapper.post @uri.to_s, body, { :content_type => mime_type} wait ? wait_for_task(uri) : uri end +=end # Save object at webservice (replace or create object) - def put wait=true, mime_type="text/plain" - bad_request_error "Mime type #{mime_type} is not supported. Please use 'text/plain' (default) or 'application/rdf+xml'." unless mime_type == "text/plain" or mime_type == "application/rdf+xml" - @metadata[RDF::OT.created_at] = DateTime.now unless URI.accessible? @uri + def put wait=true, mime_type="application/json" + bad_request_error "Mime type #{mime_type} is not supported. Please use 'application/json' (default)." unless mime_type == "application/json" or mime_type == "text/plain" or mime_type == "application/rdf+xml" + @metadata[:created_at] = DateTime.now unless URI.accessible? @uri #@metadata[RDF::DC.modified] = DateTime.now case mime_type when 'text/plain' body = self.to_ntriples when 'application/rdf+xml' body = self.to_rdfxml + when 'application/json' + body = self.to_json end uri = RestClientWrapper.put @uri, body, { :content_type => mime_type} wait ? wait_for_task(uri) : uri @@ -157,8 +166,11 @@ module OpenTox RDF::Reader.for(format).new(rdf) do |reader| reader.each_statement{ |statement| @rdf << statement } end + # return values as plain strings instead of RDF objects + @metadata = @rdf.to_hash[RDF::URI.new(@uri)].inject({}) { |h, (predicate, values)| h[predicate] = values.collect{|v| v.to_s}; h } end +=begin # rdf serialization methods for all formats e.g. to_rdfxml send :define_method, "to_#{format}".to_sym do create_rdf @@ -170,6 +182,7 @@ module OpenTox end end end +=end end # @return [String] converts object to turtle-string @@ -182,20 +195,28 @@ module OpenTox end end + def to_json + @metadata[:uri] = @uri + @metadata.to_json + end + # @return [String] converts OpenTox object into html document (by first converting it to a string) def to_html to_turtle.to_html end +=begin # short access for metadata keys title, description and type - { :title => RDF::DC.title, :description => RDF::DC.description, :type => RDF.type }.each do |method,predicate| + #{ :title => RDF::DC.title, :description => RDF::DC.description, :type => RDF.type }.each do |method,predicate| + [ :title , :description, :type ].each do |method| send :define_method, method do - self.[](predicate) + self.[](method) end send :define_method, "#{method}=" do |value| - self.[]=(predicate,value) + self.[]=(method,value) end end +=end # define class methods within module def self.included(base) @@ -242,40 +263,8 @@ module OpenTox end def self.find_or_create metadata - t = Time.now - sparql = "SELECT DISTINCT ?s WHERE { " - # flatten 3.level arrays in objects - metadata.each{|p,o| o.flatten! if o.is_a? Array} - metadata.each do |predicate,objects| - unless [RDF::DC.date,RDF::DC.modified,RDF::DC.description].include? predicate # remove dates and description (strange characters in description may lead to SPARQL errors) - if objects.is_a? String - #URI.valid?(objects) ? o = "<#{objects}>" : o = "'''#{objects}'''" #DG: do not understand this quotation - URI.valid?(objects) ? o = "<#{objects}>" : o = "\"#{objects}\"" - sparql << "?s <#{predicate}> #{o}. " - elsif objects.is_a? Array - objects.each do |object| - #URI.valid?(object) ? o = "<#{object}>" : o = "'#{object}'" #DG: do not understand this quotation - URI.valid?(object) ? o = "<#{object}>" : o = "\"#{object}\"" - sparql << "?s <#{predicate}> #{o}. " - end - end - end - end - sparql << "}" - puts "Create SPARQL: #{Time.now-t}" - t = Time.new - uris = RestClientWrapper.get(service_uri,{:query => sparql},{:accept => "text/uri-list"}).split("\n") - puts "Query: #{Time.now-t}" - t = Time.new - if uris.empty? - f=self.create metadata - puts "Create: #{Time.now-t}" - f - else - f=self.new uris.first - puts "Found: #{Time.now-t}" - f - end + uris = RestClientWrapper.get(service_uri,{:query => metadata},{:accept => "text/uri-list"}).split("\n") + uris.empty? ? self.create(metadata) : self.new(uris.first) end end OpenTox.const_set klass,c diff --git a/lib/overwrite.rb b/lib/overwrite.rb index c96c2d8..692e239 100644 --- a/lib/overwrite.rb +++ b/lib/overwrite.rb @@ -158,8 +158,8 @@ module Kernel error = OpenTox::RestClientWrapper.known_errors.select{|error| error[:code] == t.code}.first error_method = error ? error[:method] : :internal_server_error report = t.error_report - error_message = report ? report[RDF::OT.message] : $!.message - error_cause = report ? report[RDF::OT.errorCause] : nil + error_message = report ? report[:message] : $!.message + error_cause = report ? report[:errorCause] : nil Object.send(error_method,error_message,t.uri,error_cause) end uri = t.resultURI diff --git a/lib/rest-client-wrapper.rb b/lib/rest-client-wrapper.rb index 7011eca..de1b74f 100644 --- a/lib/rest-client-wrapper.rb +++ b/lib/rest-client-wrapper.rb @@ -62,13 +62,11 @@ module OpenTox #parameters[:url] = parameters[:url].gsub(/(http|https|)\:\/\/[a-zA-Z0-9\-]+\:[a-zA-Z0-9]+\@/, "REMOVED@") if parameters[:url] #message += "\nREST parameters:\n#{parameters.inspect}" error = known_errors.collect{|e| e if e[:code] == response.code}.compact.first - begin # errors are returned as error reports in turtle, try to parse - content = {} - RDF::Reader.for(:turtle).new(response) do |reader| - reader.each_triple{|triple| content[triple[1]] = triple[2]} - end - msg = content[RDF::OT.message].to_s - cause = content[RDF::OT.errorCause].to_s + begin # errors are returned as error reports in json, try to parse + # TODO: may be the reason for failure of task.rb -n test_11_wait_for_error_task + content = JSON.parse(response) + msg = content["message"].to_s + cause = content["errorCause"].to_s raise if msg.size==0 && cause.size==0 # parsing failed rescue # parsing error failed, use complete content as message msg = "Could not parse error response from rest call '#{method}' to '#{uri}':\n#{response}" diff --git a/lib/task.rb b/lib/task.rb index a64e668..7f4d39a 100644 --- a/lib/task.rb +++ b/lib/task.rb @@ -17,11 +17,11 @@ module OpenTox def self.run(description, creator=nil, uri=nil) task = Task.new uri - task[RDF::OT.created_at] = DateTime.now - task[RDF::OT.hasStatus] = "Running" - task[RDF::DC.description] = description.to_s - task[RDF::DC.creator] = creator.to_s - task[RDF::OT.percentageCompleted] = "0" + task[:created_at] = DateTime.now + task[:hasStatus] = "Running" + task[:description] = description.to_s + task[:creator] = creator.to_s + task[:percentageCompleted] = "0" task.put pid = fork do begin @@ -30,7 +30,7 @@ module OpenTox # wrap non-opentox-errors first e = OpenTox::Error.new(500,e.message,nil,e.backtrace) unless e.is_a?(OpenTox::Error) $logger.error "error in task #{task.uri} created by #{creator}" # creator is not logged because error is logged when thrown - RestClientWrapper.put(File.join(task.uri,'Error'),{:errorReport => e.to_ntriples},{:content_type => 'text/plain'}) + RestClientWrapper.put(File.join(task.uri,'Error'),{:errorReport => e.to_json},{:content_type => 'application/json'}) task.kill end end @@ -59,25 +59,25 @@ module OpenTox end def description - self.[](RDF::DC.description) + self.[](:description) end def creator - self.[](RDF::DC.creator) + self.[](:creator) end def cancel kill - self.[]=(RDF::OT.hasStatus, "Cancelled") - self.[]=(RDF::OT.finished_at, DateTime.now) + self.[]=(:hasStatus, "Cancelled") + self.[]=(:finished_at, DateTime.now.to_s) put end def completed(uri) - self.[]=(RDF::OT.resultURI, uri) - self.[]=(RDF::OT.hasStatus, "Completed") - self.[]=(RDF::OT.finished_at, DateTime.now) - self.[]=(RDF::OT.percentageCompleted, "100") + self.[]=(:resultURI, uri) + self.[]=(:hasStatus, "Completed") + self.[]=(:finished_at, DateTime.now.to_s) + self.[]=(:percentageCompleted, "100") put end @@ -118,31 +118,19 @@ module OpenTox [:hasStatus, :resultURI, :created_at, :finished_at, :percentageCompleted].each do |method| define_method method do - response = self.[](RDF::OT[method]) - response = self.[](RDF::OT1[method]) unless response # API 1.1 compatibility - response + self.[](method) end end # Check status of a task # @return [String] Status def status - self[RDF::OT.hasStatus] + self[:hasStatus] end def error_report - get - report = {} - query = RDF::Query.new({ - :report => { - RDF.type => RDF::OT.ErrorReport, - :property => :value, - } - }) - query.execute(@rdf).each do |solution| - report[solution.property] = solution.value.to_s - end - report + #get + self[:errorReport] end #TODO: subtasks (only for progress in validation) -- cgit v1.2.3