FileBasedConfiguration.java

/*
 * This file is part of Waarp Project (named also Waarp or GG).
 *
 *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
 *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
 * individual contributors.
 *
 *  All Waarp Project is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 * Waarp . If not, see <http://www.gnu.org/licenses/>.
 */
package org.waarp.openr66.proxy.configuration;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.waarp.common.database.DbAdmin;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.xml.XmlDecl;
import org.waarp.common.xml.XmlHash;
import org.waarp.common.xml.XmlRootHash;
import org.waarp.common.xml.XmlType;
import org.waarp.common.xml.XmlUtil;
import org.waarp.common.xml.XmlValue;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
import org.waarp.openr66.proxy.network.ProxyEntry;

import java.net.InetSocketAddress;

import static org.waarp.common.database.DbConstant.*;
import static org.waarp.openr66.configuration.FileBasedConfiguration.*;
import static org.waarp.openr66.configuration.FileBasedElements.*;

/**
 * File Based ConfigurationProxyR66
 */
public class FileBasedConfiguration {
  /**
   * Internal Logger
   */
  private static final WaarpLogger logger =
      WaarpLoggerFactory.getLogger(FileBasedConfiguration.class);

  /**
   * SERVER Association entry
   */
  private static final String XML_SERVER_PROXY = "serverproxy";
  /**
   * SERVER Listening address
   */
  private static final String XML_SERVER_LISTENADDR = "serverlistenaddr";
  /**
   * SERVER Listening port
   */
  private static final String XML_SERVER_LISTENPORT = "serverlistenport";
  /**
   * SERVER Listening ssl mode
   */
  private static final String XML_SERVER_LISTENSSL = "serverlistenssl";
  /**
   * SERVER Remote address
   */
  private static final String XML_SERVER_REMOTEADDR = "serverremoteaddr";
  /**
   * SERVER Remote PORT
   */
  private static final String XML_SERVER_REMOTEPORT = "serverremoteport";
  /**
   * SERVER Remote ssl mode
   */
  private static final String XML_SERVER_REMOTESSL = "serverremotessl";

  /**
   * Structure of the ConfigurationProxyR66 file
   */
  private static final XmlDecl[] configServerParamDecls = {
      // server
      new XmlDecl(XmlType.BOOLEAN, XML_USESSL),
      new XmlDecl(XmlType.BOOLEAN, XML_USENOSSL),
      new XmlDecl(XmlType.BOOLEAN, XML_USEHTTPCOMP),
      new XmlDecl(XmlType.STRING, XML_SERVER_ADMIN),
      new XmlDecl(XmlType.STRING, XML_SERVER_PASSWD),
      new XmlDecl(XmlType.STRING, XML_SERVER_PASSWD_FILE),
      new XmlDecl(XmlType.STRING, XML_HTTPADMINPATH),
      new XmlDecl(XmlType.STRING, XML_PATH_ADMIN_KEYPATH),
      new XmlDecl(XmlType.STRING, XML_PATH_ADMIN_KEYSTOREPASS),
      new XmlDecl(XmlType.STRING, XML_PATH_ADMIN_KEYPASS),
      new XmlDecl(XmlType.LONG, XML_MONITOR_PASTLIMIT),
      new XmlDecl(XmlType.LONG, XML_MONITOR_MINIMALDELAY),
      new XmlDecl(XmlType.STRING, XML_MONITOR_SNMP_CONFIG)
  };
  /**
   * Structure of the ConfigurationProxyR66 file
   */
  private static final XmlDecl[] configNetworkProxyDecls = {
      // proxy
      new XmlDecl(XmlType.STRING, XML_SERVER_LISTENADDR),
      new XmlDecl(XmlType.INTEGER, XML_SERVER_LISTENPORT),
      new XmlDecl(XmlType.BOOLEAN, XML_SERVER_LISTENSSL),
      new XmlDecl(XmlType.STRING, XML_SERVER_REMOTEADDR),
      new XmlDecl(XmlType.INTEGER, XML_SERVER_REMOTEPORT),
      new XmlDecl(XmlType.BOOLEAN, XML_SERVER_REMOTESSL)
  };
  /**
   * Structure of the ConfigurationProxyR66 file
   */
  private static final XmlDecl[] configNetworkServerDecls = {
      // network
      new XmlDecl(XML_SERVER_PROXY, XmlType.XVAL, XML_SERVER_PROXY,
                  configNetworkProxyDecls, true),
      new XmlDecl(XmlType.INTEGER, XML_SERVER_HTTPPORT),
      new XmlDecl(XmlType.INTEGER, XML_SERVER_HTTPSPORT)
  };

  /**
   * Structure of the ConfigurationProxyR66 file
   */
  private static final XmlDecl[] configDirectoryDecls = {
      // directory
      new XmlDecl(XmlType.STRING, XML_SERVER_HOME),
      new XmlDecl(XmlType.STRING, XML_ARCHIVEPATH),
      new XmlDecl(XmlType.STRING, XML_CONFIGPATH)
  };

  /**
   * Structure of the ConfigurationProxyR66 file
   */
  private static final XmlDecl[] configLimitDecls = {
      // limit
      new XmlDecl(XmlType.LONG, XML_LIMITSESSION),
      new XmlDecl(XmlType.LONG, XML_LIMITGLOBAL),
      new XmlDecl(XmlType.LONG, XML_LIMITDELAY),
      new XmlDecl(XmlType.LONG, XML_DELAYRETRY),
      new XmlDecl(XmlType.INTEGER, XML_SERVER_THREAD),
      new XmlDecl(XmlType.INTEGER, XML_CLIENT_THREAD),
      new XmlDecl(XmlType.LONG, XML_MEMORY_LIMIT),
      new XmlDecl(XmlType.BOOLEAN, XML_CSTRT_USECPULIMIT),
      new XmlDecl(XmlType.BOOLEAN, XML_CSTRT_USECPUJDKLIMIT),
      new XmlDecl(XmlType.DOUBLE, XML_CSTRT_CPULIMIT),
      new XmlDecl(XmlType.INTEGER, XML_CSTRT_CONNLIMIT),
      new XmlDecl(XmlType.DOUBLE, XML_CSTRT_LOWCPULIMIT),
      new XmlDecl(XmlType.DOUBLE, XML_CSTRT_HIGHCPULIMIT),
      new XmlDecl(XmlType.DOUBLE, XML_CSTRT_PERCENTDECREASE),
      new XmlDecl(XmlType.LONG, XML_CSTRT_LIMITLOWBANDWIDTH),
      new XmlDecl(XmlType.LONG, XML_CSTRT_DELAYTHROTTLE),
      new XmlDecl(XmlType.LONG, XML_TIMEOUTCON),
      new XmlDecl(XmlType.BOOLEAN, XML_CHECKVERSION)
  };

  /**
   * Global Structure for Server ConfigurationProxyR66
   */
  private static final XmlDecl[] configServer = {
      new XmlDecl(XML_IDENTITY, XmlType.XVAL, XML_ROOT + XML_IDENTITY,
                  configIdentityDecls, false),
      new XmlDecl(XML_SERVER, XmlType.XVAL, XML_ROOT + XML_SERVER,
                  configServerParamDecls, false),
      new XmlDecl(XML_NETWORK, XmlType.XVAL, XML_ROOT + XML_NETWORK,
                  configNetworkServerDecls, false),
      new XmlDecl(XML_SSL, XmlType.XVAL, XML_ROOT + XML_SSL, configSslDecls,
                  false),
      new XmlDecl(XML_DIRECTORY, XmlType.XVAL, XML_ROOT + XML_DIRECTORY,
                  configDirectoryDecls, false),
      new XmlDecl(XML_LIMIT, XmlType.XVAL, XML_ROOT + XML_LIMIT,
                  configLimitDecls, false)
  };

  private static XmlHash hashConfig;

  private FileBasedConfiguration() {
  }

  private static boolean loadServerParam(final Configuration config) {
    XmlHash hashConfigSub = new XmlHash(hashConfig.get(XML_SERVER));
    try {
      return org.waarp.openr66.configuration.FileBasedConfiguration.loadServerConfig(
          config, hashConfigSub);
    } finally {
      hashConfigSub.clear();
      hashConfigSub = null;
    }
  }

  private static boolean loadDirectory(final Configuration config) {
    XmlHash hashConfigSub = new XmlHash(hashConfig.get(XML_DIRECTORY));
    try {
      if (loadMinimalDirectory(config, hashConfigSub)) {
        return false;
      }
    } finally {
      hashConfigSub.clear();
      hashConfigSub = null;
    }
    return true;
  }

  private static boolean alreadySetLimit;

  private static void loadLimit(final Configuration config,
                                final boolean updateLimit) {
    if (alreadySetLimit) {
      return;
    }
    final XmlHash hashConfigSub = new XmlHash(hashConfig.get(XML_LIMIT));
    try {
      loadCommonLimit(config, hashConfigSub, updateLimit);
      if (config.getRunnerThread() < 10) {
        config.setRunnerThread(10);
      }
      logger.info("Limit of Runner: {}", config.getRunnerThread());
      alreadySetLimit = true;
    } finally {
      hashConfigSub.clear();
    }
  }

  @SuppressWarnings("unchecked")
  private static boolean loadNetworkServer(final Configuration config) {
    config.setServerPort(0);
    config.setServerSslPort(0);
    XmlValue value = hashConfig.get(XML_SERVER_HTTPPORT);
    int httpport = 8066;
    if (value != null && !value.isEmpty()) {
      httpport = value.getInteger();
    }
    config.setServerHttpport(httpport);
    value = hashConfig.get(XML_SERVER_HTTPSPORT);
    int httpsport = 8067;
    if (value != null && !value.isEmpty()) {
      httpsport = value.getInteger();
    }
    config.setServerHttpsPort(httpsport);
    // XXX FIXME to change to accept multiple port according to relation local-proxy
    value = hashConfig.get(XML_SERVER_PROXY);
    if (value != null && value.getList() != null) {
      for (final XmlValue[] xml : (Iterable<XmlValue[]>) value.getList()) {
        final XmlHash subHash = new XmlHash(xml);
        value = subHash.get(XML_SERVER_LISTENADDR);
        if (value == null || value.isEmpty()) {
          continue;
        }
        final String listenaddr = value.getString();
        value = subHash.get(XML_SERVER_LISTENPORT);
        if (value == null || value.isEmpty()) {
          continue;
        }
        final int listenport = value.getInteger();
        value = subHash.get(XML_SERVER_LISTENSSL);
        if (value == null || value.isEmpty()) {
          continue;
        }
        final boolean listenssl = value.getBoolean();
        value = subHash.get(XML_SERVER_REMOTEADDR);
        if (value == null || value.isEmpty()) {
          continue;
        }
        final String remoteaddr = value.getString();
        value = subHash.get(XML_SERVER_REMOTEPORT);
        if (value == null || value.isEmpty()) {
          continue;
        }
        final int remoteport = value.getInteger();
        value = subHash.get(XML_SERVER_REMOTESSL);
        if (value == null || value.isEmpty()) {
          continue;
        }
        final boolean remotessl = value.getBoolean();
        final InetSocketAddress local =
            new InetSocketAddress(listenaddr, listenport);
        final InetSocketAddress remote =
            new InetSocketAddress(remoteaddr, remoteport);
        try {
          ProxyEntry.add(local, listenssl, remote, remotessl);
        } catch (final OpenR66ProtocolSystemException e) {
          logger.error("Issue on configuration: {}", e.getMessage());
          return false;
        }
      }
      if (ProxyEntry.proxyEntries.isEmpty()) {
        logger.error("No proxy configuration found");
        return false;
      }
    } else {
      logger.error("No proxy configuration found");
      return false;
    }
    return true;
  }

  /**
   * Load database parameter
   *
   * @param config
   *
   * @return True if OK
   */
  private static boolean loadDatabase(final Configuration config) {
    logger.info("No DBDriver in Config file for Proxy");
    config.setSaveTaskRunnerWithNoDb(true);
    admin = new DbAdmin(); // no database support
    noCommitAdmin = admin;
    return true;
  }

  /**
   * Initiate the configuration from the xml file for server
   *
   * @param config
   * @param filename
   *
   * @return True if OK
   */
  public static boolean setConfigurationProxyFromXml(final Configuration config,
                                                     final String filename) {
    final Document document;
    // Open config file
    try {
      document = XmlUtil.getNewSaxReader().read(filename);
    } catch (final DocumentException e) {
      logger.error("Unable to read the XML Config file: " + filename + ": {}",
                   e.getMessage());
      return false;
    }
    if (document == null) {
      logger.error("Unable to read the XML Config file: " + filename);
      return false;
    }
    XmlValue[] configuration = XmlUtil.read(document, configServer);
    hashConfig = new XmlHash(configuration);
    final XmlRootHash xmlRootHash = new XmlRootHash(configuration);
    setXmlRootHash(xmlRootHash);
    // Now read the configuration
    if (!loadIdentity(config, xmlRootHash)) {
      logger.error("Cannot load Identity");
      return false;
    }
    if (!loadDatabase(config)) {
      logger.error("Cannot load Database configuration");
      return false;
    }
    if (!loadServerParam(config)) {
      logger.error("Cannot load Server Parameters");
      return false;
    }
    if (!loadDirectory(config)) {
      logger.error("Cannot load Directory configuration");
      return false;
    }
    loadLimit(config, false);
    if (config.isUseSSL() && !loadSsl(config, xmlRootHash)) {
      logger.error("Cannot load SSL configuration");
      return false;
    }
    if (!loadNetworkServer(config)) {
      logger.error("Cannot load Network configuration");
      return false;
    }
    hashConfig.clear();
    hashConfig = null;
    xmlRootHash.clear();
    configuration = null;
    return true;
  }

}