All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.mojo.versions.AbstractDependencyUpdatesReport Maven / Gradle / Ivy

Go to download

Versions Plugin for Maven. The Versions Plugin updates the versions of components in the POM.

The newest version!
package org.codehaus.mojo.versions;

/*
 * Copyright MojoHaus and Contributors
 *
 * 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.
 *
 */

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReportException;
import org.apache.maven.wagon.Wagon;
import org.codehaus.mojo.versions.api.ArtifactVersions;
import org.codehaus.mojo.versions.api.VersionRetrievalException;
import org.codehaus.mojo.versions.reporting.ReportRendererFactory;
import org.codehaus.mojo.versions.reporting.model.DependencyUpdatesModel;
import org.codehaus.mojo.versions.utils.DependencyComparator;
import org.codehaus.mojo.versions.xml.DependencyUpdatesXmlReportRenderer;
import org.codehaus.plexus.i18n.I18N;
import org.eclipse.aether.RepositorySystem;

import static java.util.Collections.emptyMap;
import static java.util.Optional.ofNullable;
import static org.codehaus.mojo.versions.utils.MavenProjectUtils.interpolateVersion;
import static org.codehaus.mojo.versions.utils.MiscUtils.filter;

/**
 * Generates a report of available updates for the dependencies of a project.
 */
public abstract class AbstractDependencyUpdatesReport extends AbstractVersionsReport {

    private static final DependencyComparator DEPENDENCY_COMPARATOR = DependencyComparator.INSTANCE;

    /**
     * Whether to process the dependencyManagement in pom or not.
     *
     * @since 2.5
     */
    @Parameter(property = "processDependencyManagement", defaultValue = "true")
    protected boolean processDependencyManagement;

    /**
     * Whether to process the dependencyManagement part transitive or not.
     * In case of <type>pom</type>and
     * <scope>import</scope> this means
     * by default to report also the imported dependencies.
     * If processTransitive is set to false the report will only show
     * updates of the imported pom itself.
     *
     * @since 2.5 Note: Currently in experimental state.
     */
    @Parameter(property = "processDependencyManagementTransitive", defaultValue = "true")
    protected boolean processDependencyManagementTransitive;

    /**
     * Report formats (html and/or xml). HTML by default.
     */
    @Parameter(property = "dependencyUpdatesReportFormats", defaultValue = "html")
    protected String[] formats = new String[] {"html"};

    /**
     * If true, only shows the subsection of the dependencyManagement artifacts that
     * are actually used in the project's dependency graph. false by default.
     *
     * @since 2.12
     */
    @Parameter(property = "onlyProjectDependencies", defaultValue = "false")
    protected boolean onlyProjectDependencies;

    /**
     * If true, only shows upgradable dependencies in the report. false by default.
     *
     * @since 2.12
     */
    @Parameter(property = "onlyUpgradable", defaultValue = "false")
    protected boolean onlyUpgradable;

    protected AbstractDependencyUpdatesReport(
            I18N i18n,
            ArtifactHandlerManager artifactHandlerManager,
            RepositorySystem repositorySystem,
            Map wagonMap,
            ReportRendererFactory rendererFactory) {
        super(i18n, artifactHandlerManager, repositorySystem, wagonMap, rendererFactory);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isExternalReport() {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean canGenerateReport() {
        return true;
    }

    /**
     * generates an empty report in case there are no sources to generate a report with
     *
     * @param locale the locale to generate the report for.
     * @param sink   the report formatting tool
     */
    @Override
    protected void doGenerateReport(Locale locale, Sink sink) throws MavenReportException {

        Set dependencies = getDependencies();

        Set dependencyManagement;

        if (processDependencyManagement) {
            dependencyManagement = getDependencyManagement(dependencies);
            handleOnlyProjectDependencies(dependencyManagement, dependencies);
        } else {
            dependencyManagement = Collections.emptySet();
        }

        try {

            Map dependencyUpdates = getHelper()
                    .lookupDependenciesUpdates(
                            dependencies.stream().filter(d -> d.getVersion() != null), false, allowSnapshots);

            Map dependencyManagementUpdates = processDependencyManagement
                    ? getHelper()
                            .lookupDependenciesUpdates(
                                    dependencyManagement.stream().filter(d -> d.getVersion() != null),
                                    false,
                                    allowSnapshots)
                    : emptyMap();

            if (onlyUpgradable) {
                dependencyUpdates = filter(dependencyUpdates, e -> !e.isEmpty(allowSnapshots));
                dependencyManagementUpdates = filter(dependencyManagementUpdates, e -> !e.isEmpty(allowSnapshots));
            }

            if (getLog().isDebugEnabled()) {
                getLog().debug("Dependency versions:");
                dependencyUpdates.forEach((key, value) -> getLog().debug(key.toString() + ": "
                        + Arrays.stream(value.getVersions(true /* already filtered */))
                                .map(ArtifactVersion::toString)
                                .collect(Collectors.joining(", "))));

                getLog().debug("Dependency management versions:");
                dependencyManagementUpdates.forEach((key, value) -> getLog().debug(key.toString() + ": "
                        + Arrays.stream(value.getVersions(true /* already filtered */))
                                .map(ArtifactVersion::toString)
                                .collect(Collectors.joining(", "))));
            }

            DependencyUpdatesModel model = new DependencyUpdatesModel(dependencyUpdates, dependencyManagementUpdates);

            renderReport(locale, sink, model);
        } catch (VersionRetrievalException e) {
            throw new RuntimeException(e);
        }
    }

    protected void handleDependencyManagementTransitive(
            MavenProject project, Set dependencyManagementCollector) {
        if (processDependencyManagementTransitive) {
            if (hasDependencyManagement(project)) {
                if (getLog().isDebugEnabled()) {
                    project.getDependencyManagement().getDependencies().forEach(dep -> getLog().debug(
                                    "Dpmg: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion()
                                            + ":" + dep.getType() + ":" + dep.getScope()));
                }
                dependencyManagementCollector.addAll(
                        project.getDependencyManagement().getDependencies());
            }
        } else {
            // Using the original model to getModel the original dependencyManagement entries and
            // not the interpolated model.
            // TODO: I'm not 100% sure if this will work correctly in all cases.
            ofNullable(project.getOriginalModel().getDependencyManagement())
                    .map(DependencyManagement::getDependencies)
                    .ifPresent(list -> list.stream()
                            .map(dep -> interpolateVersion(dep, project))
                            .peek(dep -> {
                                if (getLog().isDebugEnabled()) {
                                    getLog().debug("Original Dpmg: " + dep.getGroupId() + ":" + dep.getArtifactId()
                                            + ":" + dep.getVersion() + ":" + dep.getType() + ":" + dep.getScope());
                                }
                            })
                            .forEach(dependencyManagementCollector::add));
        }
    }

    private void handleOnlyProjectDependencies(Set dependencyManagement, Set dependencies) {
        if (!onlyProjectDependencies) {
            // Retains only dependencies not present in dependencyManagement
            dependencies.removeIf(dep -> dependencyManagement.stream().anyMatch(dmDep -> match(dep, dmDep)));
        } else {
            // Retain only dependencies in dependencyManagement that are also present in dependencies
            dependencyManagement.removeIf(dep -> dependencies.stream().noneMatch(dmDep -> match(dep, dmDep)));
        }
    }

    /**
     * Constructs a final instance of a {@link Set} with a {@link DependencyComparator} comparator.
     * This set can be further populated by implementations, and should contain dependencies, that are present
     * in projects direct dependencies section.
     *
     * @return a {@link Set} that can be additionally populated by {@link #populateDependencies(Set)}.
     * If not, an empty set is returned
     * */
    private Set getDependencies() {
        final Set dependenciesCollector = new TreeSet<>(DEPENDENCY_COMPARATOR);
        populateDependencies(dependenciesCollector);
        return dependenciesCollector;
    }

    /**
     * Implementations of {@link AbstractDependencyUpdatesReport} may use this to supply the main processing logic
     * (see {@link #getDependencyManagement(Set)}) with desired dependency data, which will be used
     * in the creation of the report.
     *
     * @param dependenciesCollector, a Set, initialized with a DependencyComparator
     * comparator.
     * */
    protected abstract void populateDependencies(Set dependenciesCollector);

    /**
     * Constructs a final instance of a {@link Set} with a {@link DependencyComparator} comparator.
     * This set can be further populated by implementations, and should contain dependencies, that are present
     * in projects dependencyManagement section.
     *
     * @return a {@link Set} that can be additionally populated by
     * {@link #populateDependencyManagement(Set, Set)}. If not, an empty set is returned
     * */
    private Set getDependencyManagement(Set dependencies) throws MavenReportException {
        final Set dependencyManagementCollector = new TreeSet<>(DEPENDENCY_COMPARATOR);
        populateDependencyManagement(dependencyManagementCollector, dependencies);
        return dependencyManagementCollector;
    }

    /**
     * Implementations of {@link AbstractDependencyUpdatesReport} may use this to supply the main processing logic
     * (see {@link #getDependencyManagement(Set)}) with desired managed dependencies data, which will be used
     * in the creation of the report.
     *
     * @param dependencyManagementCollector, a Set initialized with a DependencyComparator
     * @param dependencies an already populated set of dependencies(non-managed)
     * comparator.
     *
     * @throws MavenReportException when things go wrong.
     * */
    protected abstract void populateDependencyManagement(
            Set dependencyManagementCollector, Set dependencies) throws MavenReportException;

    private void renderReport(Locale locale, Sink sink, DependencyUpdatesModel model) throws MavenReportException {
        for (String format : formats) {
            if ("html".equals(format)) {
                rendererFactory
                        .createReportRenderer(getOutputPath(), sink, locale, model, allowSnapshots)
                        .render();
            } else if ("xml".equals(format)) {
                Path outputDir = Paths.get(getProject().getBuild().getDirectory());
                if (!Files.exists(outputDir)) {
                    try {
                        Files.createDirectories(outputDir);
                    } catch (IOException e) {
                        throw new MavenReportException("Could not create the output directory");
                    }
                }
                Path outputFile = outputDir.resolve(getOutputPath() + ".xml");
                new DependencyUpdatesXmlReportRenderer(model, outputFile, allowSnapshots).render();
            }
        }
    }

    /**
     * Compares two dependencies with each other
     *
     * @return true if the two dependencies match
     */
    private boolean match(Dependency dep, Dependency dmDep) {
        return dmDep.getGroupId().equals(dep.getGroupId())
                && dmDep.getArtifactId().equals(dep.getArtifactId())
                && (dmDep.getScope() == null || dmDep.getScope().equals(dep.getScope()))
                && (dmDep.getClassifier() == null || dmDep.getClassifier().equals(dep.getClassifier()))
                && (dep.getVersion() == null
                        || dmDep.getVersion() == null
                        || dmDep.getVersion().equals(dep.getVersion()));
    }

    protected boolean hasDependencyManagement(MavenProject project) {
        if (project == null) {
            return false;
        }
        return project.getDependencyManagement() != null
                && project.getDependencyManagement().getDependencies() != null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy