com.thoughtworks.go.plugin.infra.commons.PluginsZip.java Source code

Java tutorial

Introduction

Here is the source code for com.thoughtworks.go.plugin.infra.commons.PluginsZip.java

Source

/*
 * Copyright 2019 ThoughtWorks, Inc.
 *
 * Licensed 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 com.thoughtworks.go.plugin.infra.commons;

import com.thoughtworks.go.plugin.infra.PluginChangeListener;
import com.thoughtworks.go.plugin.infra.PluginManager;
import com.thoughtworks.go.plugin.infra.plugininfo.GoPluginDescriptor;
import com.thoughtworks.go.util.SystemEnvironment;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
public class PluginsZip implements PluginChangeListener {
    private static final Logger LOG = LoggerFactory.getLogger(PluginsZip.class);
    private static final Function<GoPluginDescriptor, String> PLUGIN_ID_COMPARATOR = GoPluginDescriptor::id;
    private static final Function<GoPluginDescriptor, Boolean> IS_BUNDLED_PLUGIN_COMPARATOR = GoPluginDescriptor::isBundledPlugin;
    private final Comparator<GoPluginDescriptor> PLUGIN_COMPARATOR = Comparator
            .comparing(IS_BUNDLED_PLUGIN_COMPARATOR).thenComparing(PLUGIN_ID_COMPARATOR);

    private final Predicate<GoPluginDescriptor> predicate;
    private String md5DigestOfPlugins;
    private List<GoPluginDescriptor> agentPlugins = new CopyOnWriteArrayList<>();
    private final File destZipFile;
    private final File bundledPlugins;
    private final File externalPlugins;
    private final PluginManager pluginManager;

    @Autowired
    public PluginsZip(SystemEnvironment systemEnvironment, PluginManager pluginManager) {
        destZipFile = new File(systemEnvironment.get(SystemEnvironment.ALL_PLUGINS_ZIP_PATH));
        bundledPlugins = new File(systemEnvironment.get(SystemEnvironment.PLUGIN_GO_PROVIDED_PATH));
        externalPlugins = new File(systemEnvironment.get(SystemEnvironment.PLUGIN_EXTERNAL_PROVIDED_PATH));
        this.pluginManager = pluginManager;
        this.pluginManager.addPluginChangeListener(this);
        predicate = goPluginDescriptor -> PluginsZip.this.pluginManager.isPluginOfType("task",
                goPluginDescriptor.id())
                || PluginsZip.this.pluginManager.isPluginOfType("scm", goPluginDescriptor.id())
                || PluginsZip.this.pluginManager.isPluginOfType("package-repository", goPluginDescriptor.id())
                || PluginsZip.this.pluginManager.isPluginOfType("artifact", goPluginDescriptor.id());
    }

    public void create() {
        checkFilesAccessibility(bundledPlugins, externalPlugins);
        reset();

        MessageDigest md5Digest = DigestUtils.getMd5Digest();
        try (ZipOutputStream zos = new ZipOutputStream(
                new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(destZipFile)), md5Digest))) {
            for (GoPluginDescriptor agentPlugins : agentPlugins()) {
                String zipEntryPrefix = "external/";

                if (agentPlugins.isBundledPlugin()) {
                    zipEntryPrefix = "bundled/";
                }

                zos.putNextEntry(
                        new ZipEntry(zipEntryPrefix + new File(agentPlugins.pluginFileLocation()).getName()));
                Files.copy(new File(agentPlugins.pluginFileLocation()).toPath(), zos);
                zos.closeEntry();
            }
        } catch (Exception e) {
            LOG.error("Could not create zip of plugins for agent to download.", e);
        }

        md5DigestOfPlugins = Hex.encodeHexString(md5Digest.digest());
    }

    private void reset() {
        md5DigestOfPlugins = null;
        agentPlugins.clear();
    }

    public String md5() {
        return md5DigestOfPlugins;
    }

    private List<GoPluginDescriptor> agentPlugins() {
        if (agentPlugins.isEmpty()) {
            List<GoPluginDescriptor> agentPlugins = pluginManager.plugins().stream().filter(predicate)
                    .sorted(PLUGIN_COMPARATOR).collect(Collectors.toList());
            this.agentPlugins.addAll(agentPlugins);
        }

        return agentPlugins;
    }

    private void checkFilesAccessibility(File bundledPlugins, File externalPlugins) {
        boolean bundled = bundledPlugins.canRead();
        boolean external = externalPlugins.canRead();
        if (!bundled || !external) {
            String folder = bundled ? externalPlugins.getAbsolutePath() : bundledPlugins.getAbsolutePath();
            LOG.error("Could not read plugins. Please check access rights on files in folder: {}.", folder);
            throw new FileAccessRightsCheckException(String.format(
                    "Could not read plugins. Please make sure that the user running GoCD can access %s", folder));
        }
    }

    @Override
    public void pluginLoaded(GoPluginDescriptor pluginDescriptor) {
        maybeUpdatePluginsZip(pluginDescriptor);
    }

    @Override
    public void pluginUnLoaded(GoPluginDescriptor pluginDescriptor) {
        if (agentPlugins().contains(pluginDescriptor)) {
            create();
        }
    }

    private void maybeUpdatePluginsZip(GoPluginDescriptor pluginDescriptor) {
        if (predicate.test(pluginDescriptor)) {
            create();
        }
    }
}