/**
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.apache.kerby.has.server;

import org.apache.commons.dbutils.DbUtils;
import org.apache.hadoop.http.HttpConfig;
import org.apache.kerby.has.common.HasConfig;
import org.apache.kerby.has.common.HasException;
import org.apache.kerby.has.common.util.HasUtil;
import org.apache.kerby.has.server.web.WebConfigKey;
import org.apache.kerby.has.server.web.WebServer;
import org.apache.kerby.kerberos.kdc.impl.NettyKdcServerImpl;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
import org.apache.kerby.kerberos.kerb.client.ClientUtil;
import org.apache.kerby.kerberos.kerb.client.KrbConfig;
import org.apache.kerby.kerberos.kerb.client.KrbSetting;
import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
import org.apache.kerby.kerberos.kerb.keytab.Keytab;
import org.apache.kerby.kerberos.kerb.server.KdcServer;
import org.apache.kerby.kerberos.kerb.server.KdcUtil;
import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
import org.apache.kerby.util.IOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * The HAS KDC server implementation.
 */
public class HasServer {
    public static final Logger LOG = LoggerFactory.getLogger(HasServer.class);

    private static HasServer server = null;

    private KrbSetting krbSetting;
    private KdcServer kdcServer;
    private WebServer webServer;
    private File confDir;
    private File workDir;
    private String kdcHost;
    private HasConfig hasConfig;

    public HasServer(File confDir) throws KrbException {
        this.confDir = confDir;
    }

    private void setConfDir(File confDir) {
        this.confDir = confDir;
    }

    public File getConfDir() {
        return confDir;
    }

    public File getWorkDir() {
        return workDir;
    }

    public void setWorkDir(File workDir) {
        this.workDir = workDir;
    }

    public void setKdcHost(String host) {
        this.kdcHost = host;
    }

    public String getKdcHost() {
        return kdcHost;
    }

    public KrbSetting getKrbSetting() {
        return krbSetting;
    }

    public KdcServer getKdcServer() {
        return kdcServer;
    }

    public WebServer getWebServer() {
        return webServer;
    }

    public void setWebServer(WebServer webServer) {
        this.webServer = webServer;
    }

    public void startKdcServer() throws HasException {
        BackendConfig backendConfig;
        try {
            backendConfig = KdcUtil.getBackendConfig(getConfDir());
        } catch (KrbException e) {
            throw new HasException("Failed to get backend config. " + e);
        }
        String backendJar = backendConfig.getString("kdc_identity_backend");
        if (backendJar.equals("org.apache.kerby.kerberos.kdc.identitybackend.MySQLIdentityBackend")) {
            updateKdcConf();
        }
        try {
            kdcServer = new KdcServer(confDir);
        } catch (KrbException e) {
            throw new HasException("Failed to create KdcServer. " + e.getMessage());
        }
        kdcServer.setWorkDir(workDir);
        kdcServer.setInnerKdcImpl(new NettyKdcServerImpl(kdcServer.getKdcSetting()));
        try {
            kdcServer.init();
        } catch (KrbException e) {
            LOG.error("Errors occurred when init has kdc server:  " + e.getMessage());
            throw new HasException("Errors occurred when init has kdc server:  " + e.getMessage());
        }

        KrbConfig krbConfig;
        try {
            krbConfig = ClientUtil.getConfig(confDir);
        } catch (KrbException e) {
            throw new HasException("Errors occurred when getting the config from conf dir. "
                + e.getMessage());
        }
        if (krbConfig == null) {
            krbConfig = new KrbConfig();
        }
        this.krbSetting = new KrbSetting(krbConfig);
        try {
            kdcServer.start();
        } catch (KrbException e) {
            throw new HasException("Failed to start kdc server. " + e.getMessage());
        }
        try {
            HasUtil.setEnableConf(new File(confDir, "has-server.conf"), "false");
        } catch (Exception e) {
            throw new HasException("Failed to enable conf. " + e.getMessage());
        }
        setHttpFilter();
    }

    public File initKdcServer() throws KrbException {
        File adminKeytabFile = new File(workDir, "admin.keytab");
        if (kdcServer == null) {
            throw new KrbException("Please start KDC server first.");
        }
        LocalKadmin kadmin = new LocalKadminImpl(kdcServer.getKdcSetting(),
            kdcServer.getIdentityService());
        String kadminPrincipalName = kadmin.getKadminPrincipal();
        if (adminKeytabFile.exists()) {
            try {
                Keytab keytab = Keytab.loadKeytab(adminKeytabFile);
                boolean deleteFlag = false;
                if (!keytab.getPrincipals().isEmpty()) {
                    for (PrincipalName principal: keytab.getPrincipals()) {
                        if (!principal.getName().equals(kadminPrincipalName)) {
                            deleteFlag = true;
                            break;
                        }
                    }
                } else {
                    deleteFlag = true;
                }
                if (deleteFlag) {
                    if (!adminKeytabFile.delete()) {
                        throw new KrbException("Failed to delete wrong admin keytab file.");
                    } else {
                        System.out.println("The old admin.keytab is wrong and will be regenerated.");
                    }
                } else {
                    return adminKeytabFile;
                }
            } catch (IOException e) {
                throw new KrbException("Failed to load existing admin keytab file.");
            }
        }
        if (kadmin.getPrincipal(kadminPrincipalName) == null) {
            kadmin.createBuiltinPrincipals();
        }
        kadmin.exportKeytab(adminKeytabFile, kadmin.getKadminPrincipal());
        System.out.println("The keytab for kadmin principal "
            + "has been exported to the specified file "
            + adminKeytabFile.getAbsolutePath() + ", please keep it safe, "
            + "in order to use kadmin tool later");

        return adminKeytabFile;
    }

    private void setHttpFilter() throws HasException {
        File httpKeytabFile = new File(workDir, "http.keytab");
        LocalKadmin kadmin = new LocalKadminImpl(kdcServer.getKdcSetting(),
            kdcServer.getIdentityService());
        createHttpPrincipal(kadmin);
        try {
            kadmin.exportKeytab(httpKeytabFile, getHttpPrincipal());
        } catch (KrbException e) {
            throw new HasException("Failed to export keytab: " + e.getMessage());
        }
        webServer.getConf().setString(WebConfigKey.HAS_AUTHENTICATION_FILTER_AUTH_TYPE,
            hasConfig.getFilterAuthType());
        webServer.getConf().setString(WebConfigKey.HAS_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY,
            getHttpPrincipal());
        webServer.getConf().setString(WebConfigKey.HAS_AUTHENTICATION_KERBEROS_KEYTAB_KEY,
            httpKeytabFile.getPath());
        webServer.defineFilter();
    }

    public void createHttpPrincipal(LocalKadmin kadmin) throws HasException {
        String httpPrincipal = getHttpPrincipal();
        IdentityBackend backend = kdcServer.getIdentityService();
        try {
            if (backend.getIdentity(httpPrincipal) == null) {
                kadmin.addPrincipal(httpPrincipal);
            } else {
                LOG.info("The http principal already exists in backend.");
            }
        } catch (KrbException e) {
            throw new HasException("Failed to add principal, " + e.getMessage());
        }
    }

    public String getHttpPrincipal() throws HasException {
        String realm = kdcServer.getKdcSetting().getKdcRealm();
        String nameString;
        try {
            InetAddress addr = InetAddress.getLocalHost();
            String fqName = addr.getCanonicalHostName();
            nameString = "HTTP/" + fqName + "@" + realm;
        } catch (UnknownHostException e) {
            throw new HasException(e);
        }
        LOG.info("The http principal name is: " + nameString);
        return nameString;
    }

     /**
     * Update conf file.
     *
     * @param confName  conf file name
     * @param values    customized values
     * @throws IOException throw IOException
     * @throws HasException e
     */
    public void updateConfFile(String confName, Map<String, String> values)
        throws IOException, HasException {
        File confFile = new File(getConfDir().getAbsolutePath(), confName);
        if (confFile.exists()) {
            // Update conf file content
            InputStream templateResource;
            if (confName.equals("has-server.conf")) {
                templateResource = new FileInputStream(confFile);
            } else {
                String resourcePath = "/" + confName + ".template";
                templateResource = getClass().getResourceAsStream(resourcePath);
            }
            String content;
            try {
                content = IOUtil.readInput(templateResource);
            } finally {
                templateResource.close();
            }
            for (Map.Entry<String, String> entry : values.entrySet()) {
                content = content.replaceAll(Pattern.quote(entry.getKey()), entry.getValue());
            }

            // Delete the original conf file
            boolean delete = confFile.delete();
            if (!delete) {
                throw new HasException("Failed to delete conf file: " + confName);
            }

            // Save the updated conf file
            IOUtil.writeFile(content, confFile);
        } else {
            throw new HasException("Conf file: " + confName + " not found.");
        }
    }

    /**
     * Get KDC Config from MySQL.
     *
     * @return Kdc config
     * @throws HasException e
     */
    private Map<String, String> getKdcConf() throws HasException {
        PreparedStatement preStm = null;
        ResultSet result = null;
        Map<String, String> kdcConf = new HashMap<>();
        BackendConfig backendConfig;
        try {
            backendConfig = KdcUtil.getBackendConfig(getConfDir());
        } catch (KrbException e) {
            throw new HasException("Getting backend config failed." + e.getMessage());
        }
        String driver = backendConfig.getString("mysql_driver");
        String url = backendConfig.getString("mysql_url");
        String user = backendConfig.getString("mysql_user");
        String password = backendConfig.getString("mysql_password");
        Connection connection = startConnection(driver, url, user, password);
        try {

            // Get Kdc configuration from kdc_config table
            String stmKdc = "SELECT * FROM `kdc_config` WHERE id = 1";
            preStm = connection.prepareStatement(stmKdc);
            result = preStm.executeQuery();
            while (result.next()) {
                String realm = result.getString("realm");
                String servers = result.getString("servers");
                String port = String.valueOf(result.getInt("port"));
                kdcConf.put("servers", servers);
                kdcConf.put("_PORT_", port);
                kdcConf.put("_REALM_", realm);
            }

        } catch (SQLException e) {
            LOG.error("Error occurred while getting kdc config.");
            throw new HasException("Failed to get kdc config. ", e);
        } finally {
            DbUtils.closeQuietly(preStm);
            DbUtils.closeQuietly(result);
            DbUtils.closeQuietly(connection);
        }

        return kdcConf;
    }

    /**
     * Update KDC conf file.
     *
     * @throws HasException e
     */
    private void updateKdcConf() throws HasException {
        try {
            Map<String, String> values = getKdcConf();
            String host = getKdcHost();
            if (host == null) {
                InetSocketAddress bind = getWebServer().getBindAddress();
                if (bind != null) {
                    host = bind.getHostName();
                }
            }
            values.remove("servers");
            values.put("_HOST_", host);
            updateConfFile("kdc.conf", values);
        } catch (IOException e) {
            throw new HasException("Failed to update kdc config. ", e);
        }
    }

      /**
     * Start the MySQL connection.
     *
     * @param url url of connection
     * @param user username of connection
     * @param password password of connection
     * @throws HasException e
     * @return MySQL JDBC connection
     */
    private Connection startConnection(String driver, String url, String user,
                                       String password) throws HasException {
        Connection connection;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user, password);
            if (!connection.isClosed()) {
                LOG.info("Succeeded in connecting to MySQL.");
            }
        } catch (ClassNotFoundException e) {
            throw new HasException("JDBC Driver Class not found. ", e);
        } catch (SQLException e) {
            throw new HasException("Failed to connecting to MySQL. ", e);
        }

        return connection;
    }

    /**
     * Config HAS server KDC which have MySQL backend.
     * @param backendConfig MySQL backend config
     * @param realm KDC realm to set
     * @param port has server port
     * @param host KDC host to set
     * @param hasServer has server to get param
     * @throws HasException e
     */
    public void configMySQLKdc(BackendConfig backendConfig, String realm, int port,
                               String host, HasServer hasServer) throws HasException {

        // Start mysql connection
        String driver = backendConfig.getString("mysql_driver");
        String url = backendConfig.getString("mysql_url");
        String user = backendConfig.getString("mysql_user");
        String password = backendConfig.getString("mysql_password");
        Connection connection = startConnection(driver, url, user, password);

        ResultSet resConfig = null;
        PreparedStatement preStm = null;
        try {
            createKdcTable(connection); // Create kdc_config table if not exists
            String stm = "SELECT * FROM `kdc_config` WHERE id = 1";
            preStm = connection.prepareStatement(stm);
            resConfig = preStm.executeQuery();
            if (!resConfig.next()) {
                addKdcConfig(connection, realm, port, host);
            } else {
                String oldHost = hasServer.getKdcHost();
                String servers = resConfig.getString("servers");
                String[] serverArray = servers.split(",");
                List<String> serverList = new ArrayList<>();
                Collections.addAll(serverList, serverArray);
                if (serverList.contains(oldHost)) {
                    servers = servers.replaceAll(oldHost, host);
                } else {
                    servers = servers + "," + host;
                }
                boolean initialized = resConfig.getBoolean("initialized");
                updateKdcConfig(connection, initialized, port, realm, servers);
            }
            hasServer.setKdcHost(host);
        } catch (SQLException e) {
            throw new HasException("Failed to config HAS KDC. ", e);
        } finally {
            DbUtils.closeQuietly(preStm);
            DbUtils.closeQuietly(resConfig);
            DbUtils.closeQuietly(connection);
        }
    }

    /**
     * Create kdc_config table in database.
     * @param conn database connection
     * @throws HasException e
     */
    private void createKdcTable(final Connection conn) throws HasException {
        PreparedStatement preStm = null;
        try {
            String stm = "CREATE TABLE IF NOT EXISTS `kdc_config` ("
                + "port INTEGER DEFAULT 88, servers VARCHAR(255) NOT NULL, "
                + "initialized bool DEFAULT FALSE, realm VARCHAR(255) "
                + "DEFAULT NULL, id INTEGER DEFAULT 1, CHECK (id=1), PRIMARY KEY (id)) "
                + "ENGINE=INNODB;";
            preStm = conn.prepareStatement(stm);
            preStm.executeUpdate();
        } catch (SQLException e) {
            throw new HasException("Failed to create kdc_config table. ", e);
        } finally {
            DbUtils.closeQuietly(preStm);
        }
    }

    /**
     * Add KDC Config information in database.
     * @param conn database connection
     * @param realm realm to add
     * @param port port to add
     * @param host host to add
     */
    private void addKdcConfig(Connection conn, String realm, int port, String host)
        throws HasException {
        PreparedStatement preStm = null;
        try {
            String stm = "INSERT INTO `kdc_config` (port, servers, realm)" + " VALUES(?, ?, ?)";
            preStm = conn.prepareStatement(stm);
            preStm.setInt(1, port);
            preStm.setString(2, host);
            preStm.setString(3, realm);
            preStm.executeUpdate();
        } catch (SQLException e) {
            throw new HasException("Failed to insert into kdc_config table. ", e);
        } finally {
            DbUtils.closeQuietly(preStm);
        }
    }

    /**
     * Update KDC Config record in database.
     * @param conn database connection
     * @param realm realm to update
     * @param port port to update
     * @param servers servers to update
     * @param initialized initial state of KDC Config
     */
    private void updateKdcConfig(Connection conn, boolean initialized, int port,
                                 String realm, String servers) throws HasException {
        PreparedStatement preStm = null;
        try {
            if (initialized) {
                String stmUpdate = "UPDATE `kdc_config` SET servers = ? WHERE id = 1";
                preStm = conn.prepareStatement(stmUpdate);
                preStm.setString(1, servers);
                preStm.executeUpdate();
            } else {
                String stmUpdate = "UPDATE `kdc_config` SET port = ?, realm = ?, servers = ? WHERE id = 1";
                preStm = conn.prepareStatement(stmUpdate);
                preStm.setInt(1, port);
                preStm.setString(2, realm);
                preStm.setString(3, servers);
                preStm.executeUpdate();
            }
        } catch (SQLException e) {
            throw new HasException("Failed to update KDC Config. ", e);
        } finally {
            DbUtils.closeQuietly(preStm);
        }
    }

    /**
     * Read in krb5-template.conf and substitute in the correct port.
     *
     * @return krb5 conf file
     * @throws HasException e
     */
    public File generateKrb5Conf() throws HasException {
        Map<String, String> kdcConf = getKdcConf();
        String[] servers = kdcConf.get("servers").split(",");
        int kdcPort = Integer.parseInt(kdcConf.get("_PORT_"));
        String kdcRealm = kdcConf.get("_REALM_");
        StringBuilder kdcBuilder = new StringBuilder();
        for (String server : servers) {
            String append = "\t\tkdc = " + server.trim() + ":" + kdcPort + "\n";
            kdcBuilder.append(append);
        }
        String kdc = kdcBuilder.toString();
        kdc = kdc.substring(0, kdc.length() - 1);
        String resourcePath = "/krb5.conf.template";
        String content;
        try (InputStream templateResource = getClass().getResourceAsStream(resourcePath)) {
            content = IOUtil.readInput(templateResource);
        } catch (IOException e) {
            throw new HasException("Read template resource failed. " + e.getMessage());
        }
        content = content.replaceAll("_REALM_", kdcRealm);
        content = content.replaceAll("_PORT_", String.valueOf(kdcPort));
        content = content.replaceAll("_UDP_LIMIT_", "4096");
        content = content.replaceAll("_KDCS_", kdc);
        File confFile = new File(confDir, "krb5.conf");
        if (confFile.exists()) {
            boolean delete = confFile.delete();
            if (!delete) {
                throw new HasException("File delete error!");
            }
        }
        try {
            IOUtil.writeFile(content, confFile);
        } catch (IOException e) {
            throw new HasException("Write content to conf file failed. " + e.getMessage());
        }

        return confFile;
    }

    /**
     * Read in has-server.conf and create has-client.conf.
     *
     * @return has conf file
     * @throws IOException e
     * @throws HasException e
     */
    public File generateHasConf() throws HasException, IOException {
        Map<String, String> kdcConf = getKdcConf();
        String servers = kdcConf.get("servers");
        File confFile = new File(getConfDir().getAbsolutePath(), "has-server.conf");
        HasConfig hasConfig = HasUtil.getHasConfig(confFile);
        if (hasConfig != null) {
            String defaultValue = hasConfig.getHttpsHost();
            String content;
            try (InputStream templateResource = new FileInputStream(confFile)) {
                content = IOUtil.readInput(templateResource);
            }
            content = content.replaceFirst(Pattern.quote(defaultValue), servers);
            File hasFile = new File(confDir, "has-client.conf");
            IOUtil.writeFile(content, hasFile);
            return hasFile;
        } else {
            throw new HasException("has-server.conf not found. ");
        }
    }

    public void stopKdcServer() {
        try {
            kdcServer.stop();
        } catch (KrbException e) {
            LOG.error("Fail to stop has kdc server");
        }
    }

    public void startWebServer() throws HasException {
        if (webServer == null) {
            HasConfig conf = new HasConfig();

            // Parse has-server.conf to get http_host and http_port
            File confFile = new File(confDir, "has-server.conf");
            hasConfig = HasUtil.getHasConfig(confFile);
            hasConfig.setConfDir(getConfDir().getAbsoluteFile());
            try {
                String httpHost;
                String httpPort;
                String httpsHost;
                String httpsPort;
                if (hasConfig.getHttpHost() != null) {
                    httpHost = hasConfig.getHttpHost();
                } else {
                    LOG.info("Cannot get the http_host from has-server.conf, using the default http host.");
                    httpHost = WebConfigKey.HAS_HTTP_HOST_DEFAULT;
                }
                if (hasConfig.getHttpPort() != null) {
                    httpPort = hasConfig.getHttpPort();
                } else {
                    LOG.info("Cannot get the http_port from has-server.conf, using the default http port.");
                    httpPort = String.valueOf(WebConfigKey.HAS_HTTP_PORT_DEFAULT);
                }
                if (hasConfig.getHttpsHost() != null) {
                    httpsHost = hasConfig.getHttpsHost();
                } else {
                    LOG.info("Cannot get the https_host from has-server.conf, using the default https host.");
                    httpsHost = WebConfigKey.HAS_HTTPS_HOST_DEFAULT;
                }
                if (hasConfig.getHttpsPort() != null) {
                    httpsPort = hasConfig.getHttpsPort();
                } else {
                    LOG.info("Cannot get the https_port from has-server.conf , using the default https port.");
                    httpsPort = String.valueOf(WebConfigKey.HAS_HTTPS_PORT_DEFAULT);
                }
                String hasHttpAddress = httpHost + ":" + httpPort;
                String hasHttpsAddress = httpsHost + ":" + httpsPort;
                LOG.info("The web server http address: " + hasHttpAddress);
                LOG.info("The web server https address: " + hasHttpsAddress);

                conf.setString(WebConfigKey.HAS_HTTP_ADDRESS_KEY, hasHttpAddress);
                conf.setString(WebConfigKey.HAS_HTTPS_ADDRESS_KEY, hasHttpsAddress);
                conf.setString(WebConfigKey.HAS_HTTP_POLICY_KEY,
                    HttpConfig.Policy.HTTP_AND_HTTPS.name());
                conf.setString(WebConfigKey.HAS_SERVER_HTTPS_KEYSTORE_RESOURCE_KEY,
                    hasConfig.getSslServerConf());
                webServer = new WebServer(conf);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("https_port should be a number. "
                    + e.getMessage());
            }
        } else {
            hasConfig = webServer.getConf();
        }
        webServer.start();
        webServer.defineConfFilter();
        try {
            HasUtil.setEnableConf(new File(confDir, "has-server.conf"), "true");
        } catch (IOException e) {
            throw new HasException("Errors occurred when enable conf. " + e.getMessage());
        }
        webServer.setWebServerAttribute(this);
    }

    public void stopWebServer() {
        if (webServer != null) {
            try {
                webServer.stop();
            } catch (Exception e) {
                LOG.error("Failed to stop http server. " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        if (args[0].equals("-start")) {
            String confDirPath = args[1];
            String workDirPath = args[2];
            File confDir = new File(confDirPath);
            if (!confDir.exists()) {
                System.err.println("The conf-dir is invalid or does not exist");
                System.exit(3);
            }
            File workDir = new File(workDirPath);
            if (!workDir.exists()) {
                System.err.println("The work-dir is invalid or does not exist");
                System.exit(3);
            }

            try {
                server = new HasServer(confDir);
            } catch (KrbException e) {
                LOG.error("Errors occurred when create kdc server:  " + e.getMessage());
                System.exit(4);
            }
            server.setConfDir(confDir);
            server.setWorkDir(workDir);
            //Only start the webserver, the kdcserver could be started after setting the realm
            try {
                server.startWebServer();
            } catch (HasException e) {
                LOG.error("Errors occurred when start has http server:  " + e.getMessage());
                System.exit(6);
            }

            if (server.getWebServer().getHttpAddress() != null) {
                LOG.info("HAS http server started.");
                LOG.info("host: " + server.getWebServer().getHttpAddress().getHostName());
                LOG.info("port: " + server.getWebServer().getHttpAddress().getPort());
            }
            if (server.getWebServer().getHttpsAddress() != null) {
                LOG.info("HAS https server started.");
                LOG.info("host: " + server.getWebServer().getHttpsAddress().getHostName());
                LOG.info("port: " + server.getWebServer().getHttpsAddress().getPort());
            }
        } else if (args[0].equals("-stop")) {
            if (server != null) {
                server.stopWebServer();
                server.stopKdcServer();
            }
        } else {
            System.exit(2);
        }
    }
}
