com.github.goldin.plugins.duplicates.DuplicatesFinderMojo.groovy Maven / Gradle / Ivy
package com.github.goldin.plugins.duplicates
import static com.github.goldin.plugins.common.GMojoUtils.*
import com.github.goldin.plugins.common.BaseGroovyMojo
import org.apache.maven.artifact.Artifact
import org.apache.maven.plugin.MojoFailureException
import org.jfrog.maven.annomojo.annotations.MojoGoal
import org.jfrog.maven.annomojo.annotations.MojoParameter
import org.jfrog.maven.annomojo.annotations.MojoPhase
import org.jfrog.maven.annomojo.annotations.MojoRequiresDependencyResolution
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
/**
* A plugin that finds duplicate classes in the scope(s) specified
*/
@MojoGoal ( 'find-duplicates' )
@MojoPhase ( 'process-resources' )
@MojoRequiresDependencyResolution ( 'test' )
@SuppressWarnings( [ 'StatelessClass', 'PublicInstanceField', 'NonFinalPublicField' ] )
class DuplicatesFinderMojo extends BaseGroovyMojo
{
/**
* Cache of file on the disk to classes it contains
*/
private static final Map> CLASSES_CACHE = [:]
@MojoParameter
public String scopes = 'compile'
@MojoParameter
public boolean verbose = false
@MojoParameter
public boolean fail = true
@Override
void doExecute ()
{
/**
* Mapping of File to Maven Artifact
*/
Map f2A = [:]
def time = System.currentTimeMillis()
/**
* Makes sure map specified has a new list entry and adds a new value to it
*/
def updateMap = { Map m, Object key, Object value ->
m[ key ] = m[ key ]?: []
m[ key ] << value }
/**
* Mapping of class names to files they were found it
*/
Set scopes = split( this.scopes ) as Set
Map> classes =
project.artifacts.findAll { Artifact a -> scopes.contains( a.scope ) && ( a.type != 'pom' ) }.
// Artifact => File
collect { Artifact a ->
File f = resolveArtifact( a, false, true ).file
f2A[ f ] = a
if ( verbose ) { log.info( "Checking [$a]" ) }
f }.
// Files => Mapping of class names to files
inject( [:] ){ Map map, File f ->
classNames( f ).each{ String className -> updateMap( map, className, f ) }
map }
/**
* Mapping of violating artifacts (Stringified list) to duplicate class names
*/
Map> violations =
classes.findAll{ String className, List files -> files.size() > 1 }.
// Class names with more then 1 files they were found in => Mapping of violating artifacts
inject( [:] ){ Map map, Map.Entry> entry ->
String className = entry.key
String violatingArtifacts = entry.value.collect{ File f -> f2A[ f ] }.toString()
updateMap( map, violatingArtifacts, className )
map }
log.info( "[${ f2A.size() }] artifact${ general().s( f2A.size())} analyzed in [${ System.currentTimeMillis() - time }] ms" )
if ( violations ) { reportViolations( violations ) }
else { log.info( 'No duplicate libraries found' ) }
}
/**
* Reads Zip archive and returns a list of class names stored in it.
*
* @param file Zip archive to read
* @return list of class names stored in it
*/
private List classNames ( File file )
{
if ( CLASSES_CACHE.containsKey( file ))
{
return CLASSES_CACHE[ file ]
}
ZipFile zip = new ZipFile( file )
try
{
CLASSES_CACHE[ file ] = verify().notNull( zip.entries().findAll{ ZipEntry entry -> entry.name.endsWith( '.class' ) }.
collect{ ZipEntry entry -> entry.name.replace( '/', '.' ).
replaceAll( /\.class$/, '' ) } )
}
finally { zip.close() }
}
/**
* Reports violations found by throwing an exception or logging an error message.
*
* @param violations violations found
*/
@SuppressWarnings( 'UseCollectMany' )
private void reportViolations( Map> violations )
{
def message =
'\nDuplicates found in:\n' +
violations.collect{ String artifacts, List classes ->
[ "-=-= $artifacts =-=-" ] + ( verbose ? classes.sort().collect { " --- [$it]" } : [] ) }.
flatten().
grep().
join( constants().CRLF )
if ( fail ) { throw new MojoFailureException( message )}
else { log.error( message )}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy