/** Shutsdown the gateway and cleans up resources associated with it. */
 public void shutdown() {
   GatewayState.getInstance().lockForUpdating();
   try {
     // place holder for shutdown code
   } finally {
     GatewayState.getInstance().unlockFromUpdating();
   }
 }
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  @Override
  public void service(Mail mail) throws MessagingException {
    GatewayState.getInstance().lockForProcessing();
    try {

      Tx txToMonitor = null;

      LOGGER.trace("Entering service(Mail mail)");

      onPreprocessMessage(mail);

      final MimeMessage msg = mail.getMessage();

      final NHINDAddressCollection recipients = getMailRecipients(mail);

      // get the sender
      final NHINDAddress sender = getMailSender(mail);

      LOGGER.info("Proccessing incoming message from sender " + sender.toString());
      MessageProcessResult result = null;

      final boolean isOutgoing = this.isOutgoing(msg, sender);

      // if the message is outgoing, then the tracking information must be
      // gathered now before the message is transformed
      if (isOutgoing) txToMonitor = getTxToTrack(msg, sender, recipients);

      // recipients can get modified by the security and trust agent, so make a local copy
      // before processing
      final NHINDAddressCollection originalRecipList = NHINDAddressCollection.create(recipients);

      try {
        // process the message with the agent stack
        LOGGER.trace("Calling agent.processMessage");
        result = agent.processMessage(msg, recipients, sender);
        LOGGER.trace("Finished calling agent.processMessage");

        if (result == null) {
          LOGGER.error("Failed to process message.  processMessage returned null.");

          onMessageRejected(mail, originalRecipList, sender, isOutgoing, txToMonitor, null);

          mail.setState(Mail.GHOST);

          LOGGER.trace("Exiting service(Mail mail)");
          return;
        }
      } catch (Exception e) {
        // catch all

        LOGGER.error("Failed to process message: " + e.getMessage(), e);

        onMessageRejected(mail, originalRecipList, sender, isOutgoing, txToMonitor, e);

        mail.setState(Mail.GHOST);
        LOGGER.trace("Exiting service(Mail mail)");

        return;
      }

      if (result.getProcessedMessage() != null) {
        mail.setMessage(result.getProcessedMessage().getMessage());
      } else {
        /*
         * TODO: Handle exception... GHOST the message for now and eat it
         */
        LOGGER.debug("Processed message is null.  GHOST and eat the message.");

        onMessageRejected(mail, recipients, sender, null);

        mail.setState(Mail.GHOST);

        return;
      }

      // remove reject recipients from the RCTP headers
      if (result.getProcessedMessage().getRejectedRecipients() != null
          && result.getProcessedMessage().getRejectedRecipients().size() > 0
          && mail.getRecipients() != null
          && mail.getRecipients().size() > 0) {

        final Collection<MailAddress> newRCPTList = new ArrayList<MailAddress>();
        for (MailAddress rctpAdd : (Collection<MailAddress>) mail.getRecipients()) {
          if (!isRcptRejected(rctpAdd, result.getProcessedMessage().getRejectedRecipients())) {
            newRCPTList.add(rctpAdd);
          }
        }

        mail.setRecipients(newRCPTList);
      }

      /*
       * Handle sending MDN messages
       */
      final Collection<NotificationMessage> notifications = result.getNotificationMessages();
      if (notifications != null && notifications.size() > 0) {
        LOGGER.info("MDN messages requested.  Sending MDN \"processed\" messages");
        // create a message for each notification and put it on James "stack"
        for (NotificationMessage message : notifications) {
          try {
            this.getMailetContext().sendMail(message);
          } catch (Throwable t) {
            // don't kill the process if this fails
            LOGGER.error("Error sending MDN message.", t);
          }
        }
      }

      // track message
      trackMessage(txToMonitor, isOutgoing);

      onPostprocessMessage(mail, result, isOutgoing, txToMonitor);

      LOGGER.trace("Exiting service(Mail mail)");
    } finally {
      GatewayState.getInstance().unlockFromProcessing();
    }
  }
  /** {@inheritDoc} */
  @Override
  public void init() throws MessagingException {
    LOGGER.info("Initializing NHINDSecurityAndTrustMailet");

    super.init();

    // set the outbound policy for notifications if possible
    try {
      final boolean useOutboundPolicy =
          Boolean.parseBoolean(
              GatewayConfiguration.getConfigurationParam(
                  SecurityAndTrustMailetOptions.USE_OUTGOING_POLICY_FOR_INCOMING_NOTIFICATIONS,
                  this,
                  "false"));

      // we don't know if this parameter came from the mailet config or the options manager, so just
      // go ahead and set it at
      // the options manager level because that it where the agent reads the value... no danger that
      // we will overwrite the value that we want...
      // we would just be writing the same value if the information came from the options manager
      // module
      // the mailet parameter gets precedence, so we want to overwrite the options manager if the
      // value exists in the mailet configuration
      OptionsManager.getInstance()
          .setOptionsParameter(
              new OptionsParameter(
                  OptionsParameter.USE_OUTGOING_POLICY_FOR_INCOMING_NOTIFICATIONS,
                  Boolean.toString(useOutboundPolicy)));
    } catch (Exception e) {
      // log a warning that the parameter could not be set
    }

    // set the rejection policy for tampered routing headers
    try {
      final boolean rejectOnTamperPolicy =
          Boolean.parseBoolean(
              GatewayConfiguration.getConfigurationParam(
                  SecurityAndTrustMailetOptions.REJECT_ON_ROUTING_TAMPER, this, "false"));

      OptionsManager.getInstance()
          .setOptionsParameter(
              new OptionsParameter(
                  OptionsParameter.REJECT_ON_ROUTING_TAMPER,
                  Boolean.toString(rejectOnTamperPolicy)));
    } catch (Exception e) {
      // log a warning that the parameter could not be set
    }

    // set the JCE providers if available
    final String JCEName =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.JCE_PROVIDER_NAME, this, "");
    if (!StringUtils.isEmpty(JCEName))
      OptionsManager.getInstance()
          .setOptionsParameter(new OptionsParameter(OptionsParameter.JCE_PROVIDER, JCEName));

    final String sensitiveJCEName =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.JCE_SENTITIVE_PROVIDER, this, "");
    if (!StringUtils.isEmpty(sensitiveJCEName))
      OptionsManager.getInstance()
          .setOptionsParameter(
              new OptionsParameter(OptionsParameter.JCE_SENTITIVE_PROVIDER, sensitiveJCEName));

    // Get the configuration URL
    final String configURLParam = getInitParameter(SecurityAndTrustMailetOptions.CONFIG_URL_PARAM);

    if (StringUtils.isEmpty(configURLParam)) {
      LOGGER.error("NHINDSecurityAndTrustMailet Configuration URL cannot be empty or null.");
      throw new MessagingException(
          "NHINDSecurityAndTrustMailet Configuration URL cannot be empty or null.");
    }

    // parse into a URL and validate it is properly formed
    URL configURL = null;
    try {
      configURL = new URL(configURLParam);
    } catch (MalformedURLException ex) {
      LOGGER.error("Invalid configuration URL:" + ex.getMessage(), ex);
      throw new MessagingException(
          "NHINDSecurityAndTrustMailet Configuration URL cannot be empty or null.", ex);
    }

    final Collection<Module> modules = getInitModules();

    Provider<SmtpAgentConfig> configProvider;
    try {
      configProvider = this.getConfigProvider();

      if (configProvider == null) configProvider = createCompatConfigProvider(configURL);

      if (configProvider instanceof URLAccessedConfigProvider)
        ((URLAccessedConfigProvider) configProvider).setConfigURL(configURL);

      final Provider<ServiceSecurityManager> srvSecMgr = getServiceSecurityManagerProvider();
      if (configProvider instanceof SecureURLAccessedConfigProvider)
        ((SecureURLAccessedConfigProvider) configProvider).setServiceSecurityManager(srvSecMgr);

      final Provider<KeyStoreProtectionManager> keyStoreManagerProvider =
          getKeyStoreManagerProvider();
      if (configProvider instanceof KeyStoreProtectionConfigProvider
          && keyStoreManagerProvider != null)
        ((KeyStoreProtectionConfigProvider) configProvider)
            .setKeyStoreProtectionManger(keyStoreManagerProvider);

      agent = SmtpAgentFactory.createAgent(configURL, configProvider, null, modules);

    } catch (SmtpAgentException e) {
      LOGGER.error("Failed to create the SMTP agent: " + e.getMessage(), e);
      throw new MessagingException("Failed to create the SMTP agent: " + e.getMessage(), e);
    }

    // this should never happen because an exception should be thrown by Guice or one of the
    // providers, but check
    // just in case...
    /// CLOVER:OFF
    if (agent == null) {
      LOGGER.error("Failed to create the SMTP agent. Reason unknown.");
      throw new MessagingException("Failed to create the SMTP agent.  Reason unknown.");
    }
    /// CLOVER:ON

    // get the DSN creation options
    // default is RELIABLE_DSN_OPTION
    final String dnsCreateOptions =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.AUTO_DSN_FAILURE_CREATION_PARAM,
            this,
            RELIABLE_DSN_OPTION);

    for (String dsnOption : dnsCreateOptions.split(",")) {
      if (dsnOption.equalsIgnoreCase(RELIABLE_DSN_OPTION)) autoDSNForTimelyAndReliable = true;
      else if (dsnOption.equalsIgnoreCase(GENERAL_DSN_OPTION)) autoDSNForGeneral = true;
    }

    // set the agent and config in the Gateway state
    final GatewayState gwState = GatewayState.getInstance();
    if (gwState.isAgentSettingManagerRunning()) gwState.stopAgentSettingsManager();

    gwState.setSmtpAgent(agent);
    gwState.setSmptAgentConfig(
        SmptAgentConfigFactory.createSmtpAgentConfig(configURL, configProvider, null));
    gwState.startAgentSettingsManager();

    LOGGER.info("NHINDSecurityAndTrustMailet initialization complete.");
  }