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