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