Class: Arrow::Broker

Inherits:
Object show all
Defined in:
lib/arrow/broker.rb

Overview

The broker is the applet manager. It maintains a registry of applets, and delegates transactions based on the request’s URI.

Authors

  • Michael Granger

Please see the file LICENSE in the top-level directory for licensing details.

Constant Summary

FILE_SEPARATOR =

A regular expression that matches the file separator on this system

Regexp.new( Regexp.compile(File::SEPARATOR) )

Instance Attribute Summary

Instance Method Summary

Methods inherited from Object

deprecate_class_method, deprecate_method, inherited

Methods included from Loggable

#log

Constructor Details

- (Broker) initialize(config)

Create a new Arrow::Broker object from the specified config (an Arrow::Config object).



30
31
32
33
34
# File 'lib/arrow/broker.rb', line 30

def initialize( config )
  @config = config
  @registry = Arrow::AppletRegistry.new( config )
  @start_time = Time.now
end

Instance Attribute Details

- (Object) registry

The Hash of RegistryEntry structs keyed by uri



42
43
44
# File 'lib/arrow/broker.rb', line 42

def registry
  @registry
end

- (Object) start_time (readonly)

The Time when the Broker was started



45
46
47
# File 'lib/arrow/broker.rb', line 45

def start_time
  @start_time
end

Instance Method Details

- (Object) builtin_error_handler(applet, txn, err) (protected)

The builtin error handler routine. Outputs a plain-text backtrace for the given exception err and applet to the given transaction txn.



239
240
241
242
243
244
245
246
# File 'lib/arrow/broker.rb', line 239

def builtin_error_handler( applet, txn, err )
  self.log.notice "Using builtin error handler."
  txn.request.content_type = "text/plain"
  txn.status = Apache::OK

  return "Arrow Applet Error in '%s': %s\n\t%s" %
    [ applet.class.signature.name, err.message, err.backtrace.join("\n\t") ]
end

- (Object) builtin_missing_handler(txn, *args) (protected)

The builtin missing-applet handler routine. Returns false, which causes the dispatcher to decline the request.



230
231
232
233
# File 'lib/arrow/broker.rb', line 230

def builtin_missing_handler( txn, *args )
  self.log.notice "Using builtin missing-applet handler."
  return false
end

- (Object) delegate(txn)

Dispatch the specified transaction txn to the appropriate handler based on the request’s path_info.



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
# File 'lib/arrow/broker.rb', line 51

def delegate( txn )
  rval = appletchain = nil
  self.log.debug "Start of delegation (%s)" % [ txn.unparsed_uri ]

  # Fetch the path and trim the leading '/'
  path = txn.path
  path.sub!( %r{^/}, '' )
  self.log.debug "Request's path is %p" % path

  # Check for updated/deleted/added applets
  @registry.check_for_updates

  # Get the chain of applets to execute for the request
  appletchain = @registry.find_applet_chain( path )

  # If the pathinfo doesn't correspond to at least one applet, run
  # the no-such-applet handler.
  if appletchain.empty?
    rval = self.run_missing_applet_handler( txn, path )
  else
    rval = self.run_applet_chain( txn, appletchain )
  end

  # Set the request status to declined if it hasn't been set yet and
  # the return value is false.
  if !rval
    self.log.error "Applet returned false value. " +
      "Setting status to DECLINED"
    txn.status = Apache::DECLINED
  end

  # self.log.debug "Returning %p" % [ rval ]
  return rval
end

- (Object) run_applet(applet, txn, rest)

Run the specified applet with the given txn (an Arrow::Transaction) and the rest of the path_info split on ’/’.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/arrow/broker.rb', line 89

def run_applet( applet, txn, rest )
  self.log.debug "Running '%s' with args: %p" %
    [ applet.signature.name, rest ]
  return applet.run( txn, *rest )
rescue ::Exception => err
  self.log.error "[%s]: Error running %s (%s): %s:\n\t%s" % [
      txn.serial,
    applet.signature.name,
    applet.class.filename,
    err.message,
    err.backtrace.join("\n\t"),
  ]
  return self.run_error_handler( applet, txn, err )
end

- (Object) run_applet_chain(txn, chain) (protected)

Given a chain of applets built from a URI, run the indexth one with the specified transaction (txn). Applets before the last get called via their #delegate method, while the last one is called via #run.

Raises:

  • (Arrow::AppletError)


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/arrow/broker.rb', line 170

def run_applet_chain( txn, chain )
  self.log.debug "Running applet chain: #{chain.inspect}"
  raise Arrow::AppletError, "Malformed applet chain" if
    chain.empty? || !chain.first.respond_to?( :applet )

  res = nil
  applet, txn.applet_path, args = self.unwrap_chain_link( chain.first )

  # If there's only one item left, run it
  if chain.nitems == 1
    self.log.debug "Running final applet in chain"
    res = self.run_applet( applet, txn, args )

  # Otherwise, delegate the transaction to the next applet with the
  # remainder of the chain.
  else
    dchain = chain[ 1..-1 ]
    self.log.debug "Running applet %s in chain of %d; chain = %p" %
      [ applet.signature.name, chain.nitems, dchain ]

    begin
      res = applet.delegate( txn, dchain, *args ) do |subchain|
        subchain = dchain if subchain.nil?
        self.log.debug "Delegated call to appletchain %p" % [ subchain ]
        self.run_applet_chain( txn, subchain )
      end
    rescue ::Exception => err
      self.log.error "Error while executing applet chain: %p (/%s): %s:\n\t%s" % [
        applet,
        chain.first[1],
        err.message,
        err.backtrace.join("\n\t"),
      ]
      res = self.run_error_handler( applet, txn, err )
    end
  end

  return res
end

- (Object) run_error_handler(applet, txn, err)

Handle the given applet error err for the specified applet, using the given transaction txn. This will attempt to run whatever applet is configured as the error-handler, or run a builtin handler applet if none is configured or the configured one isn’t loaded.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/arrow/broker.rb', line 134

def run_error_handler( applet, txn, err )
  rval = nil
  handlerName = @config.applets.errorApplet.sub( %r{^/}, '' )

  unless handlerName == "(builtin)" or !@registry.key?( handlerName )
    handler = @registry[handlerName]
    self.log.notice "Running error handler applet '%s' (%s)" %
      [ handler.signature.name, handlerName ]

    begin
      rval = handler.run( txn, "report_error", applet, err )
    rescue ::Exception => err2
      self.log.error "Error while attempting to use custom error "\
      "handler '%s': %s\n\t%s" % [
        handler.signature.name,
        err2.message,
        err2.backtrace.join("\n\t"),
      ]

      rval = self.builtin_error_handler( applet, txn, err )
    end
  else
    rval = self.builtin_error_handler( applet, txn, err )
  end

  return rval
end

- (Object) run_missing_applet_handler(txn, uri)

Handle requests that target an applet that doesn’t exist.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/arrow/broker.rb', line 106

def run_missing_applet_handler( txn, uri )
  rval = appletchain = nil
  handlerUri = @config.applets.missingApplet
  args = uri.split( %r{/} )

  # Build an applet chain for user-configured handlers
  if handlerUri != "(builtin)"
    appletchain = @registry.find_applet_chain( handlerUri )
    self.log.error "Configured MissingApplet handler (%s) doesn't exist" %
      handlerUri if appletchain.empty?
  end

  # If the user-configured handler maps to one or more handlers, run
  # them. Otherwise, run the build-in handler.
  unless appletchain.nil? || appletchain.empty?
    rval = self.run_applet_chain( txn, appletchain )
  else
    rval = self.builtin_missing_handler( txn, *args )
  end

  return rval
end

Check the specified link of an applet chain for sanity and return its constituent bits for assignment. This is necessary to provide sensible errors if a delegating app screws up a chain somehow.



214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/arrow/broker.rb', line 214

def unwrap_chain_link( link )
  applet = link.applet or raise Arrow::AppletChainError, "Null applet"
  path = link.path or raise Arrow::AppletChainError, "Null path"
  args = link.args or raise Arrow::AppletChainError, "Null argument list"
  unless args.is_a?( Array )
    emsg = "Argument list is a %s: expected an Array" %
      args.class.name
    raise Arrow::AppletChainError, emsg
  end

  return applet, path, args
end