Clirr is a tool that checks Java libraries for binary and source compatibility with older releases. Basically you give it two sets of jar files and Clirr dumps out a list of changes in the public api. Clirr can be used to break the build if it detects incompatible api changes. In a continuous integration process Clirr can automatically prevent accidental introduction of binary or source compatibility problems.
Clirr is provided by the SourceForge project with the same name and may be used under the terms of the GNU Lesser General Public License.
comparisonVersion
, if present.
* Each comparisonArtifact
is made of a groupId
, an artifactId
and
* a version
number. Optionally it may have a classifier
* (default null) and a type
(default "jar").
* @parameter
*/
protected ArtifactSpecification[] comparisonArtifacts;
/**
* Show only messages of this severity or higher. Valid values are
* info
, warning
and error
.
*
* @parameter expression="${minSeverity}" default-value="warning"
*/
protected String minSeverity;
/**
* A text output file to render to. If omitted, no output is rendered to a text file.
*
* @parameter expression="${textOutputFile}"
*/
protected File textOutputFile;
/**
* An XML file to render to. If omitted, no output is rendered to an XML file.
*
* @parameter expression="${xmlOutputFile}"
*/
protected File xmlOutputFile;
/**
* A list of classes to include. Anything not included is excluded. If omitted, all are assumed to be included.
* Values are specified in path pattern notation, e.g. org/codehaus/mojo/**
.
*
* @parameter
*/
protected String[] includes;
/**
* A list of classes to exclude. These classes are excluded from the list of classes that are included.
* Values are specified in path pattern notation, e.g. org/codehaus/mojo/**
.
*
* @parameter
*/
protected String[] excludes;
/**
* Whether to log the results to the console or not.
*
* @parameter expression="${logResults}" default-value="false"
*/
protected boolean logResults;
private static final URL[] EMPTY_URL_ARRAY = new URL[0];
public void execute()
throws MojoExecutionException, MojoFailureException
{
if ( skip ) {
getLog().info( "Skipping execution" );
}
else {
doExecute();
}
}
protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;
public ClirrDiffListener executeClirr()
throws MojoExecutionException, MojoFailureException
{
return executeClirr( null );
}
protected ClirrDiffListener executeClirr( Severity minSeverity )
throws MojoExecutionException, MojoFailureException
{
ClirrDiffListener listener = new ClirrDiffListener();
ClassFilter classFilter = new ClirrClassFilter( includes, excludes );
JavaType[] origClasses = resolvePreviousReleaseClasses( classFilter );
JavaType[] currentClasses = resolveCurrentClasses( classFilter );
// Create a Clirr checker and execute
Checker checker = new Checker();
List listeners = new ArrayList();
listeners.add( listener );
if ( xmlOutputFile != null )
{
try
{
listeners.add( new XmlDiffListener( xmlOutputFile.getAbsolutePath() ) );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Error adding '" + xmlOutputFile + "' for output: " + e.getMessage(),
e );
}
}
if ( textOutputFile != null )
{
try
{
listeners.add( new PlainDiffListener( textOutputFile.getAbsolutePath() ) );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Error adding '" + textOutputFile + "' for output: " + e.getMessage(),
e );
}
}
if ( logResults )
{
listeners.add( new LogDiffListener( getLog() ) );
}
checker.addDiffListener( new DelegatingListener( listeners, minSeverity ) );
reportDiffs( checker, origClasses, currentClasses );
return listener;
}
private JavaType[] resolveCurrentClasses( ClassFilter classFilter )
throws MojoExecutionException
{
try
{
ClassLoader currentDepCL = createClassLoader( project.getArtifacts(), null );
return createClassSet( classesDirectory, currentDepCL, classFilter );
}
catch ( MalformedURLException e )
{
throw new MojoExecutionException( "Error creating classloader for current classes", e );
}
}
private JavaType[] resolvePreviousReleaseClasses( ClassFilter classFilter )
throws MojoFailureException, MojoExecutionException
{
final Set previousArtifacts;
final Artifact firstPreviousArtifact;
if ( comparisonArtifacts == null )
{
firstPreviousArtifact = getComparisonArtifact();
comparisonVersion = firstPreviousArtifact.getVersion();
getLog().info( "Comparing to version: " + comparisonVersion );
previousArtifacts = Collections.singleton( firstPreviousArtifact );
}
else
{
previousArtifacts = resolveArtifacts( comparisonArtifacts );
Artifact a = null;
for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
{
Artifact artifact = (Artifact) iter.next();
if ( a == null )
{
a = artifact;
}
getLog().debug( "Comparing to "
+ artifact.getGroupId() + ":"
+ artifact.getArtifactId() + ":"
+ artifact.getVersion() + ":"
+ artifact.getClassifier() + ":"
+ artifact.getType() );
}
firstPreviousArtifact = a;
}
try
{
for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
{
Artifact artifact = (Artifact) iter.next();
resolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
}
final List dependencies = getTransitiveDependencies( previousArtifacts );
ClassLoader origDepCL = createClassLoader( dependencies, previousArtifacts );
final Set files = new HashSet();
for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
{
Artifact artifact = (Artifact) iter.next();
// clirr expects jar files, so let's not pass other artifact files.
if ("jar".equals(artifact.getType())) {
files.add( new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ) );
}
}
return BcelTypeArrayBuilder.createClassSet( (File[]) files.toArray(new File[files.size()]), origDepCL, classFilter );
}
catch ( ProjectBuildingException e )
{
throw new MojoExecutionException( "Failed to build project for previous artifact: " + e.getMessage(), e );
}
catch ( InvalidDependencyVersionException e )
{
throw new MojoExecutionException( e.getMessage(), e );
}
catch ( ArtifactResolutionException e )
{
throw new MissingPreviousException( "Error resolving previous version: " + e.getMessage(), e );
}
catch ( ArtifactNotFoundException e )
{
getLog().warn( "Impossible to find previous version" );
return new JavaType[0];
//throw new MojoExecutionException( "Error finding previous version: " + e.getMessage(), e );
}
catch ( MalformedURLException e )
{
throw new MojoExecutionException( "Error creating classloader for previous version's classes", e );
}
}
protected List getTransitiveDependencies( final Set previousArtifacts )
throws ProjectBuildingException, InvalidDependencyVersionException, ArtifactResolutionException,
ArtifactNotFoundException
{
final List dependencies = new ArrayList();
for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
{
final Artifact a = (Artifact) iter.next();
final Artifact pomArtifact = factory.createArtifact(
a.getGroupId(),
a.getArtifactId(),
a.getVersion(),
a.getScope(),
"pom" );
final MavenProject pomProject = mavenProjectBuilder.buildFromRepository(
pomArtifact,
project.getRemoteArtifactRepositories(),
localRepository );
final Set pomProjectArtifacts = pomProject.createArtifacts( factory, null, null );
final ArtifactResolutionResult result = resolver.resolveTransitively( pomProjectArtifacts,
pomArtifact, localRepository,
project.getRemoteArtifactRepositories(),
metadataSource, null );
dependencies.addAll( result.getArtifacts() );
}
return dependencies;
}
private Artifact resolveArtifact( ArtifactSpecification artifactSpec )
throws MojoFailureException, MojoExecutionException
{
final String groupId = artifactSpec.getGroupId();
if ( groupId == null )
{
throw new MojoFailureException( "An artifacts groupId is required." );
}
final String artifactId = artifactSpec.getArtifactId();
if ( artifactId == null )
{
throw new MojoFailureException( "An artifacts artifactId is required." );
}
final String version = artifactSpec.getVersion();
if ( version == null )
{
throw new MojoFailureException( "An artifacts version number is required." );
}
final VersionRange versionRange = VersionRange.createFromVersion( version );
String type = artifactSpec.getType();
if ( type == null )
{
type = "jar";
}
Artifact artifact =
factory.createDependencyArtifact( groupId, artifactId, versionRange, type, artifactSpec.getClassifier(),
Artifact.SCOPE_COMPILE );
return artifact;
}
protected Set resolveArtifacts( ArtifactSpecification[] artifacts )
throws MojoFailureException, MojoExecutionException
{
Set artifactSet = new HashSet();
Artifact[] result = new Artifact[artifacts.length];
for ( int i = 0; i < result.length; i++ )
{
artifactSet.add( resolveArtifact( artifacts[i] ) );
}
return artifactSet;
}
private Artifact getComparisonArtifact()
throws MojoFailureException, MojoExecutionException
{
// Find the previous version JAR and resolve it, and it's dependencies
VersionRange range;
try
{
range = VersionRange.createFromVersionSpec( comparisonVersion );
}
catch ( InvalidVersionSpecificationException e )
{
throw new MojoFailureException( "Invalid comparison version: " + e.getMessage() );
}
Artifact previousArtifact;
try
{
previousArtifact = factory.createDependencyArtifact( project.getGroupId(), project.getArtifactId(), range,
project.getPackaging(), null, Artifact.SCOPE_COMPILE );
if ( !previousArtifact.getVersionRange().isSelectedVersionKnown( previousArtifact ) )
{
getLog().debug( "Searching for versions in range: " + previousArtifact.getVersionRange() );
List availableVersions = metadataSource.retrieveAvailableVersions( previousArtifact, localRepository,
project.getRemoteArtifactRepositories() );
filterSnapshots( availableVersions );
ArtifactVersion version = range.matchVersion( availableVersions );
if ( version != null )
{
previousArtifact.selectVersion( version.toString() );
}
}
}
catch ( OverConstrainedVersionException e1 )
{
throw new MojoFailureException( "Invalid comparison version: " + e1.getMessage() );
}
catch ( ArtifactMetadataRetrievalException e11 )
{
throw new MojoExecutionException( "Error determining previous version: " + e11.getMessage(), e11 );
}
if ( previousArtifact.getVersion() == null )
{
getLog().info( "Unable to find a previous version of the project in the repository" );
}
return previousArtifact;
}
private void filterSnapshots( List versions )
{
for ( Iterator versionIterator = versions.iterator(); versionIterator.hasNext(); )
{
ArtifactVersion version = (ArtifactVersion) versionIterator.next();
if ( "SNAPSHOT".equals( version.getQualifier() ) )
{
versionIterator.remove();
}
}
}
public static JavaType[] createClassSet( File classes, ClassLoader thirdPartyClasses, ClassFilter classFilter )
throws MalformedURLException
{
ClassLoader classLoader = new URLClassLoader( new URL[]{classes.toURI().toURL()}, thirdPartyClasses );
Repository repository = new ClassLoaderRepository( classLoader );
List selected = new ArrayList();
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir( classes );
scanner.setIncludes( new String[]{"**/*.class"} );
scanner.scan();
String[] files = scanner.getIncludedFiles();
for ( int i = 0; i < files.length; i++ )
{
File f = new File( classes, files[i] );
JavaClass clazz = extractClass( f, repository );
if ( classFilter.isSelected( clazz ) )
{
selected.add( new BcelJavaType( clazz ) );
repository.storeClass( clazz );
}
}
JavaType[] ret = new JavaType[selected.size()];
selected.toArray( ret );
return ret;
}
private static JavaClass extractClass( File f, Repository repository )
throws CheckerException
{
InputStream is = null;
try
{
is = new FileInputStream( f );
ClassParser parser = new ClassParser( is, f.getName() );
JavaClass clazz = parser.parse();
clazz.setRepository( repository );
return clazz;
}
catch ( IOException ex )
{
throw new CheckerException( "Cannot read " + f, ex );
}
finally
{
IOUtil.close( is );
}
}
/**
* Create a ClassLoader, which includes the artifacts in artifacts
,
* but excludes the artifacts in previousArtifacts
. The intention is,
* that we let BCEL inspect the artifacts in the latter set, using a
* {@link ClassLoader}, which contains the dependencies. However, the
* {@link ClassLoader} must not contain the jar files, which are being inspected.
* @param artifacts The artifacts, from which to build a {@link ClassLoader}.
* @param previousArtifacts The artifacts being inspected, or null, if te
* returned {@link ClassLoader} should contain all the elements of
* artifacts
.
* @return A {@link ClassLoader} which may be used to inspect the classes in
* previousArtifacts.
* @throws MalformedURLException Failed to convert a file to an URL.
*/
protected static ClassLoader createClassLoader( Collection artifacts, Set previousArtifacts )
throws MalformedURLException
{
URLClassLoader cl = null;
if ( !artifacts.isEmpty() )
{
List urls = new ArrayList( artifacts.size() );
for ( Iterator i = artifacts.iterator(); i.hasNext(); )
{
Artifact artifact = (Artifact) i.next();
if ( previousArtifacts == null || !previousArtifacts.contains( artifact ) )
{
urls.add( artifact.getFile().toURI().toURL() );
}
}
if ( !urls.isEmpty() )
{
cl = new URLClassLoader( (URL[]) urls.toArray( EMPTY_URL_ARRAY ) );
}
}
return cl;
}
protected static Severity convertSeverity( String minSeverity )
{
Severity s;
if ( "info".equals( minSeverity ) )
{
s = Severity.INFO;
}
else if ( "warning".equals( minSeverity ) )
{
s = Severity.WARNING;
}
else if ( "error".equals( minSeverity ) )
{
s = Severity.ERROR;
}
else
{
s = null;
}
return s;
}
protected boolean canGenerate()
throws MojoFailureException, MojoExecutionException
{
boolean classes = false;
if ( classesDirectory.exists() )
{
classes = true;
}
else
{
getLog().debug( "Classes directory not found: " + classesDirectory );
}
if ( !classes )
{
getLog().info( "Not generating Clirr report as there are no classes generated by the project" );
return false;
}
if ( comparisonArtifacts == null || comparisonArtifacts.length == 0 )
{
Artifact previousArtifact = getComparisonArtifact();
if ( previousArtifact.getVersion() == null )
{
getLog().info( "Not generating Clirr report as there is no previous version of the library to compare against" );
return false;
}
}
return true;
}
/**
* Calls {@link Checker#reportDiffs(JavaType[], JavaType[])} and take care of BCEL errors.
*
* @param checker not null
* @param origClasses not null
* @param currentClasses not null
* @see Checker#reportDiffs(JavaType[], JavaType[])
*/
private void reportDiffs( Checker checker, JavaType[] origClasses, JavaType[] currentClasses )
{
try
{
checker.reportDiffs( origClasses, currentClasses );
}
catch ( CheckerException e )
{
getLog().error( e.getMessage() );
// remove class with errors
JavaType[] origClasses2 = new JavaType[origClasses.length - 1];
int j = 0;
for ( int i = 0; i < origClasses.length; i++ )
{
if ( !e.getMessage().endsWith( origClasses[i].getName() ) )
{
origClasses2[j++] = origClasses[i];
}
}
JavaType[] currentClasses2 = new JavaType[currentClasses.length - 1];
j = 0;
for ( int i = 0; i < currentClasses.length; i++ )
{
if ( !e.getMessage().endsWith( currentClasses[i].getName() ) )
{
currentClasses2[j++] = currentClasses[i];
}
}
reportDiffs( checker, origClasses2, currentClasses2 );
}
}
} clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/ClirrCheckNoForkMojo.java 0000644 0001750 0001750 00000001632 11511725010 030737 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Codehaus
*
* 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.
*/
/**
* Check for compatibility with previous version without forking the project
*
* @author Brett Porter
* @goal check-no-fork
* @phase verify
* @since 2.3
*/
public class ClirrCheckNoForkMojo
extends AbstractClirrCheckMojo
{
// no op
}
clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/ClirrReportGenerator.java 0000644 0001750 0001750 00000023675 11241751601 031120 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Codehaus
*
* 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 net.sf.clirr.core.ApiDifference;
import net.sf.clirr.core.MessageTranslator;
import net.sf.clirr.core.Severity;
import org.apache.maven.doxia.sink.Sink;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* Generate the Clirr report.
*
* @author Brett Porter
*/
public class ClirrReportGenerator
{
private final ResourceBundle bundle;
private final Sink sink;
private boolean enableSeveritySummary;
private final Locale locale;
private Severity minSeverity;
private String xrefLocation;
private String currentVersion;
private String comparisonVersion;
public ClirrReportGenerator( Sink sink, ResourceBundle bundle, Locale locale )
{
this.bundle = bundle;
this.sink = sink;
this.enableSeveritySummary = true;
this.locale = locale;
}
public void generateReport( ClirrDiffListener listener )
{
doHeading();
if ( enableSeveritySummary )
{
doSeveritySummary( listener );
}
doDetails( listener );
sink.body_();
sink.flush();
sink.close();
}
private void doHeading()
{
sink.head();
sink.title();
String title = bundle.getString( "report.clirr.title" );
sink.text( title );
sink.title_();
sink.head_();
sink.body();
sink.section1();
sink.sectionTitle1();
sink.text( title );
sink.sectionTitle1_();
sink.paragraph();
sink.text( bundle.getString( "report.clirr.clirrlink" ) + " " );
sink.link( "http://clirr.sourceforge.net/" );
sink.text( "Clirr" );
sink.link_();
sink.text( "." );
sink.paragraph_();
sink.list();
sink.listItem();
sink.text( bundle.getString( "report.clirr.version.current" ) + " " );
sink.text( getCurrentVersion() );
sink.listItem_();
if ( getComparisonVersion() != null )
{
sink.listItem();
sink.text( bundle.getString( "report.clirr.version.comparison" ) + " " );
sink.text( getComparisonVersion() );
sink.listItem_();
}
sink.list_();
sink.section1_();
}
private void iconInfo()
{
sink.figure();
sink.figureCaption();
sink.text( bundle.getString( "report.clirr.level.info" ) );
sink.figureCaption_();
sink.figureGraphics( "images/icon_info_sml.gif" );
sink.figure_();
}
private void iconWarning()
{
sink.figure();
sink.figureCaption();
sink.text( bundle.getString( "report.clirr.level.warning" ) );
sink.figureCaption_();
sink.figureGraphics( "images/icon_warning_sml.gif" );
sink.figure_();
}
private void iconError()
{
sink.figure();
sink.figureCaption();
sink.text( bundle.getString( "report.clirr.level.error" ) );
sink.figureCaption_();
sink.figureGraphics( "images/icon_error_sml.gif" );
sink.figure_();
}
private void doSeveritySummary( ClirrDiffListener listener )
{
sink.section1();
sink.sectionTitle1();
sink.text( bundle.getString( "report.clirr.summary" ) );
sink.sectionTitle1_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.clirr.column.severity" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.clirr.column.number" ) );
sink.tableHeaderCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
iconError();
sink.nonBreakingSpace();
sink.text( bundle.getString( "report.clirr.level.error" ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( listener.getSeverityCount( Severity.ERROR ) ) );
sink.tableCell_();
sink.tableRow_();
if ( minSeverity == null || minSeverity.compareTo( Severity.WARNING ) <= 0 )
{
sink.tableRow();
sink.tableCell();
iconWarning();
sink.nonBreakingSpace();
sink.text( bundle.getString( "report.clirr.level.warning" ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( listener.getSeverityCount( Severity.WARNING ) ) );
sink.tableCell_();
sink.tableRow_();
}
if ( minSeverity == null || minSeverity.compareTo( Severity.INFO ) <= 0 )
{
sink.tableRow();
sink.tableCell();
iconInfo();
sink.nonBreakingSpace();
sink.text( bundle.getString( "report.clirr.level.info" ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( listener.getSeverityCount( Severity.INFO ) ) );
sink.tableCell_();
sink.tableRow_();
}
sink.table_();
if ( minSeverity == null || minSeverity.compareTo( Severity.INFO ) > 0 )
{
sink.paragraph();
sink.italic();
sink.text( bundle.getString( "report.clirr.filtered" ) );
sink.italic_();
sink.paragraph_();
}
sink.section1_();
}
private void doDetails( ClirrDiffListener listener )
{
sink.section1();
sink.sectionTitle1();
sink.text( bundle.getString( "report.clirr.details" ) );
sink.sectionTitle1_();
List differences = listener.getApiDifferences();
if ( !differences.isEmpty() )
{
doTable( differences );
}
else
{
sink.paragraph();
sink.text( bundle.getString( "report.clirr.noresults" ) );
sink.paragraph_();
}
sink.section1_();
}
private void doTable( List differences )
{
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.clirr.column.severity" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.clirr.column.message" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.clirr.column.class" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.clirr.column.methodorfield" ) );
sink.tableHeaderCell_();
sink.tableRow_();
MessageTranslator translator = new MessageTranslator();
translator.setLocale( locale );
for ( Iterator events = differences.iterator(); events.hasNext(); )
{
ApiDifference difference = (ApiDifference) events.next();
// TODO: differentiate source and binary? The only difference seems to be MSG_CONSTANT_REMOVED at this point
Severity maximumSeverity = difference.getMaximumSeverity();
if ( minSeverity == null || minSeverity.compareTo( maximumSeverity ) <= 0 )
{
sink.tableRow();
sink.tableCell();
levelIcon( maximumSeverity );
sink.tableCell_();
sink.tableCell();
sink.text( difference.getReport( translator ) );
sink.tableCell_();
sink.tableCell();
if ( xrefLocation != null )
{
sink.link( xrefLocation + "/" + difference.getAffectedClass().replace( '.', '/' ) + ".html" );
}
sink.text( difference.getAffectedClass() );
if ( xrefLocation != null )
{
sink.link_();
}
sink.tableCell_();
sink.tableCell();
sink.text( difference.getAffectedMethod() != null ? difference.getAffectedMethod()
: difference.getAffectedField() );
sink.tableCell_();
sink.tableRow_();
}
}
sink.table_();
}
private void levelIcon( Severity level )
{
if ( Severity.INFO.equals( level ) )
{
iconInfo();
}
else if ( Severity.WARNING.equals( level ) )
{
iconWarning();
}
else if ( Severity.ERROR.equals( level ) )
{
iconError();
}
}
public void setEnableSeveritySummary( boolean enableSeveritySummary )
{
this.enableSeveritySummary = enableSeveritySummary;
}
public void setMinSeverity( Severity minSeverity )
{
this.minSeverity = minSeverity;
}
public String getXrefLocation()
{
return xrefLocation;
}
public void setXrefLocation( String xrefLocation )
{
this.xrefLocation = xrefLocation;
}
public String getCurrentVersion()
{
return currentVersion;
}
public void setCurrentVersion( String currentVersion )
{
this.currentVersion = currentVersion;
}
public String getComparisonVersion()
{
return comparisonVersion;
}
public void setComparisonVersion( String comparisonVersion )
{
this.comparisonVersion = comparisonVersion;
}
}
clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/ClirrClassFilter.java 0000644 0001750 0001750 00000004454 10562322571 030210 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Codehaus
*
* 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 net.sf.clirr.core.ClassFilter;
import org.apache.bcel.classfile.JavaClass;
import org.codehaus.plexus.util.SelectorUtils;
/**
* Filter classes by pattern sets.
*
* @author Brett Porter
*/
public class ClirrClassFilter
implements ClassFilter
{
private final String[] excludes;
private final String[] includes;
private boolean alwaysTrue;
public ClirrClassFilter( String[] includes, String[] excludes )
{
if ( excludes == null || excludes.length == 0 )
{
this.excludes = null;
}
else
{
this.excludes = (String[]) excludes.clone();
}
if ( includes == null || includes.length == 0 )
{
this.includes = new String[]{"**"};
if ( excludes == null )
{
alwaysTrue = true;
}
}
else
{
this.includes = (String[]) includes.clone();
}
}
public boolean isSelected( JavaClass javaClass )
{
boolean result = false;
if ( alwaysTrue )
{
result = true;
}
else
{
String path = javaClass.getClassName().replace( '.', '/' );
for ( int i = 0; i < includes.length && !result; i++ )
{
result = SelectorUtils.matchPath( includes[i], path );
}
if ( excludes != null )
{
for ( int i = 0; i < excludes.length && result; i++ )
{
result = !SelectorUtils.matchPath( excludes[i], path );
}
}
}
return result;
}
}
clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/ClirrReport.java 0000644 0001750 0001750 00000027543 11511725010 027242 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Codehaus
*
* 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 net.sf.clirr.core.Severity;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.doxia.module.xhtml.decoration.render.RenderingContext;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.site.decoration.Body;
import org.apache.maven.doxia.site.decoration.DecorationModel;
import org.apache.maven.doxia.site.decoration.Skin;
import org.apache.maven.doxia.siterenderer.Renderer;
import org.apache.maven.doxia.siterenderer.RendererException;
import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.reporting.MavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.util.PathTool;
import org.codehaus.plexus.util.StringUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
/**
* Generate a report from the Clirr output.
*
* @author Brett Porter
* @goal clirr
* @execute phase="compile"
*/
public class ClirrReport
extends AbstractClirrMojo
implements MavenReport
{
/**
* Specifies the directory where the report will be generated.
*
* @parameter default-value="${project.reporting.outputDirectory}"
* @required
*/
private File outputDirectory;
/**
* @component
*/
private Renderer siteRenderer;
/**
* Whether to show the summary of the number of errors, warnings and informational messages.
*
* @parameter expression="${showSummary}" default-value="true"
*/
private boolean showSummary;
/**
* Whether to render the HTML report or not.
*
* @parameter expression="${htmlReport}" default-value="true"
*/
private boolean htmlReport;
/**
* Link the violation line numbers to the source Xref. This will create links
* if the JXR Plugin is being used.
*
* @parameter expression="${linkXRef}" default-value="true"
*/
private boolean linkXRef;
/**
* Location of the Xrefs to link to.
*
* @parameter expression="${xrefLocation}" default-value="${project.build.directory}/site/xref"
*/
private File xrefLocation;
public String getCategoryName()
{
return MavenReport.CATEGORY_PROJECT_REPORTS;
}
public void setReportOutputDirectory( File file )
{
outputDirectory = file;
}
public File getReportOutputDirectory()
{
return outputDirectory;
}
public boolean isExternalReport()
{
return false;
}
private File getSkinArtifactFile()
throws MojoFailureException, MojoExecutionException
{
Skin skin = Skin.getDefaultSkin();
String version = skin.getVersion();
Artifact artifact;
try
{
if ( version == null )
{
version = Artifact.RELEASE_VERSION;
}
VersionRange versionSpec = VersionRange.createFromVersionSpec( version );
artifact = factory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, "jar",
null, null );
resolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
}
catch ( InvalidVersionSpecificationException e )
{
throw new MojoFailureException( "The skin version '" + version + "' is not valid: " + e.getMessage() );
}
catch ( ArtifactResolutionException e )
{
throw new MojoExecutionException( "Unable to find skin", e );
}
catch ( ArtifactNotFoundException e )
{
throw new MojoFailureException( "The skin does not exist: " + e.getMessage() );
}
return artifact.getFile();
}
protected void doExecute()
throws MojoExecutionException, MojoFailureException
{
if ( !canGenerateReport() )
{
return;
}
// TODO: push to a helper? Could still be improved by taking more of the site information from the site plugin
try
{
DecorationModel model = new DecorationModel();
model.setBody( new Body() );
Map attributes = new HashMap();
attributes.put( "outputEncoding", "UTF-8" );
Locale locale = Locale.getDefault();
SiteRenderingContext siteContext = siteRenderer.createContextForSkin( getSkinArtifactFile(), attributes,
model, getName( locale ), locale );
RenderingContext context = new RenderingContext( outputDirectory, getOutputName() + ".html" );
SiteRendererSink sink = new SiteRendererSink( context );
generate( sink, locale );
outputDirectory.mkdirs();
Writer writer = new FileWriter( new File( outputDirectory, getOutputName() + ".html" ) );
siteRenderer.generateDocument( writer, sink, siteContext );
siteRenderer.copyResources( siteContext, new File( project.getBasedir(), "src/site/resources" ),
outputDirectory );
}
catch ( RendererException e )
{
throw new MojoExecutionException(
"An error has occurred in " + getName( Locale.ENGLISH ) + " report generation.", e );
}
catch ( IOException e )
{
throw new MojoExecutionException(
"An error has occurred in " + getName( Locale.ENGLISH ) + " report generation.", e );
}
catch ( MavenReportException e )
{
throw new MojoExecutionException(
"An error has occurred in " + getName( Locale.ENGLISH ) + " report generation.", e );
}
}
public void generate( Sink sink, Locale locale )
throws MavenReportException
{
if ( !canGenerateReport() )
{
getLog().info( "Not generating report as there are no sources to compare" );
}
else
{
doReport( sink, locale );
}
}
private void doReport( Sink sink, Locale locale )
throws MavenReportException
{
Severity minSeverity = convertSeverity( this.minSeverity );
ResourceBundle bundle = getBundle( locale );
if ( minSeverity == null )
{
getLog().warn( bundle.getString( "report.clirr.error.invalid.minseverity" ) + ": '" + this
.minSeverity + "'." );
}
if ( !htmlReport && xmlOutputFile == null && textOutputFile == null && !logResults )
{
getLog().error( bundle.getString( "report.clirr.error.noreports" ) );
}
else
{
ClirrDiffListener listener;
try
{
listener = executeClirr( minSeverity );
}
catch ( MissingPreviousException e )
{
getLog().error( bundle.getString( "report.clirr.error.nopredecessor" ) );
return;
}
catch ( MojoExecutionException e )
{
throw new MavenReportException( e.getMessage(), e );
}
catch ( MojoFailureException e )
{
throw new MavenReportException( e.getMessage() );
}
if ( htmlReport )
{
ClirrReportGenerator generator = new ClirrReportGenerator( sink, bundle, locale );
generator.setEnableSeveritySummary( showSummary );
generator.setMinSeverity( minSeverity );
generator.setCurrentVersion( project.getVersion() );
if ( comparisonVersion != null )
{
generator.setComparisonVersion( comparisonVersion );
}
if ( linkXRef )
{
String relativePath =
PathTool.getRelativePath( outputDirectory.getAbsolutePath(), xrefLocation.getAbsolutePath() );
if ( StringUtils.isEmpty( relativePath ) )
{
relativePath = ".";
}
relativePath = relativePath + "/" + xrefLocation.getName();
if ( xrefLocation.exists() )
{
// XRef was already generated by manual execution of a lifecycle binding
generator.setXrefLocation( relativePath );
}
else
{
// Not yet generated - check if the report is on its way
for ( Iterator reports = project.getReportPlugins().iterator(); reports.hasNext(); )
{
ReportPlugin report = (ReportPlugin) reports.next();
String artifactId = report.getArtifactId();
if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
{
generator.setXrefLocation( relativePath );
}
}
}
if ( generator.getXrefLocation() == null )
{
getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
}
}
generator.generateReport( listener );
}
}
}
public String getDescription( Locale locale )
{
return getBundle( locale ).getString( "report.clirr.description" );
}
public String getName( Locale locale )
{
return getBundle( locale ).getString( "report.clirr.name" );
}
public String getOutputName()
{
return "clirr-report";
}
private ResourceBundle getBundle( Locale locale )
{
return ResourceBundle.getBundle( "clirr-report", locale, getClass().getClassLoader() );
}
public boolean canGenerateReport()
{
try
{
return canGenerate();
}
catch ( MojoFailureException e )
{
getLog().error( "Can't generate Clirr report: " + e.getMessage() );
return false;
}
catch ( MojoExecutionException e )
{
getLog().error( "Can't generate Clirr report: " + e.getMessage(), e );
return false;
}
}
// eventually, we must replace this with the o.a.m.d.s.Sink class as a parameter
public void generate( org.codehaus.doxia.sink.Sink sink, Locale locale )
throws MavenReportException
{
generate( (Sink) sink, locale );
}
}
clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/AbstractClirrCheckMojo.java 0000644 0001750 0001750 00000011706 11511725010 031307 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Codehaus
*
* 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 net.sf.clirr.core.ApiDifference;
import net.sf.clirr.core.Severity;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.i18n.I18N;
import java.util.Iterator;
import java.util.Locale;
/**
* Check for compatibility with previous version.
*
* @author Brett Porter
*/
public class AbstractClirrCheckMojo
extends AbstractClirrMojo
{
/**
* Whether to fail on errors.
*
* @parameter expression="${failOnError}" default-value="true"
*/
private boolean failOnError;
/**
* Whether to fail on warnings.
*
* @parameter expression="${failOnWarning}" default-value="false"
*/
private boolean failOnWarning;
/**
* Whether to fail on info.
*
* @parameter expression="${failOnInfo}" default-value="false"
*/
private boolean failOnInfo;
/**
* @component
*/
private I18N i18n;
protected void doExecute()
throws MojoExecutionException, MojoFailureException
{
if ( !canGenerate() )
{
return;
}
Severity minSeverity = convertSeverity( this.minSeverity );
ClirrDiffListener listener;
try
{
listener = executeClirr(minSeverity);
}
catch ( MissingPreviousException e )
{
getLog().debug( e );
getLog().info( "No previous version was found. Use 'comparisonArtifacts'"
+ " for explicit configuration if you think this is wrong." );
return;
}
Locale locale = Locale.getDefault();
int errorCount = listener.getSeverityCount( Severity.ERROR );
if ( failOnError && errorCount > 0 )
{
log( listener, Severity.ERROR );
String message;
if ( errorCount > 1 )
{
String[] args = new String[]{String.valueOf( errorCount )};
message = i18n.format( "clirr-report", locale, "check.clirr.failure.errors", args );
}
else
{
message = i18n.getString( "clirr-report", locale, "check.clirr.failure.error" );
}
throw new MojoFailureException( message );
}
int warningCount = listener.getSeverityCount( Severity.WARNING );
if ( failOnWarning && warningCount > 0 )
{
log( listener, Severity.WARNING );
String message;
if ( warningCount > 1 )
{
String[] args = new String[]{String.valueOf( warningCount )};
message = i18n.format( "clirr-report", locale, "check.clirr.failure.warnings", args );
}
else
{
message = i18n.getString( "clirr-report", locale, "check.clirr.failure.warning" );
}
throw new MojoFailureException( message );
}
int infoCount = listener.getSeverityCount( Severity.INFO );
if ( failOnInfo && infoCount > 0)
{
log( listener, Severity.INFO );
String message;
if ( infoCount > 1 )
{
String[] args = new String[]{String.valueOf( infoCount )};
message = i18n.format( "clirr-report", locale, "check.clirr.failure.infos", args );
}
else
{
message = i18n.getString( "clirr-report", locale, "check.clirr.failure.info" );
}
throw new MojoFailureException( message );
}
String[] args =
new String[]{String.valueOf( errorCount ), String.valueOf( warningCount ), String.valueOf( infoCount )};
getLog().info( i18n.format( "clirr-report", locale, "check.clirr.success", args ) );
}
private void log( ClirrDiffListener listener, Severity severity )
{
if ( !logResults )
{
LogDiffListener l = new LogDiffListener( getLog() );
for ( Iterator i = listener.getApiDifferences().iterator(); i.hasNext(); )
{
ApiDifference difference = (ApiDifference) i.next();
if ( difference.getMaximumSeverity().equals( severity ) )
{
l.reportDiff( difference );
}
}
}
}
}
clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/MissingPreviousException.java 0000644 0001750 0001750 00000002017 11063037762 032021 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Codehaus
*
* 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 org.apache.maven.plugin.MojoExecutionException;
/**
* This exception is thrown, if no previous version of the current artifact was found.
*/
class MissingPreviousException
extends MojoExecutionException
{
private static final long serialVersionUID = -5160106292241626179L;
MissingPreviousException( String pMessage, Throwable pCause )
{
super( pMessage, pCause );
}
}
clirr-maven-plugin-2.3/src/main/java/org/codehaus/mojo/clirr/ArtifactSpecification.java 0000644 0001750 0001750 00000004735 10773161441 031242 0 ustar twerner twerner package org.codehaus.mojo.clirr;
/*
* Copyright 2006 The Apache Software Foundation.
*
* 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.
*/
/**
* An artifact specification.
*/
public class ArtifactSpecification
{
/**
* The artifacts groupID.
*/
private String groupId;
/**
* The artifacts artifactID.
*/
private String artifactId;
/**
* The artifacts version number.
*/
private String version;
/**
* The artifacts classifier.
*/
private String classifier;
/**
* The artifacts type; defaults to "jar".
*/
private String type;
/**
* Returns the artifacts groupId.
*/
public String getGroupId()
{
return groupId;
}
/**
* Sets the artifacts groupId.
*/
public void setGroupId( String groupId )
{
this.groupId = groupId;
}
/**
* Returns the artifacts artifactId.
*/
public String getArtifactId()
{
return artifactId;
}
/**
* Sets the artifacts artifactId.
*/
public void setArtifactId( String artifactId )
{
this.artifactId = artifactId;
}
/**
* Returns the artifacts version number.
*/
public String getVersion()
{
return version;
}
/**
* Sets the artifacts version number.
*/
public void setVersion( String version )
{
this.version = version;
}
/**
* Returns the artifacts classifier.
*/
public String getClassifier()
{
return classifier;
}
/**
* Sets the artifacts classifier.
*/
public void setClassifier( String classifier )
{
this.classifier = classifier;
}
/**
* Returns the artifacts type; defaults to "jar".
*/
public String getType()
{
return type;
}
/**
* Sets the artifacts type; defaults to "jar".
*/
public void setType( String type )
{
this.type = type;
}
}
clirr-maven-plugin-2.3/src/main/resources/ 0000755 0001750 0001750 00000000000 11617003250 020547 5 ustar twerner twerner clirr-maven-plugin-2.3/src/main/resources/clirr-report_fr.properties 0000644 0001750 0001750 00000003721 11416376205 026015 0 ustar twerner twerner #
# Copyright 2007 The Codehaus
#
# 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.
#
report.clirr.name=Clirr
report.clirr.description=Rapport sur les différences entre révisions sur les API source et binaire
report.clirr.title=Résultats Clirr
report.clirr.clirrlink=Le document suivant contient les résultats de
report.clirr.summary=Résumé
report.clirr.details=Détails
report.clirr.column.severity=Gravité
report.clirr.column.number=Nombre
report.clirr.level.info=Info
report.clirr.level.warning=Avertissement
report.clirr.level.error=Erreur
report.clirr.column.message=Message
report.clirr.column.class=Classe
report.clirr.column.methodorfield=Méthode / Champ
report.clirr.noresults=Pas de résultat pour la gravité sélectionnée.
report.clirr.filtered=(les résultats ont été filtrés pour omettre les moins graves)
report.clirr.error.invalid.minseverity=Gravité minimale invalide ignorée
report.clirr.error.noreports=Aucun rapport n''a été configuré
report.clirr.error.nopredecessor=Pas de version précédente trouvée. Utilisez ''comparisonArtifacts'' pour configurer explicitement si vous pensez que ceci est erroné.
check.clirr.failure.errors=Il y a {0} erreurs.
check.clirr.failure.warnings=Il y a {0} avertissements.
check.clirr.failure.infos=Il y a {0} informations.
check.clirr.failure.error=Il y a 1 erreur.
check.clirr.failure.warning=Il y a 1 avertissement.
check.clirr.failure.info=Il y a 1 information.
check.clirr.success=Succès avec {0} erreurs, {1} avertissements, et {2} autres changements.
clirr-maven-plugin-2.3/src/main/resources/clirr-report.properties 0000644 0001750 0001750 00000003753 11416376205 025333 0 ustar twerner twerner #
# Copyright 2006 The Codehaus
#
# 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.
#
report.clirr.name=Clirr
report.clirr.description=Report on binary and source API differences between releases
report.clirr.title=Clirr Results
report.clirr.clirrlink=The following document contains the results of
report.clirr.version.current=Current Version:
report.clirr.version.comparison=Comparison Version:
report.clirr.summary=Summary
report.clirr.details=Details
report.clirr.column.severity=Severity
report.clirr.column.number=Number
report.clirr.level.info=Info
report.clirr.level.warning=Warning
report.clirr.level.error=Error
report.clirr.column.message=Message
report.clirr.column.class=Class
report.clirr.column.methodorfield=Method / Field
report.clirr.noresults=No results for the given severities.
report.clirr.filtered=(The results have been filtered to omit less severe results)
report.clirr.error.invalid.minseverity=Ignoring invalid minimum severity
report.clirr.error.noreports=No reports were configured
report.clirr.error.nopredecessor=No previous version was found. Use ''comparisonArtifacts'' for explicit configuration if you think this is wrong.
check.clirr.failure.errors=There were {0} errors.
check.clirr.failure.warnings=There were {0} warnings.
check.clirr.failure.infos=There were {0} infos.
check.clirr.failure.error=There was 1 error.
check.clirr.failure.warning=There was 1 warning.
check.clirr.failure.info=There was 1 info.
check.clirr.success=Succeeded with {0} errors; {1} warnings; and {2} other changes.