it.greenvulcano.configuration.BaseConfigurationManager.java Source code

Java tutorial

Introduction

Here is the source code for it.greenvulcano.configuration.BaseConfigurationManager.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2016 GreenVulcano ESB Open Source Project.
 * All rights reserved.
 *
 * This file is part of GreenVulcano ESB.
 *
 * GreenVulcano ESB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GreenVulcano ESB 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with GreenVulcano ESB. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package it.greenvulcano.configuration;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.karaf.config.core.ConfigRepository;
import org.osgi.framework.InvalidSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import it.greenvulcano.gvesb.GVConfigurationManager;

public class BaseConfigurationManager implements GVConfigurationManager {

    private final Logger LOG = LoggerFactory.getLogger(getClass());
    private final ReentrantLock LOCK = new ReentrantLock();

    private ConfigRepository configRepository;
    private final List<DeployListener> deployListeners = Collections.synchronizedList(new LinkedList<>());

    public void setConfigRepository(ConfigRepository configRepository) {
        this.configRepository = configRepository;
    }

    public void setDeployListeners(List<DeployListener> deployListeners) {

        this.deployListeners.clear();
        if (deployListeners != null) {
            this.deployListeners.addAll(deployListeners);
        }
    }

    @Override
    public String getCurrentConfigurationName() {
        Path current = Paths.get(XMLConfig.getBaseConfigPath());

        return current.getFileName().toString();
    }

    @Override
    public void reload() throws XMLConfigException {
        XMLConfig.reloadAll();

    }

    @Override
    public byte[] export(String name) throws IOException, FileNotFoundException {

        Path configurationPath = getConfigurationPath(name);

        if (Files.exists(configurationPath) && !Files.isDirectory(configurationPath)) {
            return Files.readAllBytes(configurationPath);
        } else {
            throw new FileNotFoundException(configurationPath.toString());
        }
    }

    @Override
    public Set<File> getHistory() throws IOException {
        Path history = getHistoryPath();
        if (Files.exists(history)) {

            Path currentConfigArchive = getConfigurationPath(getCurrentConfigurationName());
            Predicate<Path> currentConfig = p -> {
                try {
                    return Files.isSameFile(p, currentConfigArchive);
                } catch (IOException e) {
                    return false;
                }
            };

            return Files.list(history).filter(currentConfig.negate()).map(Path::toFile).collect(Collectors.toSet());
        }

        return new LinkedHashSet<>();
    }

    @Override
    public void install(String name, byte[] archive) throws IOException {

        Path configurationPath = getConfigurationPath(name);
        try (ZipInputStream zipArchive = new ZipInputStream(new ByteArrayInputStream(archive))) {
            if (zipArchive.getNextEntry() != null) {
                Files.write(configurationPath, archive, StandardOpenOption.WRITE, StandardOpenOption.CREATE,
                        StandardOpenOption.TRUNCATE_EXISTING);
            } else {
                throw new IOException("Empty or invalid zip archive");
            }

        }

    }

    @Override
    public void delete(String name) throws IOException, FileNotFoundException {
        Path configurationPath = getConfigurationPath(name);
        if (!Files.deleteIfExists(configurationPath)) {
            throw new FileNotFoundException(configurationPath.toString());
        }
    }

    @Override
    public byte[] extract(String name, String entry) {

        Path configurationArchivePath = getConfigurationPath(name);

        try (ZipInputStream configurationArchive = new ZipInputStream(
                Files.newInputStream(configurationArchivePath, StandardOpenOption.READ))) {

            ZipEntry zipEntry = null;
            while ((zipEntry = configurationArchive.getNextEntry()) != null) {

                if (zipEntry.getName().equals(entry)) {
                    byte[] entryData = IOUtils.toByteArray(configurationArchive);
                    return entryData;
                }
            }
        } catch (Exception e) {
            LOG.error("Failed to extract entry " + entry + " from archive " + configurationArchivePath, e);
        }

        return new byte[] {};
    }

    @Override
    public Properties getXMLConfigProperties() throws FileNotFoundException, IOException {

        Path xmlConfigPath = Paths.get(XMLConfig.getBaseConfigPath(), "XMLConfig.properties");

        if (Files.exists(xmlConfigPath)) {
            Properties properties = new Properties();
            properties.load(Files.newInputStream(xmlConfigPath, StandardOpenOption.READ));
            return properties;
        } else {
            throw new FileNotFoundException("XMLConfig.properties");
        }

    }

    @Override
    public synchronized void saveXMLConfigProperties(Properties xmlConfigProperties) throws IOException {

        if (xmlConfigProperties != null) {

            try (OutputStream xmlConfigPropertiesOutputStream = Files.newOutputStream(
                    Paths.get(XMLConfig.getBaseConfigPath(), "XMLConfig.properties"), StandardOpenOption.WRITE,
                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
                xmlConfigProperties.store(xmlConfigPropertiesOutputStream, null);
            }
        }
    }

    @Override
    public void deploy(String name) throws XMLConfigException, FileNotFoundException {

        Path configurationArchivePath = getConfigurationPath(name);

        Path current = Paths.get(XMLConfig.getBaseConfigPath());
        Path staging = current.getParent().resolve("deploy");
        Path destination = current.getParent().resolve(name);

        if (LOCK.tryLock()) {

            if (Files.exists(configurationArchivePath) && !Files.isDirectory(configurationArchivePath)) {

                try {

                    ZipInputStream configurationArchive = new ZipInputStream(
                            Files.newInputStream(configurationArchivePath, StandardOpenOption.READ));

                    LOG.debug("Starting deploy of configuration " + name);
                    ZipEntry zipEntry = null;

                    for (Path cfgFile : Files.walk(current).collect(Collectors.toSet())) {

                        if (!Files.isDirectory(cfgFile)) {

                            Path target = staging.resolve(current.relativize(cfgFile));
                            Files.createDirectories(target);

                            Files.copy(cfgFile, target, StandardCopyOption.REPLACE_EXISTING);
                        }

                    }

                    LOG.debug("Staging new config " + name);

                    while ((zipEntry = configurationArchive.getNextEntry()) != null) {

                        Path entryPath = staging.resolve(zipEntry.getName());

                        LOG.debug("Adding resource: " + entryPath);
                        if (zipEntry.isDirectory()) {
                            entryPath.toFile().mkdirs();
                        } else {

                            Path parent = entryPath.getParent();
                            if (!Files.exists(parent)) {
                                Files.createDirectories(parent);
                            }

                            Files.copy(configurationArchive, entryPath, StandardCopyOption.REPLACE_EXISTING);
                        }

                    }

                    //**** Deleting old config dir
                    LOG.debug("Removing old config: " + current);
                    Files.walk(current, FileVisitOption.FOLLOW_LINKS).sorted(Comparator.reverseOrder())
                            .map(java.nio.file.Path::toFile).forEach(File::delete);

                    LOG.debug("Deploy new config " + name + " in path " + destination);
                    Files.move(staging, destination, StandardCopyOption.ATOMIC_MOVE);

                    setXMLConfigBasePath(destination.toString());
                    LOG.debug("Deploy complete");
                    deployListeners.forEach(l -> l.onDeploy(destination));

                } catch (Exception e) {

                    if (Objects.nonNull(staging) && Files.exists(staging)) {
                        LOG.error("Deploy failed, rollback to previous configuration", e);
                        try {
                            Files.walk(staging, FileVisitOption.FOLLOW_LINKS).sorted(Comparator.reverseOrder())
                                    .map(java.nio.file.Path::toFile).forEach(File::delete);

                            setXMLConfigBasePath(current.toString());
                        } catch (IOException | InvalidSyntaxException rollbackException) {
                            LOG.error("Failed to delete old configuration", e);
                        }
                    } else {
                        LOG.error("Deploy failed", e);
                    }

                    throw new XMLConfigException("Deploy failed", e);
                } finally {
                    LOCK.unlock();
                }
            } else {
                throw new FileNotFoundException(configurationArchivePath.toString());
            }
        } else {
            throw new IllegalStateException("A deploy is already in progress");
        }

    }

    private void setXMLConfigBasePath(String path) throws IOException, InvalidSyntaxException {
        @SuppressWarnings("unchecked")
        Dictionary<String, Object> gvesbCfg = Optional
                .of(configRepository.getConfigProperties(XMLConfig.CONFIG_PID)).orElse(new Hashtable<>());

        gvesbCfg.put(XMLConfig.CONFIG_KEY_HOME, path);
        configRepository.update(XMLConfig.CONFIG_PID, gvesbCfg);

        XMLConfig.setBaseConfigPath(path);
    }

    private Path getConfigurationPath(String name) {
        return getHistoryPath().resolve(name.concat(".zip"));
    }

    private Path getHistoryPath() {
        return Paths.get(XMLConfig.getBaseConfigPath()).getParent().resolve("history");
    }

}