/**
   * Gets a custom audit module if one if configured.
   *
   * @return A custom audit module. Null if a custom module is not configured.
   */
  protected Module getAuditModule() {
    Module retVal = null;

    Provider<Auditor> provider = null;

    final String providerClazz =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.SMTP_AGENT_AUDITOR_PROVIDER, this, "");
    final String auditoConfigLoc =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.SMTP_AGENT_AUDITOR_CONFIG_LOC, this, "");

    if (providerClazz != null && !providerClazz.isEmpty()) {
      try {
        // create an instance of the provider
        @SuppressWarnings("unchecked")
        Class<Provider<Auditor>> clazz =
            (Class<Provider<Auditor>>) getClass().getClassLoader().loadClass(providerClazz);

        if (auditoConfigLoc != null && !auditoConfigLoc.isEmpty()) {
          try {
            Constructor<Provider<Auditor>> constr =
                (Constructor<Provider<Auditor>>) clazz.getConstructor(String.class);
            provider = constr.newInstance(auditoConfigLoc);
          } catch (InvocationTargetException e) {

            if (e.getTargetException() instanceof IllegalStateException) {
              LOGGER.warn(
                  "Could not create auditor from specified audito configuration file "
                      + auditoConfigLoc,
                  e);
              return null;
            } else
              LOGGER.warn(
                  "Auditor configuration location "
                      + auditoConfigLoc
                      + " provided but not supported by Auditor provider:"
                      + e.getMessage(),
                  e);
          } catch (Exception e) {
            LOGGER.warn(
                "Auditor configuration location "
                    + auditoConfigLoc
                    + " provided but not supported by Auditor provider:"
                    + e.getMessage(),
                e);
          }
        }
        if (provider == null) provider = clazz.newInstance();

        retVal = AuditorModule.create(provider);
      } catch (Exception e) {
        LOGGER.warn(
            "Failed to load auditor provider class " + providerClazz + ": " + e.getMessage(), e);
      }
    }

    return retVal;
  }
  /**
   * Gets a custom keystore manager provider. If this is null, the system will not use a keystore
   * manager.
   *
   * @return Gets a keystore manager provider.
   */
  protected Provider<KeyStoreProtectionManager> getKeyStoreManagerProvider() {
    Provider<KeyStoreProtectionManager> retVal = null;

    String providerClazz =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.KEYSTORE_MGR_PROVIDER, this, "");

    if (providerClazz != null && !providerClazz.isEmpty()) {
      try {
        // create an instance of the provider
        @SuppressWarnings("unchecked")
        Class<Provider<KeyStoreProtectionManager>> clazz =
            (Class<Provider<KeyStoreProtectionManager>>)
                getClass().getClassLoader().loadClass(providerClazz);
        retVal = clazz.newInstance();
      } catch (Exception e) {
        LOGGER.warn(
            "Failed to load keystore manager provider class "
                + providerClazz
                + ". "
                + "Falling back to no keystore manager: "
                + e.getMessage(),
            e);
        return null;
      }
    } else {
      retVal = null;
    }

    if (retVal instanceof MailetAwareProvider) ((MailetAwareProvider) retVal).setMailet(this);

    return retVal;
  }
  /**
   * Gets a custom service security manager provider. If this is null, the system will us a default
   * provider.
   *
   * @return Gets a service security manager provider.
   */
  protected Provider<ServiceSecurityManager> getServiceSecurityManagerProvider() {
    Provider<ServiceSecurityManager> retVal = null;

    String providerClazz =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.SERVICE_SECURITY_MANAGER_PROVIDER, this, "");

    if (providerClazz != null && !providerClazz.isEmpty()) {
      try {
        // create an instance of the provider
        @SuppressWarnings("unchecked")
        Class<Provider<ServiceSecurityManager>> clazz =
            (Class<Provider<ServiceSecurityManager>>)
                getClass().getClassLoader().loadClass(providerClazz);
        retVal = clazz.newInstance();
      } catch (Exception e) {
        LOGGER.warn(
            "Failed to load service security manager provider class "
                + providerClazz
                + ": "
                + e.getMessage(),
            e);
        retVal = new OpenServiceSecurityManagerProvider();
      }
    } else {
      retVal = new OpenServiceSecurityManagerProvider();
    }

    if (retVal instanceof MailetAwareProvider) ((MailetAwareProvider) retVal).setMailet(this);

    return retVal;
  }
  /**
   * Gets a custom configuration provider. If this is null, the system will us a default provider.
   *
   * @return Gets a custom configuration provider.
   */
  protected Provider<SmtpAgentConfig> getConfigProvider() {
    Provider<SmtpAgentConfig> retVal = null;

    final String providerClazz =
        GatewayConfiguration.getConfigurationParam(
            SecurityAndTrustMailetOptions.SMTP_AGENT_CONFIG_PROVIDER, this, "");

    if (providerClazz != null && !providerClazz.isEmpty()) {
      try {
        // create an instance of the provider
        @SuppressWarnings("unchecked")
        Class<Provider<SmtpAgentConfig>> clazz =
            (Class<Provider<SmtpAgentConfig>>) getClass().getClassLoader().loadClass(providerClazz);
        retVal = clazz.newInstance();
      } catch (Exception e) {
        LOGGER.warn(
            "Failed to load smtp agent config provider class "
                + providerClazz
                + ": "
                + e.getMessage(),
            e);
        retVal = null;
      }
    } else {
      retVal = null;
    }

    return retVal;
  }
  /** {@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.");
  }