# Purpose: Wrapper for FreeBASE plugin instanciation # # $Id: plugin.rb,v 1.1 2002/02/06 03:24:38 richkilmer Exp $ # # Authors: Rich Kilmer # Contributors: # # This file is part of the FreeRIDE project # # This application is free software; you can redistribute it and/or # modify it under the terms of the Ruby license defined in the # COPYING file. # # Copyright (c) 2001 Rich Kilmer. All rights reserved. # require 'rexml/document' module FreeBASE ## # The Plugin class is the wrapper for the actual plugin instance # class Plugin attr_reader :name, :version, :require_file, :startup_module #transition definitions FAILURE_TRANSITIONS = { LOADING => UNLOADED, STARTING => LOADED, STOPPING => RUNNING, UNLOADING => LOADED } LEGAL_TRANSITIONS = { UNLOADED => [LOADING], LOADING => [LOADED], LOADED => [STARTING, UNLOADING], STARTING => [RUNNING], RUNNING => [STOPPING], STOPPING => [LOADED], UNLOADING => [UNLOADED] } SYSTEM_TRANSITIONS = { LOADING => [UNLOADED, :load], STARTING => [LOADED, :start], STOPPING => [RUNNING, :stop], UNLOADING => [LOADED, :unload] } ST_CONDITION = 0 ST_METHOD = 1 ## # Returns the properties object for this plugin. This raises an # exception if there is not properties file definded in the plugin.xml # file ..... error puts "Error loading required file (#{require_file}) for plugin: #{@name}" transition_failure return end eval(@startup_module).load(self) end ## # Starts the plugin instance by calling start on the Module defined by startup_module. # def start transition(STARTING) self["log/info"] << "Starting" eval(@startup_module).start(self) end ## # Stops the plugin instance by calling stop on the Module defined by startup_module. # def stop transition(STOPPING) self["log/info"] << "Stopping" eval(@startup_module).stop(self) end ## # Unloads the plugin instance by calling unload on the Module defined by startup_module. # def unload transition(UNLOADING) self["log/info"] << "Unloading" eval(@startup_module).unload(self) end ## # Calls the [](path) method on the @local_bus DataBus # def [](path) return @base_slot[path] end ## # Holds dependency information for the slot # Each Dependency manages a particular state (LOADED, etc) # class Dependency @@DependencyNode = Struct.new("DependencyNode", :name, :version, :state) ## # Creates a Dependency for a given state # # state:: [String] The state for the dependency # action:: [String] The action to execute if the dependencies are met # def initialize(state, action) @state = state.upcase @action = action.downcase.intern #make symbol @depends = [] end ## # Adds a plugin to monitor for dependency # # plugin:: [String] The plugin name # version:: [String] The version (use * for now) # state:: [String] The state the dependency must be in # def add_plugin(plugin, version, state) @depends << @@DependencyNode.new(plugin, version, state.upcase) end ## # Registers to listen for all dependency plugin's state # for appropriate transitions. If all dependencies are # met, calls the method defined by action. # # plugin:: [FreeBASE::Plugin] The plugin to call # def register(plugin) return unless plugin.autoload? @depends.each do |dependencyNode| plugin["/plugins/#{dependencyNode.name}/state"].subscribe do |event, slot| if slot.data == dependencyNode.state @triggers.delete(dependencyNode.name) plugin.send(@action) if @triggers.size==0 end end end end ## # Returns if the dependencies have been met # # return:: [Boolean] true if the dependencies have been met # def met? return @triggers.size==0 end ## # Resets and reloads the dependencies (when the plugin is unloaded, or initialized) # def reset @triggers = [] @depends.each {|dependencyNode| @triggers << dependencyNode.name} end end private def parse_xml file = File.new(@plugin_file) xml =REXML::Document.new file file.close @name = xml.root.attributes["name"] @version = xml.root.attributes["version"] @autoload = xml.root.attributes["autoload"]=="true" ? true : false @require_file = xml.root.elements["require"].text @startup_module = xml.root.elements["module"].text if xml.root.elements["properties"] @properties_file = xml.root.elements["properties"].text end @dependencies = {} xml.root.each_element("dependency") do |element| dependency = Dependency.new(element.attributes["state"], element.attributes["action"]) element.each_element("when") do |pelement| attr = pelement.attributes dependency.add_plugin(attr["plugin"], attr["version"], attr["state"]) end @dependencies[element.attributes["state"]] = dependency end end end end