org.jfrog.build.extractor.maven.BuildDeploymentHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jfrog.build.extractor.maven.BuildDeploymentHelper.java

Source

/*
 * Copyright (C) 2011 JFrog Ltd.
 *
 * 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 org.jfrog.build.extractor.maven;

import com.google.common.collect.Sets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.jfrog.build.api.Artifact;
import org.jfrog.build.api.Build;
import org.jfrog.build.api.BuildInfoConfigProperties;
import org.jfrog.build.api.Module;
import org.jfrog.build.api.util.FileChecksumCalculator;
import org.jfrog.build.client.*;
import org.jfrog.build.extractor.BuildInfoExtractorUtils;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author Noam Y. Tenne
 */
@Component(role = BuildDeploymentHelper.class)
public class BuildDeploymentHelper {

    @Requirement
    private Logger logger;

    @Requirement
    private BuildInfoClientBuilder buildInfoClientBuilder;

    private final JsonMergeHelper buildInfoMergeHelper = new JsonMergeHelper("id", "name");
    private final JsonMergeHelper deployablesMergeHelper = new JsonMergeHelper("artifactPath");

    public void deploy(Build build, ArtifactoryClientConfiguration clientConf,
            Map<String, DeployDetails> deployableArtifactBuilders, boolean wereThereTestFailures, File basedir) {

        Set<DeployDetails> deployableArtifacts = prepareDeployableArtifacts(build, deployableArtifactBuilders);

        logger.debug("Build Info Recorder: " + clientConf.publisher.isPublishBuildInfo());

        File aggregateDirectory;
        File buildInfoAggregated = null;
        File buildInfoFile = null;
        if (clientConf.publisher.isPublishBuildInfo() || clientConf.publisher.getAggregateArtifacts() != null) {
            buildInfoFile = saveBuildInfoToFile(build, clientConf, basedir);
        }
        if (clientConf.publisher.getAggregateArtifacts() != null) {
            aggregateDirectory = new File(clientConf.publisher.getAggregateArtifacts());
            buildInfoAggregated = new File(aggregateDirectory, "build-info.json");
            boolean isCopyAggregatedArtifacts = clientConf.publisher.isCopyAggregatedArtifacts();
            boolean isPublishAggregatedArtifacts = clientConf.publisher.isPublishAggregatedArtifacts();
            deployableArtifacts = aggregateArtifacts(aggregateDirectory, buildInfoFile, buildInfoAggregated,
                    deployableArtifacts, isCopyAggregatedArtifacts, isPublishAggregatedArtifacts);

            if (!isPublishAggregatedArtifacts) {
                return;
            }
        }

        if (clientConf.publisher.isPublishBuildInfo() || clientConf.publisher.isPublishArtifacts()) {
            ArtifactoryBuildInfoClient client = buildInfoClientBuilder.resolveProperties(clientConf);
            boolean isDeployArtifacts = clientConf.publisher.isPublishArtifacts() && (deployableArtifacts != null)
                    && (!deployableArtifacts.isEmpty())
                    && (clientConf.publisher.isEvenUnstable() || (!wereThereTestFailures));
            boolean isSendBuildInfo = clientConf.publisher.isPublishBuildInfo()
                    && (clientConf.publisher.isEvenUnstable() || (!wereThereTestFailures));

            try {
                if (isDeployArtifacts) {
                    deployArtifacts(clientConf.publisher, deployableArtifacts, client);
                }

                if (isSendBuildInfo) {
                    logger.info("Artifactory Build Info Recorder: Deploying build info ...");
                    try {
                        if (buildInfoAggregated != null) {
                            String buildInfoJson = client.buildInfoToJsonString(build);
                            String buildInfoAggregatedJson = FileUtils.readFileToString(buildInfoAggregated,
                                    "UTF-8");
                            String buildInfoMerged = buildInfoMergeHelper.mergeJsons(buildInfoAggregatedJson,
                                    buildInfoJson);
                            client.sendBuildInfo(buildInfoMerged);
                        } else {
                            client.sendBuildInfo(build);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException("Error occurred while publishing Build Info to Artifactory.", e);
                    }
                }
            } finally {
                client.shutdown();
            }
        }
    }

    private File saveBuildInfoToFile(Build build, ArtifactoryClientConfiguration clientConf, File basedir) {
        String outputFile = clientConf.getExportFile();
        File buildInfoFile = StringUtils.isBlank(outputFile) ? new File(basedir, "target/build-info.json")
                : new File(outputFile);

        logger.debug("Build Info Recorder: " + BuildInfoConfigProperties.EXPORT_FILE + " = " + outputFile);
        logger.info("Artifactory Build Info Recorder: Saving Build Info to '" + buildInfoFile + "'");

        try {
            BuildInfoExtractorUtils.saveBuildInfoToFile(build, buildInfoFile.getCanonicalFile());
        } catch (IOException e) {
            throw new RuntimeException("Error occurred while persisting Build Info to '" + buildInfoFile + "'", e);
        }
        return buildInfoFile;
    }

    @SuppressWarnings({ "TypeMayBeWeakened", "SuppressionAnnotation" })
    private Set<DeployDetails> aggregateArtifacts(File aggregateDirectory, File buildInfoSource,
            File buildInfoDestination, Set<DeployDetails> deployables, boolean isCopyAggregatedArtifacts,
            boolean isPublishAggregatedArtifacts) {
        try {
            File deployablesDestination = new File(aggregateDirectory, "deployables.json");
            List<Map<String, ?>> mergedDeployables = null;

            if (buildInfoDestination.isFile()) {
                Map<String, Object> buildInfoSourceMap = buildInfoMergeHelper.jsonToObject(buildInfoSource,
                        Map.class);
                Map<String, Object> buildInfoDestinationMap = buildInfoMergeHelper
                        .jsonToObject(buildInfoDestination, Map.class);
                int durationMillis = (Integer) buildInfoSourceMap.get("durationMillis")
                        + (Integer) buildInfoDestinationMap.get("durationMillis");
                buildInfoSourceMap.put("started", buildInfoDestinationMap.get("started"));
                buildInfoSourceMap.put("durationMillis", durationMillis);
                buildInfoMergeHelper.mergeAndWrite(buildInfoSourceMap, buildInfoDestinationMap,
                        buildInfoDestination);
            } else {
                FileUtils.copyFile(buildInfoSource, buildInfoDestination);
            }

            if (deployablesDestination.isFile()) {
                List<Map<String, ?>> currentDeployables = deployablesMergeHelper
                        .jsonToObject(deployablesMergeHelper.objectToJson(deployables), List.class);
                List<Map<String, ?>> previousDeployables = deployablesMergeHelper
                        .jsonToObject(deployablesDestination, List.class);
                mergedDeployables = deployablesMergeHelper.mergeAndWrite(currentDeployables, previousDeployables,
                        deployablesDestination);
            } else {
                FileUtils.write(deployablesDestination, deployablesMergeHelper.objectToJson(deployables), "UTF-8");
            }

            if (isCopyAggregatedArtifacts) {
                for (DeployDetails details : deployables) {
                    /**
                     * We could check MD5 checksum of destination file (if it exists) and save on copy operation but since most *.jar
                     * files contain a timestamp in pom.properties (thanks, Maven) - checksum would only match for POM files.
                     */
                    File aggregatedFile = aggregatedFile(aggregateDirectory, details.getFile());
                    FileUtils.copyFile(details.getFile(), aggregatedFile);
                }
            }

            return (isPublishAggregatedArtifacts && (mergedDeployables != null))
                    ? convertDeployables(aggregateDirectory, mergedDeployables, isCopyAggregatedArtifacts)
                    : deployables;
        } catch (IOException e) {
            throw new RuntimeException(
                    "Failed to aggregate artifacts and Build Info in [" + aggregateDirectory + "]", e);
        }
    }

    @SuppressWarnings({ "FeatureEnvy", "SuppressionAnnotation" })
    private Set<DeployDetails> convertDeployables(File aggregateDirectory, Iterable<Map<String, ?>> deployables,
            boolean isCopyAggregatedArtifacts) throws IOException {
        Set<DeployDetails> result = new HashSet<DeployDetails>();

        for (Map<String, ?> map : deployables) {

            File file = new File((String) map.get("file"));
            if (isCopyAggregatedArtifacts) {
                file = aggregatedFile(aggregateDirectory, file);
            }

            DeployDetails.Builder builder = new DeployDetails.Builder()
                    .targetRepository((String) map.get("targetRepository"))
                    .artifactPath((String) map.get("artifactPath")).file(file).sha1((String) map.get("sha1"))
                    .md5((String) map.get("md5")).addProperties((Map<String, String>) map.get("properties"));
            result.add(builder.build());
        }

        return result;
    }

    private File aggregatedFile(File aggregateDirectory, File file) throws IOException {
        String workspacePath = aggregateDirectory.getParentFile().getCanonicalPath().replace('\\', '/');
        String artifactPath = file.getCanonicalPath().replace('\\', '/');
        String artifactRelativePath = artifactPath.startsWith(workspacePath) ?
        /**
        * "/Users/evgenyg/.hudson/jobs/teamcity-artifactory-plugin/workspace/agent/target/teamcity-artifactory-plugin-agent-2.1.x-SNAPSHOT.jar" =>
        * "agent/target/teamcity-artifactory-plugin-agent-2.1.x-SNAPSHOT.jar"
        */
                artifactPath.substring(workspacePath.length() + 1) :
                /**
                * Artifact is outside workspace, wonder if it works on Windows
                */
                artifactPath;

        return new File(aggregateDirectory, artifactRelativePath);
    }

    private Set<DeployDetails> prepareDeployableArtifacts(Build build,
            Map<String, DeployDetails> deployableArtifactBuilders) {
        Set<DeployDetails> deployableArtifacts = Sets.newLinkedHashSet();
        List<Module> modules = build.getModules();
        for (Module module : modules) {
            List<Artifact> artifacts = module.getArtifacts();
            if (artifacts != null) {
                for (Artifact artifact : artifacts) {
                    String artifactId = BuildInfoExtractorUtils.getArtifactId(module.getId(), artifact.getName());
                    DeployDetails deployable = deployableArtifactBuilders.get(artifactId);
                    if (deployable != null) {
                        File file = deployable.getFile();
                        setArtifactChecksums(file, artifact);
                        deployableArtifacts.add(new DeployDetails.Builder()
                                .artifactPath(deployable.getArtifactPath()).file(file).md5(artifact.getMd5())
                                .sha1(artifact.getSha1()).addProperties(deployable.getProperties())
                                .targetRepository(deployable.getTargetRepository()).build());
                    }
                }
            }
        }
        return deployableArtifacts;
    }

    private void deployArtifacts(ArtifactoryClientConfiguration.PublisherHandler publishConf,
            Set<DeployDetails> deployableArtifacts, ArtifactoryBuildInfoClient client) {
        IncludeExcludePatterns includeExcludePatterns = getArtifactDeploymentPatterns(publishConf);
        for (DeployDetails artifact : deployableArtifacts) {
            String artifactPath = artifact.getArtifactPath();
            if (PatternMatcher.pathConflicts(artifactPath, includeExcludePatterns)) {
                logger.info("Artifactory Build Info Recorder: Skipping the deployment of '" + artifactPath
                        + "' due to the defined include-exclude patterns.");
                continue;
            }

            try {
                client.deployArtifact(artifact);
            } catch (IOException e) {
                throw new RuntimeException(
                        "Error occurred while publishing artifact to Artifactory: " + artifact.getFile()
                                + ".\n Skipping deployment of remaining artifacts (if any) and build info.",
                        e);
            }
        }
    }

    private void setArtifactChecksums(File artifactFile, org.jfrog.build.api.Artifact artifact) {
        if ((artifactFile != null) && (artifactFile.isFile())) {
            try {
                Map<String, String> checksums = FileChecksumCalculator.calculateChecksums(artifactFile, "md5",
                        "sha1");
                artifact.setMd5(checksums.get("md5"));
                artifact.setSha1(checksums.get("sha1"));
            } catch (Exception e) {
                logger.error("Could not set checksum values on '" + artifact.getName() + "': " + e.getMessage(), e);
            }
        }
    }

    private IncludeExcludePatterns getArtifactDeploymentPatterns(
            ArtifactoryClientConfiguration.PublisherHandler publishConf) {
        return new IncludeExcludePatterns(publishConf.getIncludePatterns(), publishConf.getExcludePatterns());
    }
}