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