summaryrefslogtreecommitdiff
path: root/lib/error.rb
blob: 90b55e35069fddd1959d6e7dee43e4bf31ebac8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# adding additional fields to Exception class to format errors according to OT-API

class RuntimeError
  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

  class Error < RuntimeError
    def initialize code, message
      @http_code = code
      super message
    end
  end

  # OpenTox errors
  {
    "BadRequestError" => 400,
    "NotAuthorizedError" => 401,
    "NotFoundError" => 404,
    "LockedError" => 423,
    "InternalServerError" => 500,
    "NotImplementedError" => 501,
    "ServiceUnavailableError" => 503,
    "TimeOutError" => 504,
  }.each do |klass,code|
    # create error classes 
    c = Class.new Error do
      define_method :initialize do |message|
        super code, message
      end
    end
    OpenTox.const_set klass,c
    
    # define global methods for raising errors, eg. bad_request_error
    Object.send(:define_method, klass.underscore.to_sym) do |message|
      raise c.new message
    end
  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
    
    attr_accessor :rdf # RDF Graph
    attr_accessor :http_code # TODO: remove when task service is fixed

    def initialize 
      @rdf = RDF::Graph.new
    end

    # creates a error report object, from an ruby-exception object
    # @param [Exception] error
    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

    def actor=(uri)
      # TODO: test actor assignement (in opentox-server)
      subject = RDF::Query.execute(@rdf) do
          pattern [:subject, RDF.type, RDF::OT.ErrorReport]
      end.limit(1).select(:subject)
  })
      @rdf << [subject, RDF::OT.actor, uri]
    end
    
    # define to_ and self.from_ methods for various rdf formats
    [:rdfxml,:ntriples,:turtle].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
end

# overwrite backtick operator to catch system errors
class Object
  def `(code)
    msg = super("#{code} 2>&1").chomp
    internal_server_error msg unless $?.to_i == 0
    msg
  rescue Errno::ENOENT => e
    internal_server_error e
  end
end

class Array
  def short_backtrace
    short = []
    each do |c|
      break if c =~ /sinatra\/base/
      short << c
    end
    short.join("\n")
  end
end