ee.ria.xroad.confproxy.util.ConfProxyHelper.java Source code

Java tutorial

Introduction

Here is the source code for ee.ria.xroad.confproxy.util.ConfProxyHelper.java

Source

/**
 * The MIT License
 * Copyright (c) 2015 Estonian Information System Authority (RIA), Population Register Centre (VRK)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package ee.ria.xroad.confproxy.util;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import ee.ria.xroad.common.conf.globalconf.ConfigurationDirectory;
import ee.ria.xroad.common.conf.globalconf.ConfigurationDirectoryV1;
import org.apache.commons.io.FileUtils;

import ee.ria.xroad.common.SystemProperties;
import ee.ria.xroad.common.conf.globalconf.ConfigurationDirectoryV2;
import ee.ria.xroad.confproxy.ConfProxyProperties;
import lombok.extern.slf4j.Slf4j;

/**
 * Provides configuration proxy utility functions.
 */
@Slf4j
public final class ConfProxyHelper {
    private static final int SUCCESS = 0;
    private static final int ERROR_CODE_INTERNAL = 125;
    private static final int ERROR_CODE_INVALID_SIGNATURE_VALUE = 124;
    private static final int ERROR_CODE_EXPIRED_CONF = 123;
    private static final int ERROR_CODE_CANNOT_DOWNLOAD_CONF = 122;
    private static final int MAX_CONFIGURATION_LIFETIME_SECONDS = 600;

    /**
     * Unavailable utility class constructor.
     */
    private ConfProxyHelper() {
    }

    /**
     * Invoke the configuration client script to download the global
     * configuration from the source defined in the provided source anchor.
     * @param path where the downloaded files should be placed
     * @param sourceAnchor path to the source anchor xml file
     * @return downloaded configuration directory
     * @throws Exception if an configuration client error occurs
     */
    public static ConfigurationDirectory downloadConfiguration(final String path, final String sourceAnchor,
            final int version) throws Exception {
        ProcessBuilder pb = new ProcessBuilder(ConfProxyProperties.getDownloadScriptPath(), sourceAnchor, path,
                String.format("%d", version));
        pb.redirectErrorStream(true);
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        log.info("Running '{} {} {} {}' ...", ConfProxyProperties.getDownloadScriptPath(), sourceAnchor, path,
                version);
        runConfClient(pb);
        if (version != SystemProperties.CURRENT_GLOBAL_CONFIGURATION_VERSION) {
            return new ConfigurationDirectoryV1(path);
        } else {
            return new ConfigurationDirectoryV2(path);
        }
    }

    /**
     * Invoke the configuration client script to check whether the downloaded
     * global configuration is valid according to the provided source anchor.
     * @param sourceAnchor path to the source anchor xml file
     * @throws Exception if an configuration client error occurs
     */
    public static void validateConfiguration(final String sourceAnchor) throws Exception {
        ProcessBuilder pb = new ProcessBuilder(ConfProxyProperties.getDownloadScriptPath(), sourceAnchor);
        pb.redirectErrorStream(true);
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        log.info("Running '{} {}' ...", ConfProxyProperties.getDownloadScriptPath(), sourceAnchor);
        runConfClient(pb);
    }

    /**
     * Helper method for running the configuration client script.
     * @param pb the configuration client script process builder
     * @throws Exception if errors occur when running the configuration client
     */
    private static void runConfClient(final ProcessBuilder pb) throws Exception {
        int exitCode = -1;
        try {
            Process process = pb.start();
            exitCode = process.waitFor();

        } catch (IOException e) {
            log.error("IOException", e);
            exitCode = 2;
            throw e;
        } catch (Exception e) {
            log.error("Undetermined ConfigurationClient exitCode", e);
            //undetermined ConfigurationClient exitCode, fail in 'finally'
            throw e;
        }
        switch (exitCode) {
        case SUCCESS:
            break;
        case ERROR_CODE_CANNOT_DOWNLOAD_CONF:
            throw new Exception("configuration-client error (exit code " + exitCode + "), download failed");
        case ERROR_CODE_EXPIRED_CONF:
            throw new Exception(
                    "configuration-client error (exit code " + exitCode + "), configuration is outdated");
        case ERROR_CODE_INVALID_SIGNATURE_VALUE:
            throw new Exception(
                    "configuration-client error (exit code " + exitCode + "), configuration is incorrect");
        case ERROR_CODE_INTERNAL:
            throw new Exception("configuration-client error (exit code " + exitCode + ")");
        default:
            throw new Exception("Failed to download GlobalConf " + "(configuration-client exit code " + exitCode
                    + "), " + "make sure configuration-client is" + "installed correctly");
        }
    }

    /**
     * Gets all existing subdirectory names from the configuration proxy
     * configuration directory, which correspond to the configuration proxy
     * instance ids.
     * @return list of configuration proxy instance ids
     * @throws IOException if the configuration proxy configuration path is
     * erroneous
     */
    public static List<String> availableInstances() throws IOException {
        Path confPath = Paths.get(SystemProperties.getConfigurationProxyConfPath());
        return subDirectoryNames(confPath);
    }

    /**
     * Deletes outdated previously generated global configurations from configuration target path
     * e.g. /var/lib/xroad/public, as defined by the 'validity interval' configuration proxy property.
     * @param conf the configuration proxy instance configuration
     * @throws IOException
     * in case an old global configuration could not be deleted
     */
    public static void purgeOutdatedGenerations(final ConfProxyProperties conf) throws IOException {
        Path instanceDir = Paths.get(conf.getConfigurationTargetPath());
        log.debug("Create directories {}", instanceDir);
        Files.createDirectories(instanceDir); //avoid errors if it's not present
        for (String genTime : subDirectoryNames(instanceDir)) {
            Date current = new Date();
            Date old;
            try {
                old = new Date(Long.parseLong(genTime));
            } catch (NumberFormatException e) {
                log.error("Unable to parse directory name {}", genTime);
                continue;
            }
            long diffSeconds = TimeUnit.MILLISECONDS.toSeconds((current.getTime() - old.getTime()));
            long timeToKeep = Math.min(MAX_CONFIGURATION_LIFETIME_SECONDS, conf.getValidityIntervalSeconds());
            if (diffSeconds > timeToKeep) {
                Path oldPath = Paths.get(conf.getConfigurationTargetPath(), genTime);
                FileUtils.deleteDirectory(oldPath.toFile());
                log.debug("Purge directory {}", oldPath);
            } else {
                Path valid = instanceDir.resolve(genTime);
                log.debug("A valid generated configuration exists in '{}'", valid);
            }
        }
    }

    /**
     * Gets the list of subdirectory names in the given directory path.
     * @param dir path to the directory
     * @return list of subdirectory names
     * @throws IOException if opening the directory fails
     */
    private static List<String> subDirectoryNames(final Path dir) throws IOException {
        List<String> subdirs = new ArrayList<>();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, Files::isDirectory)) {
            for (Path subDir : stream) {
                String conf = subDir.getFileName().toString();
                subdirs.add(conf);
            }
            return subdirs;
        }
    }
}