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

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

package org.codehaus.mojo.versions;

/*
 * 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.
 */

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.stream.XMLStreamException;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.codehaus.mojo.versions.api.ArtifactAssociation;
import org.codehaus.mojo.versions.api.PomHelper;
import org.codehaus.mojo.versions.api.PropertyVersions;
import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader;

/**
 * Compare dependency versions of the current project to dependencies or dependency management of a remote repository
 * project. Can optionally update locally the project instead of reporting the comparison
 *
 * @author Paul Gier
 * @since 1.3
 */
@Mojo( name = "compare-dependencies", requiresProject = true, requiresDirectInvocation = true )
public class CompareDependenciesMojo
    extends AbstractVersionsDependencyUpdaterMojo
{

    /**
     * The width to pad info messages.
     *
     * @since 1.0-alpha-1
     */
    private static final int INFO_PAD_SIZE = 68;

    /**
     * The groupId, artifactId, and version of the remote project (POM) to which we are comparing. This should be in the
     * form "groupId:artifactId:version"
     */
    @Parameter( property = "remotePom", required = true )
    protected String remotePom;

    /**
     * Ignore the list of remote dependencies and only compare the remote dependencyManagement
     */
    @Parameter( property = "ignoreRemoteDependencies", defaultValue = "false" )
    protected boolean ignoreRemoteDependencies;

    /**
     * Ignore the remote dependency management and only check against the actual dependencies of the remote project
     */
    @Parameter( property = "ignoreRemoteDependencyManagement", defaultValue = "false" )
    protected boolean ignoreRemoteDependencyManagement;

    /**
     * Update dependency versions in the current POM.
     */
    @Parameter( property = "updateDependencies", defaultValue = "false" )
    protected boolean updateDependencies;

    /**
     * Update dependency versions stored in properties
     *
     * @parameter property="updatePropertyVersions" default-value="false"
     */
    @Parameter( property = "updatePropertyVersions", defaultValue = "false" )
    protected boolean updatePropertyVersions;

    /**
     * Display the dependency version differences on the command line, but do not update the versions in the current
     * pom. If updateDependencies is set to "true" this will automatically be set to false.
     */
    @Parameter( property = "reportMode", defaultValue = "true" )
    protected boolean reportMode;

    /**
     * If the output file is set, the diff report will be written to this file.
     */
    @Parameter( property = "reportOutputFile" )
    protected File reportOutputFile;

    /**
     * The project builder used to initialize the remote project.
     */
    @Component
    protected MavenProjectBuilder mavenProjectBuilder;

    // ------------------------------ METHODS --------------------------

    /**
     * @param pom the pom to update.
     * @throws org.apache.maven.plugin.MojoExecutionException Something wrong with the plugin itself
     * @throws org.apache.maven.plugin.MojoFailureException The plugin detected an error in the build
     * @throws javax.xml.stream.XMLStreamException when things go wrong with XML streaming
     * @see AbstractVersionsUpdaterMojo#update(org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader)
     */
    protected void update( ModifiedPomXMLEventReader pom )
        throws MojoExecutionException, MojoFailureException, XMLStreamException
    {
        if ( this.ignoreRemoteDependencies && this.ignoreRemoteDependencyManagement )
        {
            throw new MojoFailureException( " ignoreRemoteDependencies and ignoreRemoteDependencyManagement"
                + "are both set to true.  At least one of these needs to be false " );
        }

        if ( updateDependencies )
        {
            reportMode = false;
        }

        String[] remotePomParts = this.remotePom.split( ":" );
        if ( remotePomParts.length != 3 )
        {
            throw new MojoFailureException( " Invalid format for remotePom: " + remotePom );
        }
        String rGroupId = remotePomParts[0];
        String rArtifactId = remotePomParts[1];
        String rVersion = remotePomParts[2];

        Dependency remoteDependency = new Dependency();
        remoteDependency.setGroupId( rGroupId );
        remoteDependency.setArtifactId( rArtifactId );
        remoteDependency.setVersion( rVersion );

        Artifact remoteArtifact = this.toArtifact( remoteDependency );
        MavenProject remoteMavenProject = null;
        try
        {
            remoteMavenProject =
                mavenProjectBuilder.buildFromRepository( remoteArtifact, remoteArtifactRepositories, localRepository );
        }
        catch ( ProjectBuildingException e )
        {
            throw new MojoExecutionException( "Unable to build remote project " + remoteArtifact, e );
        }

        Map remoteDepsMap = new HashMap();
        if ( !ignoreRemoteDependencyManagement )
        {
            List remoteProjectDepMgmtDeps = ( remoteMavenProject.getDependencyManagement() == null ) ? null
                            : remoteMavenProject.getDependencyManagement().getDependencies();
            mapDependencies( remoteDepsMap, remoteProjectDepMgmtDeps );
        }
        if ( !ignoreRemoteDependencies )
        {
            List remoteProjectDeps = remoteMavenProject.getDependencies();
            mapDependencies( remoteDepsMap, remoteProjectDeps );
        }

        List totalDiffs = new ArrayList();
        List propertyDiffs = new ArrayList();
        if ( getProject().getDependencyManagement() != null && isProcessingDependencyManagement() )
        {
            List depManDiffs =
                compareVersions( pom, getProject().getDependencyManagement().getDependencies(), remoteDepsMap );
            totalDiffs.addAll( depManDiffs );
        }
        if ( isProcessingDependencies() )
        {
            List depDiffs = compareVersions( pom, getProject().getDependencies(), remoteDepsMap );
            totalDiffs.addAll( depDiffs );
        }
        if ( updatePropertyVersions )
        {
            Map versionProperties =
                this.getHelper().getVersionPropertiesMap( getProject(), null, null, null, true );
            List diff = updatePropertyVersions( pom, versionProperties, remoteDepsMap );
            propertyDiffs.addAll( diff );
        }

        if ( reportMode )
        {
            getLog().info( "The following differences were found:" );
            if ( totalDiffs.size() == 0 )
            {
                getLog().info( "  none" );
            }
            else
            {
                for ( String totalDiff : totalDiffs )
                {
                    getLog().info( "  " + totalDiff );
                }
            }
            getLog().info( "The following property differences were found:" );
            if ( propertyDiffs.size() == 0 )
            {
                getLog().info( "  none" );
            }
            else
            {
                for ( String propertyDiff : propertyDiffs )
                {
                    getLog().info( "  " + propertyDiff );
                }
            }
        }

        if ( reportOutputFile != null )
        {
            writeReportFile( totalDiffs, propertyDiffs );
        }

    }

    /**
     * Compare the dependency versions of the current project with the dependency versions of a remote project
     *
     * @throws XMLStreamException
     */
    private List compareVersions( ModifiedPomXMLEventReader pom, List dependencies,
                                          Map remoteDependencies )
        throws MojoExecutionException, XMLStreamException
    {
        List updates = new ArrayList();
        for ( Dependency dep : dependencies )
        {
            Artifact artifact = this.toArtifact( dep );
            if ( !isIncluded( artifact ) )
            {
                continue;
            }

            Dependency remoteDep = remoteDependencies.get( dep.getManagementKey() );
            if ( remoteDep != null )
            {
                String remoteVersion = remoteDep.getVersion();

                if ( !dep.getVersion().equals( remoteVersion ) )
                {
                    StringBuilder buf = writeDependencyDiffMessage( dep, remoteVersion );
                    updates.add( buf.toString() );
                    if ( !reportMode )
                    {
                        if ( PomHelper.setDependencyVersion( pom, dep.getGroupId(), dep.getArtifactId(),
                                                             dep.getVersion(), remoteVersion,
                                                             getProject().getModel() ) )
                        {
                            getLog().info( "Updated " + toString( dep ) + " to version " + remoteVersion );
                        }
                    }

                }

            }
        }

        return updates;

    }

    /**
     * Updates the properties holding a version if necessary.
     */
    private List updatePropertyVersions( ModifiedPomXMLEventReader pom,
                                                 Map versionProperties,
                                                 Map remoteDependencies )
        throws XMLStreamException
    {
        List result = new ArrayList();
        for ( Map.Entry entry : versionProperties.entrySet() )
        {
            Property property = entry.getKey();
            PropertyVersions version = entry.getValue();

            String candidateVersion = computeCandidateVersion( remoteDependencies, property, version );
            if ( candidateVersion != null )
            {
                String originalVersion = version.getAssociations()[0].getArtifact().getVersion(); // Yekes
                if ( !candidateVersion.equals( originalVersion ) ) // Update needed
                {
                    result.add( writeDiffMessage( property.getName(), originalVersion, candidateVersion ).toString() );
                    if ( !reportMode
                        && PomHelper.setPropertyVersion( pom, null, property.getName(), candidateVersion ) )
                    {
                        getLog().info( "Updated ${" + property.getName() + "} from " + originalVersion + " to "
                            + candidateVersion );
                    }
                }
            }
        }
        return result;
    }

    /**
     * Returns the candidate version to use for the specified property.
     * 

* The dependencies currently linked to the property must all be defined by the remote POM and they should refer to * the same version. If that's the case, that same version is returned. Otherwise, null is returned * indicating that there is no candidate. * * @param remoteDependencies the remote dependencies * @param property the property to update * @param propertyVersions the association * @return the candidate version or null if there isn't any */ private String computeCandidateVersion( Map remoteDependencies, Property property, PropertyVersions propertyVersions ) { String candidateVersion = null; for ( ArtifactAssociation artifactAssociation : propertyVersions.getAssociations() ) { String id = generateId( artifactAssociation.getArtifact() ); Dependency dependency = remoteDependencies.get( id ); if ( dependency == null ) { getLog().info( "Not updating ${" + property.getName() + "}: no info for " + id ); return null; } else { if ( candidateVersion == null ) { candidateVersion = dependency.getVersion(); } else if ( !candidateVersion.equals( dependency.getVersion() ) ) { getLog().warn( "Could not update ${" + property.getName() + "}: version mismatch" ); return null; } } } return candidateVersion; } private void writeReportFile( List dependenciesUpdate, List propertiesUpdate ) throws MojoExecutionException { if ( !reportOutputFile.getParentFile().exists() ) { reportOutputFile.getParentFile().mkdirs(); } FileWriter fw = null; PrintWriter pw = null; try { fw = new FileWriter( reportOutputFile ); pw = new PrintWriter( fw ); pw.println( "The following differences were found:" ); pw.println(); if ( dependenciesUpdate.size() == 0 ) { pw.println( " none" ); } else { for ( String dependencyUpdate : dependenciesUpdate ) { pw.println( " " + dependencyUpdate ); } } pw.println(); pw.println( "The following property differences were found:" ); pw.println(); if ( propertiesUpdate.size() == 0 ) { pw.println( " none" ); } else { for ( String propertyUpdate : propertiesUpdate ) { pw.println( " " + propertyUpdate ); } } pw.close(); fw.close(); } catch ( IOException e ) { throw new MojoExecutionException( "Unable to write report file. ", e ); } finally { if ( pw != null ) { pw.close(); } if ( fw != null ) { try { fw.close(); } catch ( IOException io ) { // Ignore } } } } /** * Create a simple message describing the version diff * * @param dep * @param remoteVersion * @return The message */ private StringBuilder writeDependencyDiffMessage( Dependency dep, String remoteVersion ) { String id = dep.getGroupId() + ":" + dep.getArtifactId(); return writeDiffMessage( id, dep.getVersion(), remoteVersion ); } private StringBuilder writeDiffMessage( String id, String originalVersion, String targetVersion ) { StringBuilder buf = new StringBuilder(); buf.append( id ); buf.append( ' ' ); int padding = INFO_PAD_SIZE - originalVersion.length() - targetVersion.length() - 4; while ( buf.length() < padding ) { buf.append( '.' ); } buf.append( ' ' ); buf.append( originalVersion ); buf.append( " -> " ); buf.append( targetVersion ); return buf; } /** * Add a list of dependencies to a Map for easy access * * @param map * @param deps */ private void mapDependencies( Map map, List deps ) { if ( deps != null ) { for ( Dependency nextDep : deps ) { map.put( nextDep.getManagementKey(), nextDep ); } } } /** * Creates a key that is similar to what {@link Dependency#getManagementKey()} generates for a dependency. */ private static String generateId( Artifact artifact ) { StringBuilder sb = new StringBuilder(); sb.append( artifact.getGroupId() ).append( ":" ).append( artifact.getArtifactId() ).append( ":" ).append( artifact.getType() ); if ( artifact.getClassifier() != null ) { sb.append( ":" ).append( artifact.getClassifier() ); } return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy