Package Products :: Package ZenHub :: Module zodb |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenHub.zodb

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 or (at your
      8  # option) any later version as published by the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13  import logging
     14  log = logging.getLogger('zen.ZenHub')
     15
     16  from zope.component.event import objectEventNotify
     17  from zope.interface import implements
     18  from zope.interface.advice import addClassAdvisor
     19  from zope.component import provideHandler
     20  from zope.component.interfaces import ObjectEvent
     21  from ZODB.utils import u64
     22  from twisted.internet import defer, reactor
     23
     24  from Products.ZenModel.Device import Device
     25  from Products.ZenModel.DeviceComponent import DeviceComponent
     26  from time import time
     27  from Products.ZenRelations.PrimaryPathObjectManager import PrimaryPathObjectManager
     28  from Products.ZenHub.interfaces import IUpdateEvent, IDeletionEvent
     29
     30
     31 -class InvalidationEvent(ObjectEvent):
     32 -    def __init__(self, object, oid):
     33          super(InvalidationEvent, self).__init__(object)
     34          self.oid = oid
     35
     36
     37 -class UpdateEvent(InvalidationEvent):
     38      implements(IUpdateEvent)
     39
     40
     41 -class DeletionEvent(InvalidationEvent):
     42      implements(IDeletionEvent)
     43
     44
     45 -def _remove(_ignored, oid, queue):
     46      """
     47      We don't want bad oids hanging around forever.
     48      """
     49      queue.remove(oid)
     50
     51
     52 -def _dispatch(dmd, oid, ioid, queue):
     53      """
     54      Send to all the services that care by firing events.
     55      """
     56      d = defer.Deferred()
     57      # Closure to use as a callback
     58      def inner(_ignored):
     59          try:
     60              if dmd.pauseHubNotifications:
     61                  log.debug('notifications are currently paused')
     62                  return
     63
     64              # Go pull the object out of the database
     65              obj = dmd._p_jar[oid]
     66              # Don't bother with all the catalog stuff; we're depending on primaryAq
     67              # existing anyway, so only deal with it if it actually has primaryAq.
     68              if (isinstance(obj, PrimaryPathObjectManager)
     69                    or isinstance(obj, DeviceComponent)):
     70                  try:
     71                      # Try to get the object
     72                      obj = obj.__of__(dmd).primaryAq()
     73                  except (AttributeError, KeyError), ex:
     74                      # Object has been removed from its primary path (i.e. was
     75                      # deleted), so make a DeletionEvent
     76                      log.debug("Notifying services that %r has been deleted" % obj)
     77                      event = DeletionEvent(obj, oid)
     78                  else:
     79                      # Object was updated, so make an UpdateEvent
     80                      log.debug("Notifying services that %r has been updated" % obj)
     81                      event = UpdateEvent(obj, oid)
     82                  # Fire the event for all interested services to pick up
     83                  objectEventNotify(event)
     84              # Return the oid, although we don't currently use it
     85              return oid
     86          finally:
     87              queue.remove(ioid)
     88      d.addCallback(inner)
     89      # Call the deferred in the reactor so we give time to other things
     90      reactor.callLater(0, d.callback, True)
     91      return d
     92
     93
     94  @defer.inlineCallbacks
     95 -def processInvalidations(dmd, queue, oids):
     96      i = 0
     97      for i, oid in enumerate(oids):
     98          ioid = u64(oid)
     99          # Try pushing it into the queue, which is an IITreeSet. If it inserted
    100          # successfully it returns 1, else 0.
    101          if queue.insert(ioid):
    102              # Get the deferred that does the notification
    103              d = _dispatch(dmd, oid, ioid, queue)
    104              yield d
    105      defer.returnValue(i)
    106
    107
    108 -def _listener_decorator_factory(eventtype):
    109      """
    110      Given a particular event interface, returns a decorator factory that may be
    111      used to create decorators for methods causing those methods, when bound, to
    112      be registered as object event subscribers.
    113
    114      @param eventtype: The event interface to which the subscribers should
    115      listen.
    116      """
    117      def factory(*types):
    118          """
    119          The eventtype-specific decorator factory. Calling this factory both
    120          produces a decorator and wraps the __init__ of the class of the
    121          decorated method with a function that registers the handlers.
    122          """
    123          # Create a mutable to store the handler name between the call to
    124          # decorator and the call to advisor (simple assignment won't work for
    125          # scope reasons)
    126          _f = {}
    127
    128          def decorator(f):
    129              """
    130              The decorator. All it does is print a log message, then call the
    131              original function.
    132              """
    133              def inner(self, obj, event):
    134                  # Log that we've called this listener
    135                  fname = '.'.join((self.__class__.__name__, f.__name__))
    136                  log.debug('%s is interested in %r for %r' % (fname, event, obj))
    137
    138                  # Call the original function
    139                  return f(self, obj, event)
    140
    141              # Push the name of the function outside the decorator scope so the
    142              # class advisor has access when it needs to register handlers.
    143              _f[f.__name__] = 1
    144
    145              # Return the closure to replace the original function.
    146              return inner
    147
    148          def advisor(cls):
    149              """
    150              A class advisor that is called after the class is created. We use
    151              this to wrap __init__ in a function that registers any handlers
    152              created via this factory, which are stored on the class.
    153              """
    154              # Set one flag per fname on the class so we don't double-register
    155              # when we override in a subclass (once for super, once for sub)
    156              fname = _f.keys()[0]
    157              cls.__registered = getattr(cls, '__registered', {})
    158
    159              # Check our flag
    160              if fname not in cls.__registered or not issubclass(cls, tuple(cls.__registered[fname])):
    161                  # Decorator for __init__
    162                  def registerHandlers(f):
    163                      def __init__(self, *args, **kwargs):
    164                          # Call the original constructor; we'll register handlers
    165                          # afterwards
    166                          f(self, *args, **kwargs)
    167                          handler = getattr(self, fname)
    168                          for t in types:
    169                              # Register the handler. Here's where we use
    170                              # eventtype, which was passed in to the outermost
    171                              # function in this behemoth.
    172                              provideHandler(handler, (t, eventtype))
    173
    174                      # Return the closure to replace the decorated method
    175                      return __init__
    176
    177                  # Decorate __init__ so it will register the handlers on
    178                  # instantiation
    179                  cls.__init__ = registerHandlers(cls.__init__)
    180                  # Set the flag for this fname
    181                  cls.__registered.setdefault(fname, []).append(cls)
    182
    183
    184              # Return the class, which will replace the original class.
    185              return cls
    186
    187          # Add the advisor to the class.
    188          addClassAdvisor(advisor)
    189
    190          # Return the decorator so we get the log message when called
    191          return decorator
    192
    193      return factory
    194
    195
    196  # Create two decorator factories for the two kinds of events.
    197  onUpdate = _listener_decorator_factory(IUpdateEvent)
    198  onDelete = _listener_decorator_factory(IDeletionEvent)
    199