Subversion Repositories HomeAutomation

Rev

Blame | Last modification | View Log | SVN | RSS feed

  1. /**
  2.  * File created by Jimmy
  3.  * at 2003-dec-25 16:15:54
  4.  */
  5. package Macbeth.System;
  6.  
  7. import Macbeth.Utilities.*;
  8. import Macbeth.XML.XMLDataHandler;
  9. import Macbeth.XML.XMLParser;
  10. import Macbeth.Transports.MbLocalTransport;
  11. import Macbeth.Transports.MbInternetTransport;
  12.  
  13. import java.io.*;
  14. import java.util.Iterator;
  15. import java.util.LinkedList;
  16. import java.util.HashMap;
  17. import java.util.List;
  18.  
  19. import org.xml.sax.SAXException;
  20.  
  21. /**
  22.  * The Macbeth kernel. This is the core component that does all the
  23.  * work. You need to extend this class in order to create command-line
  24.  * kernels, GUI-kernels or whatever else.
  25.  * @author Jimmy
  26.  */
  27. public abstract class MbKernel extends MbComponent {
  28.  
  29.     //list of all existing modules (module names mapped to module class names)
  30.     protected HashMap moduleList;
  31.     //list of loaded modules (module names mapped to module objects)
  32.     protected HashMap loadedModules;
  33.     //our debug-output-stream (write meaningless debug-info here)
  34.     protected PrintStream _debug;
  35.     //our info-output-stream (write important info here)
  36.     protected PrintStream _info;
  37.     //our error-output-stream (write error messages here)
  38.     protected PrintStream _errors;
  39.     //our logfile-stream
  40.     protected PrintStream _log;
  41.     //a null-stream that ignores any data written to it
  42.     protected PrintStream _null;
  43.     //list of packet transports
  44.     protected List packetTransports;
  45.     //default transports
  46.     protected MbLocalTransport localTransport;
  47.     protected MbInternetTransport internetTransport;
  48.     //packet dumper
  49.     protected PacketDumper packetDumper;
  50.     //our kernel configuration XML-file
  51.     protected String configFile;
  52.     //our data repository. stores options and other data from the XML-file
  53.     protected DataRepository dataRepository;
  54.     //this points to the options data entry inside the data repository
  55.     protected DataRepository.DataListItem options;
  56.     //in this map, all used script events are mapped to lists filled with MbActions
  57.     protected HashMap scriptEventActions;
  58.  
  59.     /**
  60.      * Creates a new instance of MbKernel.
  61.      * @param configFile The XML-configuration file to use for this kernel.
  62.      */
  63.     public MbKernel(String configFile) {
  64.         //construct MbComponent
  65.         super();
  66.  
  67.         //remember configuration file
  68.         this.configFile = configFile;
  69.  
  70.         //create data repository
  71.         dataRepository = new DataRepository();
  72.         dataRepository.advancedBuild_BeginList("options");
  73.         dataRepository.advancedBuild_BeginListItem();
  74.         dataRepository.advancedBuild_EndListItem();
  75.         dataRepository.advancedBuild_EndList();
  76.         options = (DataRepository.DataListItem)dataRepository.getList("options").firstItem();
  77.  
  78.         //set default values on all our options
  79.         options.putField("kernelname", "Macbeth@" + System.getProperty("user.name").toString());
  80.         options.putField("moduledirectory", System.getProperty("user.dir") + "\\bin\\");
  81.         options.putField("debugstream.logged", "false");
  82.         options.putField("debugstream.timestamped", "false");
  83.         options.putField("debugstream.visible", "false");
  84.         options.putField("debugstream.prefix", "D ");
  85.         options.putField("infostream.logged", "false");
  86.         options.putField("infostream.timestamped", "true");
  87.         options.putField("infostream.visible", "true");
  88.         options.putField("infostream.prefix", "I ");
  89.         options.putField("errorstream.logged", "false");
  90.         options.putField("errorstream.timestamped", "true");
  91.         options.putField("errorstream.visible", "true");
  92.         options.putField("errorstream.prefix", "E ");
  93.  
  94.         //create output streams
  95.         _debug = System.out;
  96.         _info = System.out;
  97.         _errors = System.err;
  98.  
  99.         //log stream will be created during startup
  100.         _log = null;
  101.  
  102.         //create null stream
  103.         _null = new PrintStream(new PipedOutputStream());
  104.  
  105.         //create module lists
  106.         moduleList = new HashMap(32);
  107.         loadedModules = new HashMap(32);
  108.  
  109.         //create packet transport list
  110.         packetTransports = new LinkedList();
  111.         //TODO: search dynamically for installed transports at runtime instead.
  112.         //add local delivery transport
  113.         localTransport = new MbLocalTransport(this);
  114.         installTransport(localTransport);
  115.         //add internet delivery transport
  116.         internetTransport = new MbInternetTransport(this);
  117.         installTransport(internetTransport);
  118.  
  119.         //create packet dumper
  120.         packetDumper = new PacketDumper();
  121.  
  122.         //create the script map
  123.         scriptEventActions = new HashMap();
  124.     }
  125.  
  126.     /**
  127.      * Installs a packet delivery transport into this kernel.
  128.      * @param t The transport.
  129.      */
  130.     protected void installTransport(MbPacketTransport t) {
  131.         packetTransports.add(t);
  132.     }
  133.  
  134.     /**
  135.      * Gets the name of this kernel. Per default, this is
  136.      * "Macbeth@username" where username is your computer
  137.      * username.
  138.      * @return The kernel name.
  139.      */
  140.     public String name() {
  141.         return (String)options.getField("kernelname");
  142.     }
  143.  
  144.     /**
  145.      * Gets a short description of the kernel. This should
  146.      * be kept short (1-2 lines) and should be formatted as
  147.      * plain text.
  148.      * @return A short description of the kernel.
  149.      */
  150.     public abstract String description();
  151.  
  152.  
  153.     /**
  154.      * Call this when you want to trigger events from the kernel. Note that
  155.      * the event name you provide will automatically be prefixed by the string
  156.      * "Kernel.On" so that the final event string takes the form
  157.      * 'Kernel.OnXXX" where XXX is the event name you provide.
  158.      *
  159.      * @param eventName The name of the event. Please read above.
  160.      */
  161.     private final void triggerEvent(String eventName) {
  162.         triggerMacbethEvent("Kernel.On" + eventName);
  163.     }
  164.  
  165.  
  166.     /**
  167.      * Triggers a Macbeth event. The kernel will perform all Macbeth
  168.      * actions that are associated with this event.
  169.      *
  170.      * @param eventName The name of the Macbeth event that is being triggered.
  171.      */
  172.     public void triggerMacbethEvent(String eventName) {
  173.         //get all actions associated with this event
  174.         List l = (List) scriptEventActions.get(eventName);
  175.         if (l!=null) {
  176.             Iterator it = l.iterator();
  177.             while (it.hasNext()) {
  178.                 MbAction a = (MbAction) it.next();
  179.                 try {
  180.                     performAction(a);
  181.                 } catch (MbActionNotPerformedException e) {
  182.                     _errors.println("The scripted action '" + a.getActionString() + " " + a.getParameters() + "' could not be performed! (" + e.getMessage() + ")");
  183.                 }
  184.             }
  185.         }
  186.     }
  187.  
  188.  
  189.     /**
  190.      * Performs an action (called by scripts or by the user).
  191.      * @param action The Macbeth action that should be performed.
  192.      * @throws MbActionNotPerformedException If, for some reason, this action could not be performed.
  193.      */
  194.     protected void performAction(MbAction action) throws MbActionNotPerformedException {
  195.         //split the action string into owner and action name
  196.         String[] tokens = action.getActionString().split("\\.");
  197.         if (tokens.length!=2) {
  198.             throw new MbActionNotPerformedException("Invalid action string (format should be OWNER.ACTION)");
  199.         }
  200.         //extract owner
  201.         String owner = tokens[0];
  202.         String actionName = tokens[1];
  203.  
  204.         //if the action was owned by us (that is by the kernel), let's perform it
  205.         if (owner.equalsIgnoreCase("Kernel")) {
  206.             if (actionName.equalsIgnoreCase("LoadModule")) {
  207.                 try {
  208.                     loadModule(action.getParameters());
  209.                 } catch (MbModuleNotFoundException e) {
  210.                     throw new MbActionNotPerformedException("Module not found!");
  211.                 } catch (MbModuleLoadException e) {
  212.                     throw new MbActionNotPerformedException("Module found, but couldn't be loaded!");
  213.                 }
  214.             }
  215.             else if (actionName.equalsIgnoreCase("UnloadModule")) {
  216.                 if (!unloadModule(action.getParameters())) {
  217.                     throw new MbActionNotPerformedException("Module not loaded!");
  218.                 }
  219.             }
  220.             //TODO: action that can clean up the packet queue with some filtering abilities
  221.             //TODO: action that reloads all modules
  222.             else {
  223.                 throw new MbActionNotPerformedException("Unknown kernel action!");
  224.             }
  225.         }
  226.         //the action isn't owned by the kernel, so it must be owned by a module
  227.         else {
  228.             //try to get the owner module
  229.             MbModule m = (MbModule) loadedModules.get(owner);
  230.             //check if it existed
  231.             if (m==null) {
  232.                 throw new MbActionNotPerformedException("The module '" + owner + "' isn't loaded!");
  233.             }
  234.             //handle over the action to the module
  235.             m.performAction(action);
  236.         }
  237.     }
  238.  
  239.     /**
  240.      * Starts up this kernel.
  241.      */
  242.     public void startup() throws MbStartupException {
  243.         _debug.println("Macbeth kernel is starting up");
  244.         //start up MbComponent
  245.         super.startup();
  246.  
  247.         //create XML parser and parse configuration file
  248.         _debug.println("Parsing kernel configuration file: " + configFile);
  249.         XMLParser xmlParser = new XMLParser(new ConfigDataHandler());
  250.         try {
  251.             xmlParser.parseFile(configFile);
  252.         } catch (FileNotFoundException e) {
  253.             _errors.println("Configuration file '" + configFile + "' not found. Default settings will be used!");
  254.         } catch (IOException e) {
  255.             _errors.println("I/O error while reading '" + configFile + "'. Default settings will be used!");
  256.         } catch (SAXException e) {
  257.             _errors.println("XML Parsing error while reading '" + configFile + "'. Settings might have been lost. Please check syntax!");
  258.         }
  259.         _debug.println("Finished parsing kernel configuration file");
  260.  
  261.         //start up packet transport systems
  262.         _debug.println("Starting up packet transport systems:");
  263.         Iterator it = packetTransports.iterator();
  264.         while (it.hasNext()) {
  265.             MbPacketTransport pt = (MbPacketTransport)it.next();
  266.             if (pt!=null) {
  267.                 _debug.println("  - " + pt.getName());
  268.                 pt.startup();
  269.             }
  270.         }
  271.         _debug.println("OK, " + packetTransports.size() + " transports installed");
  272.  
  273.         //create a module list
  274.         _debug.println("Enumerating modules...");
  275.         LinkedList classList = new LinkedList();
  276.         try {
  277.             //try to load all classes that inherit from Macbeth.Systen.MbModule, since these are all modules
  278.             DynamicClassLoader.locateClasses(options.getField("moduledirectory"), "", new String[]{"Macbeth.System.MbModule"}, null, classList);
  279.         } catch (FileNotFoundException e) {
  280.             e.printStackTrace();
  281.         }
  282.         //iterate through the class list to find out the module names
  283.         it = classList.iterator();
  284.         while (it.hasNext()) {
  285.             String classname = (String)it.next();
  286.             try {
  287.                 Class c = DynamicClassLoader.loadClass(options.getField("moduledirectory"), classname);
  288.                 //construct a new module (so we can get its name)
  289.                 MbModule mod = (MbModule)c.newInstance();
  290.                 //save the module name mapped to the full classname in module list
  291.                 moduleList.put(mod.name(),classname);
  292.             } catch (Exception e) {
  293.                 e.printStackTrace();
  294.             }
  295.         }
  296.         _debug.println("OK, " + Integer.toString(classList.size()) + " modules found");
  297.  
  298.         _debug.println("Setting up streams (visibility, timestamping, logging)...");
  299.  
  300.         //create logstreams (that might be needed if logging is enabled)
  301.         String logfile = "Macbeth log " + MyDateTime.now("yyyy-MM-dd HH_mm_ss") + ".log";
  302.  
  303.         //if any logging is enabled
  304.         if (options.getField("debugstream.logged").equalsIgnoreCase("true") ||
  305.             options.getField("infostream.logged").equalsIgnoreCase("true") ||
  306.             options.getField("errorstream.logged").equalsIgnoreCase("true")) {
  307.             //create log stream
  308.             try {
  309.                 _log = new PrintStream(new FileOutputStream(logfile));
  310.             } catch (FileNotFoundException e) {
  311.                 //the log stream has to be valid in order to proceed
  312.                 throw new MbStartupException("Error while trying to create the log file '" + logfile + "'!");
  313.             }
  314.         }
  315.  
  316.         //if visibility is turned off for any of the streams, redirect them to null stream
  317.         if (options.getField("debugstream.visible").equalsIgnoreCase("false")) {
  318.             _debug = _null;
  319.         }
  320.         if (options.getField("infostream.visible").equalsIgnoreCase("false")) {
  321.             _info = _null;
  322.         }
  323.         if (options.getField("errorstream.visible").equalsIgnoreCase("false")) {
  324.             _errors = _null;
  325.         }
  326.  
  327.         //add logging to the streams
  328.         if (options.getField("debugstream.logged").equalsIgnoreCase("true")) {
  329.             _debug = new MultipleStreamsAdapter(_debug, _log);
  330.         }
  331.         if (options.getField("infostream.logged").equalsIgnoreCase("true")) {
  332.             _info = new MultipleStreamsAdapter(_info, _log);
  333.         }
  334.         if (options.getField("errorstream.logged").equalsIgnoreCase("true")) {
  335.             _errors = new MultipleStreamsAdapter(_errors, _log);
  336.         }
  337.  
  338.         //add prefixes to streams
  339.         _debug = new TextFormatterStream(_debug);
  340.         ((TextFormatterStream) _debug).setPreString(options.getField("debugstream.prefix"));
  341.         _info = new TextFormatterStream(_info);
  342.         ((TextFormatterStream) _info).setPreString(options.getField("infostream.prefix"));
  343.         _errors = new TextFormatterStream(_errors);
  344.         ((TextFormatterStream) _errors).setPreString(options.getField("errorstream.prefix"));
  345.  
  346.         //add timestamps to streams
  347.         if (options.getField("debugstream.timestamped").equalsIgnoreCase("true")) {
  348.             _debug = new TimeStampedStream(_debug);
  349.         }
  350.         if (options.getField("infostream.timestamped").equalsIgnoreCase("true")) {
  351.             _info = new TimeStampedStream(_info);
  352.         }
  353.         if (options.getField("errorstream.timestamped").equalsIgnoreCase("true")) {
  354.             _errors = new TimeStampedStream(_errors);
  355.         }
  356.  
  357.         //trigger the Startup-event now so any scripted post-processing can take place
  358.         triggerEvent("Startup");
  359.  
  360.         //everything is started up now
  361.         _info.println("Macbeth kernel started!");
  362.     }
  363.  
  364.     /**
  365.      * Loads and starts up a specific module. Before any modules
  366.      * can be loaded, the module list must have been created. The
  367.      * kernel will do this during startup, so normally this is not
  368.      * a problem.
  369.      * @param moduleName The name of the module that should be loaded.
  370.      * @return true if this module was loaded and started successfully,
  371.      * false if not (because the module was already started!).
  372.      * @throws MbModuleNotFoundException if the specified module isn't found.
  373.      * @throws MbModuleLoadException if the module cannot be loaded and started.
  374.      */
  375.     protected boolean loadModule(String moduleName) throws MbModuleNotFoundException, MbModuleLoadException {
  376.         //if module is already loaded
  377.         if (loadedModules.containsKey(moduleName)) {
  378.             //get out of here
  379.             return false;
  380.         }
  381.         //get classname from our module list
  382.         String classname = (String)moduleList.get(moduleName);
  383.         //if it was in the list
  384.         if (classname!=null) {
  385.             Class c;
  386.             MbModule mod;
  387.             try {
  388.                 //try to load class
  389.                 c = DynamicClassLoader.loadClass(options.getField("moduledirectory"),classname);
  390.                 //try to create new instance (and thus, constuct the module)
  391.                 mod = (MbModule)c.newInstance();
  392.             } catch (Exception e) {
  393.                 //module couldn't be loaded
  394.                 throw new MbModuleLoadException(e.getMessage());
  395.             }
  396.             //work out dependencies for this module
  397.             List dependencies = mod.getDependencies();
  398.             //if there are dependencies
  399.             if (!dependencies.isEmpty()) {
  400.                 //for each dependency
  401.                 Iterator it = dependencies.iterator();
  402.                 while (it.hasNext()) {
  403.                     String dpmod = (String)it.next();
  404.                     if (!loadedModules.containsKey(dpmod)) {
  405.                         _info.println("'" + mod.name() + "' depends on '" + dpmod + "'. This module will now be loaded as well!");
  406.                         loadModule(dpmod);
  407.                     }
  408.                 }
  409.             }
  410.             //try to start up module now
  411.             mod.setParentKernel(this);
  412.             try {
  413.                 mod.startup();
  414.                 //put module in our loaded modules-list
  415.                 loadedModules.put(mod.name(), mod);
  416.             }
  417.             //if module failed to start up
  418.             catch (MbStartupException e) {
  419.                 _errors.println("Module '" + mod.name() + "' failed to load! (" + e.getMessage() + ")");
  420.                 //shut down the parts of the module that were started up
  421.                 mod.shutdown();
  422.                 //and throw exception
  423.                 throw new MbModuleLoadException(e.getMessage());
  424.             }
  425.         }
  426.         //module wasn't found
  427.         else {
  428.             //so throw exception
  429.             throw new MbModuleNotFoundException("Module not found in module list!");
  430.         }
  431.         return true;
  432.     }
  433.  
  434.     /**
  435.      * Shuts down and unloads a specific module.
  436.      * @param moduleName The name of the module that should be unloaded.
  437.      * @return true if the module was shut down, false if not (because
  438.      * it wasn't even started in the first place!).
  439.      */
  440.     protected boolean unloadModule(String moduleName) {
  441.         //get the module from the enumeration list
  442.         MbModule mod = (MbModule)loadedModules.get(moduleName);
  443.         //if module was found (and thus, loaded)
  444.         if (mod != null) {
  445.             //shut it down
  446.             mod.shutdown();
  447.             loadedModules.remove(moduleName);
  448.         }
  449.         //module wasn't started
  450.         else {
  451.             return false;
  452.         }
  453.         return true;
  454.     }
  455.  
  456.     /**
  457.      * Returns a given macbeth module. The module must be loaded
  458.      * in this local kernel, or else an exception will be thrown.
  459.      * @param moduleName Classname of the module.
  460.      * @return The module. Null if it isn't found (that is, not loaded).
  461.      * be found within this kernel.
  462.      */
  463.     public MbModule getModule(String moduleName) {
  464.         return (MbModule)loadedModules.get(moduleName);
  465.     }
  466.  
  467.     /**
  468.      * Shuts down this kernel. Modules will be shut
  469.      * down and removed from module collection.
  470.      */
  471.     public void shutdown() {
  472.         _debug.println("Macbeth kernel is about to shut down");
  473.  
  474.         //trigger shutdown-event first so any scripted pre-processing can take place
  475.         triggerEvent("Shutdown");
  476.  
  477.         //shut down any loaded modules
  478.         if (!loadedModules.isEmpty()) {
  479.             _debug.println("Shutting down started modules:");
  480.             //iterate through all loaded modules
  481.             Iterator it = loadedModules.keySet().iterator();
  482.             while (it.hasNext()) {
  483.                 //shut down each loaded module
  484.                 String modname = (String)it.next();
  485.                 unloadModule(modname);
  486.                 //we have removed a module from the map, so iterator needs to be re-initialized!
  487.                 it = loadedModules.keySet().iterator();
  488.             }
  489.         }
  490.  
  491.         //shut down packet transport systems
  492.         if (!packetTransports.isEmpty()) {
  493.             _debug.println("Shutting down packet transport systems");
  494.             Iterator it = packetTransports.iterator();
  495.             while (it.hasNext()) {
  496.                 MbPacketTransport t = (MbPacketTransport)it.next();
  497.                 if (t!=null) {
  498.                     t.shutdown();
  499.                 }
  500.             }
  501.         }
  502.  
  503.         _info.println("Macbeth kernel successfully shut down");
  504.  
  505.         //shut down stream logging
  506.         if (_log!=null) {
  507.             _log.close();
  508.         }
  509.  
  510.         //shutdown MbComponent
  511.         super.shutdown();
  512.     }
  513.  
  514.     /**
  515.      * Will be called when a packet needs to be delivered to
  516.      * its specified destination module.
  517.      * @param p The packet that needs to be handled.
  518.      */
  519.     public void handlePacket(MbPacket p) {
  520.         //iterate through all registered packet transports
  521.         Iterator it = packetTransports.iterator();
  522.         boolean deliverySuccessful = false;
  523.         while (it.hasNext()) {
  524.             //try to deliver with each transport
  525.             MbPacketTransport t = (MbPacketTransport)it.next();
  526.             if (t!=null) {
  527.                 try {
  528.                     deliverySuccessful = t.deliverPacket(p);
  529.                 } catch (MbPacketTransport.MbPacketDeliveryException e) {
  530.                     //the transport attempted to deliver the packet, but failed
  531.                     _errors.println("Packet delivery error: " + e.getMessage());
  532.                     //dump the packet
  533.                     packetDumper.dumpPacket(p,t.getName() + " attempted to deliver the packet, but failed with the message \"" + e.getMessage() + "\"");
  534.                     //and get out of here
  535.                     return;
  536.                 }
  537.                 //if delivery was successful
  538.                 if (deliverySuccessful) {
  539.                     //get out of here
  540.                     return;
  541.                 }
  542.             }
  543.         }
  544.         //no transport could deliver the packet, so dump it
  545.         packetDumper.dumpPacket(p,"None of the transports could deliver this packet!");
  546.     }
  547.  
  548.  
  549.     /**
  550.      * A data handler for the XML-configuration file parser.
  551.      */
  552.     public class ConfigDataHandler implements XMLDataHandler {
  553.         private String currentEvent = null;
  554.  
  555.         public void XMLstartElement(String element, HashMap attributes) {
  556.             if (element.equals("list")) {
  557.                 if (attributes.containsKey("name")) {
  558.                     dataRepository.advancedBuild_BeginList((String)attributes.get("name"));
  559.                 }
  560.                 else {
  561.                     _errors.println("Syntax error in '" + configFile + "': 'name'-attribute missing in 'list'-tag!");
  562.                 }
  563.             }
  564.             else if (element.equals("item")) {
  565.                 dataRepository.advancedBuild_BeginListItem();
  566.             }
  567.             else if (element.equals("field")) {
  568.                 if (attributes.containsKey("name") && attributes.containsKey("value")) {
  569.                     dataRepository.advancedBuild_PutField((String)attributes.get("name"), (String)attributes.get("value"));
  570.                 }
  571.                 else {
  572.                     _errors.println("Syntax error in '" + configFile + "': 'name' and/or 'value'-attributes missing in 'datafield'-tag!");
  573.                 }
  574.             }
  575.             else if (element.equalsIgnoreCase("option")) {
  576.                 if (attributes.containsKey("name") && attributes.containsKey("value")) {
  577.                     options.putField((String)attributes.get("name"), (String)attributes.get("value"));
  578.                 }
  579.                 else {
  580.                     _errors.println("Syntax error in '" + configFile + "': 'name'- and/or 'value'-attributes are missing in an 'option'-tag!");
  581.                 }
  582.             }
  583.             else if (element.equalsIgnoreCase("event")) {
  584.                 if (attributes.containsKey("name")) {
  585.                     currentEvent = (String) attributes.get("name");
  586.                 }
  587.                 else {
  588.                     _errors.println("Syntax error in '" + configFile + "': 'name'-attribute missing in an 'event'-tag!");
  589.                 }
  590.             }
  591.             else if (element.equalsIgnoreCase("do")) {
  592.                 if (attributes.containsKey("action")) {
  593.                     String action = (String) attributes.get("action");
  594.                     String params = (String) attributes.get("params");
  595.                     //get the list of actions that are associated with this event so far
  596.                     List actionList = (List) scriptEventActions.get(currentEvent);
  597.                     //is this the first action associated with this event?
  598.                     if (actionList==null) {
  599.                         //if so, a new list needs to be created
  600.                         actionList = new LinkedList();
  601.                         //..and put into the events-actions-map
  602.                         scriptEventActions.put(currentEvent,actionList);
  603.                     }
  604.                     //now, add the new action to the list
  605.                     actionList.add(new MbAction(action, params));
  606.                 }
  607.                 else {
  608.                     _errors.println("Syntax error in '" + configFile + "': 'action'-attribute missing in a 'do'-tag!");
  609.                 }
  610.             }
  611.         }
  612.  
  613.         public void XMLendElement(String element) {
  614.             if (element.equals("list")) {
  615.                 dataRepository.advancedBuild_EndList();
  616.             }
  617.             else if (element.equals("item")) {
  618.                 dataRepository.advancedBuild_EndListItem();
  619.             }
  620.             else if (element.equals("event")) {
  621.                 currentEvent = null;
  622.             }
  623.         }
  624.         public void XMLelementData(String data) {}
  625.         public void XMLdocumentStart() {}
  626.         public void XMLdocumentEnd() {}
  627.     }
  628.  
  629.  
  630.     /**
  631.      * A packet dumper. This is invoked to kill packets that cannot be
  632.      * delivered. All packet dumps will be logged to a file,
  633.      */
  634.     protected class PacketDumper {
  635.         protected PrintStream log;
  636.         protected String startupTime;
  637.         protected String startupTime2;
  638.         protected int packetsDumped;
  639.  
  640.         public PacketDumper() {
  641.             //remember the startup time, because when packets are dumped for the first time
  642.             //we will need to create a file whose name contains our startup time.
  643.             startupTime = MyDateTime.now();
  644.             startupTime2 = MyDateTime.now("yyyy-MM-dd HH_mm_ss");
  645.             packetsDumped = 0;
  646.         }
  647.  
  648.         protected void finalize() throws Throwable {
  649.             if (log != null) {
  650.                 //close log stream
  651.                 log.flush();
  652.                 log.close();
  653.             }
  654.             super.finalize();
  655.         }
  656.  
  657.         /**
  658.          * Dumps a packet. Call this when a packet is invalid or the packet destination
  659.          * cannot be found. A log event will be created and the packet will be killed.
  660.          * @param p The packet that is invalid somehow, and thus needs to be dumped.
  661.          * @param reason The reason why the packet needs to be dumped.
  662.          */
  663.         public void dumpPacket(MbPacket p, String reason) {
  664.             if (log == null) {
  665.                 //create log output stream
  666.                 try {
  667.                     log = new PrintStream(new FileOutputStream("dumped packets " + startupTime2 + ".log"));
  668.                     log.println("Macbeth session started at " + startupTime + MySystem.lineBreak);
  669.                 } catch (FileNotFoundException e) {
  670.                     e.printStackTrace();
  671.                 }
  672.             }
  673.             String now = MyDateTime.now();
  674.             log.println("--Packet dump log event-----------------------------------------------");
  675.             log.println("[" + now + "]");
  676.             log.println("Dump reason:");
  677.             log.println(reason);
  678.             log.println("Packet was:");
  679.             log.println(p.serializeToString());
  680.             log.println("----------------------------------------------------------------------");
  681.             log.println();
  682.             _info.println("A packet was dumped! (Reason: " + reason + ")");
  683.             p = null;
  684.             packetsDumped++;
  685.         }
  686.     }
  687.  
  688. }
  689.