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