Changeset 284

Show
Ignore:
Timestamp:
04/21/06 14:29:42 (3 years ago)
Author:
ged
Message:
  • Changed broker's applet-lookup logic a bit.
    • No longer uses a 'default applet', as one can bind an applet to '/' to achieve this effect, and what should happen when both are in place is non-intuitive at best.
    • Delegated the actual building of the applet chain to the new registry object.
    • Reworked error-handling while running under delegation a bit for consistency.
  • Fixed a bug in Broker#unwrap_chain_link when a malformed chain was encountered.
  • Fixed a bug in Arrow::Template when reloading.
  • Added logging to track reloading in Arrow::Config.
  • Added a 'name' attribute to Arrow::Dispatcher to make tracking them a bit easier.
  • Added Rails-style argument unfolding to Arrow::FormValidator#valid?
  • Fixed a bug in Arrow::Template that occurred when something tried to check the memsize of a template whose source was nil.
  • Added a new 'prettyprint' templating directive that uses 'pp' to dump an object.
  • Added some new methods to the transaction for building absolute URLs and setting up client-side refresh
  • Added DSL-style signature directives to Arrow::Applet.
Location:
branches/broker-rewrite
Files:
4 added
23 modified

Legend:

Unmodified
Added
Removed
  • branches/broker-rewrite/default.cfg

    r85 r284  
    44templateLogLevel: notice 
    55applets:  
    6   defaultApplet: "/status" 
    76  missingApplet: "/missing" 
    87  errorApplet: "/error" 
  • branches/broker-rewrite/demo.cfg

    r214 r284  
    44templateLogLevel: notice 
    55applets:  
    6   defaultApplet: "/status" 
    76  missingApplet: "/missing" 
    87  errorApplet: "/error" 
  • branches/broker-rewrite/docs/manual/config.html

    r277 r284  
    9696templateLogLevel: info 
    9797applets:  
    98   defaultApplet: "/status" 
    9998  missingApplet: "/missing" 
    10099  errorApplet: "/error" 
     
    168167          <dd> 
    169168            <dl class="linelist config-subitems"> 
    170               <dt>defaultApplet</dt> 
    171               <dd>This specifies the URI of the applet which is executed when 
    172               none is specified by a request's URI. If this value is not 
    173               specified or set to <tt>(builtin)</tt>, a very simple status 
    174               handler is called instead (<a 
    175               href="http://dev.rubycrafters.com/html/classes/Arrow/Broker.html#M000086" 
    176               >Arrow::Broker#runDefaultApplet</a>).</dd> 
    177  
    178169              <dt>missingApplet</dt> 
    179170              <dd>Specifies the URI of an applet which should be executed when 
  • branches/broker-rewrite/lib/arrow.rb

    r277 r284  
    6565    ### this. 
    6666    def self.load_dispatchers( hosts_file ) 
     67        $deferr.puts "Loading dispatchers; $SAFE is #$SAFE" 
    6768        hosts_file.untaint 
    6869        configs = YAML.load( File.read(hosts_file) ) 
  • branches/broker-rewrite/lib/arrow/appletregistry.rb

    r280 r284  
    205205        end 
    206206 
    207         #  Output the applet chain to the debugging log. 
    208         self.log.debug "Found %d applets in %p: %p" % [ 
    209             appletchain.nitems, 
    210             uri_parts.join("/"), 
    211             describe_appletchain( appletchain ) 
    212         ] 
    213  
    214207        return appletchain 
    215208    end 
     
    301294    def load_applets_from_file( path ) 
    302295 
     296        # Reload mode -- don't do anything unless the file's been updated 
    303297        if @filemap.key?( path ) 
    304         # Reload mode -- don't do anything unless the file's been updated 
     298            if @filemap[ path ].has_changed? 
    305299                self.log.info "File %p has changed since loaded. Reloading." % [path] 
    306             if @filemap[ path ].has_changed? 
     300                self.purge_deleted_applets( path ) 
    307301            else 
    308                 self.purge_deleted_applets( path ) 
     302                self.log.debug "File %p has not changed." % [path] 
    309303                return nil 
    310                 self.log.debug "File %p has not changed." % [path] 
    311         end 
    312304            end 
     305        end 
    313306 
    314307        self.log.debug "Attempting to load applet objects from %p" % path 
     
    428421    end 
    429422 
    430  
    431     ### Return a string which describes the given +appletchain+. 
    432     def describe_appletchain( appletchain ) 
    433         appletchain.collect {|item| 
    434             applet = item[0] 
    435             "%s (%s): '%s': %p" % [ 
    436                 applet.class.signature.name, 
    437                 applet.class, 
    438                 item[1], 
    439                 item[2] 
    440             ] 
    441         }.join("\n\t") 
    442     end 
    443  
    444  
    445423end # class Arrow::AppletRegistry 
    446424 
  • branches/broker-rewrite/lib/arrow/broker.rb

    r282 r284  
    7676        @registry.check_for_updates 
    7777 
    78         # Run the default applet if no path info was given, or  
    79         if path.empty? 
    80             rval = self.run_default_applet( txn ) 
    81         else 
    82  
    83             # Get the chain of applets to execute for the request 
    84             appletchain = @registry.find_applet_chain( path ) 
    85  
    86             # If the pathinfo doesn't correspond to at least one applet, run 
    87             # the no-such-applet handler. 
    88             if appletchain.empty? 
    89                 rval = self.run_missing_applet_handler( txn, path ) 
    90             else 
    91                 rval = self.run_applet_chain( txn, appletchain ) 
    92             end 
     78        # Get the chain of applets to execute for the request 
     79        appletchain = @registry.find_applet_chain( path ) 
     80 
     81        # If the pathinfo doesn't correspond to at least one applet, run 
     82        # the no-such-applet handler. 
     83        if appletchain.empty? 
     84            rval = self.run_missing_applet_handler( txn, path ) 
     85        else 
     86            rval = self.run_applet_chain( txn, appletchain ) 
    9387        end 
    9488 
     
    118112            chain.empty? || !chain.first.is_a?( Array ) 
    119113 
     114        res = nil 
    120115        applet, txn.applet_path, args = self.unwrap_chain_link( chain.first ) 
    121116 
     
    123118        if chain.nitems == 1 
    124119            self.log.debug "Running final applet in chain" 
    125             return self.run_applet( applet, txn, args ) 
     120            res = self.run_applet( applet, txn, args ) 
    126121        else 
    127122            dchain = chain[ 1..-1 ] 
     
    129124                [ applet.signature.name, chain.nitems, dchain ] 
    130125 
    131             return applet.delegate( txn, dchain, *args ) {|subchain| 
    132                 subchain = dchain if subchain.nil? 
    133                 self.log.debug "Delegated call to appletchain %p" % [ subchain ] 
    134                 self.run_applet_chain( txn, subchain ) 
    135             } 
    136         end 
    137     rescue ::Exception => err 
    138         self.log.error "Error while executing applet chain: %p (/%s): %s:\n\t%s" % [ 
    139             applet, 
    140             chain.first[1], 
    141             err.message, 
    142             err.backtrace.join("\n\t"), 
    143         ] 
    144         return self.run_error_handler( applet, txn, err ) 
     126            begin 
     127                res = applet.delegate( txn, dchain, *args ) do |subchain| 
     128                    subchain = dchain if subchain.nil? 
     129                    self.log.debug "Delegated call to appletchain %p" % [ subchain ] 
     130                    self.run_applet_chain( txn, subchain ) 
     131                end 
     132            rescue ::Exception => err 
     133                self.log.error "Error while executing applet chain: %p (/%s): %s:\n\t%s" % [ 
     134                    applet, 
     135                    chain.first[1], 
     136                    err.message, 
     137                    err.backtrace.join("\n\t"), 
     138                ] 
     139                res = self.run_error_handler( applet, txn, err ) 
     140            end 
     141        end 
     142 
     143        return res 
    145144    end 
    146145 
     
    150149    ### sensible errors if a delegating app screws up a chain somehow. 
    151150    def unwrap_chain_link( link ) 
    152         applet = link[0] or raise AppletChainError, "Null applet" 
    153  
    154         unless applet.is_a?( Arrow::Applet ) 
    155             emsg = "cannot convert %s into an Arrow::Applet" % 
    156                 applet.class.name 
    157             raise AppletChainError, emsg 
    158         end 
    159  
    160         path = link[1] or raise AppletChainError, "Null path" 
    161         args = link[2] or raise AppletChainError, "Null argument list" 
     151        applet = link[0] or raise Arrow::AppletChainError, "Null applet" 
     152 
     153        path = link[1] or raise Arrow::AppletChainError, "Null path" 
     154        args = link[2] or raise Arrow::AppletChainError, "Null argument list" 
    162155        unless args.is_a?( Array ) 
    163156            emsg = "Argument list is a %s: expected an Array" % 
    164157                args.class.name 
    165             raise AppletChainError, emsg 
     158            raise Arrow::AppletChainError, emsg 
    166159        end                  
    167160 
     
    187180 
    188181 
    189     ### Handle requests that don't target a specific applet (i.e., 
    190     ### their path_info is empty). This will attempt to run whatever applet is 
    191     ### configured as the default one (:defaultApplet), or run a builtin status 
    192     ### applet if no default is configured or if the configured one isn't 
    193     ### loaded. 
    194     def run_default_applet( txn, *args ) 
    195         rval = appletchain = nil 
    196         handlerUri = @config.applets.defaultApplet 
    197  
    198         if handlerUri != "(builtin)" 
    199             appletchain = @registry.find_applet_chain( handlerUri, true ) 
    200             self.log.debug "Found appletchain %p for default applet" % [ appletchain ] 
    201  
    202             if appletchain.empty? 
    203                 rval = self.run_missing_applet_handler( txn, handlerUri ) 
    204             else 
    205                 rval = self.run_applet_chain( txn, appletchain ) 
    206             end 
    207         else 
    208             rval = self.builtin_default_handler( txn ) 
    209         end 
    210  
    211         return rval 
    212     end 
    213  
    214  
    215     ### The builtin default handler routine. Outputs a plain-text status 
    216     ### message. 
    217     def builtin_default_handler( txn ) 
    218         self.log.notice "Using builtin default handler." 
    219  
    220         txn.request.content_type = "text/plain" 
    221         return "Arrow Status: Running %s (%d applets loaded)" % 
    222             [ Time.now, @registry.length ] 
    223     end 
    224  
    225  
    226182    ### Handle requests that target an applet that doesn't exist. 
    227183    def run_missing_applet_handler( txn, uri ) 
  • branches/broker-rewrite/lib/arrow/cache.rb

    r277 r284  
    2525require 'arrow/logger' 
    2626 
    27 module Arrow 
     27### Instances of this class are LRU caches for disk-based objects which keep 
     28### track of the cached object's modification time, expiring the cached 
     29### version when the disk-based version changes.. 
     30class Arrow::Cache < ::Cache 
    2831 
    29     ### Instances of this class are LRU caches for disk-based objects which keep 
    30     ### track of the cached object's modification time, expiring the cached 
    31     ### version when the disk-based version changes.. 
    32     class Cache < ::Cache 
     32    # Default configuration values 
     33    DefaultConfig = { 
     34        :maxNum         => 10, 
     35        :maxObjSize     => nil, 
     36        :maxSize        => nil, 
     37        :expiration     => 3600, 
     38    } 
    3339 
    34         # Default configuration values 
    35         DefaultConfig = { 
    36             :maxNum         => 10, 
    37             :maxObjSize     => nil, 
    38             :maxSize        => nil, 
    39             :expiration     => 3600, 
     40     
     41    ############################################################# 
     42    ### C L A S S   M E T H O D S 
     43    ############################################################# 
     44 
     45    @extent = [] 
     46    class << self 
     47        attr_reader :extent 
     48    end 
     49 
     50 
     51    ############################################################# 
     52    ### I N S T A N C E   M E T H O D S 
     53    ############################################################# 
     54 
     55    ### Create a new cache. This merges the DefaultConfig with the specified 
     56    ### values and transforms camelCased keys into under_barred ones. 
     57    def initialize( name, config={}, &cleanup ) 
     58        @name = name 
     59 
     60        # Merge defaults and specified values 
     61        merged = nil 
     62        if config.is_a?( Arrow::Config::ConfigStruct ) 
     63            merged = DefaultConfig.merge( config.to_h ) 
     64        else 
     65            merged = DefaultConfig.merge( config ) 
     66        end 
     67 
     68        # Transform the config hash into the form the superclass expects 
     69        merged.each_key {|key| 
     70            lckey = key.to_s.gsub( /(.)([A-Z])/ ) {|match| 
     71                match[0,1] + "_" + match[1,1].downcase 
     72            }.intern 
     73 
     74            next if key == lckey 
     75            merged[ lckey ] = merged.delete( key ) 
    4076        } 
    4177 
     78        # Register this instance with the class for introspection (costs 
     79        # much less than ObjectSpace.each_object). 
     80        obj = super( merged, &cleanup ) 
     81        self.class.extent << obj 
     82 
     83        return obj 
     84    end 
     85 
     86 
     87    ###### 
     88    public 
     89    ###### 
     90 
     91    # The name of the cache; used in introspection 
     92    attr_reader :name 
     93 
     94 
     95    ### Overridden from the superclass to prevent .to_s from being called on 
     96    ### objects to determine their size if the object supports a #memsize 
     97    ### method. This is mostly to stop templates from being rendered every 
     98    ### time they're cached. 
     99    def []=( key, obj ) 
     100        self.expire 
    42101         
    43         ############################################################# 
    44         ### C L A S S   M E T H O D S 
    45         ############################################################# 
     102        self.invalidate( key ) if self.cached?( key ) 
    46103 
    47         @extent = [] 
    48         class << self 
    49             attr_reader :extent 
     104        if obj.respond_to?( :memsize ) 
     105            size = obj.memsize 
     106        else 
     107            size = obj.to_s.size 
    50108        end 
    51109 
    52  
    53         ############################################################# 
    54         ### I N S T A N C E   M E T H O D S 
    55         ############################################################# 
    56  
    57         ### Create a new cache. This merges the DefaultConfig with the specified 
    58         ### values and transforms camelCased keys into under_barred ones. 
    59         def initialize( name, config={}, &cleanup ) 
    60             @name = name 
    61  
    62             # Merge defaults and specified values 
    63             merged = nil 
    64             if config.is_a?( Arrow::Config::ConfigStruct ) 
    65                 merged = DefaultConfig.merge( config.to_h ) 
    66             else 
    67                 merged = DefaultConfig.merge( config ) 
    68             end 
    69  
    70             # Transform the config hash into the form the superclass expects 
    71             merged.each_key {|key| 
    72                 lckey = key.to_s.gsub( /(.)([A-Z])/ ) {|match| 
    73                     match[0,1] + "_" + match[1,1].downcase 
    74                 }.intern 
    75  
    76                 next if key == lckey 
    77                 merged[ lckey ] = merged.delete( key ) 
    78             } 
    79  
    80             # Register this instance with the class for introspection (costs 
    81             # much less than ObjectSpace.each_object). 
    82             obj = super( merged, &cleanup ) 
    83             self.class.extent << obj 
    84  
     110        # Test against size threshold 
     111        if @max_obj_size && size > @max_obj_size 
     112            Arrow::Logger[self.class].debug \ 
     113                "%p not cached: size exceeds maxObjSize: %d" % 
     114                [ obj, @max_obj_size ] 
    85115            return obj 
    86116        end 
     117        if @max_obj_size.nil? && @max_size && size > @max_size 
     118            Arrow::Logger[self.class].debug \ 
     119                "%p not cached: size exceeds maxSize: %d" % 
     120                [ obj, @max_size ] 
     121            return obj 
     122        end 
     123         
     124        if @max_num && @list.size >= @max_num 
     125            Arrow::Logger[self.class].debug \ 
     126                "Dropping %p from the cache: count exceeds maxNum: %d" % 
     127                [ @list.first, @max_num ] 
     128            self.invalidate( @list.first ) 
     129        end 
     130 
     131        @size += size 
     132        if @max_size 
     133            while @size > @max_size 
     134                Arrow::Logger[self.class].debug \ 
     135                    "Dropping %p from the cache: size exceeds maxSize: %d" % 
     136                    [ @list.first, @max_size ] 
     137                self.invalidate( @list.first ) 
     138            end 
     139        end 
     140 
     141        @objs[ key ] = Cache::CACHE_OBJECT.new( obj, size, Time.now.to_i ) 
     142        @list.push( key ) 
     143 
     144        return obj 
     145    end 
    87146 
    88147 
    89         ###### 
    90         public 
    91         ###### 
    92  
    93         # The name of the cache; used in introspection 
    94         attr_reader :name 
    95  
    96  
    97         ### Overridden from the superclass to prevent .to_s from being called on 
    98         ### objects to determine their size if the object supports a #memsize 
    99         ### method. This is mostly to stop templates from being rendered every 
    100         ### time they're cached. 
    101         def []=( key, obj ) 
    102             self.expire 
    103              
    104             self.invalidate( key ) if self.cached?( key ) 
    105  
    106             if obj.respond_to?( :memsize ) 
    107                 size = obj.memsize 
    108             else 
    109                 size = obj.to_s.size 
    110             end 
    111  
    112             # Test against size threshold 
    113             if @max_obj_size && size > @max_obj_size 
    114                 Arrow::Logger[self.class].debug \ 
    115                     "%p not cached: size exceeds maxObjSize: %d" % 
    116                     [ obj, @max_obj_size ] 
    117                 return obj 
    118             end 
    119             if @max_obj_size.nil? && @max_size && size > @max_size 
    120                 Arrow::Logger[self.class].debug \ 
    121                     "%p not cached: size exceeds maxSize: %d" % 
    122                     [ obj, @max_size ] 
    123                 return obj 
    124             end 
    125              
    126             if @max_num && @list.size >= @max_num 
    127                 Arrow::Logger[self.class].debug \ 
    128                     "Dropping %p from the cache: count exceeds maxNum: %d" % 
    129                     [ @list.first, @max_num ] 
    130                 self.invalidate( @list.first ) 
    131             end 
    132  
    133             @size += size 
    134             if @max_size 
    135                 while @size > @max_size 
    136                     Arrow::Logger[self.class].debug \ 
    137                         "Dropping %p from the cache: size exceeds maxSize: %d" % 
    138                         [ @list.first, @max_size ] 
    139                     self.invalidate( @list.first ) 
    140                 end 
    141             end 
    142  
    143             @objs[ key ] = Cache::CACHE_OBJECT.new( obj, size, Time.now.to_i ) 
    144             @list.push( key ) 
    145  
    146             return obj 
    147         end 
    148  
    149  
    150     end # class Cache 
    151  
    152 end # module Arrow 
    153  
    154  
     148end # class Arrow::Cache 
  • branches/broker-rewrite/lib/arrow/config-loaders/yaml.rb

    r277 r284  
    3838    # Add YAML domain types for Arrow classes 
    3939 
    40     YAML.add_domain_type( Arrow::YamlDomain, "arrowPath" ) {|type, val| 
     40    YAML.add_domain_type( Arrow::YamlDomain, "arrowPath" ) do |type, val| 
    4141        obj = nil 
    4242        case val 
     
    4949 
    5050        obj 
    51     }  
     51    end 
    5252 
    5353 
  • branches/broker-rewrite/lib/arrow/config.rb

    r277 r284  
    99# [<b>startMonitor</b>] 
    1010#   Start the monitoring subsystem. Defaults to +false+. 
    11 # [<b>defaultApplet</b>] 
    12 #   The URI of the default applet to run when the Arrow appserver root URI 
    13 #   is requested. A value of '(builtin)' (the default) will cause a default 
    14 #   status method to be invoked. 
    1511# [<b>noSuchAppletHandler</b>] 
    1612#   The URI of the applet which should handle requests for applets that don't 
     
    153149            :missingApplet  => '/missing', 
    154150            :errorApplet    => '/error', 
    155             :defaultApplet  => '/', 
    156151        }, 
    157152 
     
    358353    ### from which it was loaded. 
    359354    def changed? 
    360         return true if @struct.modified? 
     355        if @struct.modified? 
     356            self.log.debug "Struct was modified" 
     357            return true 
     358        end 
    361359        return false unless self.name 
    362         self.loader.isNewer?( self.name, self.createTime ) 
     360        if self.loader.isNewer?( self.name, self.createTime ) 
     361            self.log.debug "Config source (%s) has been updated since %s" % 
     362                [ self.name, self.createTime ] 
     363            return true 
     364        end 
    363365    end 
    364366 
     
    422424        def initialize( hash ) 
    423425            @hash = hash.dup 
    424             @modified = false 
     426            @dirty = false 
    425427        end 
    426428 
     
    438440        ### have changed since it was created. 
    439441        def modified? 
    440             return @modified || @hash.values.find do |obj| 
     442            @dirty || @hash.values.find do |obj| 
    441443                obj.is_a?( ConfigStruct ) && obj.modified? 
    442444            end 
     
    518520 
    519521            # :TODO: Actually check to see if anything has changed? 
    520             @modified = true 
     522            @dirty = true 
    521523 
    522524            return self 
     
    551553                define_method( "#{key}?" ) {@hash[key] ? true : false} 
    552554                define_method( "#{key}=" ) {|val| 
    553                     @modified = @hash[key] != val 
     555                    @dirty = @hash[key] != val 
    554556                    @hash[key] = val 
    555557                } 
  • branches/broker-rewrite/lib/arrow/dispatcher.rb