View Javadoc
1   package org.codehaus.mojo.clirr;
2   
3   /*
4    * Copyright 2006 The Codehaus
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import net.sf.clirr.core.ApiDifference;
20  import net.sf.clirr.core.Checker;
21  import net.sf.clirr.core.ClassFilter;
22  import net.sf.clirr.core.PlainDiffListener;
23  import net.sf.clirr.core.Severity;
24  import net.sf.clirr.core.XmlDiffListener;
25  import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder;
26  import net.sf.clirr.core.spi.JavaType;
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
29  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugin.MojoFailureException;
32  import org.apache.maven.project.ProjectBuildingException;
33  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
34  import org.codehaus.plexus.i18n.I18N;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.net.MalformedURLException;
39  import java.util.ArrayList;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Locale;
43  import java.util.Set;
44  
45  /**
46   * Check for compatibility between two arbitrary artifact sets.
47   *
48   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
49   * @author <a href="mailto:jmcconnell@apache.org">Jesse McConnell</a>
50   * @goal check-arbitrary
51   * @phase verify
52   * @execute phase="compile"
53   */
54  public class ClirrArbitraryCheckMojo
55      extends AbstractClirrMojo
56  {
57      /**
58       * Whether to fail on errors.
59       *
60       * @parameter property="failOnError" default-value="true"
61       */
62      private boolean failOnError;
63  
64      /**
65       * Whether to fail on warnings.
66       *
67       * @parameter property="failOnWarning" default-value="false"
68       */
69      private boolean failOnWarning;
70  
71      /**
72       * @component
73       */
74      private I18N i18n;
75  
76      /**
77       * List of artifacts to serve as a baseline to compare against.
78       *
79       * @parameter
80       * @required
81       */
82      protected ArtifactSpecification[] oldComparisonArtifacts;
83  
84      /**
85       * List of artifacts to compare to baseline.
86       *
87       * @parameter
88       * @required
89       */
90      protected ArtifactSpecification[] newComparisonArtifacts;
91  
92      protected void doExecute()
93          throws MojoExecutionException, MojoFailureException
94      {
95          if ( oldComparisonArtifacts == null || oldComparisonArtifacts.length == 0 )
96          {
97              getLog().info( "Missing required oldComparisonArtifacts" );
98          }
99  
100         if ( newComparisonArtifacts == null || newComparisonArtifacts.length == 0 )
101         {
102             getLog().info( "Missing required newComparisonArtifacts" );
103         }
104 
105         ClirrDiffListener listener;
106         try
107         {
108             listener = executeClirr( Severity.INFO );
109         }
110         catch ( MissingPreviousException e )
111         {
112             getLog().debug( e );
113             getLog().info( "No previous version was found. Use 'comparisonArtifacts'"
114                                + " for explicit configuration if you think this is wrong." );
115             return;
116         }
117 
118         Locale locale = Locale.getDefault();
119 
120         int errorCount = listener.getSeverityCount( Severity.ERROR );
121         if ( failOnError && errorCount > 0 )
122         {
123             log( listener, Severity.ERROR );
124             String message;
125             if ( errorCount > 1 )
126             {
127                 String[] args = new String[]{ String.valueOf( errorCount ) };
128                 message = i18n.format( "clirr-report", locale, "check.clirr.failure.errors", args );
129             }
130             else
131             {
132                 message = i18n.getString( "clirr-report", locale, "check.clirr.failure.error" );
133             }
134             throw new MojoFailureException( message );
135         }
136 
137         int warningCount = listener.getSeverityCount( Severity.WARNING );
138         if ( failOnWarning && errorCount > 0 )
139         {
140             log( listener, Severity.WARNING );
141             String message;
142             if ( errorCount > 1 )
143             {
144                 String[] args = new String[]{ String.valueOf( errorCount ) };
145                 message = i18n.format( "clirr-report", locale, "check.clirr.failure.warnings", args );
146             }
147             else
148             {
149                 message = i18n.getString( "clirr-report", locale, "check.clirr.failure.warning" );
150             }
151             throw new MojoFailureException( message );
152         }
153 
154         int infoCount = listener.getSeverityCount( Severity.INFO );
155         String[] args =
156             new String[]{ String.valueOf( errorCount ), String.valueOf( warningCount ), String.valueOf( infoCount ) };
157         getLog().info( i18n.format( "clirr-report", locale, "check.clirr.success", args ) );
158     }
159 
160     private void log( ClirrDiffListener listener, Severity severity )
161     {
162         if ( !logResults )
163         {
164             LogDiffListener l = new LogDiffListener( getLog() );
165             for ( Iterator i = listener.getApiDifferences().iterator(); i.hasNext(); )
166             {
167                 ApiDifference difference = (ApiDifference) i.next();
168                 if ( difference.getMaximumSeverity().equals( severity ) )
169                 {
170                     l.reportDiff( difference );
171                 }
172             }
173         }
174     }
175 
176     protected JavaType[] resolveClasses( ArtifactSpecification[] artifacts, ClassFilter classFilter )
177         throws MojoFailureException, MojoExecutionException
178     {
179         final Set artifactSet;
180 
181         artifactSet = resolveArtifacts( artifacts );
182         Artifact a = null;
183         for ( Iterator iter = artifactSet.iterator(); iter.hasNext(); )
184         {
185             Artifact artifact = (Artifact) iter.next();
186             if ( a == null )
187             {
188                 a = artifact;
189             }
190             getLog().debug(
191                 "Comparing to " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion()
192                     + ":" + artifact.getClassifier() + ":" + artifact.getType() );
193         }
194 
195         try
196         {
197             for ( Iterator iter = artifactSet.iterator(); iter.hasNext(); )
198             {
199                 Artifact artifact = (Artifact) iter.next();
200                 resolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
201             }
202 
203             final List dependencies = getTransitiveDependencies( artifactSet );
204 
205             ClassLoader origDepCL = createClassLoader( dependencies, artifactSet );
206             final File[] files = new File[artifactSet.size()];
207             int i = 0;
208             for ( Iterator iter = artifactSet.iterator(); iter.hasNext(); )
209             {
210                 Artifact artifact = (Artifact) iter.next();
211                 files[i++] = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
212             }
213             return BcelTypeArrayBuilder.createClassSet( files, origDepCL, classFilter );
214         }
215         catch ( ProjectBuildingException e )
216         {
217             throw new MojoExecutionException( "Failed to build project for previous artifact: " + e.getMessage(), e );
218         }
219         catch ( InvalidDependencyVersionException e )
220         {
221             throw new MojoExecutionException( e.getMessage(), e );
222         }
223         catch ( ArtifactResolutionException e )
224         {
225             throw new MissingPreviousException( "Error resolving previous version: " + e.getMessage(), e );
226         }
227         catch ( ArtifactNotFoundException e )
228         {
229             throw new MojoExecutionException( "Error finding previous version: " + e.getMessage(), e );
230         }
231         catch ( MalformedURLException e )
232         {
233             throw new MojoExecutionException( "Error creating classloader for previous version's classes", e );
234         }
235     }
236 
237     protected ClirrDiffListener executeClirr( Severity minSeverity )
238         throws MojoExecutionException, MojoFailureException
239     {
240         ClirrDiffListener listener = new ClirrDiffListener();
241 
242         ClassFilter classFilter = new ClirrClassFilter( includes, excludes );
243 
244         JavaType[] origClasses = resolveClasses( oldComparisonArtifacts, classFilter );
245 
246         JavaType[] currentClasses = resolveClasses( newComparisonArtifacts, classFilter );
247 
248         // Create a Clirr checker and execute
249         Checker checker = new Checker();
250 
251         List listeners = new ArrayList();
252 
253         listeners.add( listener );
254 
255         if ( xmlOutputFile != null )
256         {
257             try
258             {
259                 listeners.add( new XmlDiffListener( xmlOutputFile.getAbsolutePath() ) );
260             }
261             catch ( IOException e )
262             {
263                 throw new MojoExecutionException( "Error adding '" + xmlOutputFile + "' for output: " + e.getMessage(),
264                                                   e );
265             }
266         }
267 
268         if ( textOutputFile != null )
269         {
270             try
271             {
272                 listeners.add( new PlainDiffListener( textOutputFile.getAbsolutePath() ) );
273             }
274             catch ( IOException e )
275             {
276                 throw new MojoExecutionException( "Error adding '" + textOutputFile + "' for output: " + e.getMessage(),
277                                                   e );
278             }
279         }
280 
281         if ( logResults )
282         {
283             listeners.add( new LogDiffListener( getLog() ) );
284         }
285 
286         checker.addDiffListener( new DelegatingListener( listeners, minSeverity, getAllIgnored() ) );
287 
288         checker.reportDiffs( origClasses, currentClasses );
289 
290         return listener;
291     }
292 
293 }