ServerInitDatabase.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.server;

import org.apache.commons.io.FileUtils;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.logging.WaarpSlf4JLoggerFactory;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.common.utility.WaarpSystemUtil;
import org.waarp.openr66.configuration.AuthenticationFileBasedConfiguration;
import org.waarp.openr66.configuration.FileBasedConfiguration;
import org.waarp.openr66.configuration.RuleFileBasedConfiguration;
import org.waarp.openr66.database.data.DbHostConfiguration;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.configuration.Messages;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;

import java.io.File;
import java.io.IOException;

import static org.waarp.common.database.DbConstant.*;

/**
 * Utility class to initiate the database for a server
 */
public class ServerInitDatabase {
  /**
   * Internal Logger
   */
  static volatile WaarpLogger logger;

  protected static final String INFO_ARGS =
      Messages.getString("ServerInitDatabase.Help");

  static String sxml;
  static boolean database;
  static boolean upgradeDb;
  static String sbusiness;
  static String salias;
  static String sroles;
  static String sdirconfig;
  static String shostauth;
  static String slimitconfig;
  static String sextendedFactoryClassList = null;
  static long minimalSequenceValue = ILLEGALVALUE;

  private ServerInitDatabase() {
  }

  protected static boolean getParams(final String[] args) {
    if (logger == null) {
      logger = WaarpLoggerFactory.getLogger(ServerInitDatabase.class);
    }
    if (args.length < 1) {
      logger.error(INFO_ARGS);
      return false;
    }
    sxml = args[0];
    for (int i = 1; i < args.length; i++) {
      if ("-initdb".equalsIgnoreCase(args[i])) {
        database = true;
        FileBasedConfiguration.autoupgrade = false;
        upgradeDb = true;
      } else if ("-upgradeDb".equalsIgnoreCase(args[i])) {
        upgradeDb = true;
      } else if ("-loadBusiness".equalsIgnoreCase(args[i])) {
        i++;
        sbusiness = args[i];
      } else if ("-loadAlias".equalsIgnoreCase(args[i])) {
        i++;
        salias = args[i];
      } else if ("-loadRoles".equalsIgnoreCase(args[i])) {
        i++;
        sroles = args[i];
      } else if ("-dir".equalsIgnoreCase(args[i])) {
        i++;
        sdirconfig = args[i];
      } else if ("-limit".equalsIgnoreCase(args[i])) {
        i++;
        slimitconfig = args[i];
      } else if ("-auth".equalsIgnoreCase(args[i])) {
        i++;
        shostauth = args[i];
      } else if ("-loadExtendedTaskFactory".equalsIgnoreCase(args[i])) {
        i++;
        sextendedFactoryClassList = args[i];
      } else if ("-minimalSeq".equalsIgnoreCase(args[i])) {
        i++;
        try {
          minimalSequenceValue = Long.parseLong(args[i]);
        } catch (final NumberFormatException e) {
          logger.error("Number for restart sequence is not a long: {}",
                       args[i]);
          minimalSequenceValue = ILLEGALVALUE;
        }
      }
    }
    return true;
  }

  /**
   * @param args as config_database file [rules_directory host_authent
   *     limit_configuration]
   */
  public static void main(final String[] args) {
    WaarpLoggerFactory.setDefaultFactoryIfNotSame(
        new WaarpSlf4JLoggerFactory(null));
    if (logger == null) {
      logger = WaarpLoggerFactory.getLogger(ServerInitDatabase.class);
    }
    if (!getParams(args)) {
      SysErrLogger.FAKE_LOGGER.sysout(INFO_ARGS);
      if (admin != null) {
        admin.close();
      }
      WaarpSystemUtil.systemExit(2);
      return;
    }

    try {
      if (!FileBasedConfiguration.setConfigurationInitDatabase(
          Configuration.configuration, args[0], database)) {
        System.err.format(
            Messages.getString("Configuration.NeedCorrectConfig"));
        SysErrLogger.FAKE_LOGGER.syserr();
        WaarpSystemUtil.systemExit(2);
        return;
      }
      if (database) {
        // Init database
        SysErrLogger.FAKE_LOGGER.sysoutFormat(
            Messages.getString("ServerInitDatabase.Create.Start"));
        SysErrLogger.FAKE_LOGGER.sysout();
        initdb();
        SysErrLogger.FAKE_LOGGER.sysoutFormat(
            Messages.getString("ServerInitDatabase.Create.Done"));
        SysErrLogger.FAKE_LOGGER.sysout();
      }
      if (sextendedFactoryClassList != null) {
        // Load extended Factory for Task Type
        final String[] extendedFactories = sextendedFactoryClassList.split(",");
        for (final String extendedFactory : extendedFactories) {
          try {
            WaarpSystemUtil.newInstance(extendedFactory);
          } catch (final Exception e) {
            SysErrLogger.FAKE_LOGGER.sysoutFormat(Messages.getString(
                "ServerInitDatabase.ExtendedTaskFactory.error") +
                                                  e.getMessage());
            SysErrLogger.FAKE_LOGGER.sysout();
          }
        }
      }
      if (upgradeDb) {
        // try to upgrade DB schema
        SysErrLogger.FAKE_LOGGER.sysoutFormat(
            Messages.getString("ServerInitDatabase.Upgrade.Start"));
        SysErrLogger.FAKE_LOGGER.sysout();
        // TODO Split check for update and upgrade actions
        if (!upgradedb()) {
          SysErrLogger.FAKE_LOGGER.syserr(
              Messages.getString("ServerInitDatabase.SchemaNotUptodate"));
          SysErrLogger.FAKE_LOGGER.syserr();
          updateMinimalValueSequence();
          WaarpSystemUtil.systemExit(1);
          return;
        }
        SysErrLogger.FAKE_LOGGER.sysoutFormat(
            Messages.getString("ServerInitDatabase.Upgrade.Done"));
        SysErrLogger.FAKE_LOGGER.sysout();
      }
      updateMinimalValueSequence();
      // Try to load some element directly into database from first
      // configuration file
      FileBasedConfiguration.setConfigurationServerFromXml(
          Configuration.configuration, args[0], false);
      if (sdirconfig != null) {
        // load Rules
        SysErrLogger.FAKE_LOGGER.sysoutFormat(
            Messages.getString("ServerInitDatabase.LoadRule.Start"),
            sdirconfig);
        SysErrLogger.FAKE_LOGGER.sysout();
        final File dirConfig = new File(sdirconfig);
        if (dirConfig.isDirectory()) {
          if (!loadRules(dirConfig)) {
            SysErrLogger.FAKE_LOGGER.sysoutFormat(
                Messages.getString("ServerInitDatabase.LoadRule.Failed"));
            WaarpSystemUtil.systemExit(1);
            return;
          }
        } else {
          System.err.format(
              Messages.getString("ServerInitDatabase.LoadRule.NoDirectory"),
              sdirconfig);
          SysErrLogger.FAKE_LOGGER.syserr();
          WaarpSystemUtil.systemExit(1);
          return;
        }
        SysErrLogger.FAKE_LOGGER.sysoutFormat(
            Messages.getString("ServerInitDatabase.LoadRule.Done"));
        SysErrLogger.FAKE_LOGGER.sysout();
      }
      if (shostauth != null) {
        // Load Host Authentications
        System.out.format(
            Messages.getString("ServerInitDatabase.LoadAuth.Start"), shostauth);
        SysErrLogger.FAKE_LOGGER.sysout();
        if (!loadHostAuth(shostauth)) {
          System.err.format(
              Messages.getString("ServerInitDatabase.LoadAuth.Failed"));
          SysErrLogger.FAKE_LOGGER.syserr();
          WaarpSystemUtil.systemExit(1);
          return;
        }
        System.out.format(
            Messages.getString("ServerInitDatabase.LoadAuth.Done"));
        SysErrLogger.FAKE_LOGGER.sysout();
      }
      if (slimitconfig != null) {
        // Load configuration
        System.out.format(
            Messages.getString("ServerInitDatabase.LoadLimit.Start"),
            slimitconfig);
        SysErrLogger.FAKE_LOGGER.sysout();
        if (!FileBasedConfiguration.setConfigurationLoadLimitFromXml(
            Configuration.configuration, slimitconfig)) {
          System.err.format(
              Messages.getString("ServerInitDatabase.LoadLimit.Failed"));
          SysErrLogger.FAKE_LOGGER.syserr();
          WaarpSystemUtil.systemExit(1);
          return;
        }
        System.out.format(
            Messages.getString("ServerInitDatabase.LoadLimit.Done"));
        SysErrLogger.FAKE_LOGGER.sysout();
      }
      if (sbusiness != null || salias != null || sroles != null) {
        if (sbusiness != null) {
          sbusiness = getXMLFromFile(sbusiness);
        }
        if (salias != null) {
          salias = getXMLFromFile(salias);
        }
        if (sroles != null) {
          sroles = getXMLFromFile(sroles);
        }
        DbHostConfiguration hostConfiguration;
        try {
          hostConfiguration =
              new DbHostConfiguration(Configuration.configuration.getHostId());
          if (ParametersChecker.isNotEmpty(salias)) {
            hostConfiguration.setAliases(salias);
          }
          if (ParametersChecker.isNotEmpty(sbusiness)) {
            hostConfiguration.setBusiness(sbusiness);
          }
          if (ParametersChecker.isNotEmpty(sroles)) {
            hostConfiguration.setRoles(sroles);
          }
          hostConfiguration.update();
        } catch (final WaarpDatabaseException e) {
          hostConfiguration =
              new DbHostConfiguration(Configuration.configuration.getHostId(),
                                      sbusiness, sroles, salias, "");
          hostConfiguration.insert();
        }
      }
      SysErrLogger.FAKE_LOGGER.sysout(
          Messages.getString("ServerInitDatabase.LoadDone"));
      SysErrLogger.FAKE_LOGGER.sysout();
      WaarpSystemUtil.systemExit(0);
    } catch (final WaarpDatabaseException e) {
      SysErrLogger.FAKE_LOGGER.syserr(
          Messages.getString("ServerInitDatabase.ErrDatabase"));
      SysErrLogger.FAKE_LOGGER.syserr();
      WaarpSystemUtil.systemExit(3);
    } finally {
      if (admin != null) {
        admin.close();
      }
    }
  }

  private static void updateMinimalValueSequence() {
    if (minimalSequenceValue != ILLEGALVALUE) {
      try {
        admin.getSession().getAdmin().getDbModel()
             .resetSequence(admin.getSession(), minimalSequenceValue);
      } catch (final WaarpDatabaseNoConnectionException e) {
        logger.error("Cannot update sequence restart value: {}",
                     e.getMessage());
      }
    }
  }

  private static String getXMLFromFile(final String path) {
    String res = "";
    final File file = new File(path);
    if (file.canRead()) {
      try {
        final String value =
            FileUtils.readFileToString(file, WaarpStringUtils.UTF8);
        if (ParametersChecker.isNotEmpty(value)) {
          res = value.replaceAll("\r|\n|  ", " ").trim();
        }
      } catch (final IOException e) {
        SysErrLogger.FAKE_LOGGER.syserr(e);
      }
    }
    return res;
  }

  public static void initdb() throws WaarpDatabaseNoConnectionException {
    // Create tables: configuration, hosts, rules, runner, cptrunner
    admin.getSession().getAdmin().getDbModel().createTables(admin.getSession());
  }

  /**
   * @return True if the base is up to date, else False (need Upgrade)
   */
  public static boolean upgradedb() {
    if (logger == null) {
      logger = WaarpLoggerFactory.getLogger(ServerInitDatabase.class);
    }
    // Update tables: runner
    boolean uptodate = true;
    // Check if the database is up to date
    final String version = DbHostConfiguration.getVersionDb(
        Configuration.configuration.getHostId());
    try {
      if (version != null) {
        uptodate = admin.getSession().getAdmin().getDbModel()
                        .needUpgradeDb(admin.getSession(), version, true);
      } else {
        uptodate = admin.getSession().getAdmin().getDbModel()
                        .needUpgradeDb(admin.getSession(), "1.1.0", true);
      }
      if (uptodate) {
        logger.error(Messages.getString(
            "ServerInitDatabase.SchemaNotUptodate")); //$NON-NLS-1$
        return false;
      } else {
        logger.debug(Messages.getString(
            "ServerInitDatabase.SchemaUptodate")); //$NON-NLS-1$
      }
    } catch (final WaarpDatabaseNoConnectionException e) {
      logger.error(Messages.getString("Database.CannotConnect"),
                   e); //$NON-NLS-1$
      return false;
    }
    return !uptodate;
  }

  public static boolean loadRules(final File dirConfig)
      throws WaarpDatabaseException {
    try {
      RuleFileBasedConfiguration.importRules(dirConfig);
    } catch (final OpenR66ProtocolSystemException e) {
      SysErrLogger.FAKE_LOGGER.syserr(e);
      return false;
    }
    return true;
  }

  public static boolean loadHostAuth(final String filename) {
    return AuthenticationFileBasedConfiguration.loadAuthentication(
        Configuration.configuration, filename);
  }
}