/**
   * The dispose operation is called at the end of a components lifecycle. Instances of this class
   * use this method to release and destroy any resources that they own.
   *
   * <p>This implementation shuts down the LinearProcessors managed by this JamesSpoolManager
   *
   * @throws Exception if an error is encountered during shutdown
   */
  public void dispose() {
    getLogger().info("JamesSpoolManager dispose...");
    active = false; // shutdown the threads
    for (Iterator it = spoolThreads.iterator(); it.hasNext(); ) {
      ((Thread) it.next()).interrupt(); // interrupt any waiting accept() calls.
    }

    long stop = System.currentTimeMillis() + 60000;
    // give the spooler threads one minute to terminate gracefully
    while (numActive != 0 && stop > System.currentTimeMillis()) {
      try {
        Thread.sleep(1000);
      } catch (Exception ignored) {
      }
    }
    getLogger().info("JamesSpoolManager thread shutdown completed.");

    Iterator it = processors.keySet().iterator();
    while (it.hasNext()) {
      String processorName = (String) it.next();
      if (getLogger().isDebugEnabled()) {
        getLogger().debug("Processor " + processorName);
      }
      LinearProcessor processor = (LinearProcessor) processors.get(processorName);
      processor.dispose();
      processors.remove(processor);
    }
  }
  /** @see org.apache.avalon.framework.activity.Initializable#initialize() */
  public void initialize() throws Exception {

    getLogger().info("JamesSpoolManager init...");
    spool = (SpoolRepository) compMgr.lookup(SpoolRepository.ROLE);

    MailetLoader mailetLoader = (MailetLoader) compMgr.lookup(MailetLoader.ROLE);
    MatcherLoader matchLoader = (MatcherLoader) compMgr.lookup(MatcherLoader.ROLE);

    // A processor is a Collection of
    processors = new HashMap();

    final Configuration[] processorConfs = conf.getChildren("processor");
    for (int i = 0; i < processorConfs.length; i++) {
      Configuration processorConf = processorConfs[i];
      String processorName = processorConf.getAttribute("name");
      try {
        LinearProcessor processor = new LinearProcessor();
        setupLogger(processor, processorName);
        processor.setSpool(spool);
        processor.initialize();
        processors.put(processorName, processor);

        final Configuration[] mailetConfs = processorConf.getChildren("mailet");
        // Loop through the mailet configuration, load
        // all of the matcher and mailets, and add
        // them to the processor.
        for (int j = 0; j < mailetConfs.length; j++) {
          Configuration c = mailetConfs[j];
          String mailetClassName = c.getAttribute("class");
          String matcherName = c.getAttribute("match");
          Mailet mailet = null;
          Matcher matcher = null;
          try {
            matcher = matchLoader.getMatcher(matcherName);
            // The matcher itself should log that it's been inited.
            if (getLogger().isInfoEnabled()) {
              StringBuffer infoBuffer =
                  new StringBuffer(64)
                      .append("Matcher ")
                      .append(matcherName)
                      .append(" instantiated.");
              getLogger().info(infoBuffer.toString());
            }
          } catch (MessagingException ex) {
            // **** Do better job printing out exception
            if (getLogger().isErrorEnabled()) {
              StringBuffer errorBuffer =
                  new StringBuffer(256)
                      .append("Unable to init matcher ")
                      .append(matcherName)
                      .append(": ")
                      .append(ex.toString());
              getLogger().error(errorBuffer.toString(), ex);
              if (ex.getNextException() != null) {
                getLogger().error("Caused by nested exception: ", ex.getNextException());
              }
            }
            System.err.println("Unable to init matcher " + matcherName);
            System.err.println("Check spool manager logs for more details.");
            // System.exit(1);
            throw ex;
          }
          try {
            mailet = mailetLoader.getMailet(mailetClassName, c);
            if (getLogger().isInfoEnabled()) {
              StringBuffer infoBuffer =
                  new StringBuffer(64)
                      .append("Mailet ")
                      .append(mailetClassName)
                      .append(" instantiated.");
              getLogger().info(infoBuffer.toString());
            }
          } catch (MessagingException ex) {
            // **** Do better job printing out exception
            if (getLogger().isErrorEnabled()) {
              StringBuffer errorBuffer =
                  new StringBuffer(256)
                      .append("Unable to init mailet ")
                      .append(mailetClassName)
                      .append(": ")
                      .append(ex.toString());
              getLogger().error(errorBuffer.toString(), ex);
              if (ex.getNextException() != null) {
                getLogger().error("Caused by nested exception: ", ex.getNextException());
              }
            }
            System.err.println("Unable to init mailet " + mailetClassName);
            System.err.println("Check spool manager logs for more details.");
            // System.exit(1);
            throw ex;
          }
          // Add this pair to the processor
          processor.add(matcher, mailet);
        }

        // Close the processor matcher/mailet lists.
        //
        // Please note that this is critical to the proper operation
        // of the LinearProcessor code.  The processor will not be
        // able to service mails until this call is made.
        processor.closeProcessorLists();

        if (getLogger().isInfoEnabled()) {
          StringBuffer infoBuffer =
              new StringBuffer(64)
                  .append("Processor ")
                  .append(processorName)
                  .append(" instantiated.");
          getLogger().info(infoBuffer.toString());
        }
      } catch (Exception ex) {
        if (getLogger().isErrorEnabled()) {
          StringBuffer errorBuffer =
              new StringBuffer(256)
                  .append("Unable to init processor ")
                  .append(processorName)
                  .append(": ")
                  .append(ex.toString());
          getLogger().error(errorBuffer.toString(), ex);
        }
        throw ex;
      }
    }
    if (getLogger().isInfoEnabled()) {
      StringBuffer infoBuffer =
          new StringBuffer(64)
              .append("Spooler Manager uses ")
              .append(numThreads)
              .append(" Thread(s)");
      getLogger().info(infoBuffer.toString());
    }

    active = true;
    numActive = 0;
    spoolThreads = new java.util.ArrayList(numThreads);
    for (int i = 0; i < numThreads; i++) {
      Thread reader = new Thread(this, "Spool Thread #" + i);
      spoolThreads.add(reader);
      reader.start();
    }
  }
 /**
  * Process this mail message by the appropriate processor as designated in the state of the Mail
  * object.
  *
  * @param mail the mail message to be processed
  */
 protected void process(Mail mail) {
   while (true) {
     String processorName = mail.getState();
     if (processorName.equals(Mail.GHOST)) {
       // This message should disappear
       return;
     }
     try {
       LinearProcessor processor = (LinearProcessor) processors.get(processorName);
       if (processor == null) {
         StringBuffer exceptionMessageBuffer =
             new StringBuffer(128)
                 .append("Unable to find processor ")
                 .append(processorName)
                 .append(" requested for processing of ")
                 .append(mail.getName());
         String exceptionMessage = exceptionMessageBuffer.toString();
         getLogger().debug(exceptionMessage);
         mail.setState(Mail.ERROR);
         throw new MailetException(exceptionMessage);
       }
       StringBuffer logMessageBuffer = null;
       if (getLogger().isDebugEnabled()) {
         logMessageBuffer =
             new StringBuffer(64)
                 .append("Processing ")
                 .append(mail.getName())
                 .append(" through ")
                 .append(processorName);
         getLogger().debug(logMessageBuffer.toString());
       }
       processor.service(mail);
       if (getLogger().isDebugEnabled()) {
         logMessageBuffer =
             new StringBuffer(128)
                 .append("Processed ")
                 .append(mail.getName())
                 .append(" through ")
                 .append(processorName);
         getLogger().debug(logMessageBuffer.toString());
         getLogger().debug("Result was " + mail.getState());
       }
       return;
     } catch (Throwable e) {
       // This is a strange error situation that shouldn't ordinarily
       // happen
       StringBuffer exceptionBuffer =
           new StringBuffer(64)
               .append("Exception in processor <")
               .append(processorName)
               .append(">");
       getLogger().error(exceptionBuffer.toString(), e);
       if (processorName.equals(Mail.ERROR)) {
         // We got an error on the error processor...
         // kill the message
         mail.setState(Mail.GHOST);
         mail.setErrorMessage(e.getMessage());
       } else {
         // We got an error... send it to the requested processor
         if (!(e instanceof MessagingException)) {
           // We got an error... send it to the error processor
           mail.setState(Mail.ERROR);
         }
         mail.setErrorMessage(e.getMessage());
       }
     }
     if (getLogger().isErrorEnabled()) {
       StringBuffer logMessageBuffer =
           new StringBuffer(128)
               .append("An error occurred processing ")
               .append(mail.getName())
               .append(" through ")
               .append(processorName);
       getLogger().error(logMessageBuffer.toString());
       getLogger().error("Result was " + mail.getState());
     }
   }
 }