1 package org.apache.maven.report.projectinfo.dependencies.renderer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.net.URL;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.SecureRandom;
27 import java.text.DecimalFormat;
28 import java.text.DecimalFormatSymbols;
29 import java.text.FieldPosition;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.SortedSet;
41 import java.util.TreeSet;
42
43 import org.apache.commons.lang.SystemUtils;
44 import org.apache.maven.artifact.Artifact;
45 import org.apache.maven.artifact.factory.ArtifactFactory;
46 import org.apache.maven.artifact.repository.ArtifactRepository;
47 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
48 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
49 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
50 import org.apache.maven.doxia.sink.Sink;
51 import org.apache.maven.doxia.util.HtmlTools;
52 import org.apache.maven.model.License;
53 import org.apache.maven.plugin.logging.Log;
54 import org.apache.maven.project.MavenProject;
55 import org.apache.maven.project.MavenProjectBuilder;
56 import org.apache.maven.project.ProjectBuildingException;
57 import org.apache.maven.report.projectinfo.AbstractProjectInfoRenderer;
58 import org.apache.maven.report.projectinfo.ProjectInfoReportUtils;
59 import org.apache.maven.report.projectinfo.dependencies.Dependencies;
60 import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration;
61 import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
62 import org.apache.maven.settings.Settings;
63 import org.apache.maven.shared.dependency.tree.DependencyNode;
64 import org.apache.maven.shared.jar.JarData;
65 import org.codehaus.plexus.i18n.I18N;
66 import org.codehaus.plexus.util.StringUtils;
67
68
69
70
71
72
73
74 public class DependenciesRenderer
75 extends AbstractProjectInfoRenderer
76 {
77
78 private static final String IMG_INFO_URL = "./images/icon_info_sml.gif";
79
80
81 private static final String IMG_CLOSE_URL = "./images/close.gif";
82
83
84 private static final SecureRandom RANDOM;
85
86
87 protected static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat( "#,##0" );
88
89 private static final Set<String> JAR_SUBTYPE = new HashSet<String>();
90
91
92
93
94 private static final String JAVASCRIPT;
95
96 private final DependencyNode dependencyTreeNode;
97
98 private final Dependencies dependencies;
99
100 private final DependenciesReportConfiguration configuration;
101
102 private final Log log;
103
104 private final Settings settings;
105
106 private final RepositoryUtils repoUtils;
107
108
109 private final DecimalFormat fileLengthDecimalFormat;
110
111
112
113
114 private int section;
115
116
117
118
119 private Map<String, Object> licenseMap = new HashMap<String, Object>()
120 {
121 private static final long serialVersionUID = 1L;
122
123
124 public Object put( String key, Object value )
125 {
126
127 @SuppressWarnings( "unchecked" )
128 SortedSet<Object> valueList = (SortedSet<Object>) get( key );
129 if ( valueList == null )
130 {
131 valueList = new TreeSet<Object>();
132 }
133 valueList.add( value );
134 return super.put( key, valueList );
135 }
136 };
137
138 private final ArtifactFactory artifactFactory;
139
140 private final MavenProjectBuilder mavenProjectBuilder;
141
142 private final List<ArtifactRepository> remoteRepositories;
143
144 private final ArtifactRepository localRepository;
145
146 static
147 {
148 JAR_SUBTYPE.add( "jar" );
149 JAR_SUBTYPE.add( "war" );
150 JAR_SUBTYPE.add( "ear" );
151 JAR_SUBTYPE.add( "sar" );
152 JAR_SUBTYPE.add( "rar" );
153 JAR_SUBTYPE.add( "par" );
154 JAR_SUBTYPE.add( "ejb" );
155
156 try
157 {
158 RANDOM = SecureRandom.getInstance( "SHA1PRNG" );
159 }
160 catch ( NoSuchAlgorithmException e )
161 {
162 throw new RuntimeException( e );
163 }
164
165 StringBuffer sb = new StringBuffer();
166 sb.append( "<script language=\"javascript\" type=\"text/javascript\">" ).append( SystemUtils.LINE_SEPARATOR );
167 sb.append( " function toggleDependencyDetail( divId, imgId )" ).append( SystemUtils.LINE_SEPARATOR );
168 sb.append( " {" ).append( SystemUtils.LINE_SEPARATOR );
169 sb.append( " var div = document.getElementById( divId );" ).append( SystemUtils.LINE_SEPARATOR );
170 sb.append( " var img = document.getElementById( imgId );" ).append( SystemUtils.LINE_SEPARATOR );
171 sb.append( " if( div.style.display == '' )" ).append( SystemUtils.LINE_SEPARATOR );
172 sb.append( " {" ).append( SystemUtils.LINE_SEPARATOR );
173 sb.append( " div.style.display = 'none';" ).append( SystemUtils.LINE_SEPARATOR );
174 sb.append( " img.src='" + IMG_INFO_URL + "';" ).append( SystemUtils.LINE_SEPARATOR );
175 sb.append( " }" ).append( SystemUtils.LINE_SEPARATOR );
176 sb.append( " else" ).append( SystemUtils.LINE_SEPARATOR );
177 sb.append( " {" ).append( SystemUtils.LINE_SEPARATOR );
178 sb.append( " div.style.display = '';" ).append( SystemUtils.LINE_SEPARATOR );
179 sb.append( " img.src='" + IMG_CLOSE_URL + "';" ).append( SystemUtils.LINE_SEPARATOR );
180 sb.append( " }" ).append( SystemUtils.LINE_SEPARATOR );
181 sb.append( " }" ).append( SystemUtils.LINE_SEPARATOR );
182 sb.append( "</script>" ).append( SystemUtils.LINE_SEPARATOR );
183 JAVASCRIPT = sb.toString();
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 public DependenciesRenderer( Sink sink, Locale locale, I18N i18n, Log log, Settings settings,
204 Dependencies dependencies, DependencyNode dependencyTreeNode,
205 DependenciesReportConfiguration config, RepositoryUtils repoUtils,
206 ArtifactFactory artifactFactory, MavenProjectBuilder mavenProjectBuilder,
207 List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository )
208 {
209 super( sink, i18n, locale );
210
211 this.log = log;
212 this.settings = settings;
213 this.dependencies = dependencies;
214 this.dependencyTreeNode = dependencyTreeNode;
215 this.repoUtils = repoUtils;
216 this.configuration = config;
217 this.artifactFactory = artifactFactory;
218 this.mavenProjectBuilder = mavenProjectBuilder;
219 this.remoteRepositories = remoteRepositories;
220 this.localRepository = localRepository;
221
222
223 DEFAULT_DECIMAL_FORMAT.setDecimalFormatSymbols( new DecimalFormatSymbols( locale ) );
224
225 this.fileLengthDecimalFormat = new FileDecimalFormat( i18n, locale );
226 this.fileLengthDecimalFormat.setDecimalFormatSymbols( new DecimalFormatSymbols( locale ) );
227 }
228
229 @Override
230 protected String getI18Nsection()
231 {
232 return "dependencies";
233 }
234
235
236
237
238
239 @Override
240 public void renderBody()
241 {
242
243
244 if ( !dependencies.hasDependencies() )
245 {
246 startSection( getTitle() );
247
248
249 paragraph( getI18nString( "nolist" ) );
250
251 endSection();
252
253 return;
254 }
255
256
257 renderSectionProjectDependencies();
258
259
260 renderSectionProjectTransitiveDependencies();
261
262
263 renderSectionProjectDependencyGraph();
264
265
266 renderSectionDependencyLicenseListing();
267
268 if ( configuration.getDependencyDetailsEnabled() )
269 {
270
271 renderSectionDependencyFileDetails();
272 }
273
274 if ( configuration.getDependencyLocationsEnabled() )
275 {
276
277 renderSectionDependencyRepositoryLocations();
278 }
279 }
280
281
282
283
284
285
286
287
288 protected void startSection( String name )
289 {
290 startSection( name, name );
291 }
292
293
294
295
296
297
298
299 protected void startSection( String anchor, String name )
300 {
301 section = section + 1;
302
303 super.sink.anchor( HtmlTools.encodeId( anchor ) );
304 super.sink.anchor_();
305
306 switch ( section )
307 {
308 case 1:
309 sink.section1();
310 sink.sectionTitle1();
311 break;
312 case 2:
313 sink.section2();
314 sink.sectionTitle2();
315 break;
316 case 3:
317 sink.section3();
318 sink.sectionTitle3();
319 break;
320 case 4:
321 sink.section4();
322 sink.sectionTitle4();
323 break;
324 case 5:
325 sink.section5();
326 sink.sectionTitle5();
327 break;
328
329 default:
330
331 break;
332 }
333
334 text( name );
335
336 switch ( section )
337 {
338 case 1:
339 sink.sectionTitle1_();
340 break;
341 case 2:
342 sink.sectionTitle2_();
343 break;
344 case 3:
345 sink.sectionTitle3_();
346 break;
347 case 4:
348 sink.sectionTitle4_();
349 break;
350 case 5:
351 sink.sectionTitle5_();
352 break;
353
354 default:
355
356 break;
357 }
358 }
359
360
361
362
363 protected void endSection()
364 {
365 switch ( section )
366 {
367 case 1:
368 sink.section1_();
369 break;
370 case 2:
371 sink.section2_();
372 break;
373 case 3:
374 sink.section3_();
375 break;
376 case 4:
377 sink.section4_();
378 break;
379 case 5:
380 sink.section5_();
381 break;
382
383 default:
384
385 break;
386 }
387
388 section = section - 1;
389
390 if ( section < 0 )
391 {
392 throw new IllegalStateException( "Too many closing sections" );
393 }
394 }
395
396
397
398
399
400
401
402
403
404
405
406 private String[] getDependencyTableHeader( boolean withClassifier, boolean withOptional )
407 {
408 String groupId = getI18nString( "column.groupId" );
409 String artifactId = getI18nString( "column.artifactId" );
410 String version = getI18nString( "column.version" );
411 String classifier = getI18nString( "column.classifier" );
412 String type = getI18nString( "column.type" );
413 String license = getI18nString( "column.license" );
414 String optional = getI18nString( "column.optional" );
415
416 if ( withClassifier )
417 {
418 if ( withOptional )
419 {
420 return new String[] { groupId, artifactId, version, classifier, type, license, optional };
421 }
422
423 return new String[] { groupId, artifactId, version, classifier, type, license };
424 }
425
426 if ( withOptional )
427 {
428 return new String[] { groupId, artifactId, version, type, license, optional };
429 }
430
431 return new String[] { groupId, artifactId, version, type, license };
432 }
433
434 private void renderSectionProjectDependencies()
435 {
436 startSection( getTitle() );
437
438
439 Map<String, List<Artifact>> dependenciesByScope = dependencies.getDependenciesByScope( false );
440
441 renderDependenciesForAllScopes( dependenciesByScope, false );
442
443 endSection();
444 }
445
446
447
448
449
450
451
452
453
454
455 private void renderDependenciesForAllScopes( Map<String, List<Artifact>> dependenciesByScope, boolean isTransitive )
456 {
457 renderDependenciesForScope( Artifact.SCOPE_COMPILE, dependenciesByScope.get( Artifact.SCOPE_COMPILE ),
458 isTransitive );
459 renderDependenciesForScope( Artifact.SCOPE_RUNTIME, dependenciesByScope.get( Artifact.SCOPE_RUNTIME ),
460 isTransitive );
461 renderDependenciesForScope( Artifact.SCOPE_TEST, dependenciesByScope.get( Artifact.SCOPE_TEST ), isTransitive );
462 renderDependenciesForScope( Artifact.SCOPE_PROVIDED, dependenciesByScope.get( Artifact.SCOPE_PROVIDED ),
463 isTransitive );
464 renderDependenciesForScope( Artifact.SCOPE_SYSTEM, dependenciesByScope.get( Artifact.SCOPE_SYSTEM ),
465 isTransitive );
466 }
467
468 private void renderSectionProjectTransitiveDependencies()
469 {
470 Map<String, List<Artifact>> dependenciesByScope = dependencies.getDependenciesByScope( true );
471
472 startSection( getI18nString( "transitive.title" ) );
473
474 if ( dependenciesByScope.values().isEmpty() )
475 {
476 paragraph( getI18nString( "transitive.nolist" ) );
477 }
478 else
479 {
480 paragraph( getI18nString( "transitive.intro" ) );
481
482 renderDependenciesForAllScopes( dependenciesByScope, true );
483 }
484
485 endSection();
486 }
487
488 private void renderSectionProjectDependencyGraph()
489 {
490 startSection( getI18nString( "graph.title" ) );
491
492
493 renderSectionDependencyTree();
494
495 endSection();
496 }
497
498 private void renderSectionDependencyTree()
499 {
500 sink.rawText( JAVASCRIPT );
501
502
503 startSection( getI18nString( "graph.tree.title" ) );
504
505 sink.list();
506 printDependencyListing( dependencyTreeNode );
507 sink.list_();
508
509 endSection();
510 }
511
512 private void renderSectionDependencyFileDetails()
513 {
514 startSection( getI18nString( "file.details.title" ) );
515
516 List<Artifact> alldeps = dependencies.getAllDependencies();
517 Collections.sort( alldeps, getArtifactComparator() );
518
519
520 String filename = getI18nString( "file.details.column.file" );
521 String size = getI18nString( "file.details.column.size" );
522 String entries = getI18nString( "file.details.column.entries" );
523 String classes = getI18nString( "file.details.column.classes" );
524 String packages = getI18nString( "file.details.column.packages" );
525 String jdkrev = getI18nString( "file.details.column.jdkrev" );
526 String debug = getI18nString( "file.details.column.debug" );
527 String sealed = getI18nString( "file.details.column.sealed" );
528
529 int[] justification =
530 new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_RIGHT,
531 Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER };
532
533 startTable( justification, false );
534
535 TotalCell totaldeps = new TotalCell( DEFAULT_DECIMAL_FORMAT );
536 TotalCell totaldepsize = new TotalCell( fileLengthDecimalFormat );
537 TotalCell totalentries = new TotalCell( DEFAULT_DECIMAL_FORMAT );
538 TotalCell totalclasses = new TotalCell( DEFAULT_DECIMAL_FORMAT );
539 TotalCell totalpackages = new TotalCell( DEFAULT_DECIMAL_FORMAT );
540 double highestjdk = 0.0;
541 TotalCell totaldebug = new TotalCell( DEFAULT_DECIMAL_FORMAT );
542 TotalCell totalsealed = new TotalCell( DEFAULT_DECIMAL_FORMAT );
543
544 boolean hasSealed = hasSealed( alldeps );
545
546
547 String[] tableHeader;
548 if ( hasSealed )
549 {
550 tableHeader = new String[] { filename, size, entries, classes, packages, jdkrev, debug, sealed };
551 }
552 else
553 {
554 tableHeader = new String[] { filename, size, entries, classes, packages, jdkrev, debug };
555 }
556 tableHeader( tableHeader );
557
558
559 for ( Artifact artifact : alldeps )
560 {
561 if ( artifact.getFile() == null )
562 {
563 log.error( "Artifact: " + artifact.getId() + " has no file." );
564 continue;
565 }
566
567 File artifactFile = artifact.getFile();
568
569 totaldeps.incrementTotal( artifact.getScope() );
570 totaldepsize.addTotal( artifactFile.length(), artifact.getScope() );
571
572 if ( JAR_SUBTYPE.contains( artifact.getType().toLowerCase() ) )
573 {
574 try
575 {
576 JarData jarDetails = dependencies.getJarDependencyDetails( artifact );
577
578 String debugstr = "release";
579 if ( jarDetails.isDebugPresent() )
580 {
581 debugstr = "debug";
582 totaldebug.incrementTotal( artifact.getScope() );
583 }
584
585 totalentries.addTotal( jarDetails.getNumEntries(), artifact.getScope() );
586 totalclasses.addTotal( jarDetails.getNumClasses(), artifact.getScope() );
587 totalpackages.addTotal( jarDetails.getNumPackages(), artifact.getScope() );
588
589 try
590 {
591 if ( jarDetails.getJdkRevision() != null )
592 {
593 highestjdk = Math.max( highestjdk, Double.parseDouble( jarDetails.getJdkRevision() ) );
594 }
595 }
596 catch ( NumberFormatException e )
597 {
598
599 }
600
601 String sealedstr = "";
602 if ( jarDetails.isSealed() )
603 {
604 sealedstr = "sealed";
605 totalsealed.incrementTotal( artifact.getScope() );
606 }
607
608 tableRow( hasSealed, new String[] { artifactFile.getName(),
609 fileLengthDecimalFormat.format( artifactFile.length() ),
610 DEFAULT_DECIMAL_FORMAT.format( jarDetails.getNumEntries() ),
611 DEFAULT_DECIMAL_FORMAT.format( jarDetails.getNumClasses() ),
612 DEFAULT_DECIMAL_FORMAT.format( jarDetails.getNumPackages() ), jarDetails.getJdkRevision(),
613 debugstr, sealedstr } );
614 }
615 catch ( IOException e )
616 {
617 createExceptionInfoTableRow( artifact, artifactFile, e, hasSealed );
618 }
619 }
620 else
621 {
622 tableRow( hasSealed, new String[] { artifactFile.getName(),
623 fileLengthDecimalFormat.format( artifactFile.length() ), "", "", "", "", "", "" } );
624 }
625 }
626
627
628 tableHeader[0] = getI18nString( "file.details.total" );
629 tableHeader( tableHeader );
630
631 justification[0] = Sink.JUSTIFY_RIGHT;
632 justification[6] = Sink.JUSTIFY_RIGHT;
633
634 for ( int i = -1; i < TotalCell.SCOPES_COUNT; i++ )
635 {
636 if ( totaldeps.getTotal( i ) > 0 )
637 {
638 tableRow( hasSealed, new String[] { totaldeps.getTotalString( i ), totaldepsize.getTotalString( i ),
639 totalentries.getTotalString( i ), totalclasses.getTotalString( i ),
640 totalpackages.getTotalString( i ), ( i < 0 ) ? String.valueOf( highestjdk ) : "",
641 totaldebug.getTotalString( i ), totalsealed.getTotalString( i ) } );
642 }
643 }
644
645 endTable();
646 endSection();
647 }
648
649 private void tableRow( boolean fullRow, String[] content )
650 {
651 sink.tableRow();
652
653 int count = fullRow ? content.length : ( content.length - 1 );
654
655 for ( int i = 0; i < count; i++ )
656 {
657 tableCell( content[i] );
658 }
659
660 sink.tableRow_();
661 }
662
663 private void createExceptionInfoTableRow( Artifact artifact, File artifactFile, Exception e, boolean hasSealed )
664 {
665 tableRow( hasSealed, new String[] { artifact.getId(), artifactFile.getAbsolutePath(), e.getMessage(), "", "",
666 "", "", "" } );
667 }
668
669 private void populateRepositoryMap( Map<String, ArtifactRepository> repos, List<ArtifactRepository> rowRepos )
670 {
671 for ( ArtifactRepository repo : rowRepos )
672 {
673 repos.put( repo.getId(), repo );
674 }
675 }
676
677 private void blacklistRepositoryMap( Map<String, ArtifactRepository> repos,
678 List<String> repoUrlBlackListed )
679 {
680 for ( ArtifactRepository repo : repos.values() )
681 {
682
683 if ( !repo.isBlacklisted() )
684 {
685 if ( !repoUrlBlackListed.contains( repo.getUrl() ) )
686 {
687 try
688 {
689 URL repoUrl = new URL( repo.getUrl() );
690 if ( ProjectInfoReportUtils.getContent( repoUrl, settings ) == null )
691 {
692 log.warn( "The repository url '" + repoUrl + "' has no stream - Repository '"
693 + repo.getId() + "' will be blacklisted." );
694 repo.setBlacklisted( true );
695 repoUrlBlackListed.add( repo.getUrl() );
696 }
697 }
698 catch ( IOException e )
699 {
700 log.warn( "The repository url '" + repo.getUrl() + "' is invalid - Repository '" + repo.getId()
701 + "' will be blacklisted." );
702 repo.setBlacklisted( true );
703 repoUrlBlackListed.add( repo.getUrl() );
704 }
705 }
706 else
707 {
708 repo.setBlacklisted( true );
709 }
710 }
711 else
712 {
713 repoUrlBlackListed.add( repo.getUrl() );
714 }
715 }
716 }
717
718 @SuppressWarnings( "unchecked" )
719 private void renderSectionDependencyRepositoryLocations()
720 {
721 startSection( getI18nString( "repo.locations.title" ) );
722
723
724 List<Artifact> alldeps = dependencies.getAllDependencies();
725 Collections.sort( alldeps, getArtifactComparator() );
726
727
728 Map<String, ArtifactRepository> repoMap = new HashMap<String, ArtifactRepository>();
729
730 populateRepositoryMap( repoMap, repoUtils.getRemoteArtifactRepositories() );
731 for ( Artifact artifact : alldeps )
732 {
733 try
734 {
735 MavenProject artifactProject = repoUtils.getMavenProjectFromRepository( artifact );
736 populateRepositoryMap( repoMap, artifactProject.getRemoteArtifactRepositories() );
737 }
738 catch ( ProjectBuildingException e )
739 {
740 log.warn( "Unable to create Maven project from repository.", e );
741 }
742 }
743
744 List<String> repoUrlBlackListed = new ArrayList<String>();
745 blacklistRepositoryMap( repoMap, repoUrlBlackListed );
746
747
748
749 printRepositories( repoMap, repoUrlBlackListed );
750
751
752
753 printArtifactsLocations( repoMap, repoUrlBlackListed, alldeps );
754
755 endSection();
756 }
757
758 private void renderSectionDependencyLicenseListing()
759 {
760 startSection( getI18nString( "graph.tables.licenses" ) );
761 printGroupedLicenses();
762 endSection();
763 }
764
765 private void renderDependenciesForScope( String scope, List<Artifact> artifacts, boolean isTransitive )
766 {
767 if ( artifacts != null )
768 {
769 boolean withClassifier = hasClassifier( artifacts );
770 boolean withOptional = hasOptional( artifacts );
771 String[] tableHeader = getDependencyTableHeader( withClassifier, withOptional );
772
773
774 Collections.sort( artifacts, getArtifactComparator() );
775
776 String anchorByScope =
777 ( isTransitive ? getI18nString( "transitive.title" ) + "_" + scope
778 : getI18nString( "title" ) + "_" + scope );
779 startSection( anchorByScope, scope );
780
781 paragraph( getI18nString( "intro." + scope ) );
782
783 startTable();
784 tableHeader( tableHeader );
785 for ( Artifact artifact : artifacts )
786 {
787 renderArtifactRow( artifact, withClassifier, withOptional );
788 }
789 endTable();
790
791 endSection();
792 }
793 }
794
795 private Comparator<Artifact> getArtifactComparator()
796 {
797 return new Comparator<Artifact>()
798 {
799 public int compare( Artifact a1, Artifact a2 )
800 {
801
802 if ( a1.isOptional() && !a2.isOptional() )
803 {
804 return +1;
805 }
806 else if ( !a1.isOptional() && a2.isOptional() )
807 {
808 return -1;
809 }
810 else
811 {
812 return a1.compareTo( a2 );
813 }
814 }
815 };
816 }
817
818
819
820
821
822
823
824 private void renderArtifactRow( Artifact artifact, boolean withClassifier, boolean withOptional )
825 {
826 String isOptional =
827 artifact.isOptional() ? getI18nString( "column.isOptional" )
828 : getI18nString( "column.isNotOptional" );
829
830 String url =
831 ProjectInfoReportUtils.getArtifactUrl( artifactFactory, artifact, mavenProjectBuilder, remoteRepositories,
832 localRepository );
833 String artifactIdCell = ProjectInfoReportUtils.getArtifactIdCell( artifact.getArtifactId(), url );
834
835 MavenProject artifactProject;
836 StringBuffer sb = new StringBuffer();
837 try
838 {
839 artifactProject = repoUtils.getMavenProjectFromRepository( artifact );
840 @SuppressWarnings( "unchecked" )
841 List<License> licenses = artifactProject.getLicenses();
842 for ( Iterator<License> iterator = licenses.iterator(); iterator.hasNext(); )
843 {
844 License license = iterator.next();
845 String artifactIdCell2 = ProjectInfoReportUtils.getArtifactIdCell( license.getName(), license.getUrl() );
846 sb.append( artifactIdCell2 );
847 }
848 }
849 catch ( ProjectBuildingException e )
850 {
851 log.warn( "Unable to create Maven project from repository.", e );
852 }
853
854 String content[];
855 if ( withClassifier )
856 {
857 content =
858 new String[] { artifact.getGroupId(), artifactIdCell, artifact.getVersion(), artifact.getClassifier(),
859 artifact.getType(), sb.toString(), isOptional };
860 }
861 else
862 {
863 content =
864 new String[] { artifact.getGroupId(), artifactIdCell, artifact.getVersion(), artifact.getType(),
865 sb.toString(), isOptional };
866 }
867
868 tableRow( withOptional, content );
869 }
870
871 private void printDependencyListing( DependencyNode node )
872 {
873 Artifact artifact = node.getArtifact();
874 String id = artifact.getId();
875 String dependencyDetailId = getUUID();
876 String imgId = getUUID();
877
878 sink.listItem();
879
880 sink.text( id + ( StringUtils.isNotEmpty( artifact.getScope() ) ? " (" + artifact.getScope() + ") " : " " ) );
881 sink.rawText( "<img id=\"" + imgId + "\" src=\"" + IMG_INFO_URL
882 + "\" alt=\"Information\" onclick=\"toggleDependencyDetail( '" + dependencyDetailId + "', '" + imgId
883 + "' );\" style=\"cursor: pointer;vertical-align:text-bottom;\"></img>" );
884
885 printDescriptionsAndURLs( node, dependencyDetailId );
886
887 if ( !node.getChildren().isEmpty() )
888 {
889 boolean toBeIncluded = false;
890 List<DependencyNode> subList = new ArrayList<DependencyNode>();
891 for ( @SuppressWarnings( "unchecked" )
892 Iterator<DependencyNode> deps = node.getChildren().iterator(); deps.hasNext(); )
893 {
894 DependencyNode dep = deps.next();
895
896 if ( !dependencies.getAllDependencies().contains( dep.getArtifact() ) )
897 {
898 continue;
899 }
900
901 subList.add( dep );
902 toBeIncluded = true;
903 }
904
905 if ( toBeIncluded )
906 {
907 sink.list();
908 for ( DependencyNode dep : subList )
909 {
910 printDependencyListing( dep );
911 }
912 sink.list_();
913 }
914 }
915
916 sink.listItem_();
917 }
918
919 private void printDescriptionsAndURLs( DependencyNode node, String uid )
920 {
921 Artifact artifact = node.getArtifact();
922 String id = artifact.getId();
923 String unknownLicenseMessage = getI18nString( "graph.tables.unknown" );
924
925 sink.rawText( "<div id=\"" + uid + "\" style=\"display:none\">" );
926
927 sink.table();
928
929 if ( !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
930 {
931 try
932 {
933 MavenProject artifactProject = repoUtils.getMavenProjectFromRepository( artifact );
934 String artifactDescription = artifactProject.getDescription();
935 String artifactUrl = artifactProject.getUrl();
936 String artifactName = artifactProject.getName();
937 @SuppressWarnings( "unchecked" )
938 List<License> licenses = artifactProject.getLicenses();
939
940 sink.tableRow();
941 sink.tableHeaderCell();
942 sink.text( artifactName );
943 sink.tableHeaderCell_();
944 sink.tableRow_();
945
946 sink.tableRow();
947 sink.tableCell();
948
949 sink.paragraph();
950 sink.bold();
951 sink.text( getI18nString( "column.description" ) + ": " );
952 sink.bold_();
953 if ( StringUtils.isNotEmpty( artifactDescription ) )
954 {
955 sink.text( artifactDescription );
956 }
957 else
958 {
959 sink.text( getI18nString( "index", "nodescription" ) );
960 }
961 sink.paragraph_();
962
963 if ( StringUtils.isNotEmpty( artifactUrl ) )
964 {
965 sink.paragraph();
966 sink.bold();
967 sink.text( getI18nString( "column.url" ) + ": " );
968 sink.bold_();
969 if ( ProjectInfoReportUtils.isArtifactUrlValid( artifactUrl ) )
970 {
971 sink.link( artifactUrl );
972 sink.text( artifactUrl );
973 sink.link_();
974 }
975 else
976 {
977 sink.text( artifactUrl );
978 }
979 sink.paragraph_();
980 }
981
982 sink.paragraph();
983 sink.bold();
984 sink.text( getI18nString( "license", "title" ) + ": " );
985 sink.bold_();
986 if ( !licenses.isEmpty() )
987 {
988 for ( License element : licenses )
989 {
990 String licenseName = element.getName();
991 String licenseUrl = element.getUrl();
992
993 if ( licenseUrl != null )
994 {
995 sink.link( licenseUrl );
996 }
997 sink.text( licenseName );
998
999 if ( licenseUrl != null )
1000 {
1001 sink.link_();
1002 }
1003
1004 licenseMap.put( licenseName, artifactName );
1005 }
1006 }
1007 else
1008 {
1009 sink.text( getI18nString( "license", "nolicense" ) );
1010
1011 licenseMap.put( unknownLicenseMessage, artifactName );
1012 }
1013 sink.paragraph_();
1014 }
1015 catch ( ProjectBuildingException e )
1016 {
1017 log.warn( "Unable to create Maven project from repository.", e );
1018 }
1019 }
1020 else
1021 {
1022 sink.tableRow();
1023 sink.tableHeaderCell();
1024 sink.text( id );
1025 sink.tableHeaderCell_();
1026 sink.tableRow_();
1027
1028 sink.tableRow();
1029 sink.tableCell();
1030
1031 sink.paragraph();
1032 sink.bold();
1033 sink.text( getI18nString( "column.description" ) + ": " );
1034 sink.bold_();
1035 sink.text( getI18nString( "index", "nodescription" ) );
1036 sink.paragraph_();
1037
1038 if ( artifact.getFile() != null )
1039 {
1040 sink.paragraph();
1041 sink.bold();
1042 sink.text( getI18nString( "column.url" ) + ": " );
1043 sink.bold_();
1044 sink.text( artifact.getFile().getAbsolutePath() );
1045 sink.paragraph_();
1046 }
1047 }
1048
1049 sink.tableCell_();
1050 sink.tableRow_();
1051
1052 sink.table_();
1053
1054 sink.rawText( "</div>" );
1055 }
1056
1057 private void printGroupedLicenses()
1058 {
1059 for ( Map.Entry<String, Object> entry : licenseMap.entrySet() )
1060 {
1061 String licenseName = entry.getKey();
1062 sink.paragraph();
1063 sink.bold();
1064 if ( StringUtils.isEmpty( licenseName ) )
1065 {
1066 sink.text( getI18nString( "unamed" ) );
1067 }
1068 else
1069 {
1070 sink.text( licenseName );
1071 }
1072 sink.text( ": " );
1073 sink.bold_();
1074
1075 @SuppressWarnings( "unchecked" )
1076 SortedSet<String> projects = (SortedSet<String>) entry.getValue();
1077
1078 for ( Iterator<String> iterator = projects.iterator(); iterator.hasNext(); )
1079 {
1080 String projectName = iterator.next();
1081 sink.text( projectName );
1082 if ( iterator.hasNext() )
1083 {
1084 sink.text( ", " );
1085 }
1086 }
1087
1088 sink.paragraph_();
1089 }
1090 }
1091
1092 private void printRepositories( Map<String, ArtifactRepository> repoMap, List<String> repoUrlBlackListed )
1093 {
1094
1095 String repoid = getI18nString( "repo.locations.column.repoid" );
1096 String url = getI18nString( "repo.locations.column.url" );
1097 String release = getI18nString( "repo.locations.column.release" );
1098 String snapshot = getI18nString( "repo.locations.column.snapshot" );
1099 String blacklisted = getI18nString( "repo.locations.column.blacklisted" );
1100 String releaseEnabled = getI18nString( "repo.locations.cell.release.enabled" );
1101 String releaseDisabled = getI18nString( "repo.locations.cell.release.disabled" );
1102 String snapshotEnabled = getI18nString( "repo.locations.cell.snapshot.enabled" );
1103 String snapshotDisabled = getI18nString( "repo.locations.cell.snapshot.disabled" );
1104 String blacklistedEnabled = getI18nString( "repo.locations.cell.blacklisted.enabled" );
1105 String blacklistedDisabled = getI18nString( "repo.locations.cell.blacklisted.disabled" );
1106
1107
1108
1109 String[] tableHeader;
1110 int[] justificationRepo;
1111 if ( repoUrlBlackListed.isEmpty() )
1112 {
1113 tableHeader = new String[] { repoid, url, release, snapshot };
1114 justificationRepo =
1115 new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_LEFT, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER };
1116 }
1117 else
1118 {
1119 tableHeader = new String[] { repoid, url, release, snapshot, blacklisted };
1120 justificationRepo =
1121 new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_LEFT, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER,
1122 Sink.JUSTIFY_CENTER };
1123 }
1124
1125 startTable( justificationRepo, false );
1126
1127 tableHeader( tableHeader );
1128
1129
1130
1131 for ( ArtifactRepository repo : repoMap.values() )
1132 {
1133 sink.tableRow();
1134 tableCell( repo.getId() );
1135
1136 sink.tableCell();
1137 if ( repo.isBlacklisted() )
1138 {
1139 sink.text( repo.getUrl() );
1140 }
1141 else
1142 {
1143 sink.link( repo.getUrl() );
1144 sink.text( repo.getUrl() );
1145 sink.link_();
1146 }
1147 sink.tableCell_();
1148
1149 ArtifactRepositoryPolicy releasePolicy = repo.getReleases();
1150 tableCell( releasePolicy.isEnabled() ? releaseEnabled : releaseDisabled );
1151
1152 ArtifactRepositoryPolicy snapshotPolicy = repo.getSnapshots();
1153 tableCell( snapshotPolicy.isEnabled() ? snapshotEnabled : snapshotDisabled );
1154
1155 tableCell( repoUrlBlackListed.contains( repo.getUrl() ) ? blacklistedEnabled : blacklistedDisabled );
1156
1157 sink.tableRow_();
1158 }
1159
1160 endTable();
1161 }
1162
1163 private void printArtifactsLocations( Map<String, ArtifactRepository> repoMap, List<String> repoUrlBlackListed, List<Artifact> alldeps )
1164 {
1165
1166 String artifact = getI18nString( "repo.locations.column.artifact" );
1167
1168 sink.paragraph();
1169 sink.text( getI18nString( "repo.locations.artifact.breakdown" ) );
1170 sink.paragraph_();
1171
1172 List<String> repoIdList = new ArrayList<String>();
1173
1174 for ( Map.Entry<String, ArtifactRepository> entry : repoMap.entrySet() )
1175 {
1176 String repokey = entry.getKey();
1177 ArtifactRepository repo = entry.getValue();
1178 if ( !(repo.isBlacklisted() || repoUrlBlackListed.contains( repo.getUrl() ) ) )
1179 {
1180 repoIdList.add( repokey );
1181 }
1182 }
1183
1184 String[] tableHeader = new String[repoIdList.size() + 1];
1185 int[] justificationRepo = new int[repoIdList.size() + 1];
1186
1187 tableHeader[0] = artifact;
1188 justificationRepo[0] = Sink.JUSTIFY_LEFT;
1189
1190 int idnum = 1;
1191 for ( String id : repoIdList )
1192 {
1193 tableHeader[idnum] = id;
1194 justificationRepo[idnum] = Sink.JUSTIFY_CENTER;
1195 idnum++;
1196 }
1197
1198 Map<String, Integer> totalByRepo = new HashMap<String, Integer>();
1199 TotalCell totaldeps = new TotalCell( DEFAULT_DECIMAL_FORMAT );
1200
1201 startTable( justificationRepo, false );
1202
1203 tableHeader( tableHeader );
1204
1205 for ( Artifact dependency : alldeps )
1206 {
1207 totaldeps.incrementTotal( dependency.getScope() );
1208
1209 sink.tableRow();
1210
1211 if ( !Artifact.SCOPE_SYSTEM.equals( dependency.getScope() ) )
1212 {
1213 tableCell( dependency.getId() );
1214
1215 for ( String repokey : repoIdList )
1216 {
1217 ArtifactRepository repo = repoMap.get( repokey );
1218
1219 String depUrl = repoUtils.getDependencyUrlFromRepository( dependency, repo );
1220
1221 @SuppressWarnings( "cast" )
1222 Integer old = (Integer) totalByRepo.get( repokey );
1223 if ( old == null )
1224 {
1225 totalByRepo.put( repokey, new Integer( 0 ) );
1226 old = new Integer( 0 );
1227 }
1228
1229 boolean dependencyExists = false;
1230
1231 if ( ( dependency.isSnapshot() && repo.getSnapshots().isEnabled() )
1232 || ( !dependency.isSnapshot() && repo.getReleases().isEnabled() ) )
1233 {
1234 dependencyExists = repoUtils.dependencyExistsInRepo( repo, dependency );
1235 }
1236
1237 if ( dependencyExists )
1238 {
1239 sink.tableCell();
1240 if ( StringUtils.isNotEmpty( depUrl ) )
1241 {
1242 sink.link( depUrl );
1243 }
1244 else
1245 {
1246 sink.text( depUrl );
1247 }
1248
1249 sink.figure();
1250 sink.figureCaption();
1251 sink.text( "Found at " + repo.getUrl() );
1252 sink.figureCaption_();
1253 sink.figureGraphics( "images/icon_success_sml.gif" );
1254 sink.figure_();
1255
1256 sink.link_();
1257 sink.tableCell_();
1258
1259 totalByRepo.put( repokey, new Integer( old.intValue() + 1 ) );
1260 }
1261 else
1262 {
1263 tableCell( "-" );
1264 }
1265 }
1266 }
1267 else
1268 {
1269 tableCell( dependency.getId() );
1270
1271 for ( @SuppressWarnings( "unused" ) String repoId : repoIdList )
1272 {
1273 tableCell( "-" );
1274 }
1275 }
1276
1277 sink.tableRow_();
1278 }
1279
1280
1281
1282
1283 tableHeader[0] = getI18nString( "file.details.total" );
1284 tableHeader( tableHeader );
1285 String[] totalRow = new String[repoIdList.size() + 1];
1286 totalRow[0] = totaldeps.toString();
1287 idnum = 1;
1288 for ( String repokey : repoIdList )
1289 {
1290 Integer deps = totalByRepo.get( repokey );
1291 totalRow[idnum++] = deps != null ? deps.toString() : "0";
1292 }
1293
1294 tableRow( totalRow );
1295
1296 endTable();
1297 }
1298
1299
1300
1301
1302
1303 private boolean hasClassifier( List<Artifact> artifacts )
1304 {
1305 for ( Artifact artifact : artifacts )
1306 {
1307 if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
1308 {
1309 return true;
1310 }
1311 }
1312
1313 return false;
1314 }
1315
1316
1317
1318
1319
1320 private boolean hasOptional( List<Artifact> artifacts )
1321 {
1322 for ( Artifact artifact : artifacts )
1323 {
1324 if ( artifact.isOptional() )
1325 {
1326 return true;
1327 }
1328 }
1329
1330 return false;
1331 }
1332
1333
1334
1335
1336
1337 private boolean hasSealed( List<Artifact> artifacts )
1338 {
1339 for ( Artifact artifact : artifacts )
1340 {
1341
1342 if ( artifact.getFile() == null && !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
1343 {
1344 try
1345 {
1346 repoUtils.resolve( artifact );
1347 }
1348 catch ( ArtifactResolutionException e )
1349 {
1350 log.error( "Artifact: " + artifact.getId() + " has no file.", e );
1351 continue;
1352 }
1353 catch ( ArtifactNotFoundException e )
1354 {
1355 if ( ( dependencies.getProject().getGroupId().equals( artifact.getGroupId() ) )
1356 && ( dependencies.getProject().getArtifactId().equals( artifact.getArtifactId() ) )
1357 && ( dependencies.getProject().getVersion().equals( artifact.getVersion() ) ) )
1358 {
1359 log.warn( "The artifact of this project has never been deployed." );
1360 }
1361 else
1362 {
1363 log.error( "Artifact: " + artifact.getId() + " has no file.", e );
1364 }
1365
1366 continue;
1367 }
1368 }
1369
1370 if ( JAR_SUBTYPE.contains( artifact.getType().toLowerCase() ) )
1371 {
1372 try
1373 {
1374 JarData jarDetails = dependencies.getJarDependencyDetails( artifact );
1375 if ( jarDetails.isSealed() )
1376 {
1377 return true;
1378 }
1379 }
1380 catch ( IOException e )
1381 {
1382 log.error( "IOException: " + e.getMessage(), e );
1383 }
1384 }
1385 }
1386 return false;
1387 }
1388
1389
1390
1391
1392
1393 private static String getUUID()
1394 {
1395 return "_" + Math.abs( RANDOM.nextInt() );
1396 }
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409 static class FileDecimalFormat
1410 extends DecimalFormat
1411 {
1412 private static final long serialVersionUID = 4062503546523610081L;
1413
1414 private final I18N i18n;
1415
1416 private final Locale locale;
1417
1418
1419
1420
1421
1422
1423
1424 public FileDecimalFormat( I18N i18n, Locale locale )
1425 {
1426 super( "#,###.00" );
1427
1428 this.i18n = i18n;
1429 this.locale = locale;
1430 }
1431
1432
1433 public StringBuffer format( long fs, StringBuffer result, FieldPosition fieldPosition )
1434 {
1435 if ( fs > 1024 * 1024 * 1024 )
1436 {
1437 result = super.format( (float) fs / ( 1024 * 1024 * 1024 ), result, fieldPosition );
1438 result.append( " " ).append( getString( "report.dependencies.file.details.column.size.gb" ) );
1439 return result;
1440 }
1441
1442 if ( fs > 1024 * 1024 )
1443 {
1444 result = super.format( (float) fs / ( 1024 * 1024 ), result, fieldPosition );
1445 result.append( " " ).append( getString( "report.dependencies.file.details.column.size.mb" ) );
1446 return result;
1447 }
1448
1449 result = super.format( (float) fs / ( 1024 ), result, fieldPosition );
1450 result.append( " " ).append( getString( "report.dependencies.file.details.column.size.kb" ) );
1451 return result;
1452 }
1453
1454 private String getString( String key )
1455 {
1456 return i18n.getString( "project-info-report", locale, key );
1457 }
1458 }
1459
1460
1461
1462
1463 static class TotalCell
1464 {
1465 static final int SCOPES_COUNT = 5;
1466
1467 final DecimalFormat decimalFormat;
1468
1469 long total = 0;
1470
1471 long totalCompileScope = 0;
1472
1473 long totalTestScope = 0;
1474
1475 long totalRuntimeScope = 0;
1476
1477 long totalProvidedScope = 0;
1478
1479 long totalSystemScope = 0;
1480
1481 TotalCell( DecimalFormat decimalFormat )
1482 {
1483 this.decimalFormat = decimalFormat;
1484 }
1485
1486 void incrementTotal( String scope )
1487 {
1488 addTotal( 1, scope );
1489 }
1490
1491 static String getScope( int index )
1492 {
1493 switch ( index )
1494 {
1495 case 0:
1496 return Artifact.SCOPE_COMPILE;
1497 case 1:
1498 return Artifact.SCOPE_TEST;
1499 case 2:
1500 return Artifact.SCOPE_RUNTIME;
1501 case 3:
1502 return Artifact.SCOPE_PROVIDED;
1503 case 4:
1504 return Artifact.SCOPE_SYSTEM;
1505 default:
1506 return null;
1507 }
1508 }
1509
1510 long getTotal( int index )
1511 {
1512 switch ( index )
1513 {
1514 case 0:
1515 return totalCompileScope;
1516 case 1:
1517 return totalTestScope;
1518 case 2:
1519 return totalRuntimeScope;
1520 case 3:
1521 return totalProvidedScope;
1522 case 4:
1523 return totalSystemScope;
1524 default:
1525 return total;
1526 }
1527 }
1528
1529 String getTotalString( int index )
1530 {
1531 long totalString = getTotal( index );
1532
1533 if ( totalString <= 0 )
1534 {
1535 return "";
1536 }
1537
1538 StringBuffer sb = new StringBuffer();
1539 if ( index >= 0 )
1540 {
1541 sb.append( getScope( index ) ).append( ": " );
1542 }
1543 sb.append( decimalFormat.format( getTotal( index ) ) );
1544 return sb.toString();
1545 }
1546
1547 void addTotal( long add, String scope )
1548 {
1549 total += add;
1550
1551 if ( Artifact.SCOPE_COMPILE.equals( scope ) )
1552 {
1553 totalCompileScope += add;
1554 }
1555 else if ( Artifact.SCOPE_TEST.equals( scope ) )
1556 {
1557 totalTestScope += add;
1558 }
1559 else if ( Artifact.SCOPE_RUNTIME.equals( scope ) )
1560 {
1561 totalRuntimeScope += add;
1562 }
1563 else if ( Artifact.SCOPE_PROVIDED.equals( scope ) )
1564 {
1565 totalProvidedScope += add;
1566 }
1567 else if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
1568 {
1569 totalSystemScope += add;
1570 }
1571 }
1572
1573
1574 public String toString()
1575 {
1576 StringBuffer sb = new StringBuffer();
1577 sb.append( decimalFormat.format( total ) );
1578 sb.append( " (" );
1579
1580 boolean needSeparator = false;
1581 for ( int i = 0; i < SCOPES_COUNT; i++ )
1582 {
1583 if ( getTotal( i ) > 0 )
1584 {
1585 if ( needSeparator )
1586 {
1587 sb.append( ", " );
1588 }
1589 sb.append( getTotalString( i ) );
1590 needSeparator = true;
1591 }
1592 }
1593
1594 sb.append( ")" );
1595
1596 return sb.toString();
1597 }
1598 }
1599 }