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