changed plugin specification: use generic "handle_event" instead of "setup" and "cleanup"

fixed bug in reinitializing of plugins (Closes: #111)
fixed glitch that delayed the results of the plugin_manager to go into effect by one request
This commit is contained in:
lars 2007-01-08 02:30:29 +00:00
parent 0cf35e287c
commit 68e0cddc59
4 changed files with 76 additions and 54 deletions

View file

@ -31,10 +31,9 @@ Python code interface:
- function "get_status": - function "get_status":
- returns a string, that describes a state connected to this plugin (e.g. the current date and - returns a string, that describes a state connected to this plugin (e.g. the current date and
time (for the "date" plugin)) time (for the "date" plugin))
- function "setup": - function "handle_event(event, event_info)":
- may be overridden to specify bootup behaviour - may be overridden to specify event handling (e.g. "bootup", "shutdown")
- function "cleanup": - see src/cryptobox/plugins/base.py for details
- may be overridden to specify shutdown behaviour
- the class variable "plugin_capabilities" must be an array of strings (supported: "system" and - the class variable "plugin_capabilities" must be an array of strings (supported: "system" and
"volume") "volume")
- the class variable "plugin_visibility" may contain one or more of the following items: - the class variable "plugin_visibility" may contain one or more of the following items:

View file

@ -62,12 +62,13 @@ class volume_automount(cryptobox.plugins.base.CryptoBoxPlugin):
return "volume_automount" return "volume_automount"
def setup(self): def handle_event(self, event, event_info=None):
"""Override bootup behaviour. """Override bootup behaviour.
Mount all volumes marked as 'automount'. Mount all volumes marked as 'automount'.
""" """
cryptobox.plugins.base.CryptoBoxPlugin.setup(self) cryptobox.plugins.base.CryptoBoxPlugin.handle_event(self, event, event_info)
if event == "bootup":
for cont in self.cbox.get_container_list(): for cont in self.cbox.get_container_list():
if self.__is_auto_mount(cont) and not cont.is_mounted(): if self.__is_auto_mount(cont) and not cont.is_mounted():
cont.mount() cont.mount()

View file

@ -90,14 +90,12 @@ class CryptoBoxPlugin:
return self.__module__ return self.__module__
def setup(self): def handle_event(self, event_name, event_info=None):
"""Any plugin that wants to define bootup actions may override this. """Any plugin that wants to define event actions may override this.
"""
pass
currently only the following events are defined:
def cleanup(self): - "bootup" (the cryptobox server is starting)
"""Any plugin that wants to define shutdown actions may override this. - "shutdown" (the cryptobox server is stopping)
""" """
pass pass

View file

@ -43,7 +43,10 @@ GETTEXT_DOMAIN = 'cryptobox-server'
class PluginIconHandler: class PluginIconHandler:
"""deliver the icons of available plugins via cherrypy""" """deliver the icons of available plugins via cherrypy
the state (enabled/disabled) and the require-auth setting is ignored to
avoid repetitive reloading"""
def __init__(self, plugins): def __init__(self, plugins):
for plugin in plugins.get_plugins(): for plugin in plugins.get_plugins():
@ -76,13 +79,19 @@ class WebInterfaceSites:
self.cbox = cryptobox.core.main.CryptoBox(conf_file) self.cbox = cryptobox.core.main.CryptoBox(conf_file)
self.__cached_language_data = None self.__cached_language_data = None
self.__dataset = None self.__dataset = None
self.icons = None ## load the plugin manager - we will not try to detect new plugins on
self.__plugin_manager = None ## the fly ...
self.__plugin_manager = cryptobox.plugins.manage.PluginManager(
self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self)
self.__reset_dataset() self.__reset_dataset()
## store the original http error handler ## store the original http error handler
self._cp_on_http_error = self.new_http_error_handler self._cp_on_http_error = self.new_http_error_handler
## set initial language order ## set initial language order
self.lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] self.lang_order = self.cbox.prefs["WebSettings"]["Languages"][:]
## publish plugin icons
self.icons = PluginIconHandler(self.__plugin_manager)
self.icons.exposed = True
## announce that the server started up
self.setup() self.setup()
@ -92,7 +101,7 @@ class WebInterfaceSites:
self.cbox.setup() self.cbox.setup()
for plugin in self.__plugin_manager.get_plugins(): for plugin in self.__plugin_manager.get_plugins():
if plugin: if plugin:
plugin.setup() plugin.handle_event("bootup")
def cleanup(self): def cleanup(self):
@ -102,7 +111,7 @@ class WebInterfaceSites:
for plugin in self.__plugin_manager.get_plugins(): for plugin in self.__plugin_manager.get_plugins():
if plugin: if plugin:
self.cbox.log.info("Cleaning up plugin '%s' ..." % plugin.get_name()) self.cbox.log.info("Cleaning up plugin '%s' ..." % plugin.get_name())
plugin.cleanup() plugin.handle_event("shutdown")
self.cbox.cleanup() self.cbox.cleanup()
@ -114,10 +123,7 @@ class WebInterfaceSites:
""" """
self.__load_plugins() self.__load_plugins()
self.__dataset = cryptobox.web.dataset.WebInterfaceDataset( self.__dataset = cryptobox.web.dataset.WebInterfaceDataset(
self.cbox, self.cbox.prefs, self.__plugin_manager.get_plugins()) self.cbox, self.cbox.prefs, self.__plugin_manager)
## publish plugin icons
self.icons = PluginIconHandler(self.__plugin_manager)
self.icons.exposed = True
## check, if a configuration partition has become available ## check, if a configuration partition has become available
self.cbox.prefs.prepare_partition() self.cbox.prefs.prepare_partition()
@ -129,38 +135,49 @@ class WebInterfaceSites:
- reload all plugins and check their state (disabled or not) - reload all plugins and check their state (disabled or not)
- reinitilize the datasets of all plugins - reinitilize the datasets of all plugins
""" """
self.__plugin_manager = cryptobox.plugins.manage.PluginManager( #TODO: in the long-term we should create a separate object that only
self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self) # contains the plugin handlers - this avoids some hassle of namespace
# conflicts - this object will be the cherrypy.server.root
# finish this for v0.4
for plugin in self.__plugin_manager.get_plugins(): for plugin in self.__plugin_manager.get_plugins():
if not plugin: if not plugin:
continue continue
plname = plugin.get_name() plname = plugin.get_name()
## check if there are name conflicts: e.g. a local variable has the ## remove the old plugin handler and attach a new one
## same name as a plugin to be loaded -> skip these plugins
## if we do not check this here, nasty side effects may occour ...
try: try:
## check if there are name conflicts: e.g. a local variable has
## the same name as a plugin to be loaded -> skip these plugins
## if we would not check this here, nasty effects could occour
prev_obj = getattr(self, plname) prev_obj = getattr(self, plname)
if not callable(prev_obj) \ if not callable(prev_obj) or not prev_obj.exposed:
or not prev_obj.exposed: ## name conflict - see below
self.cbox.log.error("Skipped feature (%s) as its name" raise NameError
+ " conflicts with a local variable - see" ## remove the plugin handler
+ " module cryptobox.web.sites" % plname) delattr(self, plname)
except AttributeError:
## "self" does not contain the given "plname" element
## this is ok, as we are just cleaning up
pass
except NameError:
## the attribute "exposed" of the element self."plname" does
## not exist - it seems, that we have a name conflict
self.cbox.log.error("Skipping feature (%s) as its" % plname
+ " name conflicts with a local variable - see"
+ " module cryptobox.web.sites")
## skip this plugin ## skip this plugin
continue continue
except (NameError, AttributeError): ## the old attribute was cleaned up - we can reinitialize it now
## an attribute with the same name does not exist -> ok
if plugin.is_enabled(): if plugin.is_enabled():
self.cbox.log.info("Plugin '%s' loaded" % plname) self.cbox.log.info("Plugin '%s' loaded" % plname)
## expose all features as URLs ## expose all features as URLs
setattr(self, plname, self.return_plugin_action(plugin)) setattr(self, plname, self.return_plugin_action(plugin))
getattr(self, plname).exposed = True getattr(self, plname).exposed = True
#TODO: check, if this really works #TODO: check, if the stream_response feature really works
#for now the "stream_response" feature seems to be broken #for now the "stream_response" feature seems to be broken
#setattr(getattr(self, plname), "stream_respones", True) #setattr(getattr(self, plname), "stream_respones", True)
else: else:
self.cbox.log.info("Plugin '%s' is disabled" % plname) self.cbox.log.info("Plugin '%s' is disabled" % plname)
## remove the plugin, if it was active before ## nothing else has to be done
setattr(self, plname, None)
## sub pages requiring authentication may not be defined above ## sub pages requiring authentication may not be defined above
@ -273,7 +290,8 @@ class WebInterfaceSites:
""" returns a function that is suitable for handling a cherrypy """ returns a function that is suitable for handling a cherrypy
page request page request
""" """
def handler(self, weblang="", device=None, help="0", redirect=None, message_keep=None, **args): def handler(self, weblang="", device=None, help="0", redirect=None,
message_keep=None, **args):
"""this function handles a cherrypy page request """this function handles a cherrypy page request
""" """
plugin.reset() plugin.reset()
@ -323,10 +341,17 @@ class WebInterfaceSites:
else: else:
## some non-volume plugins change the internal state of other ## some non-volume plugins change the internal state of other
## plugins - e.g.: plugin_manager ## plugins - e.g.: plugin_manager
## if we do not call __load_plugins now, then it is possible
## to call a plugin directly after disabling it (only once)
self.__load_plugins()
self.__dataset.set_plugin_data() self.__dataset.set_plugin_data()
## default page for non-volume plugins is the disk selection ## default page for non-volume plugins is the disk selection
if not next_template: if not next_template:
next_template = { "plugin":"disks", "values":{} } next_template = { "plugin":"disks", "values":{} }
#TODO: there is a lot of piece-by-piece updating around here
# for v0.4 we should just call __reset_dataset - but this would
# require to store the currently changed dataset values (e.g.i
# weblang) somewhere else to not override it
## some non-volume plugins may change the state of containers ## some non-volume plugins may change the state of containers
## the mount plugin may change the number of active disks - for the logo ## the mount plugin may change the number of active disks - for the logo
self.__dataset.set_containers_state() self.__dataset.set_containers_state()
@ -412,8 +437,7 @@ class WebInterfaceSites:
## check an environment setting - this is quite common behind proxies ## check an environment setting - this is quite common behind proxies
if os.environ.has_key("HTTPS"): if os.environ.has_key("HTTPS"):
return True return True
## this arbitrarily chosen header must be documented in README.proxy ## this arbitrarily chosen header is documented in README.proxy
#TODO: check http://jamesthornton.com/writing/openacs-pound.html for this
if cherrypy.request.headers.has_key("X-SSL-Request") \ if cherrypy.request.headers.has_key("X-SSL-Request") \
and (cherrypy.request.headers["X-SSL-Request"] == "1"): and (cherrypy.request.headers["X-SSL-Request"] == "1"):
return True return True