1 package org.apache.maven.plugins.javadoc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.lang3.BooleanUtils;
23 import org.apache.commons.lang3.ClassUtils;
24 import org.apache.commons.lang3.StringUtils;
25 import org.apache.maven.archiver.MavenArchiver;
26 import org.apache.maven.artifact.Artifact;
27 import org.apache.maven.artifact.ArtifactUtils;
28 import org.apache.maven.artifact.handler.ArtifactHandler;
29 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
30 import org.apache.maven.artifact.repository.ArtifactRepository;
31 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
32 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
33 import org.apache.maven.artifact.versioning.ArtifactVersion;
34 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
35 import org.apache.maven.execution.MavenSession;
36 import org.apache.maven.model.Dependency;
37 import org.apache.maven.model.Plugin;
38 import org.apache.maven.model.Resource;
39 import org.apache.maven.plugin.AbstractMojo;
40 import org.apache.maven.plugin.MojoExecution;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugin.MojoFailureException;
43 import org.apache.maven.plugins.annotations.Component;
44 import org.apache.maven.plugins.annotations.Parameter;
45 import org.apache.maven.plugins.javadoc.options.BootclasspathArtifact;
46 import org.apache.maven.plugins.javadoc.options.DocletArtifact;
47 import org.apache.maven.plugins.javadoc.options.Group;
48 import org.apache.maven.plugins.javadoc.options.JavadocOptions;
49 import org.apache.maven.plugins.javadoc.options.JavadocPathArtifact;
50 import org.apache.maven.plugins.javadoc.options.OfflineLink;
51 import org.apache.maven.plugins.javadoc.options.ResourcesArtifact;
52 import org.apache.maven.plugins.javadoc.options.Tag;
53 import org.apache.maven.plugins.javadoc.options.Taglet;
54 import org.apache.maven.plugins.javadoc.options.TagletArtifact;
55 import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Writer;
56 import org.apache.maven.plugins.javadoc.resolver.JavadocBundle;
57 import org.apache.maven.plugins.javadoc.resolver.ResourceResolver;
58 import org.apache.maven.plugins.javadoc.resolver.SourceResolverConfig;
59 import org.apache.maven.project.DefaultProjectBuildingRequest;
60 import org.apache.maven.project.MavenProject;
61 import org.apache.maven.project.ProjectBuilder;
62 import org.apache.maven.project.ProjectBuildingException;
63 import org.apache.maven.project.ProjectBuildingRequest;
64 import org.apache.maven.reporting.MavenReportException;
65 import org.apache.maven.settings.Proxy;
66 import org.apache.maven.settings.Settings;
67 import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
68 import org.apache.maven.shared.artifact.filter.resolve.AndFilter;
69 import org.apache.maven.shared.artifact.filter.resolve.PatternExclusionsFilter;
70 import org.apache.maven.shared.artifact.filter.resolve.PatternInclusionsFilter;
71 import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
72 import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
73 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
74 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
75 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
76 import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate;
77 import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
78 import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
79 import org.apache.maven.shared.invoker.MavenInvocationException;
80 import org.apache.maven.toolchain.Toolchain;
81 import org.apache.maven.toolchain.ToolchainManager;
82 import org.apache.maven.wagon.PathUtils;
83 import org.codehaus.plexus.archiver.ArchiverException;
84 import org.codehaus.plexus.archiver.UnArchiver;
85 import org.codehaus.plexus.archiver.manager.ArchiverManager;
86 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
87 import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
88 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
89 import org.codehaus.plexus.languages.java.jpms.LocationManager;
90 import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
91 import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
92 import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
93 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
94 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
95 import org.codehaus.plexus.languages.java.version.JavaVersion;
96 import org.codehaus.plexus.util.DirectoryScanner;
97 import org.codehaus.plexus.util.FileUtils;
98 import org.codehaus.plexus.util.IOUtil;
99 import org.codehaus.plexus.util.ReaderFactory;
100 import org.codehaus.plexus.util.WriterFactory;
101 import org.codehaus.plexus.util.cli.CommandLineException;
102 import org.codehaus.plexus.util.cli.CommandLineUtils;
103 import org.codehaus.plexus.util.cli.Commandline;
104 import org.codehaus.plexus.util.xml.Xpp3Dom;
105
106 import java.io.File;
107 import java.io.FileNotFoundException;
108 import java.io.IOException;
109 import java.io.InputStream;
110 import java.io.Writer;
111 import java.lang.reflect.Method;
112 import java.net.MalformedURLException;
113 import java.net.URI;
114 import java.net.URISyntaxException;
115 import java.net.URL;
116 import java.net.URLClassLoader;
117 import java.nio.charset.Charset;
118 import java.nio.charset.StandardCharsets;
119 import java.nio.file.Files;
120 import java.nio.file.Path;
121 import java.nio.file.StandardCopyOption;
122 import java.util.ArrayList;
123 import java.util.Arrays;
124 import java.util.Calendar;
125 import java.util.Collection;
126 import java.util.Collections;
127 import java.util.Date;
128 import java.util.HashMap;
129 import java.util.HashSet;
130 import java.util.LinkedHashMap;
131 import java.util.LinkedHashSet;
132 import java.util.LinkedList;
133 import java.util.List;
134 import java.util.Locale;
135 import java.util.Map;
136 import java.util.Map.Entry;
137 import java.util.Optional;
138 import java.util.Properties;
139 import java.util.Set;
140 import java.util.StringTokenizer;
141 import java.util.stream.Collectors;
142
143 import static org.apache.commons.lang3.SystemUtils.isJavaVersionAtLeast;
144 import static org.apache.maven.plugins.javadoc.JavadocUtil.toRelative;
145 import static org.apache.maven.plugins.javadoc.JavadocUtil.toList;
146 import static org.apache.maven.plugins.javadoc.JavadocUtil.isEmpty;
147 import static org.apache.maven.plugins.javadoc.JavadocUtil.isNotEmpty;
148
149
150
151
152
153
154
155
156
157
158 public abstract class AbstractJavadocMojo
159 extends AbstractMojo
160 {
161
162
163
164
165
166
167
168 public static final String JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "javadoc-resources";
169
170
171
172
173
174
175
176
177 public static final String TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER = "test-javadoc-resources";
178
179
180
181
182 protected static final String DEBUG_JAVADOC_SCRIPT_NAME = "javadoc." + ( SystemUtils.IS_OS_WINDOWS ? "bat" : "sh" );
183
184
185
186
187
188 protected static final String OPTIONS_FILE_NAME = "options";
189
190
191
192
193
194 protected static final String PACKAGES_FILE_NAME = "packages";
195
196
197
198
199
200 protected static final String ARGFILE_FILE_NAME = "argfile";
201
202
203
204
205
206 protected static final String FILES_FILE_NAME = "files";
207
208
209
210
211 private static final String RESOURCE_DIR = ClassUtils.getPackageName( JavadocReport.class ).replace( '.', '/' );
212
213
214
215
216 private static final String DEFAULT_CSS_NAME = "stylesheet.css";
217
218
219
220
221 private static final String RESOURCE_CSS_DIR = RESOURCE_DIR + "/css";
222
223 private static final String PACKAGE_LIST = "package-list";
224 private static final String ELEMENT_LIST = "element-list";
225
226
227
228
229
230
231
232
233
234 private static final JavaVersion SINCE_JAVADOC_1_4 = JavaVersion.parse( "1.4" );
235
236
237
238
239
240
241
242
243
244 private static final JavaVersion SINCE_JAVADOC_1_4_2 = JavaVersion.parse( "1.4.2" );
245
246
247
248
249
250
251
252
253
254 private static final JavaVersion SINCE_JAVADOC_1_5 = JavaVersion.parse( "1.5" );
255
256
257
258
259
260
261
262
263 private static final JavaVersion SINCE_JAVADOC_1_6 = JavaVersion.parse( "1.6" );
264
265
266
267
268
269
270
271
272 private static final JavaVersion SINCE_JAVADOC_1_8 = JavaVersion.parse( "1.8" );
273
274
275
276
277 private static final JavaVersion JAVA_VERSION = JavaVersion.JAVA_SPECIFICATION_VERSION;
278
279
280
281
282
283
284
285
286
287
288 @Component
289 private ArchiverManager archiverManager;
290
291 @Component
292 private ResourceResolver resourceResolver;
293
294 @Component
295 private ArtifactResolver artifactResolver;
296
297 @Component
298 private ArtifactHandlerManager artifactHandlerManager;
299
300 @Component
301 private DependencyResolver dependencyResolver;
302
303
304
305
306
307
308 @Component
309 private ProjectBuilder mavenProjectBuilder;
310
311
312 @Component
313 private ToolchainManager toolchainManager;
314
315 final LocationManager locationManager = new LocationManager();
316
317
318
319
320
321
322
323
324
325 @Parameter( defaultValue = "${session}", readonly = true, required = true )
326 protected MavenSession session;
327
328
329
330
331
332
333 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
334 private Settings settings;
335
336
337
338
339 @Parameter( defaultValue = "${project}", readonly = true, required = true )
340 protected MavenProject project;
341
342 @Parameter( defaultValue = "${mojoExecution}", readonly = true )
343 private MojoExecution mojo;
344
345
346
347
348 @Parameter( defaultValue = "${settings.offline}", required = true, readonly = true )
349 private boolean isOffline;
350
351
352
353
354
355
356
357
358
359
360
361 @Parameter( defaultValue = "${basedir}/src/main/javadoc" )
362 private File javadocDirectory;
363
364
365
366
367
368
369
370 @Parameter
371 private String[] additionalOptions;
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 @Parameter( property = "additionalJOption" )
389 private String additionalJOption;
390
391
392
393
394
395
396
397
398
399
400
401 @Parameter
402 private String[] additionalJOptions;
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424 @Parameter( property = "resourcesArtifacts" )
425 private ResourcesArtifact[] resourcesArtifacts;
426
427
428
429
430 @Parameter( property = "localRepository" )
431 private ArtifactRepository localRepository;
432
433
434
435
436 @Parameter( property = "reactorProjects", readonly = true )
437 private List<MavenProject> reactorProjects;
438
439
440
441
442
443
444
445
446 @Parameter( property = "debug", defaultValue = "false" )
447 private boolean debug;
448
449
450
451
452
453
454
455 @Parameter( property = "javadocExecutable" )
456 private String javadocExecutable;
457
458
459
460
461
462
463 @Parameter( property = "javadocVersion" )
464 private String javadocVersion;
465
466
467
468
469 private JavaVersion javadocRuntimeVersion;
470
471
472
473
474
475
476 @Parameter( property = "maven.javadoc.skip", defaultValue = "false" )
477 protected boolean skip;
478
479
480
481
482
483
484 @Parameter( property = "maven.javadoc.failOnError", defaultValue = "true" )
485 protected boolean failOnError;
486
487
488
489
490
491
492
493 @Parameter( property = "maven.javadoc.failOnWarnings", defaultValue = "false" )
494 protected boolean failOnWarnings;
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515 @Parameter( property = "useStandardDocletOptions", defaultValue = "true" )
516 protected boolean useStandardDocletOptions;
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536 @Parameter( property = "detectLinks", defaultValue = "false" )
537 private boolean detectLinks;
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555 @Parameter( property = "detectOfflineLinks", defaultValue = "true" )
556 private boolean detectOfflineLinks;
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576 @Parameter( property = "detectJavaApiLink", defaultValue = "true" )
577 private boolean detectJavaApiLink;
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595 @Parameter( property = "javaApiLinks" )
596 private Properties javaApiLinks;
597
598
599
600
601
602
603
604 @Parameter( property = "validateLinks", defaultValue = "false" )
605 private boolean validateLinks;
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620 @Parameter( property = "bootclasspath" )
621 private String bootclasspath;
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645 @Parameter( property = "bootclasspathArtifacts" )
646 private BootclasspathArtifact[] bootclasspathArtifacts;
647
648
649
650
651
652
653
654
655
656
657
658 @Parameter( property = "breakiterator", defaultValue = "false" )
659 private boolean breakiterator;
660
661
662
663
664
665
666 @Parameter( property = "doclet" )
667 private String doclet;
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688 @Parameter( property = "docletArtifact" )
689 private DocletArtifact docletArtifact;
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714 @Parameter( property = "docletArtifacts" )
715 private DocletArtifact[] docletArtifacts;
716
717
718
719
720
721
722
723
724
725 @Parameter( property = "docletPath" )
726 private String docletPath;
727
728
729
730
731
732
733
734
735
736
737
738 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
739 private String encoding;
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764 @Parameter( property = "excludePackageNames" )
765 private String excludePackageNames;
766
767
768
769
770
771
772
773 @Parameter( property = "extdirs" )
774 private String extdirs;
775
776
777
778
779
780
781 @Parameter( property = "locale" )
782 private String locale;
783
784
785
786
787
788
789
790
791 @Parameter( property = "maxmemory" )
792 private String maxmemory;
793
794
795
796
797
798
799
800
801 @Parameter( property = "minmemory" )
802 private String minmemory;
803
804
805
806
807
808
809
810
811 @Parameter( property = "old", defaultValue = "false" )
812 private boolean old;
813
814
815
816
817
818
819
820
821
822
823 @Parameter( property = "overview", defaultValue = "${basedir}/src/main/javadoc/overview.html" )
824 private File overview;
825
826
827
828
829
830
831
832
833
834
835
836
837
838 @Parameter( property = "quiet", defaultValue = "false" )
839 private boolean quiet;
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856 @Parameter( property = "show", defaultValue = "protected" )
857 private String show;
858
859
860
861
862
863
864
865
866
867
868 @Parameter( property = "source", defaultValue = "${maven.compiler.source}" )
869 private String source;
870
871
872
873
874
875
876
877 @Parameter( defaultValue = "${maven.compiler.release}" )
878 private String release;
879
880
881
882
883
884
885
886
887 @Parameter( property = "sourcepath" )
888 private String sourcepath;
889
890
891
892
893
894
895
896
897
898
899
900 @Parameter( property = "subpackages" )
901 private String subpackages;
902
903
904
905
906
907
908
909 @Parameter( property = "verbose", defaultValue = "false" )
910 private boolean verbose;
911
912
913
914
915
916
917
918
919
920
921
922 @Parameter( property = "author", defaultValue = "true" )
923 private boolean author;
924
925
926
927
928
929
930
931
932
933
934
935
936 @Parameter( property = "bottom",
937 defaultValue = "Copyright © {inceptionYear}–{currentYear} {organizationName}. "
938 + "All rights reserved." )
939 private String bottom;
940
941
942
943
944
945
946
947
948 @Parameter( property = "charset" )
949 private String charset;
950
951
952
953
954
955
956
957
958 @Parameter( property = "docencoding", defaultValue = "${project.reporting.outputEncoding}" )
959 private String docencoding;
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978 @Parameter( property = "docfilessubdirs", defaultValue = "false" )
979 private boolean docfilessubdirs;
980
981
982
983
984
985
986
987
988 @Parameter( property = "doclint" )
989 private String doclint;
990
991
992
993
994
995
996
997 @Parameter( property = "doctitle", defaultValue = "${project.name} ${project.version} API" )
998 private String doctitle;
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012 @Parameter( property = "excludedocfilessubdir" )
1013 private String excludedocfilessubdir;
1014
1015
1016
1017
1018
1019
1020 @Parameter( property = "footer" )
1021 private String footer;
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055 @Parameter
1056 private Group[] groups;
1057
1058
1059
1060
1061
1062
1063 @Parameter( property = "header" )
1064 private String header;
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108 @Parameter( property = "helpfile" )
1109 private String helpfile;
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127 @Parameter( property = "keywords", defaultValue = "false" )
1128 private boolean keywords;
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 @Parameter( property = "links" )
1155 protected ArrayList<String> links;
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 @Parameter
1176 private List<DependencyLink> dependencyLinks = new ArrayList<>();
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189 @Parameter( property = "linksource", defaultValue = "false" )
1190 private boolean linksource;
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 @Parameter( property = "nocomment", defaultValue = "false" )
1202 private boolean nocomment;
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212 @Parameter( property = "nodeprecated", defaultValue = "false" )
1213 private boolean nodeprecated;
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223 @Parameter( property = "nodeprecatedlist", defaultValue = "false" )
1224 private boolean nodeprecatedlist;
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234 @Parameter( property = "nohelp", defaultValue = "false" )
1235 private boolean nohelp;
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245 @Parameter( property = "noindex", defaultValue = "false" )
1246 private boolean noindex;
1247
1248
1249
1250
1251
1252
1253
1254 @Parameter( property = "nonavbar", defaultValue = "false" )
1255 private boolean nonavbar;
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267 @Parameter( property = "nooverview", defaultValue = "false" )
1268 private boolean nooverview;
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284 @Parameter( property = "noqualifier" )
1285 private String noqualifier;
1286
1287
1288
1289
1290
1291
1292
1293 @Parameter( property = "nosince", defaultValue = "false" )
1294 private boolean nosince;
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309 @Parameter( property = "notimestamp", defaultValue = "false" )
1310 private boolean notimestamp;
1311
1312
1313
1314
1315
1316
1317 @Parameter( property = "notree", defaultValue = "false" )
1318 private boolean notree;
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342 @Parameter( property = "offlineLinks" )
1343 private OfflineLink[] offlineLinks;
1344
1345
1346
1347
1348
1349
1350 @Parameter( property = "destDir", alias = "destDir", defaultValue = "${project.build.directory}/apidocs",
1351 required = true )
1352 protected File outputDirectory;
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363 @Parameter( property = "packagesheader" )
1364 private String packagesheader;
1365
1366
1367
1368
1369
1370
1371 @Parameter( property = "serialwarn", defaultValue = "false" )
1372 private boolean serialwarn;
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389 @Parameter( property = "sourcetab", alias = "linksourcetab" )
1390 private int sourcetab;
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402 @Parameter( property = "splitindex", defaultValue = "false" )
1403 private boolean splitindex;
1404
1405
1406
1407
1408
1409
1410
1411
1412 @Parameter( property = "stylesheet", defaultValue = "java" )
1413 private String stylesheet;
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455 @Parameter( property = "stylesheetfile" )
1456 private String stylesheetfile;
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468 @Parameter
1469 private String[] addStylesheets;
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479 @Parameter( property = "taglet" )
1480 private String taglet;
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511 @Parameter( property = "tagletArtifact" )
1512 private TagletArtifact tagletArtifact;
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540 @Parameter( property = "tagletArtifacts" )
1541 private TagletArtifact[] tagletArtifacts;
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553 @Parameter( property = "tagletpath" )
1554 private String tagletpath;
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584 @Parameter( property = "taglets" )
1585 private Taglet[] taglets;
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619 @Parameter( property = "tags" )
1620 private Tag[] tags;
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631 @Parameter( property = "top" )
1632 private String top;
1633
1634
1635
1636
1637
1638
1639
1640 @Parameter( property = "use", defaultValue = "true" )
1641 private boolean use;
1642
1643
1644
1645
1646
1647
1648
1649 @Parameter( property = "version", defaultValue = "true" )
1650 private boolean version;
1651
1652
1653
1654
1655
1656
1657
1658
1659 @Parameter( property = "windowtitle", defaultValue = "${project.name} ${project.version} API" )
1660 private String windowtitle;
1661
1662
1663
1664
1665
1666
1667
1668 @Parameter( defaultValue = "false" )
1669 private boolean includeDependencySources;
1670
1671
1672
1673
1674
1675
1676
1677 @Parameter( defaultValue = "${project.build.directory}/distro-javadoc-sources" )
1678 private File sourceDependencyCacheDir;
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689 @Parameter( defaultValue = "false" )
1690 @Deprecated
1691 private boolean includeTransitiveDependencySources;
1692
1693
1694
1695
1696
1697
1698
1699 @Parameter
1700 private List<String> dependencySourceIncludes;
1701
1702
1703
1704
1705
1706
1707
1708 @Parameter
1709 private List<String> dependencySourceExcludes;
1710
1711
1712
1713
1714
1715
1716
1717 @Parameter( defaultValue = "${project.build.directory}/javadoc-bundle-options", readonly = true )
1718 private File javadocOptionsDir;
1719
1720
1721
1722
1723
1724
1725
1726 private transient List<JavadocBundle> dependencyJavadocBundles;
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743 @Parameter
1744 private List<AdditionalDependency> additionalDependencies;
1745
1746
1747
1748
1749
1750
1751
1752 @Parameter
1753 private List<String> sourceFileIncludes;
1754
1755
1756
1757
1758
1759
1760
1761 @Parameter
1762 private List<String> sourceFileExcludes;
1763
1764
1765
1766
1767
1768 @Parameter( defaultValue = "true", property = "maven.javadoc.applyJavadocSecurityFix" )
1769 private boolean applyJavadocSecurityFix = true;
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804 @Parameter
1805 private Map<String, String> jdkToolchain;
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815 @Parameter( property = "staleDataPath",
1816 defaultValue = "${project.build.directory}/maven-javadoc-plugin-stale-data.txt" )
1817 private File staleDataPath;
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827 @Parameter( property = "maven.javadoc.skippedModules" )
1828 private String skippedModules;
1829
1830
1831
1832
1833
1834
1835
1836
1837 @Parameter( defaultValue = "${project.build.outputTimestamp}" )
1838 protected String outputTimestamp;
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851 protected boolean isAggregator()
1852 {
1853 return false;
1854 }
1855
1856
1857
1858
1859
1860
1861 protected boolean isTest()
1862 {
1863 return false;
1864 }
1865
1866
1867
1868
1869 protected String getOutputDirectory()
1870 {
1871 return outputDirectory.getAbsoluteFile().toString();
1872 }
1873
1874 protected MavenProject getProject()
1875 {
1876 return project;
1877 }
1878
1879
1880
1881
1882
1883
1884 protected List<File> getProjectBuildOutputDirs( MavenProject p )
1885 {
1886 if ( StringUtils.isEmpty( p.getBuild().getOutputDirectory() ) )
1887 {
1888 return Collections.emptyList();
1889 }
1890
1891 return Collections.singletonList( new File( p.getBuild().getOutputDirectory() ) );
1892 }
1893
1894
1895
1896
1897
1898
1899
1900 protected File getClassesFile( MavenProject project )
1901 {
1902 if ( !isAggregator() && isTest() )
1903 {
1904 return null;
1905 }
1906
1907 if ( project.getArtifact() != null && project.getArtifact().getFile() != null )
1908 {
1909 File artifactFile = project.getArtifact().getFile();
1910 if ( artifactFile.isDirectory() || artifactFile.getName().endsWith( ".jar" ) )
1911 {
1912 return artifactFile;
1913 }
1914 }
1915 else if ( project.getExecutionProject() != null
1916 && project.getExecutionProject().getArtifact() != null
1917 && project.getExecutionProject().getArtifact().getFile() != null )
1918 {
1919 File artifactFile = project.getExecutionProject().getArtifact().getFile();
1920 if ( artifactFile.isDirectory() || artifactFile.getName().endsWith( ".jar" ) )
1921 {
1922 return artifactFile;
1923 }
1924 }
1925
1926 if ( project.getBuild().getOutputDirectory() != null )
1927 {
1928 return new File( project.getBuild().getOutputDirectory() );
1929 }
1930 else
1931 {
1932 return null;
1933 }
1934 }
1935
1936
1937
1938
1939
1940 protected List<String> getProjectSourceRoots( MavenProject p )
1941 {
1942 if ( "pom".equals( p.getPackaging().toLowerCase() ) )
1943 {
1944 return Collections.emptyList();
1945 }
1946
1947 return ( p.getCompileSourceRoots() == null
1948 ? Collections.<String>emptyList()
1949 : new LinkedList<>( p.getCompileSourceRoots() ) );
1950 }
1951
1952
1953
1954
1955
1956 protected List<String> getExecutionProjectSourceRoots( MavenProject p )
1957 {
1958 if ( "pom".equals( p.getExecutionProject().getPackaging().toLowerCase() ) )
1959 {
1960 return Collections.emptyList();
1961 }
1962
1963 return ( p.getExecutionProject().getCompileSourceRoots() == null
1964 ? Collections.<String>emptyList()
1965 : new LinkedList<>( p.getExecutionProject().getCompileSourceRoots() ) );
1966 }
1967
1968
1969
1970
1971 protected File getJavadocDirectory()
1972 {
1973 return javadocDirectory;
1974 }
1975
1976
1977
1978
1979 protected String getDoclint()
1980 {
1981 return doclint;
1982 }
1983
1984
1985
1986
1987 protected String getDoctitle()
1988 {
1989 return doctitle;
1990 }
1991
1992
1993
1994
1995 protected File getOverview()
1996 {
1997 return overview;
1998 }
1999
2000
2001
2002
2003 protected String getWindowtitle()
2004 {
2005 return windowtitle;
2006 }
2007
2008
2009
2010
2011 private String getCharset()
2012 {
2013 return ( StringUtils.isEmpty( charset ) ) ? getDocencoding() : charset;
2014 }
2015
2016
2017
2018
2019 private String getDocencoding()
2020 {
2021 return ( StringUtils.isEmpty( docencoding ) ) ? ReaderFactory.UTF_8 : docencoding;
2022 }
2023
2024
2025
2026
2027 private String getEncoding()
2028 {
2029 return ( StringUtils.isEmpty( encoding ) ) ? ReaderFactory.FILE_ENCODING : encoding;
2030 }
2031
2032 @Override
2033 public void execute()
2034 throws MojoExecutionException, MojoFailureException
2035 {
2036 verifyRemovedParameter( "aggregator" );
2037 verifyRemovedParameter( "proxyHost" );
2038 verifyRemovedParameter( "proxyPort" );
2039 verifyReplacedParameter( "additionalparam", "additionalOptions" );
2040
2041 doExecute();
2042 }
2043
2044 abstract void doExecute() throws MojoExecutionException, MojoFailureException;
2045
2046 protected final void verifyRemovedParameter( String paramName )
2047 {
2048 Xpp3Dom configDom = mojo.getConfiguration();
2049 if ( configDom != null )
2050 {
2051 if ( configDom.getChild( paramName ) != null )
2052 {
2053 throw new IllegalArgumentException( "parameter '" + paramName
2054 + "' has been removed from the plugin, please verify documentation." );
2055 }
2056 }
2057 }
2058
2059 private void verifyReplacedParameter( String oldParamName, String newParamNew )
2060 {
2061 Xpp3Dom configDom = mojo.getConfiguration();
2062 if ( configDom != null )
2063 {
2064 if ( configDom.getChild( oldParamName ) != null )
2065 {
2066 throw new IllegalArgumentException( "parameter '" + oldParamName
2067 + "' has been replaced with " + newParamNew + ", please verify documentation." );
2068 }
2069 }
2070 }
2071
2072
2073
2074
2075
2076
2077
2078
2079 protected void executeReport( Locale unusedLocale )
2080 throws MavenReportException
2081 {
2082 if ( skip )
2083 {
2084 getLog().info( "Skipping javadoc generation" );
2085 return;
2086 }
2087
2088 if ( getLog().isDebugEnabled() )
2089 {
2090 this.debug = true;
2091 }
2092
2093
2094
2095 try
2096 {
2097 buildJavadocOptions();
2098 }
2099 catch ( IOException e )
2100 {
2101 throw new MavenReportException( "Failed to generate javadoc options file: " + e.getMessage(), e );
2102 }
2103
2104 Collection<JavadocModule> sourcePaths = getSourcePaths();
2105
2106 Collection<Path> collectedSourcePaths = sourcePaths.stream()
2107 .flatMap( e -> e.getSourcePaths().stream() )
2108 .collect( Collectors.toList() );
2109
2110 Map<Path, Collection<String>> files = getFiles( collectedSourcePaths );
2111 if ( !canGenerateReport( files ) )
2112 {
2113 return;
2114 }
2115
2116
2117
2118
2119
2120 String jExecutable;
2121 try
2122 {
2123 jExecutable = getJavadocExecutable();
2124 }
2125 catch ( IOException e )
2126 {
2127 throw new MavenReportException( "Unable to find javadoc command: " + e.getMessage(), e );
2128 }
2129 setFJavadocVersion( new File( jExecutable ) );
2130
2131 Collection<String> packageNames;
2132 if ( javadocRuntimeVersion.isAtLeast( "9" ) )
2133 {
2134 packageNames = getPackageNamesRespectingJavaModules( sourcePaths );
2135 }
2136 else
2137 {
2138 packageNames = getPackageNames( files );
2139 }
2140
2141
2142
2143
2144
2145 File javadocOutputDirectory = new File( getOutputDirectory() );
2146 if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.isDirectory() )
2147 {
2148 throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not a directory." );
2149 }
2150 if ( javadocOutputDirectory.exists() && !javadocOutputDirectory.canWrite() )
2151 {
2152 throw new MavenReportException( "IOException: " + getOutputDirectory() + " is not writable." );
2153 }
2154 javadocOutputDirectory.mkdirs();
2155
2156
2157
2158
2159
2160 copyAllResources( javadocOutputDirectory );
2161
2162
2163
2164
2165
2166 Commandline cmd = new Commandline();
2167 cmd.getShell().setQuotedArgumentsEnabled( false );
2168 cmd.setWorkingDirectory( javadocOutputDirectory.getAbsolutePath() );
2169 cmd.setExecutable( jExecutable );
2170
2171
2172
2173
2174
2175 addMemoryArg( cmd, "-Xmx", this.maxmemory );
2176 addMemoryArg( cmd, "-Xms", this.minmemory );
2177 addProxyArg( cmd );
2178
2179 if ( StringUtils.isNotEmpty( additionalJOption ) )
2180 {
2181 cmd.createArg().setValue( additionalJOption );
2182 }
2183
2184 if ( additionalJOptions != null && additionalJOptions.length != 0 )
2185 {
2186 for ( String jo : additionalJOptions )
2187 {
2188 cmd.createArg().setValue( jo );
2189 }
2190 }
2191
2192
2193
2194
2195 List<String> standardDocletArguments = new ArrayList<>();
2196
2197 Set<OfflineLink> offlineLinks;
2198 if ( StringUtils.isEmpty( doclet ) || useStandardDocletOptions )
2199 {
2200 offlineLinks = getLinkofflines();
2201 addStandardDocletOptions( javadocOutputDirectory, standardDocletArguments, offlineLinks );
2202 }
2203 else
2204 {
2205 offlineLinks = Collections.emptySet();
2206 }
2207
2208
2209
2210
2211 List<String> javadocArguments = new ArrayList<>();
2212
2213 addJavadocOptions( javadocOutputDirectory, javadocArguments, sourcePaths, offlineLinks );
2214
2215
2216
2217
2218
2219 List<String> arguments = new ArrayList<>( javadocArguments.size() + standardDocletArguments.size() );
2220 arguments.addAll( javadocArguments );
2221 arguments.addAll( standardDocletArguments );
2222
2223 if ( arguments.size() > 0 )
2224 {
2225 addCommandLineOptions( cmd, arguments, javadocOutputDirectory );
2226 }
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236 boolean includesExcludesActive =
2237 ( sourceFileIncludes != null && !sourceFileIncludes.isEmpty() )
2238 || ( sourceFileExcludes != null && !sourceFileExcludes.isEmpty() );
2239 if ( includesExcludesActive && !StringUtils.isEmpty( subpackages ) )
2240 {
2241 getLog().warn( "sourceFileIncludes and sourceFileExcludes have no effect when subpackages are specified!" );
2242 includesExcludesActive = false;
2243 }
2244 if ( !packageNames.isEmpty() && !includesExcludesActive )
2245 {
2246 addCommandLinePackages( cmd, javadocOutputDirectory, packageNames );
2247
2248
2249
2250
2251
2252 List<String> specialFiles = getSpecialFiles( files );
2253
2254 if ( !specialFiles.isEmpty() )
2255 {
2256 addCommandLineArgFile( cmd, javadocOutputDirectory, specialFiles );
2257 }
2258 }
2259 else
2260 {
2261
2262
2263
2264
2265 List<String> allFiles = new ArrayList<>();
2266 for ( Map.Entry<Path, Collection<String>> filesEntry : files.entrySet() )
2267 {
2268 for ( String file : filesEntry.getValue() )
2269 {
2270 allFiles.add( filesEntry.getKey().resolve( file ).toString() );
2271 }
2272 }
2273
2274 if ( !files.isEmpty() )
2275 {
2276 addCommandLineArgFile( cmd, javadocOutputDirectory, allFiles );
2277 }
2278 }
2279
2280
2281
2282
2283
2284 executeJavadocCommandLine( cmd, javadocOutputDirectory );
2285
2286
2287
2288
2289 if ( !debug )
2290 {
2291 for ( int i = 0; i < cmd.getArguments().length; i++ )
2292 {
2293 String arg = cmd.getArguments()[i].trim();
2294
2295 if ( !arg.startsWith( "@" ) )
2296 {
2297 continue;
2298 }
2299
2300 File argFile = new File( javadocOutputDirectory, arg.substring( 1 ) );
2301 if ( argFile.exists() )
2302 {
2303 argFile.delete();
2304 }
2305 }
2306
2307 File scriptFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
2308 if ( scriptFile.exists() )
2309 {
2310 scriptFile.delete();
2311 }
2312 }
2313 if ( applyJavadocSecurityFix )
2314 {
2315
2316 try
2317 {
2318 final int patched = fixFrameInjectionBug( javadocOutputDirectory, getDocencoding() );
2319 if ( patched > 0 )
2320 {
2321 getLog().info(
2322 String.format( "Fixed Javadoc frame injection vulnerability (CVE-2013-1571) in %d files.",
2323 patched ) );
2324 }
2325 }
2326 catch ( IOException e )
2327 {
2328 throw new MavenReportException( "Failed to patch javadocs vulnerability: " + e.getMessage(), e );
2329 }
2330 }
2331 else
2332 {
2333 getLog().info( "applying javadoc security fix has been disabled" );
2334 }
2335 }
2336
2337
2338
2339
2340
2341
2342
2343
2344 protected Map<Path, Collection<String>> getFiles( Collection<Path> sourcePaths )
2345 throws MavenReportException
2346 {
2347 Map<Path, Collection<String>> mappedFiles = new LinkedHashMap<>( sourcePaths.size() );
2348 if ( StringUtils.isEmpty( subpackages ) )
2349 {
2350 Collection<String> excludedPackages = getExcludedPackages();
2351
2352 final boolean autoExclude;
2353 if ( release != null )
2354 {
2355 autoExclude = JavaVersion.parse( release ).isBefore( "9" );
2356 }
2357 else if ( source != null )
2358 {
2359 autoExclude = JavaVersion.parse( source ).isBefore( "9" );
2360 }
2361 else
2362 {
2363 autoExclude = false;
2364 }
2365
2366 for ( Path sourcePath : sourcePaths )
2367 {
2368 File sourceDirectory = sourcePath.toFile();
2369 List<String> files = new ArrayList<>( JavadocUtil.getFilesFromSource( sourceDirectory,
2370 sourceFileIncludes, sourceFileExcludes,
2371 excludedPackages ) );
2372
2373 if ( autoExclude && files.remove( "module-info.java" ) )
2374 {
2375 getLog().debug( "Auto exclude module-info.java due to source value" );
2376 }
2377 mappedFiles.put( sourcePath, files );
2378 }
2379 }
2380
2381 return mappedFiles;
2382 }
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392 protected Collection<JavadocModule> getSourcePaths()
2393 throws MavenReportException
2394 {
2395 Collection<JavadocModule> mappedSourcePaths = new ArrayList<>();
2396
2397 if ( StringUtils.isEmpty( sourcepath ) )
2398 {
2399 if ( !"pom".equals( project.getPackaging() ) )
2400 {
2401 Set<Path> sourcePaths =
2402 new LinkedHashSet<>( JavadocUtil.pruneDirs( project, getProjectSourceRoots( project ) ) );
2403
2404 if ( project.getExecutionProject() != null )
2405 {
2406 sourcePaths.addAll( JavadocUtil.pruneDirs( project, getExecutionProjectSourceRoots( project ) ) );
2407 }
2408
2409
2410
2411
2412
2413
2414 if ( getJavadocDirectory() != null )
2415 {
2416 File javadocDir = getJavadocDirectory();
2417 if ( javadocDir.exists() && javadocDir.isDirectory() )
2418 {
2419 Collection<Path> l =
2420 JavadocUtil.pruneDirs( project,
2421 Collections.singletonList( getJavadocDirectory().getAbsolutePath() ) );
2422 sourcePaths.addAll( l );
2423 }
2424 }
2425 if ( !sourcePaths.isEmpty() )
2426 {
2427 mappedSourcePaths.add( buildJavadocModule( project, sourcePaths ) );
2428 }
2429 }
2430
2431 if ( isAggregator() )
2432 {
2433 for ( MavenProject subProject : getAggregatedProjects() )
2434 {
2435 if ( subProject != project )
2436 {
2437 Collection<Path> additionalSourcePaths = new ArrayList<>();
2438
2439 List<String> sourceRoots = getProjectSourceRoots( subProject );
2440
2441 if ( subProject.getExecutionProject() != null )
2442 {
2443 sourceRoots.addAll( getExecutionProjectSourceRoots( subProject ) );
2444 }
2445
2446 ArtifactHandler artifactHandler = subProject.getArtifact().getArtifactHandler();
2447 if ( "java".equals( artifactHandler.getLanguage() ) )
2448 {
2449 additionalSourcePaths.addAll( JavadocUtil.pruneDirs( subProject, sourceRoots ) );
2450 }
2451
2452 if ( getJavadocDirectory() != null )
2453 {
2454 String javadocDirRelative =
2455 PathUtils.toRelative( project.getBasedir(),
2456 getJavadocDirectory().getAbsolutePath() );
2457 File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
2458 if ( javadocDir.exists() && javadocDir.isDirectory() )
2459 {
2460 Collection<Path> l = JavadocUtil.pruneDirs( subProject, Collections.singletonList(
2461 javadocDir.getAbsolutePath() ) );
2462 additionalSourcePaths.addAll( l );
2463 }
2464 }
2465
2466 if ( !additionalSourcePaths.isEmpty() )
2467 {
2468 mappedSourcePaths.add( buildJavadocModule( subProject , additionalSourcePaths ) );
2469 }
2470 }
2471 }
2472 }
2473
2474 if ( includeDependencySources )
2475 {
2476 mappedSourcePaths.addAll( getDependencySourcePaths() );
2477 }
2478 }
2479 else
2480 {
2481 Collection<Path> sourcePaths =
2482 JavadocUtil.pruneDirs( project,
2483 new ArrayList<>( Arrays.asList( JavadocUtil.splitPath( sourcepath ) ) ) );
2484 if ( getJavadocDirectory() != null )
2485 {
2486 Collection<Path> l = JavadocUtil.pruneDirs( project, Collections.singletonList(
2487 getJavadocDirectory().getAbsolutePath() ) );
2488 sourcePaths.addAll( l );
2489 }
2490
2491 if ( !sourcePaths.isEmpty() )
2492 {
2493 mappedSourcePaths.add( new JavadocModule( ArtifactUtils.versionlessKey( project.getGroupId(),
2494 project.getArtifactId() ),
2495 getClassesFile( project ),
2496 sourcePaths ) );
2497 }
2498 }
2499
2500 return mappedSourcePaths;
2501 }
2502
2503 private JavadocModule buildJavadocModule( MavenProject project, Collection<Path> sourcePaths )
2504 {
2505 File classessFile = getClassesFile( project );
2506 ResolvePathResult resolvePathResult = getResolvePathResult( classessFile );
2507 if ( resolvePathResult == null )
2508 {
2509 return new JavadocModule( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
2510 classessFile,
2511 sourcePaths );
2512 }
2513 else
2514 {
2515 return new JavadocModule( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ),
2516 classessFile,
2517 sourcePaths,
2518 resolvePathResult.getModuleDescriptor(),
2519 resolvePathResult.getModuleNameSource() );
2520 }
2521 }
2522
2523
2524
2525
2526
2527
2528
2529 private Set<MavenProject> modulesForAggregatedProject( MavenProject aggregatedProject,
2530 Map<Path, MavenProject> reactorProjectsMap )
2531 {
2532
2533
2534
2535
2536
2537 if ( aggregatedProject.getModules().isEmpty() )
2538 {
2539 return Collections.singleton( aggregatedProject );
2540 }
2541
2542 Path basePath = aggregatedProject.getBasedir().toPath();
2543 List<Path> modulePaths = new LinkedList<>();
2544 for ( String module : aggregatedProject.getModules() )
2545 {
2546 modulePaths.add( basePath.resolve( module ).normalize() );
2547 }
2548
2549 Set<MavenProject> aggregatedModules = new LinkedHashSet<>();
2550
2551 for ( Path modulePath : modulePaths )
2552 {
2553 MavenProject module = reactorProjectsMap.remove( modulePath );
2554 if ( module != null )
2555 {
2556 aggregatedModules.addAll( modulesForAggregatedProject( module, reactorProjectsMap ) );
2557 }
2558 }
2559
2560 return aggregatedModules;
2561 }
2562
2563
2564
2565
2566
2567
2568
2569 protected SourceResolverConfig configureDependencySourceResolution( final SourceResolverConfig config )
2570 {
2571 return config.withCompileSources();
2572 }
2573
2574
2575
2576
2577
2578
2579
2580 protected final Collection<JavadocModule> getDependencySourcePaths()
2581 throws MavenReportException
2582 {
2583 try
2584 {
2585 if ( sourceDependencyCacheDir.exists() )
2586 {
2587 FileUtils.forceDelete( sourceDependencyCacheDir );
2588 sourceDependencyCacheDir.mkdirs();
2589 }
2590 }
2591 catch ( IOException e )
2592 {
2593 throw new MavenReportException(
2594 "Failed to delete cache directory: " + sourceDependencyCacheDir + "\nReason: " + e.getMessage(), e );
2595 }
2596
2597 final SourceResolverConfig config = getDependencySourceResolverConfig();
2598
2599 try
2600 {
2601 return resourceResolver.resolveDependencySourcePaths( config );
2602 }
2603 catch ( final ArtifactResolutionException | ArtifactNotFoundException e )
2604 {
2605 throw new MavenReportException(
2606 "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e );
2607 }
2608 }
2609
2610
2611
2612
2613
2614
2615
2616 private TransformableFilter createDependencyArtifactFilter()
2617 {
2618 Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
2619
2620 List<String> artifactPatterns = new ArrayList<>( dependencyArtifacts.size() );
2621 for ( Artifact artifact : dependencyArtifacts )
2622 {
2623 artifactPatterns.add( artifact.getGroupId() + ":" + artifact.getArtifactId() );
2624 }
2625
2626 return new PatternInclusionsFilter( artifactPatterns );
2627 }
2628
2629
2630
2631
2632
2633
2634
2635 private SourceResolverConfig getDependencySourceResolverConfig()
2636 {
2637 final List<TransformableFilter> andFilters = new ArrayList<>();
2638
2639 final List<String> dependencyIncludes = dependencySourceIncludes;
2640 final List<String> dependencyExcludes = dependencySourceExcludes;
2641
2642 if ( !includeTransitiveDependencySources || isNotEmpty( dependencyIncludes ) || isNotEmpty(
2643 dependencyExcludes ) )
2644 {
2645 if ( !includeTransitiveDependencySources )
2646 {
2647 andFilters.add( createDependencyArtifactFilter() );
2648 }
2649
2650 if ( isNotEmpty( dependencyIncludes ) )
2651 {
2652 andFilters.add( new PatternInclusionsFilter( dependencyIncludes ) );
2653 }
2654
2655 if ( isNotEmpty( dependencyExcludes ) )
2656 {
2657 andFilters.add( new PatternExclusionsFilter( dependencyExcludes ) );
2658 }
2659 }
2660
2661 return configureDependencySourceResolution( new SourceResolverConfig( project,
2662 getProjectBuildingRequest( project ),
2663 sourceDependencyCacheDir )
2664 .withReactorProjects( this.reactorProjects ) )
2665 .withFilter( new AndFilter( andFilters ) );
2666
2667 }
2668
2669 private ProjectBuildingRequest getProjectBuildingRequest( MavenProject currentProject )
2670 {
2671 return new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() )
2672 .setRemoteRepositories( currentProject.getRemoteArtifactRepositories() );
2673 }
2674
2675
2676
2677
2678
2679
2680
2681
2682 protected boolean canGenerateReport( Map<Path, Collection<String>> files )
2683 {
2684 for ( Collection<String> filesValues : files.values() )
2685 {
2686 if ( !filesValues.isEmpty() )
2687 {
2688 return true;
2689 }
2690 }
2691
2692 return !StringUtils.isEmpty( subpackages );
2693 }
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707 private String getExcludedPackages( Collection<Path> sourcePaths )
2708 throws MavenReportException
2709 {
2710 List<String> excludedNames = null;
2711
2712 if ( StringUtils.isNotEmpty( sourcepath ) && StringUtils.isNotEmpty( subpackages ) )
2713 {
2714 Collection<String> excludedPackages = getExcludedPackages();
2715
2716 excludedNames = JavadocUtil.getExcludedPackages( sourcePaths, excludedPackages );
2717 }
2718
2719 String excludeArg = "";
2720 if ( StringUtils.isNotEmpty( subpackages ) && excludedNames != null )
2721 {
2722
2723 excludeArg = StringUtils.join( excludedNames.iterator(), ":" );
2724 }
2725
2726 return excludeArg;
2727 }
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737 private String getSourcePath( Collection<Path> sourcePaths )
2738 {
2739 String sourcePath = null;
2740
2741 if ( StringUtils.isEmpty( subpackages ) || StringUtils.isNotEmpty( sourcepath ) )
2742 {
2743 sourcePath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
2744 }
2745
2746 return sourcePath;
2747 }
2748
2749
2750
2751
2752
2753
2754
2755
2756 private Collection<String> getExcludedPackages()
2757 throws MavenReportException
2758 {
2759 Set<String> excluded = new LinkedHashSet<>();
2760
2761 if ( includeDependencySources )
2762 {
2763 try
2764 {
2765 resolveDependencyBundles();
2766 }
2767 catch ( IOException e )
2768 {
2769 throw new MavenReportException(
2770 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
2771 }
2772
2773 if ( isNotEmpty( dependencyJavadocBundles ) )
2774 {
2775 for ( JavadocBundle bundle : dependencyJavadocBundles )
2776 {
2777 JavadocOptions options = bundle.getOptions();
2778 if ( options != null && isNotEmpty( options.getExcludePackageNames() ) )
2779 {
2780 excluded.addAll( options.getExcludePackageNames() );
2781 }
2782 }
2783 }
2784 }
2785
2786
2787 if ( StringUtils.isNotEmpty( excludePackageNames ) )
2788 {
2789 List<String> packageNames = Arrays.asList( excludePackageNames.split( "[,:;]" ) );
2790 excluded.addAll( trimValues( packageNames ) );
2791 }
2792
2793 return excluded;
2794 }
2795
2796 private static List<String> trimValues( List<String> items )
2797 {
2798 List<String> result = new ArrayList<>( items.size() );
2799 for ( String item : items )
2800 {
2801 String trimmed = item.trim();
2802 if ( StringUtils.isEmpty( trimmed ) )
2803 {
2804 continue;
2805 }
2806 result.add( trimmed );
2807 }
2808 return result;
2809 }
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821 private Collection<File> getPathElements()
2822 throws MavenReportException
2823 {
2824 Set<File> classpathElements = new LinkedHashSet<>();
2825 Map<String, Artifact> compileArtifactMap = new LinkedHashMap<>();
2826
2827 if ( isTest() )
2828 {
2829 classpathElements.addAll( getProjectBuildOutputDirs( project ) );
2830 }
2831
2832 populateCompileArtifactMap( compileArtifactMap, project.getArtifacts() );
2833
2834 if ( isAggregator() )
2835 {
2836 Collection<MavenProject> aggregatorProjects = getAggregatedProjects();
2837
2838 List<String> reactorArtifacts = new ArrayList<>();
2839 for ( MavenProject p : aggregatorProjects )
2840 {
2841 reactorArtifacts.add( p.getGroupId() + ':' + p.getArtifactId() );
2842 }
2843
2844 TransformableFilter dependencyFilter = new AndFilter( Arrays.asList(
2845 new PatternExclusionsFilter( reactorArtifacts ),
2846 getDependencyScopeFilter() ) );
2847
2848 for ( MavenProject subProject : aggregatorProjects )
2849 {
2850 if ( subProject != project )
2851 {
2852 File projectArtifactFile = getClassesFile( subProject );
2853 if ( projectArtifactFile != null )
2854 {
2855 classpathElements.add( projectArtifactFile );
2856 }
2857 else
2858 {
2859 classpathElements.addAll( getProjectBuildOutputDirs( subProject ) );
2860 }
2861
2862 try
2863 {
2864 StringBuilder sb = new StringBuilder();
2865
2866 sb.append( "Compiled artifacts for " );
2867 sb.append( subProject.getGroupId() ).append( ":" );
2868 sb.append( subProject.getArtifactId() ).append( ":" );
2869 sb.append( subProject.getVersion() ).append( '\n' );
2870
2871 ProjectBuildingRequest buildingRequest = getProjectBuildingRequest( subProject );
2872
2873 List<Dependency> managedDependencies = null;
2874 if ( subProject.getDependencyManagement() != null )
2875 {
2876 managedDependencies = subProject.getDependencyManagement().getDependencies();
2877 }
2878
2879 for ( ArtifactResult artifactResult
2880 : dependencyResolver.resolveDependencies( buildingRequest,
2881 subProject.getDependencies(),
2882 managedDependencies,
2883 dependencyFilter ) )
2884 {
2885 populateCompileArtifactMap( compileArtifactMap,
2886 Collections.singletonList( artifactResult.getArtifact() ) );
2887
2888 sb.append( artifactResult.getArtifact().getFile() ).append( '\n' );
2889 }
2890
2891 if ( getLog().isDebugEnabled() )
2892 {
2893 getLog().debug( sb.toString() );
2894 }
2895
2896 }
2897 catch ( DependencyResolverException e )
2898 {
2899 throw new MavenReportException( e.getMessage(), e );
2900 }
2901 }
2902 }
2903 }
2904
2905 for ( Artifact a : compileArtifactMap.values() )
2906 {
2907 classpathElements.add( a.getFile() );
2908 }
2909
2910 if ( additionalDependencies != null )
2911 {
2912 for ( Dependency dependency : additionalDependencies )
2913 {
2914 Artifact artifact = resolveDependency( dependency );
2915 getLog().debug( "add additional artifact with path " + artifact.getFile() );
2916 classpathElements.add( artifact.getFile() );
2917 }
2918 }
2919
2920 return classpathElements;
2921 }
2922
2923 protected ScopeFilter getDependencyScopeFilter()
2924 {
2925 return ScopeFilter.including( Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_SYSTEM );
2926 }
2927
2928
2929
2930
2931
2932
2933 public Artifact resolveDependency( Dependency dependency )
2934 throws MavenReportException
2935 {
2936 DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
2937 coordinate.setGroupId( dependency.getGroupId() );
2938 coordinate.setArtifactId( dependency.getArtifactId() );
2939 coordinate.setVersion( dependency.getVersion() );
2940 coordinate.setClassifier( dependency.getClassifier() );
2941 coordinate.setExtension( artifactHandlerManager.getArtifactHandler( dependency.getType() ).getExtension() );
2942
2943 try
2944 {
2945 return artifactResolver.resolveArtifact( getProjectBuildingRequest( project ), coordinate ).getArtifact();
2946 }
2947 catch ( ArtifactResolverException e )
2948 {
2949 throw new MavenReportException( "artifact resolver problem - " + e.getMessage(), e );
2950 }
2951 }
2952
2953
2954
2955
2956 protected final Toolchain getToolchain()
2957 {
2958 Toolchain tc = null;
2959
2960 if ( jdkToolchain != null )
2961 {
2962
2963 try
2964 {
2965 Method getToolchainsMethod =
2966 toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, String.class,
2967 Map.class );
2968
2969 @SuppressWarnings( "unchecked" )
2970 List<Toolchain> tcs =
2971 (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk",
2972 jdkToolchain );
2973
2974 if ( tcs != null && tcs.size() > 0 )
2975 {
2976 tc = tcs.get( 0 );
2977 }
2978 }
2979 catch ( SecurityException | ReflectiveOperationException e )
2980 {
2981
2982 }
2983 }
2984
2985 if ( tc == null )
2986 {
2987 tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
2988 }
2989
2990 return tc;
2991 }
2992
2993
2994
2995
2996
2997
2998
2999
3000 private void populateCompileArtifactMap( Map<String, Artifact> compileArtifactMap,
3001 Collection<Artifact> artifactList )
3002 throws MavenReportException
3003 {
3004 if ( artifactList == null )
3005 {
3006 return;
3007 }
3008
3009 for ( Artifact newArtifact : artifactList )
3010 {
3011 File file = newArtifact.getFile();
3012
3013 if ( file == null )
3014 {
3015 throw new MavenReportException(
3016 "Error in plugin descriptor - " + "dependency was not resolved for artifact: "
3017 + newArtifact.getGroupId() + ":" + newArtifact.getArtifactId() + ":"
3018 + newArtifact.getVersion() );
3019 }
3020
3021 if ( compileArtifactMap.get( newArtifact.getDependencyConflictId() ) != null )
3022 {
3023 Artifact oldArtifact = compileArtifactMap.get( newArtifact.getDependencyConflictId() );
3024
3025 ArtifactVersion oldVersion = new DefaultArtifactVersion( oldArtifact.getVersion() );
3026 ArtifactVersion newVersion = new DefaultArtifactVersion( newArtifact.getVersion() );
3027 if ( newVersion.compareTo( oldVersion ) > 0 )
3028 {
3029 compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
3030 }
3031 }
3032 else
3033 {
3034 compileArtifactMap.put( newArtifact.getDependencyConflictId(), newArtifact );
3035 }
3036 }
3037 }
3038
3039
3040
3041
3042
3043
3044
3045 private String getBottomText()
3046 {
3047 final String inceptionYear = project.getInceptionYear();
3048
3049
3050 final Calendar currentYearCal = Calendar.getInstance();
3051 final Date reproducibleDate = new MavenArchiver().parseOutputTimestamp( outputTimestamp );
3052 if ( reproducibleDate != null )
3053 {
3054 currentYearCal.setTime( reproducibleDate );
3055 }
3056 final String currentYear = String.valueOf( currentYearCal.get( Calendar.YEAR ) );
3057
3058 String theBottom = StringUtils.replace( this.bottom, "{currentYear}", currentYear );
3059
3060 if ( ( inceptionYear == null ) || inceptionYear.equals( currentYear ) )
3061 {
3062 theBottom = StringUtils.replace( theBottom, "{inceptionYear}–", "" );
3063 }
3064 else
3065 {
3066 theBottom = StringUtils.replace( theBottom, "{inceptionYear}", inceptionYear );
3067 }
3068
3069 if ( project.getOrganization() == null )
3070 {
3071 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
3072 }
3073 else
3074 {
3075 if ( StringUtils.isNotEmpty( project.getOrganization().getName() ) )
3076 {
3077 if ( StringUtils.isNotEmpty( project.getOrganization().getUrl() ) )
3078 {
3079 theBottom = StringUtils.replace( theBottom, "{organizationName}",
3080 "<a href=\"" + project.getOrganization().getUrl() + "\">"
3081 + project.getOrganization().getName() + "</a>" );
3082 }
3083 else
3084 {
3085 theBottom =
3086 StringUtils.replace( theBottom, "{organizationName}", project.getOrganization().getName() );
3087 }
3088 }
3089 else
3090 {
3091 theBottom = StringUtils.replace( theBottom, " {organizationName}", "" );
3092 }
3093 }
3094
3095 return theBottom;
3096 }
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113 private Optional<File> getStylesheetFile( final File javadocOutputDirectory )
3114 {
3115 if ( StringUtils.isEmpty( stylesheetfile ) )
3116 {
3117 if ( "java".equalsIgnoreCase( stylesheet ) )
3118 {
3119
3120 return Optional.empty();
3121 }
3122
3123
3124 return Optional.of( new File( javadocOutputDirectory, DEFAULT_CSS_NAME ) );
3125 }
3126
3127 if ( new File( stylesheetfile ).exists() )
3128 {
3129 return Optional.of( new File( stylesheetfile ) );
3130 }
3131
3132 return getResource( new File( javadocOutputDirectory, DEFAULT_CSS_NAME ), stylesheetfile );
3133 }
3134
3135 private void addAddStyleSheets( List<String> arguments ) throws MavenReportException
3136 {
3137 if ( addStylesheets == null )
3138 {
3139 return;
3140 }
3141
3142 for ( String addStylesheet : addStylesheets )
3143 {
3144 Optional<File> styleSheet = getAddStylesheet( getJavadocDirectory(), addStylesheet );
3145
3146 if ( styleSheet.isPresent() )
3147 {
3148 addArgIfNotEmpty( arguments, "--add-stylesheet",
3149 JavadocUtil.quotedPathArgument( styleSheet.get().getAbsolutePath() ),
3150 JavaVersion.parse( "10" ) );
3151 }
3152 }
3153 }
3154
3155
3156 private Optional<File> getAddStylesheet( final File javadocOutputDirectory, final String stylesheet )
3157 throws MavenReportException
3158 {
3159 if ( StringUtils.isEmpty( stylesheet ) )
3160 {
3161 return Optional.empty();
3162 }
3163
3164 File addstylesheetfile = new File( getJavadocDirectory(), stylesheet );
3165 if ( addstylesheetfile.exists() )
3166 {
3167 Optional<File> stylesheetfile = getStylesheetFile( javadocOutputDirectory );
3168 if ( stylesheetfile.isPresent() )
3169 {
3170 if ( stylesheetfile.get().getName().equals( addstylesheetfile.getName() ) )
3171 {
3172 throw new MavenReportException( "additional stylesheet must have a different name "
3173 + "than stylesheetfile: " + stylesheetfile.get().getName() );
3174 }
3175 }
3176
3177 return Optional.of( addstylesheetfile );
3178 }
3179
3180 throw new MavenReportException( "additional stylesheet file does not exist: "
3181 + addstylesheetfile.getAbsolutePath() );
3182 }
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196 private Optional<File> getHelpFile( final File javadocOutputDirectory )
3197 {
3198 if ( StringUtils.isEmpty( helpfile ) )
3199 {
3200 return Optional.empty();
3201 }
3202
3203 if ( new File( helpfile ).exists() )
3204 {
3205 return Optional.of( new File( helpfile ) );
3206 }
3207
3208 return getResource( new File( javadocOutputDirectory, "help-doc.html" ), helpfile );
3209 }
3210
3211
3212
3213
3214
3215
3216
3217
3218 private String getAccessLevel()
3219 {
3220 String accessLevel;
3221 if ( "public".equalsIgnoreCase( show ) || "protected".equalsIgnoreCase( show ) || "package".equalsIgnoreCase(
3222 show ) || "private".equalsIgnoreCase( show ) )
3223 {
3224 accessLevel = "-" + show;
3225 }
3226 else
3227 {
3228 if ( getLog().isErrorEnabled() )
3229 {
3230 getLog().error( "Unrecognized access level to show '" + show + "'. Defaulting to protected." );
3231 }
3232 accessLevel = "-protected";
3233 }
3234
3235 return accessLevel;
3236 }
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246 private String getBootclassPath()
3247 throws MavenReportException
3248 {
3249 Set<BootclasspathArtifact> bootclasspathArtifacts = collectBootClasspathArtifacts();
3250
3251 List<String> bootclassPath = new ArrayList<>();
3252 for ( BootclasspathArtifact aBootclasspathArtifact : bootclasspathArtifacts )
3253 {
3254 if ( ( StringUtils.isNotEmpty( aBootclasspathArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
3255 aBootclasspathArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3256 aBootclasspathArtifact.getVersion() ) ) )
3257 {
3258 bootclassPath.addAll( getArtifactsAbsolutePath( aBootclasspathArtifact ) );
3259 }
3260 }
3261
3262 bootclassPath = JavadocUtil.pruneFiles( bootclassPath );
3263
3264 StringBuilder path = new StringBuilder();
3265 path.append( StringUtils.join( bootclassPath.iterator(), File.pathSeparator ) );
3266
3267 if ( StringUtils.isNotEmpty( bootclasspath ) )
3268 {
3269 path.append( JavadocUtil.unifyPathSeparator( bootclasspath ) );
3270 }
3271
3272 return path.toString();
3273 }
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287 private String getDocletPath()
3288 throws MavenReportException
3289 {
3290 Set<DocletArtifact> docletArtifacts = collectDocletArtifacts();
3291 List<String> pathParts = new ArrayList<>();
3292
3293 for ( DocletArtifact docletArtifact : docletArtifacts )
3294 {
3295 if ( !isDocletArtifactEmpty( docletArtifact ) )
3296 {
3297 pathParts.addAll( getArtifactsAbsolutePath( docletArtifact ) );
3298 }
3299 }
3300
3301 if ( !StringUtils.isEmpty( docletPath ) )
3302 {
3303 pathParts.add( JavadocUtil.unifyPathSeparator( docletPath ) );
3304 }
3305
3306 String path = StringUtils.join( pathParts.iterator(), File.pathSeparator );
3307
3308 if ( StringUtils.isEmpty( path ) && getLog().isWarnEnabled() )
3309 {
3310 getLog().warn( "No docletpath option was found. Please review <docletpath/> or <docletArtifact/>"
3311 + " or <doclets/>." );
3312 }
3313
3314 return path;
3315 }
3316
3317
3318
3319
3320
3321
3322
3323
3324 private boolean isDocletArtifactEmpty( DocletArtifact aDocletArtifact )
3325 {
3326 if ( aDocletArtifact == null )
3327 {
3328 return true;
3329 }
3330
3331 return StringUtils.isEmpty( aDocletArtifact.getGroupId() ) && StringUtils.isEmpty(
3332 aDocletArtifact.getArtifactId() ) && StringUtils.isEmpty( aDocletArtifact.getVersion() );
3333 }
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343 private String getTagletPath()
3344 throws MavenReportException
3345 {
3346 Set<TagletArtifact> tArtifacts = collectTagletArtifacts();
3347 Collection<String> pathParts = new ArrayList<>();
3348
3349 for ( TagletArtifact tagletArtifact : tArtifacts )
3350 {
3351 if ( ( tagletArtifact != null ) && ( StringUtils.isNotEmpty( tagletArtifact.getGroupId() ) )
3352 && ( StringUtils.isNotEmpty( tagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3353 tagletArtifact.getVersion() ) ) )
3354 {
3355 pathParts.addAll( getArtifactsAbsolutePath( tagletArtifact ) );
3356 }
3357 }
3358
3359 Set<Taglet> taglets = collectTaglets();
3360 for ( Taglet taglet : taglets )
3361 {
3362 if ( taglet == null )
3363 {
3364 continue;
3365 }
3366
3367 if ( ( taglet.getTagletArtifact() != null ) && ( StringUtils.isNotEmpty(
3368 taglet.getTagletArtifact().getGroupId() ) ) && ( StringUtils.isNotEmpty(
3369 taglet.getTagletArtifact().getArtifactId() ) ) && ( StringUtils.isNotEmpty(
3370 taglet.getTagletArtifact().getVersion() ) ) )
3371 {
3372 pathParts.addAll( JavadocUtil.pruneFiles( getArtifactsAbsolutePath( taglet.getTagletArtifact() ) ) );
3373 }
3374 else if ( StringUtils.isNotEmpty( taglet.getTagletpath() ) )
3375 {
3376 for ( Path dir : JavadocUtil.pruneDirs( project, Collections.singletonList( taglet.getTagletpath() ) ) )
3377 {
3378 pathParts.add( dir.toString() );
3379 }
3380 }
3381 }
3382
3383 StringBuilder path = new StringBuilder();
3384 path.append( StringUtils.join( pathParts.iterator(), File.pathSeparator ) );
3385
3386 if ( StringUtils.isNotEmpty( tagletpath ) )
3387 {
3388 path.append( JavadocUtil.unifyPathSeparator( tagletpath ) );
3389 }
3390
3391 return path.toString();
3392 }
3393
3394 private Set<String> collectLinks()
3395 throws MavenReportException
3396 {
3397 Set<String> links = new LinkedHashSet<>();
3398
3399 if ( includeDependencySources )
3400 {
3401 try
3402 {
3403 resolveDependencyBundles();
3404 }
3405 catch ( IOException e )
3406 {
3407 throw new MavenReportException(
3408 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3409 }
3410
3411 if ( isNotEmpty( dependencyJavadocBundles ) )
3412 {
3413 for ( JavadocBundle bundle : dependencyJavadocBundles )
3414 {
3415 JavadocOptions options = bundle.getOptions();
3416 if ( options != null && isNotEmpty( options.getLinks() ) )
3417 {
3418 links.addAll( options.getLinks() );
3419 }
3420 }
3421 }
3422 }
3423
3424 if ( isNotEmpty( this.links ) )
3425 {
3426 links.addAll( this.links );
3427 }
3428
3429 links.addAll( getDependenciesLinks() );
3430
3431 return followLinks( links );
3432 }
3433
3434 private Set<Group> collectGroups()
3435 throws MavenReportException
3436 {
3437 Set<Group> groups = new LinkedHashSet<>();
3438
3439 if ( includeDependencySources )
3440 {
3441 try
3442 {
3443 resolveDependencyBundles();
3444 }
3445 catch ( IOException e )
3446 {
3447 throw new MavenReportException(
3448 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3449 }
3450
3451 if ( isNotEmpty( dependencyJavadocBundles ) )
3452 {
3453 for ( JavadocBundle bundle : dependencyJavadocBundles )
3454 {
3455 JavadocOptions options = bundle.getOptions();
3456 if ( options != null && isNotEmpty( options.getGroups() ) )
3457 {
3458 groups.addAll( options.getGroups() );
3459 }
3460 }
3461 }
3462 }
3463
3464 if ( this.groups != null && this.groups.length > 0 )
3465 {
3466 groups.addAll( Arrays.asList( this.groups ) );
3467 }
3468
3469 return groups;
3470 }
3471
3472 private Set<ResourcesArtifact> collectResourcesArtifacts()
3473 throws MavenReportException
3474 {
3475 Set<ResourcesArtifact> result = new LinkedHashSet<>();
3476
3477 if ( includeDependencySources )
3478 {
3479 try
3480 {
3481 resolveDependencyBundles();
3482 }
3483 catch ( IOException e )
3484 {
3485 throw new MavenReportException(
3486 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3487 }
3488
3489 if ( isNotEmpty( dependencyJavadocBundles ) )
3490 {
3491 for ( JavadocBundle bundle : dependencyJavadocBundles )
3492 {
3493 JavadocOptions options = bundle.getOptions();
3494 if ( options != null && isNotEmpty( options.getResourcesArtifacts() ) )
3495 {
3496 result.addAll( options.getResourcesArtifacts() );
3497 }
3498 }
3499 }
3500 }
3501
3502 if ( this.resourcesArtifacts != null && this.resourcesArtifacts.length > 0 )
3503 {
3504 result.addAll( Arrays.asList( this.resourcesArtifacts ) );
3505 }
3506
3507 return result;
3508 }
3509
3510 private Set<BootclasspathArtifact> collectBootClasspathArtifacts()
3511 throws MavenReportException
3512 {
3513 Set<BootclasspathArtifact> result = new LinkedHashSet<>();
3514
3515 if ( includeDependencySources )
3516 {
3517 try
3518 {
3519 resolveDependencyBundles();
3520 }
3521 catch ( IOException e )
3522 {
3523 throw new MavenReportException(
3524 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3525 }
3526
3527 if ( isNotEmpty( dependencyJavadocBundles ) )
3528 {
3529 for ( JavadocBundle bundle : dependencyJavadocBundles )
3530 {
3531 JavadocOptions options = bundle.getOptions();
3532 if ( options != null && isNotEmpty( options.getBootclasspathArtifacts() ) )
3533 {
3534 result.addAll( options.getBootclasspathArtifacts() );
3535 }
3536 }
3537 }
3538 }
3539
3540 if ( this.bootclasspathArtifacts != null && this.bootclasspathArtifacts.length > 0 )
3541 {
3542 result.addAll( Arrays.asList( this.bootclasspathArtifacts ) );
3543 }
3544
3545 return result;
3546 }
3547
3548 private Set<OfflineLink> collectOfflineLinks()
3549 throws MavenReportException
3550 {
3551 Set<OfflineLink> result = new LinkedHashSet<>();
3552
3553 OfflineLink javaApiLink = getDefaultJavadocApiLink();
3554 if ( javaApiLink != null )
3555 {
3556 result.add( javaApiLink );
3557 }
3558
3559 if ( includeDependencySources )
3560 {
3561 try
3562 {
3563 resolveDependencyBundles();
3564 }
3565 catch ( IOException e )
3566 {
3567 throw new MavenReportException(
3568 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3569 }
3570
3571 if ( isNotEmpty( dependencyJavadocBundles ) )
3572 {
3573 for ( JavadocBundle bundle : dependencyJavadocBundles )
3574 {
3575 JavadocOptions options = bundle.getOptions();
3576 if ( options != null && isNotEmpty( options.getOfflineLinks() ) )
3577 {
3578 result.addAll( options.getOfflineLinks() );
3579 }
3580 }
3581 }
3582 }
3583
3584 if ( this.offlineLinks != null && this.offlineLinks.length > 0 )
3585 {
3586 result.addAll( Arrays.asList( this.offlineLinks ) );
3587 }
3588
3589 return result;
3590 }
3591
3592 private Set<Tag> collectTags()
3593 throws MavenReportException
3594 {
3595 Set<Tag> tags = new LinkedHashSet<>();
3596
3597 if ( includeDependencySources )
3598 {
3599 try
3600 {
3601 resolveDependencyBundles();
3602 }
3603 catch ( IOException e )
3604 {
3605 throw new MavenReportException(
3606 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3607 }
3608
3609 if ( isNotEmpty( dependencyJavadocBundles ) )
3610 {
3611 for ( JavadocBundle bundle : dependencyJavadocBundles )
3612 {
3613 JavadocOptions options = bundle.getOptions();
3614 if ( options != null && isNotEmpty( options.getTags() ) )
3615 {
3616 tags.addAll( options.getTags() );
3617 }
3618 }
3619 }
3620 }
3621
3622 if ( this.tags != null && this.tags.length > 0 )
3623 {
3624 tags.addAll( Arrays.asList( this.tags ) );
3625 }
3626
3627 return tags;
3628 }
3629
3630 private Set<TagletArtifact> collectTagletArtifacts()
3631 throws MavenReportException
3632 {
3633 Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
3634
3635 if ( includeDependencySources )
3636 {
3637 try
3638 {
3639 resolveDependencyBundles();
3640 }
3641 catch ( IOException e )
3642 {
3643 throw new MavenReportException(
3644 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3645 }
3646
3647 if ( isNotEmpty( dependencyJavadocBundles ) )
3648 {
3649 for ( JavadocBundle bundle : dependencyJavadocBundles )
3650 {
3651 JavadocOptions options = bundle.getOptions();
3652 if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
3653 {
3654 tArtifacts.addAll( options.getTagletArtifacts() );
3655 }
3656 }
3657 }
3658 }
3659
3660 if ( tagletArtifact != null )
3661 {
3662 tArtifacts.add( tagletArtifact );
3663 }
3664
3665 if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
3666 {
3667 tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
3668 }
3669
3670 return tArtifacts;
3671 }
3672
3673 private Set<DocletArtifact> collectDocletArtifacts()
3674 throws MavenReportException
3675 {
3676 Set<DocletArtifact> dArtifacts = new LinkedHashSet<>();
3677
3678 if ( includeDependencySources )
3679 {
3680 try
3681 {
3682 resolveDependencyBundles();
3683 }
3684 catch ( IOException e )
3685 {
3686 throw new MavenReportException(
3687 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3688 }
3689
3690 if ( isNotEmpty( dependencyJavadocBundles ) )
3691 {
3692 for ( JavadocBundle bundle : dependencyJavadocBundles )
3693 {
3694 JavadocOptions options = bundle.getOptions();
3695 if ( options != null && isNotEmpty( options.getDocletArtifacts() ) )
3696 {
3697 dArtifacts.addAll( options.getDocletArtifacts() );
3698 }
3699 }
3700 }
3701 }
3702
3703 if ( docletArtifact != null )
3704 {
3705 dArtifacts.add( docletArtifact );
3706 }
3707
3708 if ( docletArtifacts != null && docletArtifacts.length > 0 )
3709 {
3710 dArtifacts.addAll( Arrays.asList( docletArtifacts ) );
3711 }
3712
3713 return dArtifacts;
3714 }
3715
3716 private Set<Taglet> collectTaglets()
3717 throws MavenReportException
3718 {
3719 Set<Taglet> result = new LinkedHashSet<>();
3720
3721 if ( includeDependencySources )
3722 {
3723 try
3724 {
3725 resolveDependencyBundles();
3726 }
3727 catch ( IOException e )
3728 {
3729 throw new MavenReportException(
3730 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
3731 }
3732
3733 if ( isNotEmpty( dependencyJavadocBundles ) )
3734 {
3735 for ( JavadocBundle bundle : dependencyJavadocBundles )
3736 {
3737 JavadocOptions options = bundle.getOptions();
3738 if ( options != null && isNotEmpty( options.getTaglets() ) )
3739 {
3740 result.addAll( options.getTaglets() );
3741 }
3742 }
3743 }
3744 }
3745
3746 if ( taglets != null && taglets.length > 0 )
3747 {
3748 result.addAll( Arrays.asList( taglets ) );
3749 }
3750
3751 return result;
3752 }
3753
3754
3755
3756
3757
3758
3759
3760
3761 private List<String> getArtifactsAbsolutePath( JavadocPathArtifact javadocArtifact )
3762 throws MavenReportException
3763 {
3764 if ( ( StringUtils.isEmpty( javadocArtifact.getGroupId() ) ) && ( StringUtils.isEmpty(
3765 javadocArtifact.getArtifactId() ) ) && ( StringUtils.isEmpty( javadocArtifact.getVersion() ) ) )
3766 {
3767 return Collections.emptyList();
3768 }
3769
3770 List<String> path = new ArrayList<>();
3771
3772 try
3773 {
3774 Artifact artifact = createAndResolveArtifact( javadocArtifact );
3775 path.add( artifact.getFile().getAbsolutePath() );
3776
3777 DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate();
3778 coordinate.setGroupId( javadocArtifact.getGroupId() );
3779 coordinate.setArtifactId( javadocArtifact.getArtifactId() );
3780 coordinate.setVersion( javadocArtifact.getVersion() );
3781
3782 Iterable<ArtifactResult> deps =
3783 dependencyResolver.resolveDependencies( getProjectBuildingRequest( project ), coordinate,
3784 ScopeFilter.including( "compile", "provided" ) );
3785 for ( ArtifactResult a : deps )
3786 {
3787 path.add( a.getArtifact().getFile().getAbsolutePath() );
3788 }
3789
3790 return path;
3791 }
3792 catch ( ArtifactResolverException e )
3793 {
3794 throw new MavenReportException( "Unable to resolve artifact:" + javadocArtifact, e );
3795 }
3796 catch ( DependencyResolverException e )
3797 {
3798 throw new MavenReportException( "Unable to resolve dependencies for:" + javadocArtifact, e );
3799 }
3800 }
3801
3802
3803
3804
3805
3806
3807
3808
3809 private Artifact createAndResolveArtifact( JavadocPathArtifact javadocArtifact )
3810 throws ArtifactResolverException
3811 {
3812 DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
3813 coordinate.setGroupId( javadocArtifact.getGroupId() );
3814 coordinate.setArtifactId( javadocArtifact.getArtifactId() );
3815 coordinate.setVersion( javadocArtifact.getVersion() );
3816 coordinate.setClassifier( javadocArtifact.getClassifier() );
3817
3818 return artifactResolver.resolveArtifact( getProjectBuildingRequest( project ), coordinate ).getArtifact();
3819 }
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829 private void addMemoryArg( Commandline cmd, String arg, String memory )
3830 {
3831 if ( StringUtils.isNotEmpty( memory ) )
3832 {
3833 try
3834 {
3835 cmd.createArg().setValue( "-J" + arg + JavadocUtil.parseJavadocMemory( memory ) );
3836 }
3837 catch ( IllegalArgumentException e )
3838 {
3839 if ( getLog().isErrorEnabled() )
3840 {
3841 getLog().error( "Malformed memory pattern for '" + arg + memory + "'. Ignore this option." );
3842 }
3843 }
3844 }
3845 }
3846
3847
3848
3849
3850
3851
3852 private void addProxyArg( Commandline cmd )
3853 {
3854 if ( settings == null || settings.getProxies().isEmpty() )
3855 {
3856 return;
3857 }
3858
3859 Map<String, Proxy> activeProxies = new HashMap<>();
3860
3861 for ( Proxy proxy : settings.getProxies() )
3862 {
3863 if ( proxy.isActive() )
3864 {
3865 String protocol = proxy.getProtocol();
3866
3867 if ( !activeProxies.containsKey( protocol ) )
3868 {
3869 activeProxies.put( protocol, proxy );
3870 }
3871 }
3872 }
3873
3874 if ( activeProxies.containsKey( "https" ) )
3875 {
3876 Proxy httpsProxy = activeProxies.get( "https" );
3877 if ( StringUtils.isNotEmpty( httpsProxy.getHost() ) )
3878 {
3879 cmd.createArg().setValue( "-J-Dhttps.proxyHost=" + httpsProxy.getHost() );
3880 cmd.createArg().setValue( "-J-Dhttps.proxyPort=" + httpsProxy.getPort() );
3881
3882 if ( StringUtils.isNotEmpty( httpsProxy.getNonProxyHosts() )
3883 && ( !activeProxies.containsKey( "http" )
3884 || StringUtils.isEmpty( activeProxies.get( "http" ).getNonProxyHosts() ) ) )
3885 {
3886 cmd.createArg().setValue( "-J-Dhttp.nonProxyHosts=\""
3887 + httpsProxy.getNonProxyHosts().replace( "|", "^|" ) + "\"" );
3888 }
3889 }
3890 }
3891
3892 if ( activeProxies.containsKey( "http" ) )
3893 {
3894 Proxy httpProxy = activeProxies.get( "http" );
3895 if ( StringUtils.isNotEmpty( httpProxy.getHost() ) )
3896 {
3897 cmd.createArg().setValue( "-J-Dhttp.proxyHost=" + httpProxy.getHost() );
3898 cmd.createArg().setValue( "-J-Dhttp.proxyPort=" + httpProxy.getPort() );
3899
3900 if ( !activeProxies.containsKey( "https" ) )
3901 {
3902 cmd.createArg().setValue( "-J-Dhttps.proxyHost=" + httpProxy.getHost() );
3903 cmd.createArg().setValue( "-J-Dhttps.proxyPort=" + httpProxy.getPort() );
3904 }
3905
3906 if ( StringUtils.isNotEmpty( httpProxy.getNonProxyHosts() ) )
3907 {
3908 cmd.createArg().setValue( "-J-Dhttp.nonProxyHosts=\""
3909 + httpProxy.getNonProxyHosts().replace( "|", "^|" ) + "\"" );
3910 }
3911 }
3912 }
3913
3914
3915 }
3916
3917
3918
3919
3920
3921
3922
3923
3924 private String getJavadocExecutable()
3925 throws IOException
3926 {
3927 Toolchain tc = getToolchain();
3928
3929 if ( tc != null )
3930 {
3931 getLog().info( "Toolchain in maven-javadoc-plugin: " + tc );
3932 if ( javadocExecutable != null )
3933 {
3934 getLog().warn( "Toolchains are ignored, 'javadocExecutable' parameter is set to " + javadocExecutable );
3935 }
3936 else
3937 {
3938 javadocExecutable = tc.findTool( "javadoc" );
3939 }
3940 }
3941
3942 String javadocCommand = "javadoc" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
3943
3944 File javadocExe;
3945
3946
3947
3948
3949 if ( StringUtils.isNotEmpty( javadocExecutable ) )
3950 {
3951 javadocExe = new File( javadocExecutable );
3952
3953 if ( javadocExe.isDirectory() )
3954 {
3955 javadocExe = new File( javadocExe, javadocCommand );
3956 }
3957
3958 if ( SystemUtils.IS_OS_WINDOWS && javadocExe.getName().indexOf( '.' ) < 0 )
3959 {
3960 javadocExe = new File( javadocExe.getPath() + ".exe" );
3961 }
3962
3963 if ( !javadocExe.isFile() )
3964 {
3965 throw new IOException( "The javadoc executable '" + javadocExe
3966 + "' doesn't exist or is not a file. Verify the <javadocExecutable/> parameter." );
3967 }
3968
3969 return javadocExe.getAbsolutePath();
3970 }
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006 if ( SystemUtils.IS_OS_AIX )
4007 {
4008 javadocExe =
4009 new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", javadocCommand );
4010 }
4011
4012
4013 else if ( SystemUtils.IS_OS_MAC_OSX && !JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast( "1.7" ) )
4014
4015 {
4016 javadocExe = new File( SystemUtils.getJavaHome() + File.separator + "bin", javadocCommand );
4017 }
4018 else if ( isJavaVersionAtLeast( org.apache.commons.lang3.JavaVersion.JAVA_9 ) )
4019 {
4020 javadocExe =
4021 new File( SystemUtils.getJavaHome() + File.separator + "bin", javadocCommand );
4022 }
4023 else
4024 {
4025
4026 javadocExe =
4027 new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", javadocCommand );
4028 }
4029
4030
4031
4032
4033 if ( !javadocExe.exists() || !javadocExe.isFile() )
4034 {
4035 Properties env = CommandLineUtils.getSystemEnvVars();
4036 String javaHome = env.getProperty( "JAVA_HOME" );
4037 if ( StringUtils.isEmpty( javaHome ) )
4038 {
4039 throw new IOException( "The environment variable JAVA_HOME is not correctly set." );
4040 }
4041 if ( ( !new File( javaHome ).getCanonicalFile().exists() )
4042 || ( new File( javaHome ).getCanonicalFile().isFile() ) )
4043 {
4044 throw new IOException( "The environment variable JAVA_HOME=" + javaHome
4045 + " doesn't exist or is not a valid directory." );
4046 }
4047
4048 javadocExe = new File( javaHome + File.separator + "bin", javadocCommand );
4049 }
4050
4051 if ( !javadocExe.getCanonicalFile().exists() || !javadocExe.getCanonicalFile().isFile() )
4052 {
4053 throw new IOException( "The javadoc executable '" + javadocExe
4054 + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." );
4055 }
4056
4057 return javadocExe.getAbsolutePath();
4058 }
4059
4060
4061
4062
4063
4064
4065
4066
4067 private void setFJavadocVersion( File jExecutable )
4068 throws MavenReportException
4069 {
4070 JavaVersion jVersion;
4071 try
4072 {
4073 jVersion = JavadocUtil.getJavadocVersion( jExecutable );
4074 }
4075 catch ( IOException | CommandLineException | IllegalArgumentException e )
4076 {
4077 if ( getLog().isWarnEnabled() )
4078 {
4079 getLog().warn( "Unable to find the javadoc version: " + e.getMessage() );
4080 getLog().warn( "Using the Java version instead of, i.e. " + JAVA_VERSION );
4081 }
4082 jVersion = JAVA_VERSION;
4083 }
4084
4085 if ( StringUtils.isNotEmpty( javadocVersion ) )
4086 {
4087 try
4088 {
4089 javadocRuntimeVersion = JavaVersion.parse( javadocVersion );
4090 }
4091 catch ( NumberFormatException e )
4092 {
4093 throw new MavenReportException( "Unable to parse javadoc version: " + e.getMessage(), e );
4094 }
4095
4096 if ( javadocRuntimeVersion.compareTo( jVersion ) != 0 && getLog().isWarnEnabled() )
4097 {
4098 getLog().warn( "Are you sure about the <javadocVersion/> parameter? It seems to be " + jVersion );
4099 }
4100 }
4101 else
4102 {
4103 javadocRuntimeVersion = jVersion;
4104 }
4105 }
4106
4107
4108
4109
4110
4111
4112
4113
4114 private boolean isJavaDocVersionAtLeast( JavaVersion requiredVersion )
4115 {
4116 return JAVA_VERSION.compareTo( requiredVersion ) >= 0;
4117 }
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127 private void addArgIf( List<String> arguments, boolean b, String value )
4128 {
4129 if ( b )
4130 {
4131 arguments.add( value );
4132 }
4133 }
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146 private void addArgIf( List<String> arguments, boolean b, String value, JavaVersion requiredJavaVersion )
4147 {
4148 if ( b )
4149 {
4150 if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
4151 {
4152 addArgIf( arguments, true, value );
4153 }
4154 else
4155 {
4156 if ( getLog().isWarnEnabled() )
4157 {
4158 getLog().warn( value + " option is not supported on Java version < " + requiredJavaVersion
4159 + ". Ignore this option." );
4160 }
4161 }
4162 }
4163 }
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176 private void addArgIfNotEmpty( List<String> arguments, String key, String value )
4177 {
4178 addArgIfNotEmpty( arguments, key, value, false );
4179 }
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196 private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
4197 boolean splitValue, JavaVersion requiredJavaVersion )
4198 {
4199 if ( StringUtils.isNotEmpty( value ) )
4200 {
4201 if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
4202 {
4203 addArgIfNotEmpty( arguments, key, value, repeatKey, splitValue );
4204 }
4205 else
4206 {
4207 if ( getLog().isWarnEnabled() )
4208 {
4209 getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion
4210 + ". Ignore this option." );
4211 }
4212 }
4213 }
4214 }
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228 private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey,
4229 boolean splitValue )
4230 {
4231 if ( StringUtils.isNotEmpty( value ) )
4232 {
4233 if ( StringUtils.isNotEmpty( key ) )
4234 {
4235 arguments.add( key );
4236 }
4237
4238 if ( splitValue )
4239 {
4240 StringTokenizer token = new StringTokenizer( value, "," );
4241 while ( token.hasMoreTokens() )
4242 {
4243 String current = token.nextToken().trim();
4244
4245 if ( StringUtils.isNotEmpty( current ) )
4246 {
4247 arguments.add( current );
4248
4249 if ( token.hasMoreTokens() && repeatKey )
4250 {
4251 arguments.add( key );
4252 }
4253 }
4254 }
4255 }
4256 else
4257 {
4258 arguments.add( value );
4259 }
4260 }
4261 }
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274 private void addArgIfNotEmpty( List<String> arguments, String key, String value, boolean repeatKey )
4275 {
4276 addArgIfNotEmpty( arguments, key, value, repeatKey, true );
4277 }
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289 private void addArgIfNotEmpty( List<String> arguments, String key, String value,
4290 JavaVersion requiredJavaVersion )
4291 {
4292 addArgIfNotEmpty( arguments, key, value, requiredJavaVersion, false );
4293 }
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307 private void addArgIfNotEmpty( List<String> arguments, String key, String value, JavaVersion requiredJavaVersion,
4308 boolean repeatKey )
4309 {
4310 if ( StringUtils.isNotEmpty( value ) )
4311 {
4312 if ( isJavaDocVersionAtLeast( requiredJavaVersion ) )
4313 {
4314 addArgIfNotEmpty( arguments, key, value, repeatKey );
4315 }
4316 else
4317 {
4318 if ( getLog().isWarnEnabled() )
4319 {
4320 getLog().warn( key + " option is not supported on Java version < " + requiredJavaVersion );
4321 }
4322 }
4323 }
4324 }
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339 private void addLinkofflineArguments( List<String> arguments, Set<OfflineLink> offlineLinksList )
4340 throws MavenReportException
4341 {
4342 for ( OfflineLink offlineLink : offlineLinksList )
4343 {
4344 String url = offlineLink.getUrl();
4345 if ( StringUtils.isEmpty( url ) )
4346 {
4347 continue;
4348 }
4349 url = cleanUrl( url );
4350
4351 String location = offlineLink.getLocation();
4352 if ( StringUtils.isEmpty( location ) )
4353 {
4354 continue;
4355 }
4356 if ( isValidJavadocLink( location, false ) )
4357 {
4358 addArgIfNotEmpty( arguments, "-linkoffline",
4359 JavadocUtil.quotedPathArgument( url ) + " " + JavadocUtil.quotedPathArgument(
4360 location ), true );
4361 }
4362 }
4363 }
4364
4365 private Set<OfflineLink> getLinkofflines() throws MavenReportException
4366 {
4367 Set<OfflineLink> offlineLinksList = collectOfflineLinks();
4368
4369 offlineLinksList.addAll( getModulesLinks() );
4370
4371 return offlineLinksList;
4372 }
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393 private void addLinkArguments( List<String> arguments )
4394 throws MavenReportException
4395 {
4396 Set<String> links = collectLinks();
4397
4398 for ( String link : links )
4399 {
4400 if ( StringUtils.isEmpty( link ) )
4401 {
4402 continue;
4403 }
4404
4405 if ( isOffline && !link.startsWith( "file:" ) )
4406 {
4407 continue;
4408 }
4409
4410 while ( link.endsWith( "/" ) )
4411 {
4412 link = link.substring( 0, link.lastIndexOf( "/" ) );
4413 }
4414
4415 addArgIfNotEmpty( arguments, "-link", JavadocUtil.quotedPathArgument( link ), true, false );
4416 }
4417 }
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428 private void copyAllResources( File javadocOutputDirectory )
4429 throws MavenReportException
4430 {
4431
4432
4433
4434
4435 try
4436 {
4437 copyDefaultStylesheet( javadocOutputDirectory );
4438 }
4439 catch ( IOException e )
4440 {
4441 throw new MavenReportException( "Unable to copy default stylesheet: " + e.getMessage(), e );
4442 }
4443
4444
4445
4446
4447
4448 if ( docfilessubdirs )
4449 {
4450
4451
4452
4453
4454 try
4455 {
4456 copyJavadocResources( javadocOutputDirectory );
4457 }
4458 catch ( IOException e )
4459 {
4460 throw new MavenReportException( "Unable to copy javadoc resources: " + e.getMessage(), e );
4461 }
4462 }
4463
4464
4465
4466
4467
4468 copyAdditionalJavadocResources( javadocOutputDirectory );
4469 }
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481 private void copyDefaultStylesheet( File anOutputDirectory )
4482 throws IOException
4483 {
4484 if ( StringUtils.isNotEmpty( stylesheetfile ) )
4485 {
4486 return;
4487 }
4488
4489 if ( !stylesheet.equalsIgnoreCase( "maven" ) )
4490 {
4491 return;
4492 }
4493
4494 URL url = getClass().getClassLoader().getResource( RESOURCE_CSS_DIR + "/" + DEFAULT_CSS_NAME );
4495 File outFile = new File( anOutputDirectory, DEFAULT_CSS_NAME );
4496 JavadocUtil.copyResource( url, outFile );
4497 }
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509 private void copyJavadocResources( File anOutputDirectory )
4510 throws IOException
4511 {
4512 if ( anOutputDirectory == null || !anOutputDirectory.exists() )
4513 {
4514 throw new IOException( "The outputDirectory " + anOutputDirectory + " doesn't exists." );
4515 }
4516
4517 if ( includeDependencySources )
4518 {
4519 resolveDependencyBundles();
4520 if ( isNotEmpty( dependencyJavadocBundles ) )
4521 {
4522 for ( JavadocBundle bundle : dependencyJavadocBundles )
4523 {
4524 File dir = bundle.getResourcesDirectory();
4525 JavadocOptions options = bundle.getOptions();
4526 if ( dir != null && dir.isDirectory() )
4527 {
4528 JavadocUtil.copyJavadocResources( anOutputDirectory, dir, options == null
4529 ? null
4530 : options.getExcludedDocfilesSubdirs() );
4531 }
4532 }
4533 }
4534 }
4535
4536 if ( getJavadocDirectory() != null )
4537 {
4538 JavadocUtil.copyJavadocResources( anOutputDirectory, getJavadocDirectory(), excludedocfilessubdir );
4539 }
4540
4541 if ( isAggregator() )
4542 {
4543 for ( MavenProject subProject : getAggregatedProjects() )
4544 {
4545 if ( subProject != project && getJavadocDirectory() != null )
4546 {
4547 String javadocDirRelative =
4548 PathUtils.toRelative( project.getBasedir(), getJavadocDirectory().getAbsolutePath() );
4549 File javadocDir = new File( subProject.getBasedir(), javadocDirRelative );
4550 JavadocUtil.copyJavadocResources( anOutputDirectory, javadocDir, excludedocfilessubdir );
4551 }
4552 }
4553 }
4554 }
4555
4556 private synchronized void resolveDependencyBundles()
4557 throws IOException
4558 {
4559 if ( dependencyJavadocBundles == null )
4560 {
4561 dependencyJavadocBundles =
4562 resourceResolver.resolveDependencyJavadocBundles( getDependencySourceResolverConfig() );
4563 if ( dependencyJavadocBundles == null )
4564 {
4565 dependencyJavadocBundles = new ArrayList<>();
4566 }
4567 }
4568 }
4569
4570
4571
4572
4573
4574
4575
4576
4577 private void copyAdditionalJavadocResources( File anOutputDirectory )
4578 throws MavenReportException
4579 {
4580 Set<ResourcesArtifact> resourcesArtifacts = collectResourcesArtifacts();
4581 if ( isEmpty( resourcesArtifacts ) )
4582 {
4583 return;
4584 }
4585
4586 UnArchiver unArchiver;
4587 try
4588 {
4589 unArchiver = archiverManager.getUnArchiver( "jar" );
4590 }
4591 catch ( NoSuchArchiverException e )
4592 {
4593 throw new MavenReportException(
4594 "Unable to extract resources artifact. " + "No archiver for 'jar' available.", e );
4595 }
4596
4597 for ( ResourcesArtifact item : resourcesArtifacts )
4598 {
4599 Artifact artifact;
4600 try
4601 {
4602 artifact = createAndResolveArtifact( item );
4603 }
4604 catch ( ArtifactResolverException e )
4605 {
4606 throw new MavenReportException( "Unable to resolve artifact:" + item, e );
4607 }
4608
4609 unArchiver.setSourceFile( artifact.getFile() );
4610 unArchiver.setDestDirectory( anOutputDirectory );
4611
4612 IncludeExcludeFileSelector[] selectors =
4613 new IncludeExcludeFileSelector[]{ new IncludeExcludeFileSelector() };
4614 selectors[0].setExcludes( new String[]{ "META-INF/**" } );
4615 unArchiver.setFileSelectors( selectors );
4616
4617 getLog().info( "Extracting contents of resources artifact: " + artifact.getArtifactId() );
4618 try
4619 {
4620 unArchiver.extract();
4621 }
4622 catch ( ArchiverException e )
4623 {
4624 throw new MavenReportException(
4625 "Extraction of resources failed. Artifact that failed was: " + artifact.getArtifactId(), e );
4626 }
4627 }
4628 }
4629
4630
4631
4632
4633
4634 private List<String> getPackageNames( Map<Path, Collection<String>> sourcePaths )
4635 {
4636 List<String> returnList = new ArrayList<>();
4637
4638 if ( !StringUtils.isEmpty( sourcepath ) )
4639 {
4640 return returnList;
4641 }
4642
4643 for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
4644 {
4645 for ( String currentFile : currentPathEntry.getValue() )
4646 {
4647
4648
4649
4650
4651 if ( currentFile.contains( "doc-files" ) )
4652 {
4653 continue;
4654 }
4655
4656 int lastIndexOfSeparator = currentFile.lastIndexOf( "/" );
4657 if ( lastIndexOfSeparator != -1 )
4658 {
4659 String packagename = currentFile.substring( 0, lastIndexOfSeparator ).replace( '/', '.' );
4660
4661 if ( !returnList.contains( packagename ) )
4662 {
4663 returnList.add( packagename );
4664 }
4665 }
4666 }
4667 }
4668
4669 return returnList;
4670 }
4671
4672
4673
4674
4675
4676
4677
4678
4679 private Collection<String> getPackageNamesRespectingJavaModules( Collection<JavadocModule> javadocModules )
4680 throws MavenReportException
4681 {
4682 if ( !StringUtils.isEmpty( sourcepath ) )
4683 {
4684 return Collections.emptyList();
4685 }
4686
4687 Set<String> returnList = new LinkedHashSet<>();
4688 for ( JavadocModule javadocModule : javadocModules )
4689 {
4690 Collection<Path> artifactSourcePaths = javadocModule.getSourcePaths();
4691 Set<String> exportedPackages = new HashSet<>();
4692 boolean exportAllPackages;
4693 ResolvePathResult resolvedPath = getResolvePathResult( javadocModule.getArtifactFile() );
4694 if ( resolvedPath != null && resolvedPath.getModuleNameSource() == ModuleNameSource.MODULEDESCRIPTOR )
4695 {
4696 Set<JavaModuleDescriptor.JavaExports> exports = resolvedPath.getModuleDescriptor().exports();
4697 if ( exports.isEmpty() )
4698 {
4699 continue;
4700 }
4701 for ( JavaModuleDescriptor.JavaExports export : exports )
4702 {
4703 exportedPackages.add( export.source() );
4704 }
4705 exportAllPackages = false;
4706 }
4707 else
4708 {
4709 exportAllPackages = true;
4710 }
4711
4712 for ( Map.Entry<Path, Collection<String>> currentPathEntry : getFiles( artifactSourcePaths ).entrySet() )
4713 {
4714 for ( String currentFile : currentPathEntry.getValue() )
4715 {
4716
4717
4718
4719
4720 if ( currentFile.contains( "doc-files" ) )
4721 {
4722 continue;
4723 }
4724
4725 int lastIndexOfSeparator = currentFile.lastIndexOf( '/' );
4726 if ( lastIndexOfSeparator != -1 )
4727 {
4728 String packagename =
4729 currentFile.substring( 0, lastIndexOfSeparator ).replace( '/', '.' );
4730
4731 if ( exportAllPackages || exportedPackages.contains( packagename ) )
4732 {
4733 returnList.add( packagename );
4734 }
4735 }
4736 }
4737 }
4738 }
4739
4740 return returnList;
4741 }
4742
4743
4744
4745
4746
4747 private List<String> getFilesWithUnnamedPackages( Map<Path, Collection<String>> sourcePaths )
4748 {
4749 List<String> returnList = new ArrayList<>();
4750
4751 if ( !StringUtils.isEmpty( sourcepath ) )
4752 {
4753 return returnList;
4754 }
4755
4756 for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
4757 {
4758 Path currentSourcePath = currentPathEntry.getKey();
4759
4760 for ( String currentFile : currentPathEntry.getValue() )
4761 {
4762
4763
4764
4765
4766 if ( currentFile.contains( "doc-files" ) )
4767 {
4768 continue;
4769 }
4770
4771 if ( currentFile.indexOf( File.separatorChar ) == -1 )
4772 {
4773 returnList.add( currentSourcePath.resolve( currentFile ).toAbsolutePath().toString() );
4774 }
4775 }
4776 }
4777
4778 return returnList;
4779 }
4780
4781
4782
4783
4784
4785
4786 private List<String> getSpecialFiles( Map<Path, Collection<String>> sourcePaths )
4787 {
4788 if ( !StringUtils.isEmpty( sourcepath ) )
4789 {
4790 return new ArrayList<>();
4791 }
4792
4793 boolean containsModuleDescriptor = false;
4794 for ( Collection<String> sourcepathFiles : sourcePaths.values() )
4795 {
4796 containsModuleDescriptor = sourcepathFiles.contains( "module-info.java" );
4797 if ( containsModuleDescriptor )
4798 {
4799 break;
4800 }
4801 }
4802
4803 if ( containsModuleDescriptor )
4804 {
4805 return getModuleSourcePathFiles( sourcePaths );
4806 }
4807 else
4808 {
4809 return getFilesWithUnnamedPackages( sourcePaths );
4810 }
4811 }
4812
4813 private List<String> getModuleSourcePathFiles( Map<Path, Collection<String>> sourcePaths )
4814 {
4815 List<String> returnList = new ArrayList<>();
4816
4817 for ( Entry<Path, Collection<String>> currentPathEntry : sourcePaths.entrySet() )
4818 {
4819 Path currentSourcePath = currentPathEntry.getKey();
4820 if ( currentPathEntry.getValue().contains( "module-info.java" ) )
4821 {
4822 returnList.add( currentSourcePath.resolve( "module-info.java" ).toAbsolutePath().toString() );
4823 }
4824 else
4825 {
4826 for ( String currentFile : currentPathEntry.getValue() )
4827 {
4828
4829
4830
4831
4832 if ( currentFile.contains( "doc-files" ) )
4833 {
4834 continue;
4835 }
4836
4837 returnList.add( currentSourcePath.resolve( currentFile ).toAbsolutePath().toString() );
4838 }
4839 }
4840 }
4841 return returnList;
4842 }
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856 private void addCommandLineOptions( Commandline cmd, List<String> arguments, File javadocOutputDirectory )
4857 throws MavenReportException
4858 {
4859 File optionsFile = new File( javadocOutputDirectory, OPTIONS_FILE_NAME );
4860
4861 StringBuilder options = new StringBuilder();
4862 options.append( StringUtils.join( arguments.iterator(),
4863 SystemUtils.LINE_SEPARATOR ) );
4864
4865 Charset outputFileEncoding;
4866 if ( JAVA_VERSION.isAtLeast( "9" ) && JAVA_VERSION.isBefore( "12" ) )
4867 {
4868 outputFileEncoding = StandardCharsets.UTF_8;
4869 }
4870 else
4871 {
4872 outputFileEncoding = Charset.defaultCharset();
4873 }
4874 try
4875 {
4876 Files.write( optionsFile.toPath(), Collections.singleton( options ), outputFileEncoding );
4877 }
4878 catch ( IOException e )
4879 {
4880 throw new MavenReportException(
4881 "Unable to write '" + optionsFile.getName() + "' temporary file for command execution", e );
4882 }
4883
4884 cmd.createArg().setValue( "@" + OPTIONS_FILE_NAME );
4885 }
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905 private void addCommandLineArgFile( Commandline cmd, File javadocOutputDirectory, List<String> files )
4906 throws MavenReportException
4907 {
4908 File argfileFile;
4909 if ( JAVA_VERSION.compareTo( SINCE_JAVADOC_1_4 ) >= 0 )
4910 {
4911 argfileFile = new File( javadocOutputDirectory, ARGFILE_FILE_NAME );
4912 cmd.createArg().setValue( "@" + ARGFILE_FILE_NAME );
4913 }
4914 else
4915 {
4916 argfileFile = new File( javadocOutputDirectory, FILES_FILE_NAME );
4917 cmd.createArg().setValue( "@" + FILES_FILE_NAME );
4918 }
4919
4920 List<String> quotedFiles = new ArrayList<>( files.size() );
4921 for ( String file : files )
4922 {
4923 quotedFiles.add( JavadocUtil.quotedPathArgument( file ) );
4924 }
4925
4926 Charset cs;
4927 if ( JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast( "9" )
4928 && JavaVersion.JAVA_SPECIFICATION_VERSION.isBefore( "12" ) )
4929 {
4930 cs = StandardCharsets.UTF_8;
4931 }
4932 else
4933 {
4934 cs = Charset.defaultCharset();
4935 }
4936
4937 try
4938 {
4939 Files.write( argfileFile.toPath(), quotedFiles, cs );
4940 }
4941 catch ( IOException e )
4942 {
4943 throw new MavenReportException(
4944 "Unable to write '" + argfileFile.getName() + "' temporary file for command execution", e );
4945 }
4946 }
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960 private void addCommandLinePackages( Commandline cmd, File javadocOutputDirectory, Collection<String> packageNames )
4961 throws MavenReportException
4962 {
4963 File packagesFile = new File( javadocOutputDirectory, PACKAGES_FILE_NAME );
4964
4965 try
4966 {
4967 FileUtils.fileWrite( packagesFile.getAbsolutePath(), null ,
4968 StringUtils.join( packageNames.iterator(), SystemUtils.LINE_SEPARATOR ) );
4969 }
4970 catch ( IOException e )
4971 {
4972 throw new MavenReportException(
4973 "Unable to write '" + packagesFile.getName() + "' temporary file for command execution", e );
4974 }
4975
4976 cmd.createArg().setValue( "@" + PACKAGES_FILE_NAME );
4977 }
4978
4979
4980
4981
4982
4983
4984 private void validateJavadocOptions()
4985 throws MavenReportException
4986 {
4987
4988 if ( StringUtils.isNotEmpty( getEncoding() ) && !JavadocUtil.validateEncoding( getEncoding() ) )
4989 {
4990 throw new MavenReportException( "Unsupported option <encoding/> '" + getEncoding() + "'" );
4991 }
4992
4993
4994 if ( StringUtils.isNotEmpty( this.locale ) )
4995 {
4996 StringTokenizer tokenizer = new StringTokenizer( this.locale, "_" );
4997 final int maxTokens = 3;
4998 if ( tokenizer.countTokens() > maxTokens )
4999 {
5000 throw new MavenReportException(
5001 "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
5002 }
5003
5004 Locale localeObject = null;
5005 if ( tokenizer.hasMoreTokens() )
5006 {
5007 String language = tokenizer.nextToken().toLowerCase( Locale.ENGLISH );
5008 if ( !Arrays.asList( Locale.getISOLanguages() ).contains( language ) )
5009 {
5010 throw new MavenReportException(
5011 "Unsupported language '" + language + "' in option <locale/> '" + this.locale + "'" );
5012 }
5013 localeObject = new Locale( language );
5014
5015 if ( tokenizer.hasMoreTokens() )
5016 {
5017 String country = tokenizer.nextToken().toUpperCase( Locale.ENGLISH );
5018 if ( !Arrays.asList( Locale.getISOCountries() ).contains( country ) )
5019 {
5020 throw new MavenReportException(
5021 "Unsupported country '" + country + "' in option <locale/> '" + this.locale + "'" );
5022 }
5023 localeObject = new Locale( language, country );
5024
5025 if ( tokenizer.hasMoreTokens() )
5026 {
5027 String variant = tokenizer.nextToken();
5028 localeObject = new Locale( language, country, variant );
5029 }
5030 }
5031 }
5032
5033 if ( localeObject == null )
5034 {
5035 throw new MavenReportException(
5036 "Unsupported option <locale/> '" + this.locale + "', should be language_country_variant." );
5037 }
5038
5039 this.locale = localeObject.toString();
5040 final List<Locale> availableLocalesList = Arrays.asList( Locale.getAvailableLocales() );
5041 if ( StringUtils.isNotEmpty( localeObject.getVariant() ) && !availableLocalesList.contains( localeObject ) )
5042 {
5043 StringBuilder sb = new StringBuilder();
5044 sb.append( "Unsupported option <locale/> with variant '" ).append( this.locale );
5045 sb.append( "'" );
5046
5047 localeObject = new Locale( localeObject.getLanguage(), localeObject.getCountry() );
5048 this.locale = localeObject.toString();
5049
5050 sb.append( ", trying to use <locale/> without variant, i.e. '" ).append( this.locale ).append( "'" );
5051 if ( getLog().isWarnEnabled() )
5052 {
5053 getLog().warn( sb.toString() );
5054 }
5055 }
5056
5057 if ( !availableLocalesList.contains( localeObject ) )
5058 {
5059 throw new MavenReportException( "Unsupported option <locale/> '" + this.locale + "'" );
5060 }
5061 }
5062 }
5063
5064
5065
5066
5067
5068
5069
5070
5071 private void validateStandardDocletOptions()
5072 throws MavenReportException
5073 {
5074
5075 if ( StringUtils.isNotEmpty( getDocencoding() ) && !JavadocUtil.validateEncoding( getDocencoding() ) )
5076 {
5077 throw new MavenReportException( "Unsupported option <docencoding/> '" + getDocencoding() + "'" );
5078 }
5079
5080
5081 if ( StringUtils.isNotEmpty( getCharset() ) && !JavadocUtil.validateEncoding( getCharset() ) )
5082 {
5083 throw new MavenReportException( "Unsupported option <charset/> '" + getCharset() + "'" );
5084 }
5085
5086
5087 if ( StringUtils.isNotEmpty( helpfile ) && nohelp )
5088 {
5089 throw new MavenReportException( "Option <nohelp/> conflicts with <helpfile/>" );
5090 }
5091
5092
5093 if ( getOverview() != null && getOverview().exists() && nooverview )
5094 {
5095 throw new MavenReportException( "Option <nooverview/> conflicts with <overview/>" );
5096 }
5097
5098
5099 if ( splitindex && noindex )
5100 {
5101 throw new MavenReportException( "Option <noindex/> conflicts with <splitindex/>" );
5102 }
5103
5104
5105 if ( StringUtils.isNotEmpty( stylesheet ) && !( stylesheet.equalsIgnoreCase( "maven" )
5106 || stylesheet.equalsIgnoreCase( "java" ) ) )
5107 {
5108 throw new MavenReportException( "Option <stylesheet/> supports only \"maven\" or \"java\" value." );
5109 }
5110 }
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124 private void addJavadocOptions( File javadocOutputDirectory,
5125 List<String> arguments,
5126 Collection<JavadocModule> allSourcePaths,
5127 Set<OfflineLink> offlineLinks )
5128 throws MavenReportException
5129 {
5130 Collection<Path> sourcePaths = allSourcePaths.stream()
5131 .flatMap( e -> e.getSourcePaths().stream() )
5132 .collect( Collectors.toList() );
5133
5134 validateJavadocOptions();
5135
5136
5137 addArgIfNotEmpty( arguments, "-locale", JavadocUtil.quotedArgument( this.locale ) );
5138
5139
5140
5141 if ( old && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_4 ) )
5142 {
5143 if ( getLog().isWarnEnabled() )
5144 {
5145 getLog().warn( "Javadoc 1.4+ doesn't support the -1.1 switch anymore. Ignore this option." );
5146 }
5147 }
5148 else
5149 {
5150 addArgIf( arguments, old, "-1.1" );
5151 }
5152
5153 addArgIfNotEmpty( arguments, "-bootclasspath", JavadocUtil.quotedPathArgument( getBootclassPath() ) );
5154
5155 if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5156 {
5157 addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_5 );
5158 }
5159
5160 List<MavenProject> aggregatedProjects = reactorProjects;
5161 Map<String, MavenProject> reactorKeys = new HashMap<>( aggregatedProjects.size() );
5162 for ( MavenProject reactorProject : aggregatedProjects )
5163 {
5164 reactorKeys.put( ArtifactUtils.versionlessKey( reactorProject.getGroupId(),
5165 reactorProject.getArtifactId() ), reactorProject );
5166 }
5167
5168 Map<String, JavaModuleDescriptor> allModuleDescriptors = new HashMap<>();
5169
5170 boolean supportModulePath = javadocRuntimeVersion.isAtLeast( "9" );
5171 if ( release != null )
5172 {
5173 supportModulePath &= JavaVersion.parse( release ).isAtLeast( "9" );
5174 }
5175 else if ( source != null )
5176 {
5177 supportModulePath &= JavaVersion.parse( source ).isAtLeast( "9" );
5178 }
5179
5180 if ( supportModulePath )
5181 {
5182 for ( JavadocModule entry : allSourcePaths )
5183 {
5184 if ( entry.getModuleNameSource() == null || entry.getModuleNameSource() == ModuleNameSource.FILENAME )
5185 {
5186 Path moduleDescriptor = findMainDescriptor( entry.getSourcePaths() );
5187
5188 if ( moduleDescriptor != null )
5189 {
5190 try
5191 {
5192 allModuleDescriptors.put( entry.getGa(),
5193 locationManager.parseModuleDescriptor( moduleDescriptor ).getModuleDescriptor() );
5194 }
5195 catch ( IOException e )
5196 {
5197 throw new MavenReportException( e.getMessage(), e );
5198 }
5199 }
5200 }
5201 else
5202 {
5203 allModuleDescriptors.put( entry.getGa(), entry.getModuleDescriptor() );
5204 }
5205 }
5206 }
5207
5208 Collection<String> additionalModules = new ArrayList<>();
5209
5210 ResolvePathResult mainResolvePathResult = null;
5211
5212 Map<String, Collection<Path>> patchModules = new HashMap<>();
5213
5214 Path moduleSourceDir = null;
5215 if ( supportModulePath && !allModuleDescriptors.isEmpty() )
5216 {
5217 Collection<String> unnamedProjects = new ArrayList<>();
5218 for ( JavadocModule javadocModule : allSourcePaths )
5219 {
5220 MavenProject aggregatedProject = reactorKeys.get( javadocModule.getGa() );
5221 if ( aggregatedProject != null && !"pom".equals( aggregatedProject.getPackaging() ) )
5222 {
5223 ResolvePathResult result = null;
5224
5225
5226 File artifactFile = getClassesFile( aggregatedProject );
5227 if ( artifactFile != null )
5228 {
5229 ResolvePathRequest<File> request = ResolvePathRequest.ofFile( artifactFile );
5230 try
5231 {
5232 result = locationManager.resolvePath( request );
5233 }
5234 catch ( RuntimeException e )
5235 {
5236
5237 if ( !"java.lang.module.FindException".equals( e.getClass().getName() ) )
5238 {
5239 throw e;
5240 }
5241 }
5242 catch ( IOException e )
5243 {
5244 throw new MavenReportException( e.getMessage(), e );
5245 }
5246 }
5247 else
5248 {
5249 Path moduleDescriptor = findMainDescriptor( javadocModule.getSourcePaths() );
5250
5251 if ( moduleDescriptor != null )
5252 {
5253 try
5254 {
5255 result = locationManager.parseModuleDescriptor( moduleDescriptor );
5256 }
5257 catch ( IOException e )
5258 {
5259 throw new MavenReportException( e.getMessage(), e );
5260 }
5261 }
5262 }
5263
5264 if ( result != null && result.getModuleDescriptor() != null )
5265 {
5266 moduleSourceDir = javadocOutputDirectory.toPath().resolve( "src" );
5267 try
5268 {
5269 moduleSourceDir = Files.createDirectories( moduleSourceDir );
5270
5271 additionalModules.add( result.getModuleDescriptor().name() );
5272
5273 patchModules.put( result.getModuleDescriptor().name(), javadocModule.getSourcePaths() );
5274
5275 Path modulePath = moduleSourceDir.resolve( result.getModuleDescriptor().name() );
5276 if ( !Files.isDirectory( modulePath ) )
5277 {
5278 Files.createDirectory( modulePath );
5279 }
5280 }
5281 catch ( IOException e )
5282 {
5283 throw new MavenReportException( e.getMessage(), e );
5284 }
5285 }
5286 else
5287 {
5288 unnamedProjects.add( javadocModule.getGa() );
5289 }
5290
5291 if ( aggregatedProject.equals( getProject() ) )
5292 {
5293 mainResolvePathResult = result;
5294 }
5295 }
5296 else
5297 {
5298
5299 getLog().error( "no reactor project: " + javadocModule.getGa() );
5300 }
5301 }
5302
5303 if ( !unnamedProjects.isEmpty() )
5304 {
5305 getLog().error( "Creating an aggregated report for both named and unnamed modules is not possible." );
5306 getLog().error( "Ensure that every module has a module descriptor or is a jar with a MANIFEST.MF "
5307 + "containing an Automatic-Module-Name." );
5308 getLog().error( "Fix the following projects:" );
5309 for ( String unnamedProject : unnamedProjects )
5310 {
5311 getLog().error( " - " + unnamedProject );
5312 }
5313 throw new MavenReportException( "Aggregator report contains named and unnamed modules" );
5314 }
5315
5316 if ( mainResolvePathResult != null
5317 && ModuleNameSource.MANIFEST.equals( mainResolvePathResult.getModuleNameSource() ) )
5318 {
5319 arguments.add( "--add-modules" );
5320 arguments.add( "ALL-MODULE-PATH" );
5321 }
5322 }
5323
5324
5325 boolean moduleDescriptorSource = false;
5326 for ( Path sourcepath : sourcePaths )
5327 {
5328 if ( Files.isRegularFile( sourcepath.resolve( "module-info.java" ) ) )
5329 {
5330 moduleDescriptorSource = true;
5331 break;
5332 }
5333 }
5334
5335 final ModuleNameSource mainModuleNameSource;
5336 if ( mainResolvePathResult != null )
5337 {
5338 mainModuleNameSource = mainResolvePathResult.getModuleNameSource();
5339 }
5340 else
5341 {
5342 mainModuleNameSource = null;
5343 }
5344
5345 if ( supportModulePath
5346 && ( isAggregator()
5347 || ModuleNameSource.MODULEDESCRIPTOR.equals( mainModuleNameSource )
5348 || ModuleNameSource.MANIFEST.equals( mainModuleNameSource ) ) )
5349 {
5350 List<File> pathElements = new ArrayList<>( getPathElements() );
5351 File artifactFile = getClassesFile( project );
5352 if ( artifactFile != null )
5353 {
5354 pathElements.add( 0, artifactFile );
5355 }
5356
5357 ResolvePathsRequest<File> request =
5358 ResolvePathsRequest.ofFiles( pathElements );
5359
5360 String mainModuleName = null;
5361 if ( mainResolvePathResult != null )
5362 {
5363 request.setModuleDescriptor( mainResolvePathResult.getModuleDescriptor() );
5364 mainModuleName = mainResolvePathResult.getModuleDescriptor().name();
5365 }
5366
5367 request.setAdditionalModules( additionalModules );
5368 request.setIncludeStatic( isAggregator() );
5369
5370 try
5371 {
5372 ResolvePathsResult<File> result = locationManager.resolvePaths( request );
5373
5374 Set<File> modulePathElements = new HashSet<>( result.getModulepathElements().keySet() ) ;
5375
5376 Collection<File> classPathElements = new ArrayList<>( result.getClasspathElements().size() );
5377
5378 for ( File file : result.getClasspathElements() )
5379 {
5380 if ( file.isDirectory() && new File( file, "module-info.class" ).exists() )
5381 {
5382 modulePathElements.add( file );
5383 }
5384 else if ( ModuleNameSource.MANIFEST.equals( mainModuleNameSource ) )
5385 {
5386 ModuleNameSource depModuleNameSource =
5387 locationManager.resolvePath( ResolvePathRequest.ofFile( file ) ).getModuleNameSource();
5388 if ( ModuleNameSource.MODULEDESCRIPTOR.equals( depModuleNameSource )
5389 || ModuleNameSource.MANIFEST.equals( depModuleNameSource ) )
5390 {
5391 modulePathElements.add( file );
5392 }
5393 else
5394 {
5395 patchModules.get( mainModuleName ).add( file.toPath() );
5396 }
5397 }
5398 else
5399 {
5400 classPathElements.add( file );
5401 }
5402 }
5403
5404
5405 for ( Entry<File, Exception> pathExceptionEntry : result.getPathExceptions().entrySet() )
5406 {
5407 Exception exception = pathExceptionEntry.getValue();
5408
5409 if ( "java.lang.module.FindException".equals( exception.getClass().getName() ) )
5410 {
5411 File jarPath = pathExceptionEntry.getKey();
5412 classPathElements.add( jarPath );
5413 }
5414 }
5415
5416 String classpath = StringUtils.join( classPathElements.iterator(), File.pathSeparator );
5417 addArgIfNotEmpty( arguments, "--class-path", JavadocUtil.quotedPathArgument( classpath ), false,
5418 false );
5419
5420 String modulepath =
5421 StringUtils.join( modulePathElements.iterator(), File.pathSeparator );
5422 addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ), false,
5423 false );
5424 }
5425 catch ( IOException e )
5426 {
5427 throw new MavenReportException( e.getMessage(), e );
5428 }
5429 }
5430 else if ( supportModulePath && moduleDescriptorSource && !isTest() )
5431 {
5432 String modulepath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
5433 addArgIfNotEmpty( arguments, "--module-path", JavadocUtil.quotedPathArgument( modulepath ) , false, false );
5434 }
5435 else
5436 {
5437 String classpath = StringUtils.join( getPathElements().iterator(), File.pathSeparator );
5438 addArgIfNotEmpty( arguments, "-classpath", JavadocUtil.quotedPathArgument( classpath ) , false, false );
5439 }
5440
5441 for ( Entry<String, Collection<Path>> entry : patchModules.entrySet() )
5442 {
5443 addArgIfNotEmpty( arguments, "--patch-module", entry.getKey() + '='
5444 + JavadocUtil.quotedPathArgument( getSourcePath( entry.getValue() ) ),
5445 false, false );
5446 }
5447
5448 if ( StringUtils.isNotEmpty( doclet ) )
5449 {
5450 addArgIfNotEmpty( arguments, "-doclet", JavadocUtil.quotedArgument( doclet ) );
5451 addArgIfNotEmpty( arguments, "-docletpath", JavadocUtil.quotedPathArgument( getDocletPath() ) );
5452 }
5453
5454 if ( StringUtils.isEmpty( encoding ) )
5455 {
5456 getLog().warn(
5457 "Source files encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
5458 + ", i.e. build is platform dependent!" );
5459 }
5460 addArgIfNotEmpty( arguments, "-encoding", JavadocUtil.quotedArgument( getEncoding() ) );
5461
5462 addArgIfNotEmpty( arguments, "-extdirs",
5463 JavadocUtil.quotedPathArgument( JavadocUtil.unifyPathSeparator( extdirs ) ) );
5464
5465 if ( ( getOverview() != null ) && ( getOverview().exists() ) )
5466 {
5467 addArgIfNotEmpty( arguments, "-overview",
5468 JavadocUtil.quotedPathArgument( getOverview().getAbsolutePath() ) );
5469 }
5470
5471 arguments.add( getAccessLevel() );
5472
5473 if ( isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5474 {
5475 addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_5 );
5476 }
5477
5478 if ( release != null )
5479 {
5480 arguments.add( "--release" );
5481 arguments.add( release );
5482 }
5483 else
5484 {
5485 addArgIfNotEmpty( arguments, "-source", JavadocUtil.quotedArgument( source ), SINCE_JAVADOC_1_4 );
5486 }
5487
5488 if ( ( StringUtils.isEmpty( sourcepath ) ) && ( StringUtils.isNotEmpty( subpackages ) ) )
5489 {
5490 sourcepath = StringUtils.join( sourcePaths.iterator(), File.pathSeparator );
5491 }
5492
5493 if ( moduleSourceDir == null )
5494 {
5495 addArgIfNotEmpty( arguments, "-sourcepath",
5496 JavadocUtil.quotedPathArgument( getSourcePath( sourcePaths ) ), false, false );
5497 }
5498 else if ( mainResolvePathResult == null
5499 || ModuleNameSource.MODULEDESCRIPTOR.equals( mainResolvePathResult.getModuleNameSource() ) )
5500 {
5501 addArgIfNotEmpty( arguments, "--module-source-path",
5502 JavadocUtil.quotedPathArgument( moduleSourceDir.toString() ) );
5503 }
5504
5505
5506 if ( StringUtils.isNotEmpty( sourcepath ) && isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5507 {
5508 addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_5 );
5509 }
5510
5511
5512 addArgIfNotEmpty( arguments, "-exclude", getExcludedPackages( sourcePaths ), SINCE_JAVADOC_1_4 );
5513
5514 addArgIf( arguments, verbose, "-verbose" );
5515
5516 if ( additionalOptions != null && additionalOptions.length > 0 )
5517 {
5518 for ( String additionalOption : additionalOptions )
5519 {
5520 arguments.add( additionalOption.replaceAll( "(?<!\\\\)\\\\(?!\\\\|:)", "\\\\" ) );
5521 }
5522 }
5523 }
5524
5525 private ResolvePathResult getResolvePathResult( File artifactFile )
5526 {
5527 if ( artifactFile == null )
5528 {
5529 return null;
5530 }
5531
5532 ResolvePathResult resolvePathResult = null;
5533 ResolvePathRequest<File> resolvePathRequest = ResolvePathRequest.ofFile( artifactFile );
5534 try
5535 {
5536 resolvePathResult = locationManager.resolvePath( resolvePathRequest );
5537
5538
5539 if ( resolvePathResult.getModuleDescriptor() == null )
5540 {
5541 return null;
5542 }
5543 }
5544 catch ( IOException | RuntimeException e )
5545 {
5546 Throwable cause = e;
5547 while ( cause.getCause() != null )
5548 {
5549 cause = cause.getCause();
5550 }
5551 getLog().warn( e.getMessage() );
5552 }
5553 return resolvePathResult;
5554 }
5555
5556 private Path findMainDescriptor( Collection<Path> roots ) throws MavenReportException
5557 {
5558 for ( Map.Entry<Path, Collection<String>> entry : getFiles( roots ).entrySet() )
5559 {
5560 if ( entry.getValue().contains( "module-info.java" ) )
5561 {
5562 return entry.getKey().resolve( "module-info.java" );
5563 }
5564 }
5565 return null;
5566 }
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580 private void addStandardDocletOptions( File javadocOutputDirectory,
5581 List<String> arguments,
5582 Set<OfflineLink> offlineLinks )
5583 throws MavenReportException
5584 {
5585 validateStandardDocletOptions();
5586
5587
5588
5589 addArgIf( arguments, author, "-author" );
5590
5591 addArgIfNotEmpty( arguments, "-bottom", JavadocUtil.quotedArgument( getBottomText() ), false, false );
5592
5593 if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5594 {
5595 addArgIf( arguments, breakiterator, "-breakiterator", SINCE_JAVADOC_1_4 );
5596 }
5597
5598 addArgIfNotEmpty( arguments, "-charset", JavadocUtil.quotedArgument( getCharset() ) );
5599
5600 addArgIfNotEmpty( arguments, "-d", JavadocUtil.quotedPathArgument( javadocOutputDirectory.toString() ) );
5601
5602 addArgIfNotEmpty( arguments, "-docencoding", JavadocUtil.quotedArgument( getDocencoding() ) );
5603
5604 addArgIf( arguments, docfilessubdirs, "-docfilessubdirs", SINCE_JAVADOC_1_4 );
5605
5606 addArgIf( arguments, StringUtils.isNotEmpty( doclint ), "-Xdoclint:" + getDoclint(), SINCE_JAVADOC_1_8 );
5607
5608 addArgIfNotEmpty( arguments, "-doctitle", JavadocUtil.quotedArgument( getDoctitle() ), false, false );
5609
5610 if ( docfilessubdirs )
5611 {
5612 addArgIfNotEmpty( arguments, "-excludedocfilessubdir",
5613 JavadocUtil.quotedPathArgument( excludedocfilessubdir ), SINCE_JAVADOC_1_4 );
5614 }
5615
5616 addArgIfNotEmpty( arguments, "-footer", JavadocUtil.quotedArgument( footer ), false, false );
5617
5618 addGroups( arguments );
5619
5620 addArgIfNotEmpty( arguments, "-header", JavadocUtil.quotedArgument( header ), false, false );
5621
5622 Optional<File> helpFile = getHelpFile( javadocOutputDirectory );
5623 if ( helpFile.isPresent() )
5624 {
5625 addArgIfNotEmpty( arguments, "-helpfile",
5626 JavadocUtil.quotedPathArgument( helpFile.get().getAbsolutePath() ) );
5627 }
5628
5629 addArgIf( arguments, keywords, "-keywords", SINCE_JAVADOC_1_4_2 );
5630
5631 addLinkArguments( arguments );
5632
5633 addLinkofflineArguments( arguments, offlineLinks );
5634
5635 addArgIf( arguments, linksource, "-linksource", SINCE_JAVADOC_1_4 );
5636
5637 if ( sourcetab > 0 )
5638 {
5639 if ( javadocRuntimeVersion == SINCE_JAVADOC_1_4_2 )
5640 {
5641 addArgIfNotEmpty( arguments, "-linksourcetab", String.valueOf( sourcetab ) );
5642 }
5643 addArgIfNotEmpty( arguments, "-sourcetab", String.valueOf( sourcetab ), SINCE_JAVADOC_1_5 );
5644 }
5645
5646 addArgIf( arguments, nocomment, "-nocomment", SINCE_JAVADOC_1_4 );
5647
5648 addArgIf( arguments, nodeprecated, "-nodeprecated" );
5649
5650 addArgIf( arguments, nodeprecatedlist, "-nodeprecatedlist" );
5651
5652 addArgIf( arguments, nohelp, "-nohelp" );
5653
5654 addArgIf( arguments, noindex, "-noindex" );
5655
5656 addArgIf( arguments, nonavbar, "-nonavbar" );
5657
5658 addArgIf( arguments, nooverview, "-nooverview" );
5659
5660 addArgIfNotEmpty( arguments, "-noqualifier", JavadocUtil.quotedArgument( noqualifier ), SINCE_JAVADOC_1_4 );
5661
5662 addArgIf( arguments, nosince, "-nosince" );
5663
5664 addArgIf( arguments, notimestamp, "-notimestamp", SINCE_JAVADOC_1_5 );
5665
5666 addArgIf( arguments, notree, "-notree" );
5667
5668 addArgIfNotEmpty( arguments, "-packagesheader", JavadocUtil.quotedArgument( packagesheader ),
5669 SINCE_JAVADOC_1_4_2 );
5670
5671 if ( !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5672 {
5673 addArgIf( arguments, quiet, "-quiet", SINCE_JAVADOC_1_4 );
5674 }
5675
5676 addArgIf( arguments, serialwarn, "-serialwarn" );
5677
5678 addArgIf( arguments, splitindex, "-splitindex" );
5679
5680 Optional<File> stylesheetfile = getStylesheetFile( javadocOutputDirectory );
5681
5682 if ( stylesheetfile.isPresent() )
5683 {
5684 addArgIfNotEmpty( arguments, "-stylesheetfile",
5685 JavadocUtil.quotedPathArgument( stylesheetfile.get().getAbsolutePath() ) );
5686 }
5687
5688 addAddStyleSheets( arguments );
5689
5690 if ( StringUtils.isNotEmpty( sourcepath ) && !isJavaDocVersionAtLeast( SINCE_JAVADOC_1_5 ) )
5691 {
5692 addArgIfNotEmpty( arguments, "-subpackages", subpackages, SINCE_JAVADOC_1_4 );
5693 }
5694
5695 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet ), SINCE_JAVADOC_1_4 );
5696 addTaglets( arguments );
5697 addTagletsFromTagletArtifacts( arguments );
5698 addArgIfNotEmpty( arguments, "-tagletpath", JavadocUtil.quotedPathArgument( getTagletPath() ),
5699 SINCE_JAVADOC_1_4 );
5700
5701 addTags( arguments );
5702
5703 addArgIfNotEmpty( arguments, "-top", JavadocUtil.quotedArgument( top ), false, false, SINCE_JAVADOC_1_6 );
5704
5705 addArgIf( arguments, use, "-use" );
5706
5707 addArgIf( arguments, version, "-version" );
5708
5709 addArgIfNotEmpty( arguments, "-windowtitle", JavadocUtil.quotedArgument( getWindowtitle() ), false, false );
5710 }
5711
5712
5713
5714
5715
5716
5717
5718 private void addGroups( List<String> arguments )
5719 throws MavenReportException
5720 {
5721 Set<Group> groups = collectGroups();
5722 if ( isEmpty( groups ) )
5723 {
5724 return;
5725 }
5726
5727 for ( Group group : groups )
5728 {
5729 if ( group == null || StringUtils.isEmpty( group.getTitle() ) || StringUtils.isEmpty(
5730 group.getPackages() ) )
5731 {
5732 if ( getLog().isWarnEnabled() )
5733 {
5734 getLog().warn( "A group option is empty. Ignore this option." );
5735 }
5736 }
5737 else
5738 {
5739 String groupTitle = StringUtils.replace( group.getTitle(), ",", "," );
5740 addArgIfNotEmpty( arguments, "-group",
5741 JavadocUtil.quotedArgument( groupTitle ) + " " + JavadocUtil.quotedArgument(
5742 group.getPackages() ), true );
5743 }
5744 }
5745 }
5746
5747
5748
5749
5750
5751
5752
5753 private void addTags( List<String> arguments )
5754 throws MavenReportException
5755 {
5756 final String lineSeparator;
5757 if ( javadocRuntimeVersion.isBefore( "9" ) )
5758 {
5759 lineSeparator = " ";
5760 }
5761 else
5762 {
5763 lineSeparator = " \\\\" + SystemUtils.LINE_SEPARATOR;
5764 }
5765
5766 for ( Tag tag : collectTags() )
5767 {
5768 if ( StringUtils.isEmpty( tag.getName() ) )
5769 {
5770 if ( getLog().isWarnEnabled() )
5771 {
5772 getLog().warn( "A tag name is empty. Ignore this option." );
5773 }
5774 }
5775 else
5776 {
5777 String value = "\"" + tag.getName();
5778 if ( StringUtils.isNotEmpty( tag.getPlacement() ) )
5779 {
5780 value += ":" + tag.getPlacement().replaceAll( "\\R", lineSeparator );
5781 if ( StringUtils.isNotEmpty( tag.getHead() ) )
5782 {
5783 value += ":" + tag.getHead().replaceAll( "\\R", lineSeparator );
5784 }
5785 }
5786 value += "\"";
5787 addArgIfNotEmpty( arguments, "-tag", value, SINCE_JAVADOC_1_4 );
5788 }
5789 }
5790 }
5791
5792
5793
5794
5795
5796
5797 private void addTaglets( List<String> arguments )
5798 {
5799 if ( taglets == null )
5800 {
5801 return;
5802 }
5803
5804 for ( Taglet taglet1 : taglets )
5805 {
5806 if ( ( taglet1 == null ) || ( StringUtils.isEmpty( taglet1.getTagletClass() ) ) )
5807 {
5808 if ( getLog().isWarnEnabled() )
5809 {
5810 getLog().warn( "A taglet option is empty. Ignore this option." );
5811 }
5812 }
5813 else
5814 {
5815 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( taglet1.getTagletClass() ),
5816 SINCE_JAVADOC_1_4 );
5817 }
5818 }
5819 }
5820
5821
5822
5823
5824
5825
5826
5827
5828 private void addTagletsFromTagletArtifacts( List<String> arguments )
5829 throws MavenReportException
5830 {
5831 Set<TagletArtifact> tArtifacts = new LinkedHashSet<>();
5832 if ( tagletArtifacts != null && tagletArtifacts.length > 0 )
5833 {
5834 tArtifacts.addAll( Arrays.asList( tagletArtifacts ) );
5835 }
5836
5837 if ( includeDependencySources )
5838 {
5839 try
5840 {
5841 resolveDependencyBundles();
5842 }
5843 catch ( IOException e )
5844 {
5845 throw new MavenReportException(
5846 "Failed to resolve javadoc bundles from dependencies: " + e.getMessage(), e );
5847 }
5848
5849 if ( isNotEmpty( dependencyJavadocBundles ) )
5850 {
5851 for ( JavadocBundle bundle : dependencyJavadocBundles )
5852 {
5853 JavadocOptions options = bundle.getOptions();
5854 if ( options != null && isNotEmpty( options.getTagletArtifacts() ) )
5855 {
5856 tArtifacts.addAll( options.getTagletArtifacts() );
5857 }
5858 }
5859 }
5860 }
5861
5862 if ( isEmpty( tArtifacts ) )
5863 {
5864 return;
5865 }
5866
5867 List<String> tagletsPath = new ArrayList<>();
5868
5869 for ( TagletArtifact aTagletArtifact : tArtifacts )
5870 {
5871 if ( ( StringUtils.isNotEmpty( aTagletArtifact.getGroupId() ) ) && ( StringUtils.isNotEmpty(
5872 aTagletArtifact.getArtifactId() ) ) && ( StringUtils.isNotEmpty( aTagletArtifact.getVersion() ) ) )
5873 {
5874 Artifact artifact;
5875 try
5876 {
5877 artifact = createAndResolveArtifact( aTagletArtifact );
5878 }
5879 catch ( ArtifactResolverException e )
5880 {
5881 throw new MavenReportException( "Unable to resolve artifact:" + aTagletArtifact, e );
5882 }
5883
5884 tagletsPath.add( artifact.getFile().getAbsolutePath() );
5885 }
5886 }
5887
5888 tagletsPath = JavadocUtil.pruneFiles( tagletsPath );
5889
5890 for ( String tagletJar : tagletsPath )
5891 {
5892 if ( !tagletJar.toLowerCase( Locale.ENGLISH ).endsWith( ".jar" ) )
5893 {
5894 continue;
5895 }
5896
5897 List<String> tagletClasses;
5898 try
5899 {
5900 tagletClasses = JavadocUtil.getTagletClassNames( new File( tagletJar ) );
5901 }
5902 catch ( IOException e )
5903 {
5904 if ( getLog().isWarnEnabled() )
5905 {
5906 getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5907 + "'. Try to specify them with <taglets/>." );
5908 }
5909 if ( getLog().isDebugEnabled() )
5910 {
5911 getLog().debug( "IOException: " + e.getMessage(), e );
5912 }
5913 continue;
5914 }
5915 catch ( ClassNotFoundException e )
5916 {
5917 if ( getLog().isWarnEnabled() )
5918 {
5919 getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5920 + "'. Try to specify them with <taglets/>." );
5921 }
5922 if ( getLog().isDebugEnabled() )
5923 {
5924 getLog().debug( "ClassNotFoundException: " + e.getMessage(), e );
5925 }
5926 continue;
5927 }
5928 catch ( NoClassDefFoundError e )
5929 {
5930 if ( getLog().isWarnEnabled() )
5931 {
5932 getLog().warn( "Unable to auto-detect Taglet class names from '" + tagletJar
5933 + "'. Try to specify them with <taglets/>." );
5934 }
5935 if ( getLog().isDebugEnabled() )
5936 {
5937 getLog().debug( "NoClassDefFoundError: " + e.getMessage(), e );
5938 }
5939 continue;
5940 }
5941
5942 if ( tagletClasses != null && !tagletClasses.isEmpty() )
5943 {
5944 for ( String tagletClass : tagletClasses )
5945 {
5946 addArgIfNotEmpty( arguments, "-taglet", JavadocUtil.quotedArgument( tagletClass ),
5947 SINCE_JAVADOC_1_4 );
5948 }
5949 }
5950 }
5951 }
5952
5953
5954
5955
5956
5957
5958
5959
5960 private void executeJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
5961 throws MavenReportException
5962 {
5963 if ( staleDataPath != null )
5964 {
5965 if ( !isUpToDate( cmd ) )
5966 {
5967 doExecuteJavadocCommandLine( cmd, javadocOutputDirectory );
5968 StaleHelper.writeStaleData( cmd, staleDataPath.toPath() );
5969 }
5970 }
5971 else
5972 {
5973 doExecuteJavadocCommandLine( cmd, javadocOutputDirectory );
5974 }
5975 }
5976
5977
5978
5979
5980
5981
5982
5983
5984 private boolean isUpToDate( Commandline cmd )
5985 throws MavenReportException
5986 {
5987 try
5988 {
5989 String curdata = StaleHelper.getStaleData( cmd );
5990 Path cacheData = staleDataPath.toPath();
5991 String prvdata;
5992 if ( Files.isRegularFile( cacheData ) )
5993 {
5994 prvdata = new String( Files.readAllBytes( cacheData ), StandardCharsets.UTF_8 );
5995 }
5996 else
5997 {
5998 prvdata = null;
5999 }
6000 if ( curdata.equals( prvdata ) )
6001 {
6002 getLog().info( "Skipping javadoc generation, everything is up to date." );
6003 return true;
6004 }
6005 else
6006 {
6007 if ( prvdata == null )
6008 {
6009 getLog().info( "No previous run data found, generating javadoc." );
6010 }
6011 else
6012 {
6013 getLog().info( "Configuration changed, re-generating javadoc." );
6014 }
6015 }
6016 }
6017 catch ( IOException e )
6018 {
6019 throw new MavenReportException( "Error checking uptodate status", e );
6020 }
6021 return false;
6022 }
6023
6024
6025
6026
6027
6028
6029
6030
6031 private void doExecuteJavadocCommandLine( Commandline cmd, File javadocOutputDirectory )
6032 throws MavenReportException
6033 {
6034 if ( getLog().isDebugEnabled() )
6035 {
6036
6037 getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) );
6038 }
6039
6040 String cmdLine = null;
6041 if ( debug )
6042 {
6043 cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
6044
6045 writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
6046 }
6047
6048 CommandLineUtils.StringStreamConsumer err = new JavadocUtil.JavadocOutputStreamConsumer();
6049 CommandLineUtils.StringStreamConsumer out = new JavadocUtil.JavadocOutputStreamConsumer();
6050 try
6051 {
6052 int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
6053
6054 String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() );
6055
6056 if ( exitCode != 0 )
6057 {
6058 if ( cmdLine == null )
6059 {
6060 cmdLine = CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" );
6061 }
6062 writeDebugJavadocScript( cmdLine, javadocOutputDirectory );
6063
6064 if ( StringUtils.isNotEmpty( output ) && StringUtils.isEmpty( err.getOutput() )
6065 && isJavadocVMInitError( output ) )
6066 {
6067 throw new MavenReportException( output + '\n' + '\n' + JavadocUtil.ERROR_INIT_VM + '\n'
6068 + "Or, try to reduce the Java heap size for the Javadoc goal using "
6069 + "-Dminmemory=<size> and -Dmaxmemory=<size>." + '\n' + '\n' + "Command line was: " + cmdLine
6070 + '\n' + '\n' + "Refer to the generated Javadoc files in '" + javadocOutputDirectory
6071 + "' dir.\n" );
6072 }
6073
6074 if ( StringUtils.isNotEmpty( output ) )
6075 {
6076 getLog().info( output );
6077 }
6078
6079 StringBuilder msg = new StringBuilder( "\nExit code: " );
6080 msg.append( exitCode );
6081 if ( StringUtils.isNotEmpty( err.getOutput() ) )
6082 {
6083 msg.append( " - " ).append( err.getOutput() );
6084 }
6085 msg.append( '\n' );
6086 msg.append( "Command line was: " ).append( cmdLine ).append( '\n' ).append( '\n' );
6087
6088 msg.append( "Refer to the generated Javadoc files in '" ).append( javadocOutputDirectory )
6089 .append( "' dir.\n" );
6090
6091 throw new MavenReportException( msg.toString() );
6092 }
6093
6094 if ( StringUtils.isNotEmpty( output ) )
6095 {
6096 getLog().info( output );
6097 }
6098 }
6099 catch ( CommandLineException e )
6100 {
6101 throw new MavenReportException( "Unable to execute javadoc command: " + e.getMessage(), e );
6102 }
6103
6104
6105
6106
6107
6108 if ( containsWarnings( err.getOutput() ) )
6109 {
6110 if ( getLog().isWarnEnabled() )
6111 {
6112 getLog().warn( "Javadoc Warnings" );
6113
6114 StringTokenizer token = new StringTokenizer( err.getOutput(), "\n" );
6115 while ( token.hasMoreTokens() )
6116 {
6117 String current = token.nextToken().trim();
6118
6119 getLog().warn( current );
6120 }
6121 }
6122
6123 if ( failOnWarnings )
6124 {
6125 throw new MavenReportException( "Project contains Javadoc Warnings" );
6126 }
6127 }
6128 }
6129
6130 private boolean containsWarnings( String output )
6131 {
6132
6133 if ( this.javadocRuntimeVersion.isBefore( "17" ) )
6134 {
6135 return StringUtils.isNotEmpty( output );
6136 }
6137 else
6138 {
6139 return Arrays.stream( output.split( "\\R" ) )
6140 .reduce( ( first, second ) -> second )
6141 .filter( line -> line.matches( "\\d+ warnings?" ) )
6142 .isPresent();
6143 }
6144 }
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155 private int fixFrameInjectionBug( File javadocOutputDirectory, String outputEncoding )
6156 throws IOException
6157 {
6158 final String fixData;
6159
6160 try ( InputStream in = this.getClass().getResourceAsStream( "frame-injection-fix.txt" ) )
6161 {
6162 if ( in == null )
6163 {
6164 throw new FileNotFoundException( "Missing resource 'frame-injection-fix.txt' in classpath." );
6165 }
6166 fixData = org.codehaus.plexus.util.StringUtils
6167 .unifyLineSeparators( IOUtil.toString( in, "US-ASCII" ) ).trim();
6168 }
6169
6170 final DirectoryScanner ds = new DirectoryScanner();
6171 ds.setBasedir( javadocOutputDirectory );
6172 ds.setCaseSensitive( false );
6173 ds.setIncludes( new String[]{ "**/index.html", "**/index.htm", "**/toc.html", "**/toc.htm" } );
6174 ds.addDefaultExcludes();
6175 ds.scan();
6176 int patched = 0;
6177 for ( String f : ds.getIncludedFiles() )
6178 {
6179 final File file = new File( javadocOutputDirectory, f );
6180
6181
6182 final String fileContents = FileUtils.fileRead( file, outputEncoding );
6183
6184 if ( !StringUtils.contains( fileContents, "function validURL(url) {" ) )
6185 {
6186
6187 final String patchedFileContents =
6188 StringUtils.replaceOnce( fileContents, "function loadFrames() {", fixData );
6189 if ( !patchedFileContents.equals( fileContents ) )
6190 {
6191 FileUtils.fileWrite( file, outputEncoding, patchedFileContents );
6192 patched++;
6193 }
6194 }
6195 }
6196 return patched;
6197 }
6198
6199
6200
6201
6202
6203
6204
6205
6206 private Optional<File> getResource( File outputFile, String inputResourceName )
6207 {
6208 if ( inputResourceName.startsWith( "/" ) )
6209 {
6210 inputResourceName = inputResourceName.replaceFirst( "//*", "" );
6211 }
6212
6213 List<String> classPath = new ArrayList<>();
6214 classPath.add( project.getBuild().getSourceDirectory() );
6215
6216 URL resourceURL = getResource( classPath, inputResourceName );
6217 if ( resourceURL != null )
6218 {
6219 getLog().debug( inputResourceName + " found in the main src directory of the project." );
6220 return Optional.of( FileUtils.toFile( resourceURL ) );
6221 }
6222
6223 classPath.clear();
6224 List<Resource> resources = project.getBuild().getResources();
6225 for ( Resource resource : resources )
6226 {
6227 classPath.add( resource.getDirectory() );
6228 }
6229 resourceURL = getResource( classPath, inputResourceName );
6230 if ( resourceURL != null )
6231 {
6232 getLog().debug( inputResourceName + " found in the main resources directories of the project." );
6233 return Optional.of( FileUtils.toFile( resourceURL ) );
6234 }
6235
6236 if ( javadocDirectory.exists() )
6237 {
6238 classPath.clear();
6239 classPath.add( javadocDirectory.getAbsolutePath() );
6240 resourceURL = getResource( classPath, inputResourceName );
6241 if ( resourceURL != null )
6242 {
6243 getLog().debug( inputResourceName + " found in the main javadoc directory of the project." );
6244 return Optional.of( FileUtils.toFile( resourceURL ) );
6245 }
6246 }
6247
6248 classPath.clear();
6249 final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
6250 Plugin javadocPlugin = getPlugin( project, pluginId );
6251 if ( javadocPlugin != null && javadocPlugin.getDependencies() != null )
6252 {
6253 List<Dependency> dependencies = javadocPlugin.getDependencies();
6254 for ( Dependency dependency : dependencies )
6255 {
6256 ResourcesArtifact resourceArtifact = new ResourcesArtifact();
6257 resourceArtifact.setGroupId( dependency.getGroupId() );
6258 resourceArtifact.setArtifactId( dependency.getArtifactId() );
6259 resourceArtifact.setVersion( dependency.getVersion() );
6260 resourceArtifact.setClassifier( dependency.getClassifier() );
6261 Artifact artifact = null;
6262 try
6263 {
6264 artifact = createAndResolveArtifact( resourceArtifact );
6265 }
6266 catch ( Exception e )
6267 {
6268 logError( "Unable to retrieve the dependency: " + dependency + ". Ignored.", e );
6269 }
6270
6271 if ( artifact != null && artifact.getFile().exists() )
6272 {
6273 classPath.add( artifact.getFile().getAbsolutePath() );
6274 }
6275 }
6276 resourceURL = getResource( classPath, inputResourceName );
6277 if ( resourceURL != null )
6278 {
6279 getLog().debug( inputResourceName + " found in javadoc plugin dependencies." );
6280 try
6281 {
6282 JavadocUtil.copyResource( resourceURL, outputFile );
6283
6284 return Optional.of( outputFile );
6285 }
6286 catch ( IOException e )
6287 {
6288 logError( "IOException: " + e.getMessage(), e );
6289 }
6290 }
6291 }
6292
6293 getLog().warn( "Unable to find the resource '" + inputResourceName + "'. Using default Javadoc resources." );
6294
6295 return Optional.empty();
6296 }
6297
6298
6299
6300
6301
6302
6303
6304
6305 private URL getResource( final List<String> classPath, final String resource )
6306 {
6307 List<URL> urls = new ArrayList<>( classPath.size() );
6308 for ( String filename : classPath )
6309 {
6310 try
6311 {
6312 urls.add( new File( filename ).toURI().toURL() );
6313 }
6314 catch ( MalformedURLException e )
6315 {
6316 getLog().error( "MalformedURLException: " + e.getMessage() );
6317 }
6318 }
6319
6320 URLClassLoader javadocClassLoader = new URLClassLoader( urls.toArray( new URL[urls.size()] ), null );
6321 try
6322 {
6323 return javadocClassLoader.getResource( resource );
6324 }
6325 finally
6326 {
6327 try
6328 {
6329 javadocClassLoader.close();
6330 }
6331 catch ( IOException ex )
6332 {
6333
6334 }
6335 }
6336 }
6337
6338
6339
6340
6341
6342
6343 private String getFullJavadocGoal()
6344 {
6345 String javadocPluginVersion = null;
6346 String resource = "META-INF/maven/org.apache.maven.plugins/maven-javadoc-plugin/pom.properties";
6347 try ( InputStream resourceAsStream
6348 = AbstractJavadocMojo.class.getClassLoader().getResourceAsStream( resource ) )
6349 {
6350 if ( resourceAsStream != null )
6351 {
6352 Properties properties = new Properties();
6353 properties.load( resourceAsStream );
6354 if ( StringUtils.isNotEmpty( properties.getProperty( "version" ) ) )
6355 {
6356 javadocPluginVersion = properties.getProperty( "version" );
6357 }
6358 }
6359 }
6360 catch ( IOException e )
6361 {
6362
6363 }
6364
6365 StringBuilder sb = new StringBuilder();
6366
6367 sb.append( "org.apache.maven.plugins:maven-javadoc-plugin:" );
6368 if ( StringUtils.isNotEmpty( javadocPluginVersion ) )
6369 {
6370 sb.append( javadocPluginVersion ).append( ":" );
6371 }
6372
6373 if ( this instanceof TestJavadocReport )
6374 {
6375 sb.append( "test-javadoc" );
6376 }
6377 else
6378 {
6379 sb.append( "javadoc" );
6380 }
6381
6382 return sb.toString();
6383 }
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395 private List<OfflineLink> getModulesLinks()
6396 throws MavenReportException
6397 {
6398 List<MavenProject> aggregatedProjects = reactorProjects;
6399 if ( !detectOfflineLinks || isAggregator() || aggregatedProjects.isEmpty() )
6400 {
6401 return Collections.emptyList();
6402 }
6403
6404 getLog().debug( "Trying to add links for modules..." );
6405
6406 Set<String> dependencyArtifactIds = new HashSet<>();
6407 final Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
6408 for ( Artifact artifact : dependencyArtifacts )
6409 {
6410 dependencyArtifactIds.add( artifact.getId() );
6411 }
6412
6413 List<OfflineLink> modulesLinks = new ArrayList<>();
6414 String javadocDirRelative = PathUtils.toRelative( project.getBasedir(), getOutputDirectory() );
6415 for ( MavenProject p : aggregatedProjects )
6416 {
6417 if ( !dependencyArtifactIds.contains( p.getArtifact().getId() ) || ( p.getUrl() == null ) )
6418 {
6419 continue;
6420 }
6421
6422 File location = new File( p.getBasedir(), javadocDirRelative );
6423
6424 if ( !location.exists() )
6425 {
6426 if ( getLog().isDebugEnabled() )
6427 {
6428 getLog().debug( "Javadoc directory not found: " + location );
6429 }
6430
6431 String javadocGoal = getFullJavadocGoal();
6432 getLog().info(
6433 "The goal '" + javadocGoal + "' has not been previously called for the module: '" + p.getId()
6434 + "'. Trying to invoke it..." );
6435
6436 File invokerDir = new File( project.getBuild().getDirectory(), "invoker" );
6437 invokerDir.mkdirs();
6438 File invokerLogFile = FileUtils.createTempFile( "maven-javadoc-plugin", ".txt", invokerDir );
6439 try
6440 {
6441 JavadocUtil.invokeMaven( getLog(), new File( localRepository.getBasedir() ), p.getFile(),
6442 Collections.singletonList( javadocGoal ), null, invokerLogFile,
6443 session.getRequest().getGlobalSettingsFile() );
6444 }
6445 catch ( MavenInvocationException e )
6446 {
6447 logError( "MavenInvocationException: " + e.getMessage(), e );
6448
6449 String invokerLogContent = JavadocUtil.readFile( invokerLogFile, null );
6450
6451
6452
6453
6454 if ( invokerLogContent != null && invokerLogContent.contains( JavadocUtil.ERROR_INIT_VM ) )
6455 {
6456 throw new MavenReportException( e.getMessage(), e );
6457 }
6458 }
6459 finally
6460 {
6461
6462 if ( !location.exists() )
6463 {
6464 getLog().warn( "Creating fake javadoc directory to prevent repeated invocations: " + location );
6465 location.mkdirs();
6466 }
6467 }
6468 }
6469
6470 if ( location.exists() )
6471 {
6472 String url = getJavadocLink( p );
6473
6474 OfflineLink ol = new OfflineLink();
6475 ol.setUrl( url );
6476 ol.setLocation( location.getAbsolutePath() );
6477
6478 if ( getLog().isDebugEnabled() )
6479 {
6480 getLog().debug( "Added Javadoc offline link: " + url + " for the module: " + p.getId() );
6481 }
6482
6483 modulesLinks.add( ol );
6484 }
6485 }
6486
6487 return modulesLinks;
6488 }
6489
6490
6491
6492
6493
6494
6495
6496
6497
6498
6499 private List<String> getDependenciesLinks()
6500 {
6501 if ( !detectLinks )
6502 {
6503 return Collections.emptyList();
6504 }
6505
6506 getLog().debug( "Trying to add links for dependencies..." );
6507
6508 List<String> dependenciesLinks = new ArrayList<>();
6509
6510 final Set<Artifact> dependencies = project.getDependencyArtifacts();
6511 for ( Artifact artifact : dependencies )
6512 {
6513 if ( artifact.getFile() == null || !artifact.getFile().exists() )
6514 {
6515 continue;
6516 }
6517
6518 Optional<DependencyLink> depLink =
6519 this.dependencyLinks.stream().filter( d -> matches( d, artifact ) ).findAny();
6520
6521 final String url;
6522 final boolean detected;
6523 if ( depLink.isPresent() )
6524 {
6525 url = depLink.get().getUrl();
6526 detected = false;
6527 }
6528 else
6529 {
6530 try
6531 {
6532 MavenProject artifactProject =
6533 mavenProjectBuilder.build( artifact, getProjectBuildingRequest( project ) ).getProject();
6534
6535 url = getJavadocLink( artifactProject );
6536 detected = true;
6537 }
6538 catch ( ProjectBuildingException e )
6539 {
6540 logError( "ProjectBuildingException for " + artifact.toString() + ": " + e.getMessage(), e );
6541 continue;
6542 }
6543 }
6544
6545 if ( isValidJavadocLink( url, detected ) )
6546 {
6547 getLog().debug( "Added Javadoc link: " + url + " for " + artifact.getId() );
6548
6549 dependenciesLinks.add( url );
6550 }
6551 }
6552
6553 return dependenciesLinks;
6554 }
6555
6556 private boolean matches( DependencyLink d, Artifact artifact )
6557 {
6558 if ( d.getGroupId() != null && !d.getGroupId().equals( artifact.getGroupId() ) )
6559 {
6560 return false;
6561 }
6562 if ( d.getArtifactId() != null && !d.getArtifactId().equals( artifact.getArtifactId() ) )
6563 {
6564 return false;
6565 }
6566 if ( d.getClassifier() != null && !d.getClassifier().equals( artifact.getClassifier() ) )
6567 {
6568 return false;
6569 }
6570 return true;
6571 }
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584 protected final OfflineLink getDefaultJavadocApiLink()
6585 {
6586 if ( !detectJavaApiLink )
6587 {
6588 return null;
6589 }
6590
6591 final JavaVersion javaApiversion;
6592 if ( release != null )
6593 {
6594 javaApiversion = JavaVersion.parse( release );
6595 }
6596 else if ( source != null && !source.isEmpty() )
6597 {
6598 javaApiversion = JavaVersion.parse( source );
6599 }
6600 else
6601 {
6602 final String pluginId = "org.apache.maven.plugins:maven-compiler-plugin";
6603 String sourceConfigured = getPluginParameter( project, pluginId, "source" );
6604 if ( sourceConfigured != null )
6605 {
6606 javaApiversion = JavaVersion.parse( sourceConfigured );
6607 }
6608 else
6609 {
6610 getLog().debug( "No maven-compiler-plugin defined in ${build.plugins} or in "
6611 + "${project.build.pluginManagement} for the " + project.getId()
6612 + ". Added Javadoc API link according the javadoc executable version i.e.: "
6613 + javadocRuntimeVersion );
6614
6615 javaApiversion = javadocRuntimeVersion;
6616 }
6617 }
6618
6619 final String javaApiKey;
6620 if ( javaApiversion.asMajor().isAtLeast( "9" ) )
6621 {
6622 javaApiKey = "api_" + javaApiversion.asMajor();
6623 }
6624 else
6625 {
6626 javaApiKey = "api_1." + javaApiversion.asMajor().toString().charAt( 0 );
6627 }
6628
6629 final String javaApiLink;
6630 if ( javaApiLinks != null && javaApiLinks.containsKey( javaApiKey ) )
6631 {
6632 javaApiLink = javaApiLinks.getProperty( javaApiKey );
6633 }
6634 else if ( javaApiversion.isAtLeast( "16" ) )
6635 {
6636 javaApiLink = null;
6637 }
6638 else if ( javaApiversion.isAtLeast( "11" ) )
6639 {
6640 javaApiLink =
6641 String.format( "https://docs.oracle.com/en/java/javase/%s/docs/api/", javaApiversion.getValue( 1 ) );
6642 }
6643 else if ( javaApiversion.asMajor().isAtLeast( "6" ) )
6644 {
6645 javaApiLink =
6646 String.format( "https://docs.oracle.com/javase/%s/docs/api/", javaApiversion.asMajor().getValue( 1 ) );
6647 }
6648 else if ( javaApiversion.isAtLeast( "1.5" ) )
6649 {
6650 javaApiLink = "https://docs.oracle.com/javase/1.5.0/docs/api/";
6651 }
6652 else
6653 {
6654 javaApiLink = null;
6655 }
6656
6657 if ( getLog().isDebugEnabled() )
6658 {
6659 if ( javaApiLink != null )
6660 {
6661 getLog().debug( "Found Java API link: " + javaApiLink );
6662 }
6663 else
6664 {
6665 getLog().debug( "No Java API link found." );
6666 }
6667 }
6668
6669 if ( javaApiLink == null )
6670 {
6671 return null;
6672 }
6673
6674 final Path javaApiListFile;
6675 final String resourceName;
6676 if ( javaApiversion.isAtLeast( "10" ) )
6677 {
6678 javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "element-list" );
6679 resourceName = "java-api-element-list-" + javaApiversion.toString().substring( 0, 2 );
6680 }
6681 else if ( javaApiversion.asMajor().isAtLeast( "9" ) )
6682 {
6683 javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
6684 resourceName = "java-api-package-list-9";
6685 }
6686 else
6687 {
6688 javaApiListFile = getJavadocOptionsFile().getParentFile().toPath().resolve( "package-list" );
6689 resourceName = "java-api-package-list-1." + javaApiversion.asMajor().toString().charAt( 0 );
6690 }
6691
6692 OfflineLink link = new OfflineLink();
6693 link.setLocation( javaApiListFile.getParent().toAbsolutePath().toString() );
6694 link.setUrl( javaApiLink );
6695
6696 InputStream in = this.getClass().getResourceAsStream( resourceName );
6697 if ( in != null )
6698 {
6699 try ( InputStream closableIS = in )
6700 {
6701
6702 Files.copy( closableIS, javaApiListFile, StandardCopyOption.REPLACE_EXISTING );
6703 }
6704 catch ( IOException ioe )
6705 {
6706 logError( "Can't get " + resourceName + ": " + ioe.getMessage(), ioe );
6707 return null;
6708 }
6709 }
6710
6711 return link;
6712 }
6713
6714
6715
6716
6717
6718
6719
6720
6721 private Set<String> followLinks( Set<String> links )
6722 {
6723 Set<String> redirectLinks = new LinkedHashSet<>( links.size() );
6724 for ( String link : links )
6725 {
6726 try
6727 {
6728 redirectLinks.add( JavadocUtil.getRedirectUrl( new URI( link ).toURL(), settings ).toString() );
6729 }
6730 catch ( Exception e )
6731 {
6732
6733 getLog().debug( "Could not follow " + link + ". Reason: " + e.getMessage() );
6734
6735
6736
6737
6738 redirectLinks.add( link );
6739 }
6740 }
6741 return redirectLinks;
6742 }
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753 protected boolean isValidJavadocLink( String link, boolean detecting )
6754 {
6755 try
6756 {
6757 final URI packageListUri;
6758 final URI elementListUri;
6759
6760 if ( link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "http:" ) || link.trim().toLowerCase(
6761 Locale.ENGLISH ).startsWith( "https:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith(
6762 "ftp:" ) || link.trim().toLowerCase( Locale.ENGLISH ).startsWith( "file:" ) )
6763 {
6764 packageListUri = new URI( link + '/' + PACKAGE_LIST );
6765 elementListUri = new URI( link + '/' + ELEMENT_LIST );
6766 }
6767 else
6768 {
6769
6770 File dir = new File( link );
6771 if ( !dir.isAbsolute() )
6772 {
6773 dir = new File( getOutputDirectory(), link );
6774 }
6775 if ( !dir.isDirectory() )
6776 {
6777 if ( detecting )
6778 {
6779 getLog().warn( "The given File link: " + dir + " is not a dir." );
6780 }
6781 else
6782 {
6783 getLog().error( "The given File link: " + dir + " is not a dir." );
6784 }
6785 }
6786 packageListUri = new File( dir, PACKAGE_LIST ).toURI();
6787 elementListUri = new File( dir, ELEMENT_LIST ).toURI();
6788 }
6789
6790
6791 try
6792 {
6793 if ( JavadocUtil.isValidElementList( elementListUri.toURL(), settings, validateLinks ) )
6794 {
6795 return true;
6796 }
6797 }
6798 catch ( IOException e )
6799 {
6800 }
6801
6802 if ( JavadocUtil.isValidPackageList( packageListUri.toURL(), settings, validateLinks ) )
6803 {
6804 return true;
6805 }
6806
6807 if ( getLog().isErrorEnabled() )
6808 {
6809 if ( detecting )
6810 {
6811 getLog().warn( "Invalid links: "
6812 + link + " with /" + PACKAGE_LIST + " or / " + ELEMENT_LIST + ". Ignored it." );
6813 }
6814 else
6815 {
6816 getLog().error( "Invalid links: "
6817 + link + " with /" + PACKAGE_LIST + " or / " + ELEMENT_LIST + ". Ignored it." );
6818 }
6819 }
6820
6821 return false;
6822 }
6823 catch ( URISyntaxException e )
6824 {
6825 if ( getLog().isErrorEnabled() )
6826 {
6827 if ( detecting )
6828 {
6829 getLog().warn( "Malformed link: " + e.getInput() + ". Ignored it." );
6830 }
6831 else
6832 {
6833 getLog().error( "Malformed link: " + e.getInput() + ". Ignored it." );
6834 }
6835 }
6836 return false;
6837 }
6838 catch ( IOException e )
6839 {
6840 if ( getLog().isErrorEnabled() )
6841 {
6842 if ( detecting )
6843 {
6844 getLog().warn( "Error fetching link: " + link + ". Ignored it." );
6845 }
6846 else
6847 {
6848 getLog().error( "Error fetching link: " + link + ". Ignored it." );
6849 }
6850 }
6851 return false;
6852 }
6853 }
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863 private void writeDebugJavadocScript( String cmdLine, File javadocOutputDirectory )
6864 {
6865 File commandLineFile = new File( javadocOutputDirectory, DEBUG_JAVADOC_SCRIPT_NAME );
6866 commandLineFile.getParentFile().mkdirs();
6867
6868 try
6869 {
6870 FileUtils.fileWrite( commandLineFile.getAbsolutePath(), null , cmdLine );
6871
6872 if ( !SystemUtils.IS_OS_WINDOWS )
6873 {
6874 Runtime.getRuntime().exec( new String[]{ "chmod", "a+x", commandLineFile.getAbsolutePath() } );
6875 }
6876 }
6877 catch ( IOException e )
6878 {
6879 logError( "Unable to write '" + commandLineFile.getName() + "' debug script file", e );
6880 }
6881 }
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891 private boolean isJavadocVMInitError( String output )
6892 {
6893
6894
6895
6896
6897 return !( output.contains( "Javadoc" ) || output.contains( "javadoc" ) );
6898 }
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910 private static String getJavadocLink( MavenProject p )
6911 {
6912 if ( p.getUrl() == null )
6913 {
6914 return null;
6915 }
6916
6917 String url = cleanUrl( p.getUrl() );
6918 String destDir = "apidocs";
6919
6920 final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
6921 String destDirConfigured = getPluginParameter( p, pluginId, "destDir" );
6922 if ( destDirConfigured != null )
6923 {
6924 destDir = destDirConfigured;
6925 }
6926
6927 return url + "/" + destDir;
6928 }
6929
6930
6931
6932
6933
6934
6935 private static String cleanUrl( String url )
6936 {
6937 if ( url == null )
6938 {
6939 return "";
6940 }
6941
6942 url = url.trim();
6943 while ( url.endsWith( "/" ) )
6944 {
6945 url = url.substring( 0, url.lastIndexOf( "/" ) );
6946 }
6947
6948 return url;
6949 }
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959 private static Plugin getPlugin( MavenProject p, String pluginId )
6960 {
6961 if ( ( p.getBuild() == null ) || ( p.getBuild().getPluginsAsMap() == null ) )
6962 {
6963 return null;
6964 }
6965
6966 Plugin plugin = p.getBuild().getPluginsAsMap().get( pluginId );
6967
6968 if ( ( plugin == null ) && ( p.getBuild().getPluginManagement() != null ) && (
6969 p.getBuild().getPluginManagement().getPluginsAsMap() != null ) )
6970 {
6971 plugin = p.getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
6972 }
6973
6974 return plugin;
6975 }
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985 private static String getPluginParameter( MavenProject p, String pluginId, String param )
6986 {
6987
6988 Plugin plugin = getPlugin( p, pluginId );
6989 if ( plugin != null )
6990 {
6991 Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
6992 if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
6993 && StringUtils.isNotEmpty( xpp3Dom.getChild( param ).getValue() ) )
6994 {
6995 return xpp3Dom.getChild( param ).getValue();
6996 }
6997 }
6998
6999 return null;
7000 }
7001
7002
7003
7004
7005
7006
7007
7008
7009 protected final File getJavadocOptionsFile()
7010 {
7011 if ( javadocOptionsDir != null && !javadocOptionsDir.exists() )
7012 {
7013 javadocOptionsDir.mkdirs();
7014 }
7015
7016 return new File( javadocOptionsDir, "javadoc-options-" + getAttachmentClassifier() + ".xml" );
7017 }
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028 protected final JavadocOptions buildJavadocOptions()
7029 throws IOException
7030 {
7031 JavadocOptions options = new JavadocOptions();
7032
7033 options.setBootclasspathArtifacts( toList( bootclasspathArtifacts ) );
7034 options.setDocfilesSubdirsUsed( docfilessubdirs );
7035 options.setDocletArtifacts( toList( docletArtifact, docletArtifacts ) );
7036 options.setExcludedDocfilesSubdirs( excludedocfilessubdir );
7037 options.setExcludePackageNames( toList( excludePackageNames ) );
7038 options.setGroups( toList( groups ) );
7039 options.setLinks( links );
7040 options.setOfflineLinks( toList( offlineLinks ) );
7041 options.setResourcesArtifacts( toList( resourcesArtifacts ) );
7042 options.setTagletArtifacts( toList( tagletArtifact, tagletArtifacts ) );
7043 options.setTaglets( toList( taglets ) );
7044 options.setTags( toList( tags ) );
7045
7046 if ( getProject() != null && getJavadocDirectory() != null )
7047 {
7048 options.setJavadocResourcesDirectory(
7049 toRelative( getProject().getBasedir(), getJavadocDirectory().getAbsolutePath() ) );
7050 }
7051
7052 File optionsFile = getJavadocOptionsFile();
7053
7054 try ( Writer writer = WriterFactory.newXmlWriter( optionsFile ) )
7055 {
7056 new JavadocOptionsXpp3Writer().write( writer, options );
7057 }
7058
7059 return options;
7060 }
7061
7062
7063
7064
7065
7066
7067 protected String getAttachmentClassifier()
7068 {
7069 return JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER;
7070 }
7071
7072
7073
7074
7075
7076
7077
7078 protected void logError( String message, Throwable t )
7079 {
7080 if ( getLog().isDebugEnabled() )
7081 {
7082 getLog().error( message, t );
7083 }
7084 else
7085 {
7086 getLog().error( message );
7087 }
7088 }
7089
7090
7091
7092
7093
7094
7095 protected void failOnError( String prefix, Exception e )
7096 throws MojoExecutionException
7097 {
7098 if ( failOnError )
7099 {
7100 if ( e instanceof RuntimeException )
7101 {
7102 throw (RuntimeException) e;
7103 }
7104 throw new MojoExecutionException( prefix + ": " + e.getMessage(), e );
7105 }
7106
7107 getLog().error( prefix + ": " + e.getMessage(), e );
7108 }
7109
7110
7111
7112
7113
7114
7115 private List<MavenProject> getAggregatedProjects()
7116 {
7117 if ( this.reactorProjects == null )
7118 {
7119 return Collections.emptyList();
7120 }
7121 Map<Path, MavenProject> reactorProjectsMap = new HashMap<>();
7122 for ( MavenProject reactorProject : this.reactorProjects )
7123 {
7124 if ( !isSkippedJavadoc( reactorProject ) &&
7125 !isSkippedModule( reactorProject ) )
7126 {
7127 reactorProjectsMap.put( reactorProject.getBasedir().toPath(), reactorProject );
7128 }
7129 }
7130
7131 return new ArrayList<>( modulesForAggregatedProject( project, reactorProjectsMap ) );
7132 }
7133
7134
7135
7136
7137
7138 protected boolean isSkippedModule( MavenProject mavenProject )
7139 {
7140 if ( StringUtils.isEmpty( this.skippedModules ) )
7141 {
7142 return false;
7143 }
7144 List<String> modulesToSkip = Arrays.asList( StringUtils.split( this.skippedModules, ',' ) );
7145 return modulesToSkip.contains( mavenProject.getArtifactId() );
7146 }
7147
7148
7149
7150
7151
7152 protected boolean isSkippedJavadoc( MavenProject mavenProject )
7153 {
7154 String property = mavenProject.getProperties().getProperty( "maven.javadoc.skip" );
7155 if ( property != null )
7156 {
7157 boolean skip = BooleanUtils.toBoolean( property );
7158 getLog().debug( "isSkippedJavadoc " + mavenProject + " " + skip );
7159 return skip;
7160 }
7161 final String pluginId = "org.apache.maven.plugins:maven-javadoc-plugin";
7162 property = getPluginParameter( mavenProject, pluginId, "skip" );
7163 if ( property != null )
7164 {
7165 boolean skip = BooleanUtils.toBoolean( property );
7166 getLog().debug( "isSkippedJavadoc " + mavenProject + " " + skip );
7167 return skip;
7168 }
7169 if ( mavenProject.getParent() != null )
7170 {
7171 return isSkippedJavadoc( mavenProject.getParent() );
7172 }
7173 getLog().debug( "isSkippedJavadoc " + mavenProject + " " + false );
7174 return false;
7175 }
7176
7177 }