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