com.blackducksoftware.integration.hub.detect.workflow.codelocation.BdioCodeLocationCreator.java Source code

Java tutorial

Introduction

Here is the source code for com.blackducksoftware.integration.hub.detect.workflow.codelocation.BdioCodeLocationCreator.java

Source

/**
 * hub-detect
 *
 * Copyright (C) 2019 Black Duck Software, Inc.
 * http://www.blackducksoftware.com/
 *
 * 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 com.blackducksoftware.integration.hub.detect.workflow.codelocation;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blackducksoftware.integration.hub.detect.configuration.DetectConfiguration;
import com.blackducksoftware.integration.hub.detect.configuration.DetectProperty;
import com.blackducksoftware.integration.hub.detect.configuration.PropertyAuthority;
import com.blackducksoftware.integration.hub.detect.detector.DetectorType;
import com.blackducksoftware.integration.hub.detect.util.DetectEnumUtil;
import com.blackducksoftware.integration.hub.detect.workflow.event.Event;
import com.blackducksoftware.integration.hub.detect.workflow.event.EventSystem;
import com.blackducksoftware.integration.hub.detect.workflow.file.DirectoryManager;
import com.synopsys.integration.bdio.graph.DependencyGraph;
import com.synopsys.integration.bdio.graph.DependencyGraphCombiner;
import com.synopsys.integration.bdio.graph.MutableDependencyGraph;
import com.synopsys.integration.bdio.graph.MutableMapDependencyGraph;
import com.synopsys.integration.util.IntegrationEscapeUtil;
import com.synopsys.integration.util.NameVersion;

public class BdioCodeLocationCreator {
    private final Logger logger = LoggerFactory.getLogger(BdioCodeLocationCreator.class);

    private final CodeLocationNameManager codeLocationNameManager;
    private final DetectConfiguration detectConfiguration;
    private final DirectoryManager directoryManager;
    private final EventSystem eventSystem;

    public BdioCodeLocationCreator(final CodeLocationNameManager codeLocationNameManager,
            final DetectConfiguration detectConfiguration, final DirectoryManager directoryManager,
            final EventSystem eventSystem) {
        this.codeLocationNameManager = codeLocationNameManager;
        this.detectConfiguration = detectConfiguration;
        this.directoryManager = directoryManager;
        this.eventSystem = eventSystem;
    }

    public BdioCodeLocationResult createFromDetectCodeLocations(final List<DetectCodeLocation> detectCodeLocations,
            NameVersion projectNameVersion) {
        final Set<DetectorType> failedBomToolGroups = new HashSet<>();

        final String prefix = detectConfiguration.getProperty(DetectProperty.DETECT_PROJECT_CODELOCATION_PREFIX,
                PropertyAuthority.None);
        final String suffix = detectConfiguration.getProperty(DetectProperty.DETECT_PROJECT_CODELOCATION_SUFFIX,
                PropertyAuthority.None);

        final List<DetectCodeLocation> validDetectCodeLocations = findValidCodeLocations(detectCodeLocations);
        final Map<DetectCodeLocation, String> codeLocationsAndNames = createCodeLocationNameMap(
                validDetectCodeLocations, directoryManager.getSourceDirectory().getAbsolutePath(),
                projectNameVersion, prefix, suffix);
        final Map<String, List<DetectCodeLocation>> codeLocationsByName = seperateCodeLocationsByName(
                codeLocationsAndNames);

        // We can create a DetectProperty to combine duplicate code location names in the future if users want that
        final List<BdioCodeLocation> bdioCodeLocations = createBdioCodeLocations(codeLocationsByName,
                directoryManager.getSourceDirectory(), false);

        // Sanity check that code location names are unique (they should be)
        final Map<String, List<BdioCodeLocation>> bdioByCodeLocationName = groupByCodeLocationNames(
                bdioCodeLocations);
        for (final String name : bdioByCodeLocationName.keySet()) {
            if (bdioByCodeLocationName.get(name).size() > 1) {
                logger.error("Multiple code locations were generated with the name: " + name);
                failedBomToolGroups.addAll(getDetectorTypes(bdioByCodeLocationName.get(name)));
            }
        }

        // Sanity check that bdio file names are unique (they should be)
        final Map<String, List<BdioCodeLocation>> bdioByBdioName = groupByBdioNames(bdioCodeLocations);
        for (final String name : bdioByBdioName.keySet()) {
            if (bdioByBdioName.get(name).size() > 1) {
                logger.error("Multiple bdio names were generated with the name: " + name);
                failedBomToolGroups.addAll(getDetectorTypes(bdioByBdioName.get(name)));
            }
        }

        final BdioCodeLocationResult result = new BdioCodeLocationResult(bdioCodeLocations, failedBomToolGroups,
                codeLocationsAndNames);
        eventSystem.publishEvent(Event.CodeLocationsCalculated, result);
        return result;
    }

    /**
     * Attempts to map the given Code Location Types to Detector Types with EnumUtils.
     * @param bdioCodeLocations the final bdio code locations
     * @return the set of detector types
     */
    private Set<DetectorType> getDetectorTypes(final List<BdioCodeLocation> bdioCodeLocations) {
        return bdioCodeLocations.stream()
                .map(bdioCodeLocation -> bdioCodeLocation.codeLocation.getCodeLocationType().toString())
                .map(codeLocationType -> DetectEnumUtil.getValueOf(DetectorType.class, codeLocationType))
                .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toSet());
    }

    private Map<String, List<BdioCodeLocation>> groupByBdioNames(final List<BdioCodeLocation> bdioCodeLocations) {
        return bdioCodeLocations.stream().collect(Collectors.groupingBy(it -> it.bdioName, Collectors.toList()));
    }

    private Map<String, List<BdioCodeLocation>> groupByCodeLocationNames(
            final List<BdioCodeLocation> bdioCodeLocations) {
        return bdioCodeLocations.stream()
                .collect(Collectors.groupingBy(it -> it.codeLocationName, Collectors.toList()));
    }

    private Map<DetectCodeLocation, String> createCodeLocationNameMap(final List<DetectCodeLocation> codeLocations,
            final String detectSourcePath, NameVersion projectNameVersion, final String prefix,
            final String suffix) {
        final Map<DetectCodeLocation, String> nameMap = new HashMap<>();
        for (final DetectCodeLocation detectCodeLocation : codeLocations) {
            final String codeLocationName = codeLocationNameManager.createCodeLocationName(detectCodeLocation,
                    detectSourcePath, projectNameVersion.getName(), projectNameVersion.getVersion(), prefix,
                    suffix);
            nameMap.put(detectCodeLocation, codeLocationName);
        }
        return nameMap;
    }

    private List<DetectCodeLocation> findValidCodeLocations(final List<DetectCodeLocation> detectCodeLocations) {
        final List<DetectCodeLocation> validCodeLocations = new ArrayList<>();
        for (final DetectCodeLocation detectCodeLocation : detectCodeLocations) {
            if (detectCodeLocation.getDependencyGraph() == null) {
                logger.warn(String.format("Dependency graph is null for code location %s",
                        detectCodeLocation.getSourcePath()));
                continue;
            }
            if (detectCodeLocation.getDependencyGraph().getRootDependencies().size() <= 0) {
                logger.warn(String.format("Could not find any dependencies for code location %s",
                        detectCodeLocation.getSourcePath()));
            }
            validCodeLocations.add(detectCodeLocation);
        }
        return validCodeLocations;
    }

    private Map<String, List<DetectCodeLocation>> seperateCodeLocationsByName(
            final Map<DetectCodeLocation, String> detectCodeLocationNameMap) {
        final Map<String, List<DetectCodeLocation>> codeLocationNameMap = new HashMap<>();
        for (final DetectCodeLocation detectCodeLocation : detectCodeLocationNameMap.keySet()) {
            final String codeLocationName = detectCodeLocationNameMap.get(detectCodeLocation);
            if (!codeLocationNameMap.containsKey(codeLocationName)) {
                codeLocationNameMap.put(codeLocationName, new ArrayList<DetectCodeLocation>());
            }
            codeLocationNameMap.get(codeLocationName).add(detectCodeLocation);
        }
        return codeLocationNameMap;
    }

    private List<BdioCodeLocation> createBdioCodeLocations(
            final Map<String, List<DetectCodeLocation>> codeLocationsByName, final File sourcePath,
            final boolean combineCodeLocations) {
        final List<BdioCodeLocation> bdioCodeLocations = new ArrayList<>();

        for (final String codeLocationName : codeLocationsByName.keySet()) {
            final List<DetectCodeLocation> codeLocations = codeLocationsByName.get(codeLocationName);
            final List<BdioCodeLocation> transformedBdioCodeLocations = transformDetectCodeLocationsIntoBdioCodeLocations(
                    codeLocations, codeLocationName, combineCodeLocations);
            bdioCodeLocations.addAll(transformedBdioCodeLocations);
        }

        return bdioCodeLocations;
    }

    private List<BdioCodeLocation> transformDetectCodeLocationsIntoBdioCodeLocations(
            final List<DetectCodeLocation> codeLocations, final String codeLocationName,
            final boolean combineCodeLocations) {
        final List<BdioCodeLocation> bdioCodeLocations;
        final IntegrationEscapeUtil integrationEscapeUtil = new IntegrationEscapeUtil();

        if (codeLocations.size() > 1) {
            // we must either combine or create a unique name.
            if (combineCodeLocations) {
                final DependencyGraphCombiner combiner = new DependencyGraphCombiner();
                logger.info("Combining duplicate code locations with name: " + codeLocationName);
                final MutableDependencyGraph combinedGraph = new MutableMapDependencyGraph();
                final DetectCodeLocation finalCodeLocation = copyCodeLocation(codeLocations.get(0), combinedGraph);
                codeLocations.stream()
                        .filter(codeLocation -> shouldCombine(logger, finalCodeLocation, codeLocation))
                        .forEach(codeLocation -> combiner.addGraphAsChildrenToRoot(combinedGraph,
                                codeLocation.getDependencyGraph()));

                final BdioCodeLocation bdioCodeLocation = new BdioCodeLocation(finalCodeLocation, codeLocationName,
                        createBdioName(codeLocationName, integrationEscapeUtil));
                bdioCodeLocations = Arrays.asList(bdioCodeLocation);
            } else {
                bdioCodeLocations = new ArrayList<>();
                for (int i = 0; i < codeLocations.size(); i++) {
                    final DetectCodeLocation codeLocation = codeLocations.get(i);
                    final String newCodeLocationName = String.format("%s %s", codeLocationName,
                            Integer.toString(i));
                    final BdioCodeLocation bdioCodeLocation = new BdioCodeLocation(codeLocation,
                            newCodeLocationName, createBdioName(newCodeLocationName, integrationEscapeUtil));
                    bdioCodeLocations.add(bdioCodeLocation);
                }
            }
        } else if (codeLocations.size() == 1) {
            final DetectCodeLocation codeLocation = codeLocations.get(0);
            final BdioCodeLocation bdioCodeLocation = new BdioCodeLocation(codeLocation, codeLocationName,
                    createBdioName(codeLocationName, integrationEscapeUtil));
            bdioCodeLocations = Arrays.asList(bdioCodeLocation);
        } else {
            logger.error("Created a code location name but no code locations.");
            bdioCodeLocations = new ArrayList<>();
        }

        return bdioCodeLocations;
    }

    private String createBdioName(final String codeLocationName,
            final IntegrationEscapeUtil integrationEscapeUtil) {
        final String filenameRaw = StringUtils.replaceEach(codeLocationName, new String[] { "/", "\\", " " },
                new String[] { "_", "_", "_" });
        final String filename = integrationEscapeUtil.escapeForUri(filenameRaw);
        return filename + ".jsonld";
    }

    private DetectCodeLocation copyCodeLocation(final DetectCodeLocation codeLocation,
            final DependencyGraph newGraph) {
        final DetectCodeLocation.Builder builder = new DetectCodeLocation.Builder(
                codeLocation.getCodeLocationType(), codeLocation.getSourcePath(), codeLocation.getExternalId(),
                newGraph);
        builder.dockerImage(codeLocation.getDockerImage());
        final DetectCodeLocation copy = builder.build();
        return copy;
    }

    private boolean shouldCombine(final Logger logger, final DetectCodeLocation codeLocationLeft,
            final DetectCodeLocation codeLocationRight) {
        if (codeLocationLeft.getCodeLocationType() != codeLocationRight.getCodeLocationType()) {
            logger.error("Cannot combine code locations with different types.");
            return false;
        }

        if (codeLocationLeft.getDockerImage() != codeLocationRight.getDockerImage()) {
            logger.error("Cannot combine code locations with different docker images.");
            return false;
        }

        if (codeLocationLeft.getSourcePath() != codeLocationRight.getSourcePath()) {
            logger.error("Cannot combine code locations with different source paths.");
            return false;
        }

        if (codeLocationLeft.getExternalId() != codeLocationRight.getExternalId()) {
            logger.error("Cannot combine code locations with different external ids.");
            return false;
        }

        return true;
    }

}