View Javadoc
1   package org.apache.maven.report.projectinfo.dependencies.renderer;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
31  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
32  import org.apache.maven.artifact.versioning.ArtifactVersion;
33  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
34  import org.apache.maven.artifact.versioning.VersionRange;
35  import org.apache.maven.doxia.sink.Sink;
36  import org.apache.maven.model.Dependency;
37  import org.apache.maven.model.License;
38  import org.apache.maven.plugin.logging.Log;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.project.ProjectBuilder;
41  import org.apache.maven.project.ProjectBuildingException;
42  import org.apache.maven.project.ProjectBuildingRequest;
43  import org.apache.maven.report.projectinfo.AbstractProjectInfoRenderer;
44  import org.apache.maven.report.projectinfo.ProjectInfoReportUtils;
45  import org.apache.maven.report.projectinfo.dependencies.ManagementDependencies;
46  import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
47  import org.apache.maven.repository.RepositorySystem;
48  import org.codehaus.plexus.i18n.I18N;
49  import org.codehaus.plexus.util.StringUtils;
50  
51  /**
52   * @author Nick Stolwijk
53   * @since 2.1
54   */
55  public class DependencyManagementRenderer
56      extends AbstractProjectInfoRenderer
57  {
58      private final ManagementDependencies dependencies;
59  
60      private final Log log;
61  
62      private final ArtifactMetadataSource artifactMetadataSource;
63  
64      private final RepositorySystem repositorySystem;
65  
66      private final ProjectBuilder projectBuilder;
67  
68      private final ProjectBuildingRequest buildingRequest;
69  
70      private final RepositoryUtils repoUtils;
71  
72      /**
73       * Default constructor
74       *
75       * @param sink {@link Sink}
76       * @param locale {@link Locale}
77       * @param i18n {@link I18N}
78       * @param log {@link Log}
79       * @param dependencies {@link ManagementDependencies}
80       * @param artifactMetadataSource {@link ArtifactMetadataSource}
81       * @param repositorySystem {@link RepositorySystem}
82       * @param projectBuilder {@link ProjectBuilder}
83       * @param buildingRequest {@link ProjectBuildingRequest}
84       * @param repoUtils {@link RepositoryUtils}
85       */
86      public DependencyManagementRenderer( Sink sink, Locale locale, I18N i18n, Log log,
87                                           ManagementDependencies dependencies,
88                                           ArtifactMetadataSource artifactMetadataSource,
89                                           RepositorySystem repositorySystem, ProjectBuilder projectBuilder,
90                                           ProjectBuildingRequest buildingRequest, RepositoryUtils repoUtils )
91      {
92          super( sink, i18n, locale );
93  
94          this.log = log;
95          this.dependencies = dependencies;
96          this.artifactMetadataSource = artifactMetadataSource;
97          this.repositorySystem = repositorySystem;
98          this.projectBuilder = projectBuilder;
99          this.buildingRequest = buildingRequest;
100         this.repoUtils = repoUtils;
101     }
102 
103     // ----------------------------------------------------------------------
104     // Public methods
105     // ----------------------------------------------------------------------
106 
107     @Override
108     protected String getI18Nsection()
109     {
110         return "dependency-management";
111     }
112 
113     @Override
114     public void renderBody()
115     {
116         // Dependencies report
117 
118         if ( !dependencies.hasDependencies() )
119         {
120             startSection( getTitle() );
121 
122             paragraph( getI18nString( "nolist" ) );
123 
124             endSection();
125 
126             return;
127         }
128 
129         // === Section: Project Dependencies.
130         renderSectionProjectDependencies();
131     }
132 
133     // ----------------------------------------------------------------------
134     // Private methods
135     // ----------------------------------------------------------------------
136 
137     private void renderSectionProjectDependencies()
138     {
139         startSection( getTitle() );
140 
141         // collect dependencies by scope
142         Map<String, List<Dependency>> dependenciesByScope = dependencies.getManagementDependenciesByScope();
143 
144         renderDependenciesForAllScopes( dependenciesByScope );
145 
146         endSection();
147     }
148 
149     private void renderDependenciesForAllScopes( Map<String, List<Dependency>> dependenciesByScope )
150     {
151         renderDependenciesForScope( Artifact.SCOPE_COMPILE, dependenciesByScope.get( Artifact.SCOPE_COMPILE ) );
152         renderDependenciesForScope( Artifact.SCOPE_RUNTIME, dependenciesByScope.get( Artifact.SCOPE_RUNTIME ) );
153         renderDependenciesForScope( Artifact.SCOPE_TEST, dependenciesByScope.get( Artifact.SCOPE_TEST ) );
154         renderDependenciesForScope( Artifact.SCOPE_PROVIDED, dependenciesByScope.get( Artifact.SCOPE_PROVIDED ) );
155         renderDependenciesForScope( Artifact.SCOPE_SYSTEM, dependenciesByScope.get( Artifact.SCOPE_SYSTEM ) );
156     }
157 
158     private String[] getDependencyTableHeader( boolean hasClassifier )
159     {
160         String groupId = getI18nString( "column.groupId" );
161         String artifactId = getI18nString( "column.artifactId" );
162         String version = getI18nString( "column.version" );
163         String classifier = getI18nString( "column.classifier" );
164         String type = getI18nString( "column.type" );
165         String license = getI18nString( "column.license" );
166 
167         if ( hasClassifier )
168         {
169             return new String[] { groupId, artifactId, version, classifier, type, license };
170         }
171 
172         return new String[] { groupId, artifactId, version, type, license };
173     }
174 
175     private void renderDependenciesForScope( String scope, List<Dependency> artifacts )
176     {
177         if ( artifacts != null )
178         {
179             // can't use straight artifact comparison because we want optional last
180             Collections.sort( artifacts, getDependencyComparator() );
181 
182             startSection( scope );
183 
184             paragraph( getI18nString( "intro." + scope ) );
185             startTable();
186 
187             boolean hasClassifier = false;
188             for ( Dependency dependency : artifacts )
189             {
190                 if ( StringUtils.isNotEmpty( dependency.getClassifier() ) )
191                 {
192                     hasClassifier = true;
193                     break;
194                 }
195             }
196 
197             String[] tableHeader = getDependencyTableHeader( hasClassifier );
198             tableHeader( tableHeader );
199 
200             for ( Dependency dependency : artifacts )
201             {
202                 tableRow( getDependencyRow( dependency, hasClassifier ) );
203             }
204             endTable();
205 
206             endSection();
207         }
208     }
209 
210     @SuppressWarnings( "unchecked" )
211     private String[] getDependencyRow( Dependency dependency, boolean hasClassifier )
212     {
213         Artifact artifact =
214             repositorySystem.createArtifact( dependency.getGroupId(), dependency.getArtifactId(),
215                                              dependency.getVersion(), dependency.getScope(), dependency.getType() );
216 
217         StringBuilder licensesBuffer = new StringBuilder();
218         String url = null;
219         try
220         {
221             VersionRange range = VersionRange.createFromVersionSpec( dependency.getVersion() );
222 
223             if ( range.getRecommendedVersion() == null )
224             {
225                 // MPIR-216: no direct version but version range: need to choose one precise version
226                 log.debug( "Resolving range for DependencyManagement on " + artifact.getId() );
227 
228                 List<ArtifactVersion> versions =
229                     artifactMetadataSource.retrieveAvailableVersions( artifact, buildingRequest.getLocalRepository(),
230                                                                       buildingRequest.getRemoteRepositories() );
231 
232                 // only use versions from range
233                 for ( Iterator<ArtifactVersion> iter = versions.iterator(); iter.hasNext(); )
234                 {
235                     if ( ! range.containsVersion( iter.next() ) )
236                     {
237                         iter.remove();
238                     }
239                 }
240 
241                 // select latest, assuming pom information will be the most accurate
242                 if ( !versions.isEmpty() )
243                 {
244                     ArtifactVersion maxArtifactVersion = Collections.max( versions );
245 
246                     artifact.setVersion( maxArtifactVersion.toString() );
247                     log.debug( "DependencyManagement resolved: " + artifact.getId() );
248                 }
249             }
250 
251             url = ProjectInfoReportUtils.getArtifactUrl( repositorySystem, artifact, projectBuilder, buildingRequest );
252 
253             MavenProject artifactProject = repoUtils.getMavenProjectFromRepository( artifact );
254 
255             List<License> licenses = artifactProject.getLicenses();
256             for ( License license : licenses )
257             {
258                 String licenseCell = ProjectInfoReportUtils.getArtifactIdCell( license.getName(), license.getUrl() );
259                 if ( licensesBuffer.length() > 0 )
260                 {
261                     licensesBuffer.append( ", " );
262                 }
263                 licensesBuffer.append( licenseCell );
264             }
265         }
266         catch ( InvalidVersionSpecificationException e )
267         {
268             log.warn( "Unable to parse version for " + artifact.getId(), e );
269         }
270         catch ( ArtifactMetadataRetrievalException e )
271         {
272             log.warn( "Unable to retrieve versions for " + artifact.getId() + " from repository.", e );
273         }
274         catch ( ProjectBuildingException e )
275         {
276             if ( log.isDebugEnabled() ) 
277             {
278                 log.warn( "Unable to create Maven project for " + artifact.getId() + " from repository.", e );
279             } 
280             else 
281             {
282                 log.warn( "Unable to create Maven project for " + artifact.getId() + " from repository." );
283             }
284         }
285 
286         String artifactIdCell = ProjectInfoReportUtils.getArtifactIdCell( artifact.getArtifactId(), url );
287 
288         if ( hasClassifier )
289         {
290             return new String[] { dependency.getGroupId(), artifactIdCell, dependency.getVersion(),
291                 dependency.getClassifier(), dependency.getType(), licensesBuffer.toString() };
292         }
293 
294         return new String[] { dependency.getGroupId(), artifactIdCell, dependency.getVersion(),
295             dependency.getType(), licensesBuffer.toString() };
296     }
297 
298     private Comparator<Dependency> getDependencyComparator()
299     {
300         return new Comparator<Dependency>()
301         {
302             public int compare( Dependency a1, Dependency a2 )
303             {
304                 int result = a1.getGroupId().compareTo( a2.getGroupId() );
305                 if ( result != 0 )
306                 {
307                     return result;
308                 }
309 
310                 result = a1.getArtifactId().compareTo( a2.getArtifactId() );
311                 if ( result != 0 )
312                 {
313                     return result;
314                 }
315 
316                 result = a1.getType().compareTo( a2.getType() );
317                 if ( result != 0 )
318                 {
319                     return result;
320                 }
321 
322                 if ( a1.getClassifier() == null )
323                 {
324                     if ( a2.getClassifier() != null )
325                     {
326                         return 1;
327                     }
328                 }
329                 else
330                 {
331                     if ( a2.getClassifier() != null )
332                     {
333                         result = a1.getClassifier().compareTo( a2.getClassifier() );
334                     }
335                     else
336                     {
337                         return -1;
338                     }
339                 }
340 
341                 if ( result != 0 )
342                 {
343                     return result;
344                 }
345 
346                 // We don't consider the version range in the comparison, just the resolved version
347                 return a1.getVersion().compareTo( a2.getVersion() );
348             }
349         };
350     }
351 }