1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.javadoc;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStreamReader;
28 import java.io.OutputStream;
29 import java.io.PrintStream;
30 import java.io.UnsupportedEncodingException;
31 import java.lang.reflect.Modifier;
32 import java.net.SocketTimeoutException;
33 import java.net.URI;
34 import java.net.URISyntaxException;
35 import java.net.URL;
36 import java.net.URLClassLoader;
37 import java.nio.charset.Charset;
38 import java.nio.charset.IllegalCharsetNameException;
39 import java.nio.file.FileVisitResult;
40 import java.nio.file.Files;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.nio.file.SimpleFileVisitor;
44 import java.nio.file.attribute.BasicFileAttributes;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.LinkedHashSet;
50 import java.util.List;
51 import java.util.NoSuchElementException;
52 import java.util.Properties;
53 import java.util.Set;
54 import java.util.StringTokenizer;
55 import java.util.jar.JarEntry;
56 import java.util.jar.JarInputStream;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59 import java.util.regex.PatternSyntaxException;
60
61 import org.apache.http.HttpHeaders;
62 import org.apache.http.HttpHost;
63 import org.apache.http.HttpResponse;
64 import org.apache.http.HttpStatus;
65 import org.apache.http.auth.AuthScope;
66 import org.apache.http.auth.Credentials;
67 import org.apache.http.auth.UsernamePasswordCredentials;
68 import org.apache.http.client.CredentialsProvider;
69 import org.apache.http.client.config.CookieSpecs;
70 import org.apache.http.client.config.RequestConfig;
71 import org.apache.http.client.methods.HttpGet;
72 import org.apache.http.client.protocol.HttpClientContext;
73 import org.apache.http.config.Registry;
74 import org.apache.http.config.RegistryBuilder;
75 import org.apache.http.conn.socket.ConnectionSocketFactory;
76 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
77 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
78 import org.apache.http.impl.client.BasicCredentialsProvider;
79 import org.apache.http.impl.client.CloseableHttpClient;
80 import org.apache.http.impl.client.HttpClientBuilder;
81 import org.apache.http.impl.client.HttpClients;
82 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
83 import org.apache.http.message.BasicHeader;
84 import org.apache.maven.plugin.logging.Log;
85 import org.apache.maven.project.MavenProject;
86 import org.apache.maven.settings.Proxy;
87 import org.apache.maven.settings.Settings;
88 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
89 import org.apache.maven.shared.invoker.DefaultInvoker;
90 import org.apache.maven.shared.invoker.InvocationOutputHandler;
91 import org.apache.maven.shared.invoker.InvocationRequest;
92 import org.apache.maven.shared.invoker.InvocationResult;
93 import org.apache.maven.shared.invoker.Invoker;
94 import org.apache.maven.shared.invoker.MavenInvocationException;
95 import org.apache.maven.shared.invoker.PrintStreamHandler;
96 import org.apache.maven.wagon.proxy.ProxyInfo;
97 import org.apache.maven.wagon.proxy.ProxyUtils;
98 import org.codehaus.plexus.languages.java.version.JavaVersion;
99 import org.codehaus.plexus.util.DirectoryScanner;
100 import org.codehaus.plexus.util.FileUtils;
101 import org.codehaus.plexus.util.IOUtil;
102 import org.codehaus.plexus.util.Os;
103 import org.codehaus.plexus.util.StringUtils;
104 import org.codehaus.plexus.util.cli.CommandLineException;
105 import org.codehaus.plexus.util.cli.CommandLineUtils;
106 import org.codehaus.plexus.util.cli.Commandline;
107
108
109
110
111
112
113
114 public class JavadocUtil {
115
116 public static final int DEFAULT_TIMEOUT = 2000;
117
118
119 protected static final String ERROR_INIT_VM =
120 "Error occurred during initialization of VM, try to reduce the Java heap size for the MAVEN_OPTS "
121 + "environment variable using -Xms:<size> and -Xmx:<size>.";
122
123
124
125
126
127
128
129
130
131 public static Collection<Path> pruneDirs(MavenProject project, Collection<String> dirs) {
132 final Path projectBasedir = project.getBasedir().toPath();
133
134 Set<Path> pruned = new LinkedHashSet<>(dirs.size());
135 for (String dir : dirs) {
136 if (dir == null) {
137 continue;
138 }
139
140 Path directory = projectBasedir.resolve(dir);
141
142 if (Files.isDirectory(directory)) {
143 pruned.add(directory.toAbsolutePath());
144 }
145 }
146
147 return pruned;
148 }
149
150
151
152
153
154
155
156
157 protected static List<String> pruneFiles(Collection<String> files) {
158 List<String> pruned = new ArrayList<>(files.size());
159 for (String f : files) {
160 if (!shouldPruneFile(f, pruned)) {
161 pruned.add(f);
162 }
163 }
164
165 return pruned;
166 }
167
168
169
170
171
172
173
174
175
176 public static boolean shouldPruneFile(String f, List<String> pruned) {
177 if (f != null) {
178 if (Files.isRegularFile(Paths.get(f)) && !pruned.contains(f)) {
179 return false;
180 }
181 }
182
183 return true;
184 }
185
186
187
188
189
190
191
192
193 protected static List<String> getExcludedPackages(
194 Collection<Path> sourcePaths, Collection<String> excludedPackages) {
195 List<String> excludedNames = new ArrayList<>();
196 for (Path sourcePath : sourcePaths) {
197 excludedNames.addAll(getExcludedPackages(sourcePath, excludedPackages));
198 }
199
200 return excludedNames;
201 }
202
203
204
205
206
207
208
209
210
211 protected static String quotedArgument(String value) {
212 String arg = value;
213
214 if (StringUtils.isNotEmpty(arg)) {
215 if (arg.contains("'")) {
216 arg = StringUtils.replace(arg, "'", "\\'");
217 }
218 arg = "'" + arg + "'";
219
220
221 arg = StringUtils.replace(arg, "\n", " ");
222 }
223
224 return arg;
225 }
226
227
228
229
230
231
232
233
234 protected static String quotedPathArgument(String value) {
235 String path = value;
236
237 if (StringUtils.isNotEmpty(path)) {
238 path = path.replace('\\', '/');
239 if (path.contains("'")) {
240 StringBuilder pathBuilder = new StringBuilder();
241 pathBuilder.append('\'');
242 String[] split = path.split("'");
243
244 for (int i = 0; i < split.length; i++) {
245 if (i != split.length - 1) {
246 pathBuilder.append(split[i]).append("\\'");
247 } else {
248 pathBuilder.append(split[i]);
249 }
250 }
251 pathBuilder.append('\'');
252 path = pathBuilder.toString();
253 } else {
254 path = "'" + path + "'";
255 }
256 }
257
258 return path;
259 }
260
261
262
263
264
265
266
267
268
269
270
271 protected static void copyJavadocResources(File outputDirectory, File javadocDir, String excludedocfilessubdir)
272 throws IOException {
273 if (!javadocDir.isDirectory()) {
274 return;
275 }
276
277 List<String> excludes = new ArrayList<>(Arrays.asList(FileUtils.getDefaultExcludes()));
278
279 if (StringUtils.isNotEmpty(excludedocfilessubdir)) {
280 StringTokenizer st = new StringTokenizer(excludedocfilessubdir, ":");
281 String current;
282 while (st.hasMoreTokens()) {
283 current = st.nextToken();
284 excludes.add("**/" + current + "/**");
285 }
286 }
287
288 List<String> docFiles = FileUtils.getDirectoryNames(
289 javadocDir, "resources,**/doc-files", StringUtils.join(excludes.iterator(), ","), false, true);
290 for (String docFile : docFiles) {
291 File docFileOutput = new File(outputDirectory, docFile);
292 FileUtils.mkdir(docFileOutput.getAbsolutePath());
293 FileUtils.copyDirectoryStructure(new File(javadocDir, docFile), docFileOutput);
294 List<String> files = FileUtils.getFileAndDirectoryNames(
295 docFileOutput, StringUtils.join(excludes.iterator(), ","), null, true, true, true, true);
296 for (String filename : files) {
297 File file = new File(filename);
298
299 if (file.isDirectory()) {
300 FileUtils.deleteDirectory(file);
301 } else {
302 file.delete();
303 }
304 }
305 }
306 }
307
308
309
310
311
312
313
314
315
316 protected static List<String> getIncludedFiles(
317 File sourceDirectory, String[] fileList, Collection<String> excludePackages) {
318 List<String> files = new ArrayList<>();
319
320 List<Pattern> excludePackagePatterns = new ArrayList<>(excludePackages.size());
321 for (String excludePackage : excludePackages) {
322 excludePackagePatterns.add(Pattern.compile(excludePackage
323 .replace('.', File.separatorChar)
324 .replace("\\", "\\\\")
325 .replace("*", ".+")
326 .concat("[\\\\/][^\\\\/]+\\.java")));
327 }
328
329 for (String file : fileList) {
330 boolean excluded = false;
331 for (Pattern excludePackagePattern : excludePackagePatterns) {
332 if (excludePackagePattern.matcher(file).matches()) {
333 excluded = true;
334 break;
335 }
336 }
337
338 if (!excluded) {
339 files.add(file.replace('\\', '/'));
340 }
341 }
342
343 return files;
344 }
345
346
347
348
349
350
351
352
353
354 protected static Collection<String> getExcludedPackages(
355 final Path sourceDirectory, Collection<String> excludePackagenames) {
356 final String regexFileSeparator = File.separator.replace("\\", "\\\\");
357
358 final Collection<String> fileList = new ArrayList<>();
359
360 try {
361 Files.walkFileTree(sourceDirectory, new SimpleFileVisitor<Path>() {
362 @Override
363 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
364 if (file.getFileName().toString().endsWith(".java")) {
365 fileList.add(
366 sourceDirectory.relativize(file.getParent()).toString());
367 }
368 return FileVisitResult.CONTINUE;
369 }
370 });
371 } catch (IOException e) {
372
373 }
374
375 List<String> files = new ArrayList<>();
376 for (String excludePackagename : excludePackagenames) {
377
378
379
380
381
382 Pattern p = Pattern.compile(excludePackagename
383 .replace(".", regexFileSeparator)
384 .replaceFirst("^\\*", ".+")
385 .replace("*", "[^" + regexFileSeparator + "]+"));
386
387 for (String aFileList : fileList) {
388 if (p.matcher(aFileList).matches()) {
389 files.add(aFileList.replace(File.separatorChar, '.'));
390 }
391 }
392 }
393
394 return files;
395 }
396
397
398
399
400
401
402
403
404
405 protected static List<String> getFilesFromSource(
406 File sourceDirectory,
407 List<String> sourceFileIncludes,
408 List<String> sourceFileExcludes,
409 Collection<String> excludePackages) {
410 DirectoryScanner ds = new DirectoryScanner();
411 if (sourceFileIncludes == null) {
412 sourceFileIncludes = Collections.singletonList("**/*.java");
413 }
414 ds.setIncludes(sourceFileIncludes.toArray(new String[sourceFileIncludes.size()]));
415 if (sourceFileExcludes != null && sourceFileExcludes.size() > 0) {
416 ds.setExcludes(sourceFileExcludes.toArray(new String[sourceFileExcludes.size()]));
417 }
418 ds.setBasedir(sourceDirectory);
419 ds.scan();
420
421 String[] fileList = ds.getIncludedFiles();
422
423 List<String> files = new ArrayList<>();
424 if (fileList.length != 0) {
425 files.addAll(getIncludedFiles(sourceDirectory, fileList, excludePackages));
426 }
427
428 return files;
429 }
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446 protected static JavaVersion getJavadocVersion(File javadocExe)
447 throws IOException, CommandLineException, IllegalArgumentException {
448 if ((javadocExe == null) || (!javadocExe.exists()) || (!javadocExe.isFile())) {
449 throw new IOException("The javadoc executable '" + javadocExe + "' doesn't exist or is not a file. ");
450 }
451
452 Commandline cmd = new Commandline();
453 cmd.setExecutable(javadocExe.getAbsolutePath());
454 cmd.setWorkingDirectory(javadocExe.getParentFile());
455 cmd.createArg().setValue("-J-version");
456
457 CommandLineUtils.StringStreamConsumer out = new JavadocOutputStreamConsumer();
458 CommandLineUtils.StringStreamConsumer err = new JavadocOutputStreamConsumer();
459
460 int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err);
461
462 if (exitCode != 0) {
463 StringBuilder msg = new StringBuilder("Exit code: " + exitCode + " - " + err.getOutput());
464 msg.append('\n');
465 msg.append("Command line was:").append(CommandLineUtils.toString(cmd.getCommandline()));
466 throw new CommandLineException(msg.toString());
467 }
468
469 if (StringUtils.isNotEmpty(err.getOutput())) {
470 return JavaVersion.parse(extractJavadocVersion(err.getOutput()));
471 } else if (StringUtils.isNotEmpty(out.getOutput())) {
472 return JavaVersion.parse(extractJavadocVersion(out.getOutput()));
473 }
474
475 throw new IllegalArgumentException("No output found from the command line 'javadoc -J-version'");
476 }
477
478 private static final Pattern EXTRACT_JAVADOC_VERSION_PATTERN =
479 Pattern.compile("(?s).*?[^a-zA-Z](([0-9]+\\.?[0-9]*)(\\.[0-9]+)?).*");
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521 protected static String extractJavadocVersion(String output) throws IllegalArgumentException {
522 if (StringUtils.isEmpty(output)) {
523 throw new IllegalArgumentException("The output could not be null.");
524 }
525
526 Pattern pattern = EXTRACT_JAVADOC_VERSION_PATTERN;
527
528 Matcher matcher = pattern.matcher(output);
529 if (!matcher.matches()) {
530 throw new PatternSyntaxException(
531 "Unrecognized version of Javadoc: '" + output + "'",
532 pattern.pattern(),
533 pattern.toString().length() - 1);
534 }
535
536 return matcher.group(1);
537 }
538
539 private static final Pattern PARSE_JAVADOC_MEMORY_PATTERN_0 = Pattern.compile("^\\s*(\\d+)\\s*?\\s*$");
540
541 private static final Pattern PARSE_JAVADOC_MEMORY_PATTERN_1 =
542 Pattern.compile("^\\s*(\\d+)\\s*k(b)?\\s*$", Pattern.CASE_INSENSITIVE);
543
544 private static final Pattern PARSE_JAVADOC_MEMORY_PATTERN_2 =
545 Pattern.compile("^\\s*(\\d+)\\s*m(b)?\\s*$", Pattern.CASE_INSENSITIVE);
546
547 private static final Pattern PARSE_JAVADOC_MEMORY_PATTERN_3 =
548 Pattern.compile("^\\s*(\\d+)\\s*g(b)?\\s*$", Pattern.CASE_INSENSITIVE);
549
550 private static final Pattern PARSE_JAVADOC_MEMORY_PATTERN_4 =
551 Pattern.compile("^\\s*(\\d+)\\s*t(b)?\\s*$", Pattern.CASE_INSENSITIVE);
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581 protected static String parseJavadocMemory(String memory) throws IllegalArgumentException {
582 if (StringUtils.isEmpty(memory)) {
583 throw new IllegalArgumentException("The memory could not be null.");
584 }
585
586 Matcher m0 = PARSE_JAVADOC_MEMORY_PATTERN_0.matcher(memory);
587 if (m0.matches()) {
588 return m0.group(1) + "m";
589 }
590
591 Matcher m1 = PARSE_JAVADOC_MEMORY_PATTERN_1.matcher(memory);
592 if (m1.matches()) {
593 return m1.group(1) + "k";
594 }
595
596 Matcher m2 = PARSE_JAVADOC_MEMORY_PATTERN_2.matcher(memory);
597 if (m2.matches()) {
598 return m2.group(1) + "m";
599 }
600
601 Matcher m3 = PARSE_JAVADOC_MEMORY_PATTERN_3.matcher(memory);
602 if (m3.matches()) {
603 return (Integer.parseInt(m3.group(1)) * 1024) + "m";
604 }
605
606 Matcher m4 = PARSE_JAVADOC_MEMORY_PATTERN_4.matcher(memory);
607 if (m4.matches()) {
608 return (Integer.parseInt(m4.group(1)) * 1024 * 1024) + "m";
609 }
610
611 throw new IllegalArgumentException("Could convert not to a memory size: " + memory);
612 }
613
614
615
616
617
618
619
620 protected static boolean validateEncoding(String charsetName) {
621 if (StringUtils.isEmpty(charsetName)) {
622 return false;
623 }
624
625 try {
626 return Charset.isSupported(charsetName);
627 } catch (IllegalCharsetNameException e) {
628 return false;
629 }
630 }
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645 protected static List<String> getTagletClassNames(File jarFile)
646 throws IOException, ClassNotFoundException, NoClassDefFoundError {
647 List<String> classes = getClassNamesFromJar(jarFile);
648 URLClassLoader cl;
649
650
651 File tools = new File(System.getProperty("java.home"), "../lib/tools.jar");
652 if (tools.exists() && tools.isFile()) {
653 cl = new URLClassLoader(
654 new URL[] {jarFile.toURI().toURL(), tools.toURI().toURL()}, null);
655 } else {
656 cl = new URLClassLoader(new URL[] {jarFile.toURI().toURL()}, ClassLoader.getSystemClassLoader());
657 }
658
659 List<String> tagletClasses = new ArrayList<>();
660
661 Class<?> tagletClass;
662
663 try {
664 tagletClass = cl.loadClass("com.sun.tools.doclets.Taglet");
665 } catch (ClassNotFoundException e) {
666 tagletClass = cl.loadClass("jdk.javadoc.doclet.Taglet");
667 }
668
669 for (String s : classes) {
670 Class<?> c = cl.loadClass(s);
671
672 if (tagletClass.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) {
673 tagletClasses.add(c.getName());
674 }
675 }
676
677 try {
678 cl.close();
679 } catch (IOException ex) {
680
681 }
682
683 return tagletClasses;
684 }
685
686
687
688
689
690
691
692
693
694 protected static void copyResource(URL url, File file) throws IOException {
695 if (file == null) {
696 throw new IOException("The file can't be null.");
697 }
698 if (url == null) {
699 throw new IOException("The url could not be null.");
700 }
701
702 FileUtils.copyURLToFile(url, file);
703 }
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721 protected static void invokeMaven(
722 Log log,
723 File localRepositoryDir,
724 File projectFile,
725 List<String> goals,
726 Properties properties,
727 File invokerLog,
728 File globalSettingsFile)
729 throws MavenInvocationException {
730 if (projectFile == null) {
731 throw new IllegalArgumentException("projectFile should be not null.");
732 }
733 if (!projectFile.isFile()) {
734 throw new IllegalArgumentException(projectFile.getAbsolutePath() + " is not a file.");
735 }
736 if (goals == null || goals.size() == 0) {
737 throw new IllegalArgumentException("goals should be not empty.");
738 }
739 if (localRepositoryDir == null || !localRepositoryDir.isDirectory()) {
740 throw new IllegalArgumentException(
741 "localRepositoryDir '" + localRepositoryDir + "' should be a directory.");
742 }
743
744 String mavenHome = getMavenHome(log);
745 if (StringUtils.isEmpty(mavenHome)) {
746 String msg = "Could NOT invoke Maven because no Maven Home is defined. You need to have set the M2_HOME "
747 + "system env variable or a maven.home Java system properties.";
748 if (log != null) {
749 log.error(msg);
750 } else {
751 System.err.println(msg);
752 }
753 return;
754 }
755
756 Invoker invoker = new DefaultInvoker();
757 invoker.setMavenHome(new File(mavenHome));
758 invoker.setLocalRepositoryDirectory(localRepositoryDir);
759
760 InvocationRequest request = new DefaultInvocationRequest();
761 request.setBaseDirectory(projectFile.getParentFile());
762 request.setPomFile(projectFile);
763 request.setGlobalSettingsFile(globalSettingsFile);
764 request.setBatchMode(true);
765 if (log != null) {
766 request.setDebug(log.isDebugEnabled());
767 } else {
768 request.setDebug(true);
769 }
770 request.setGoals(goals);
771 if (properties != null) {
772 request.setProperties(properties);
773 }
774 File javaHome = getJavaHome(log);
775 if (javaHome != null) {
776 request.setJavaHome(javaHome);
777 }
778
779 if (log != null && log.isDebugEnabled()) {
780 log.debug("Invoking Maven for the goals: " + goals + " with "
781 + (properties == null ? "no properties" : "properties=" + properties));
782 }
783 InvocationResult result = invoke(log, invoker, request, invokerLog, goals, properties, null);
784
785 if (result.getExitCode() != 0) {
786 String invokerLogContent = readFile(invokerLog, "UTF-8");
787
788
789 if (invokerLogContent != null
790 && (!invokerLogContent.contains("Scanning for projects...")
791 || invokerLogContent.contains(OutOfMemoryError.class.getName()))) {
792 if (log != null) {
793 log.error("Error occurred during initialization of VM, trying to use an empty MAVEN_OPTS...");
794
795 if (log.isDebugEnabled()) {
796 log.debug("Reinvoking Maven for the goals: " + goals + " with an empty MAVEN_OPTS...");
797 }
798 }
799 result = invoke(log, invoker, request, invokerLog, goals, properties, "");
800 }
801 }
802
803 if (result.getExitCode() != 0) {
804 String invokerLogContent = readFile(invokerLog, "UTF-8");
805
806
807 if (invokerLogContent != null
808 && (!invokerLogContent.contains("Scanning for projects...")
809 || invokerLogContent.contains(OutOfMemoryError.class.getName()))) {
810 throw new MavenInvocationException(ERROR_INIT_VM);
811 }
812
813 throw new MavenInvocationException(
814 "Error when invoking Maven, consult the invoker log file: " + invokerLog.getAbsolutePath());
815 }
816 }
817
818
819
820
821
822
823
824
825
826
827 protected static String readFile(final File javaFile, final String encoding) {
828 try {
829 return FileUtils.fileRead(javaFile, encoding);
830 } catch (IOException e) {
831 return null;
832 }
833 }
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850 protected static String[] splitPath(final String path) {
851 if (path == null) {
852 return null;
853 }
854
855 List<String> subpaths = new ArrayList<>();
856 PathTokenizer pathTokenizer = new PathTokenizer(path);
857 while (pathTokenizer.hasMoreTokens()) {
858 subpaths.add(pathTokenizer.nextToken());
859 }
860
861 return subpaths.toArray(new String[subpaths.size()]);
862 }
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880 protected static String unifyPathSeparator(final String path) {
881 if (path == null) {
882 return null;
883 }
884
885 return StringUtils.join(splitPath(path), File.pathSeparator);
886 }
887
888
889
890
891
892
893
894
895
896
897 private static List<String> getClassNamesFromJar(File jarFile) throws IOException {
898 if (jarFile == null || !jarFile.exists() || !jarFile.isFile()) {
899 throw new IOException("The jar '" + jarFile + "' doesn't exist or is not a file.");
900 }
901
902 List<String> classes = new ArrayList<>();
903 Pattern pattern = Pattern.compile("(?i)^(META-INF/versions/(?<v>[0-9]+)/)?(?<n>.+)[.]class$");
904 try (JarInputStream jarStream = new JarInputStream(new FileInputStream(jarFile))) {
905 for (JarEntry jarEntry = jarStream.getNextJarEntry();
906 jarEntry != null;
907 jarEntry = jarStream.getNextJarEntry()) {
908 Matcher matcher = pattern.matcher(jarEntry.getName());
909 if (matcher.matches()) {
910 String version = matcher.group("v");
911 if (StringUtils.isEmpty(version) || JavaVersion.JAVA_VERSION.isAtLeast(version)) {
912 String name = matcher.group("n");
913
914 classes.add(name.replaceAll("/", "\\."));
915 }
916 }
917
918 jarStream.closeEntry();
919 }
920 }
921
922 return classes;
923 }
924
925
926
927
928
929
930
931
932
933
934
935
936
937 private static InvocationResult invoke(
938 Log log,
939 Invoker invoker,
940 InvocationRequest request,
941 File invokerLog,
942 List<String> goals,
943 Properties properties,
944 String mavenOpts)
945 throws MavenInvocationException {
946 PrintStream ps;
947 OutputStream os = null;
948 if (invokerLog != null) {
949 if (log != null && log.isDebugEnabled()) {
950 log.debug("Using " + invokerLog.getAbsolutePath() + " to log the invoker");
951 }
952
953 try {
954 if (!invokerLog.exists()) {
955
956 invokerLog.getParentFile().mkdirs();
957 }
958 os = new FileOutputStream(invokerLog);
959 ps = new PrintStream(os, true, "UTF-8");
960 } catch (FileNotFoundException e) {
961 if (log != null && log.isErrorEnabled()) {
962 log.error("FileNotFoundException: " + e.getMessage() + ". Using System.out to log the invoker.");
963 }
964 ps = System.out;
965 } catch (UnsupportedEncodingException e) {
966 if (log != null && log.isErrorEnabled()) {
967 log.error("UnsupportedEncodingException: " + e.getMessage()
968 + ". Using System.out to log the invoker.");
969 }
970 ps = System.out;
971 }
972 } else {
973 if (log != null && log.isDebugEnabled()) {
974 log.debug("Using System.out to log the invoker.");
975 }
976
977 ps = System.out;
978 }
979
980 if (mavenOpts != null) {
981 request.setMavenOpts(mavenOpts);
982 }
983
984 InvocationOutputHandler outputHandler = new PrintStreamHandler(ps, false);
985 request.setOutputHandler(outputHandler);
986
987 try {
988 outputHandler.consumeLine("Invoking Maven for the goals: " + goals + " with "
989 + (properties == null ? "no properties" : "properties=" + properties));
990 outputHandler.consumeLine("");
991 outputHandler.consumeLine("M2_HOME=" + getMavenHome(log));
992 outputHandler.consumeLine("MAVEN_OPTS=" + getMavenOpts(log));
993 outputHandler.consumeLine("JAVA_HOME=" + getJavaHome(log));
994 outputHandler.consumeLine("JAVA_OPTS=" + getJavaOpts(log));
995 outputHandler.consumeLine("");
996 } catch (IOException ioe) {
997 throw new MavenInvocationException("IOException while consuming invocation output", ioe);
998 }
999
1000 try {
1001 return invoker.execute(request);
1002 } finally {
1003 IOUtil.close(os);
1004 }
1005 }
1006
1007
1008
1009
1010
1011
1012
1013 private static String getMavenHome(Log log) {
1014 String mavenHome = System.getProperty("maven.home");
1015
1016 File m2Home = new File(mavenHome);
1017 if (!m2Home.exists()) {
1018 if (log != null && log.isErrorEnabled()) {
1019 log.error("Cannot find Maven application directory. Either specify 'maven.home' system property.");
1020 }
1021 }
1022
1023 return mavenHome;
1024 }
1025
1026
1027
1028
1029
1030
1031 private static String getMavenOpts(Log log) {
1032 return CommandLineUtils.getSystemEnvVars().getProperty("MAVEN_OPTS");
1033 }
1034
1035
1036
1037
1038
1039
1040
1041
1042 private static File getJavaHome(Log log) {
1043 File javaHome = null;
1044
1045 String javaHomeValue = CommandLineUtils.getSystemEnvVars().getProperty("JAVA_HOME");
1046
1047
1048 if (System.getProperty("maven.home") == null || javaHomeValue == null) {
1049
1050 if (SystemUtils.IS_OS_MAC_OSX || JavaVersion.JAVA_VERSION.isAtLeast("9")) {
1051 javaHome = SystemUtils.getJavaHome();
1052 } else {
1053 javaHome = new File(SystemUtils.getJavaHome(), "..");
1054 }
1055 }
1056
1057 if (javaHome == null || !javaHome.exists()) {
1058 javaHome = new File(javaHomeValue);
1059 }
1060
1061 if (javaHome == null || !javaHome.exists()) {
1062 if (log != null && log.isErrorEnabled()) {
1063 log.error("Cannot find Java application directory. Either specify 'java.home' system property, or "
1064 + "JAVA_HOME environment variable.");
1065 }
1066 }
1067
1068 return javaHome;
1069 }
1070
1071
1072
1073
1074
1075
1076 private static String getJavaOpts(Log log) {
1077 return CommandLineUtils.getSystemEnvVars().getProperty("JAVA_OPTS");
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087 private static class PathTokenizer {
1088
1089
1090
1091 private StringTokenizer tokenizer;
1092
1093
1094
1095
1096 private String lookahead = null;
1097
1098
1099
1100
1101
1102 private boolean onNetWare = Os.isFamily("netware");
1103
1104
1105
1106
1107 private boolean dosStyleFilesystem;
1108
1109
1110
1111
1112
1113
1114 PathTokenizer(String path) {
1115 if (onNetWare) {
1116
1117
1118 tokenizer = new StringTokenizer(path, ":;", true);
1119 } else {
1120
1121
1122 tokenizer = new StringTokenizer(path, ":;", false);
1123 }
1124 dosStyleFilesystem = File.pathSeparatorChar == ';';
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134 public boolean hasMoreTokens() {
1135 return lookahead != null || tokenizer.hasMoreTokens();
1136 }
1137
1138
1139
1140
1141
1142
1143
1144 public String nextToken() throws NoSuchElementException {
1145 String token;
1146 if (lookahead != null) {
1147 token = lookahead;
1148 lookahead = null;
1149 } else {
1150 token = tokenizer.nextToken().trim();
1151 }
1152
1153 if (!onNetWare) {
1154 if (token.length() == 1
1155 && Character.isLetter(token.charAt(0))
1156 && dosStyleFilesystem
1157 && tokenizer.hasMoreTokens()) {
1158
1159
1160 String nextToken = tokenizer.nextToken().trim();
1161 if (nextToken.startsWith("\\") || nextToken.startsWith("/")) {
1162
1163
1164
1165 token += ":" + nextToken;
1166 } else {
1167
1168 lookahead = nextToken;
1169 }
1170 }
1171 } else {
1172
1173
1174 if (token.equals(File.pathSeparator) || token.equals(":")) {
1175
1176 token = tokenizer.nextToken().trim();
1177 }
1178
1179 if (tokenizer.hasMoreTokens()) {
1180
1181 String nextToken = tokenizer.nextToken().trim();
1182
1183
1184 if (!nextToken.equals(File.pathSeparator)) {
1185 if (nextToken.equals(":")) {
1186 if (!token.startsWith("/")
1187 && !token.startsWith("\\")
1188 && !token.startsWith(".")
1189 && !token.startsWith("..")) {
1190
1191 String oneMore = tokenizer.nextToken().trim();
1192 if (!oneMore.equals(File.pathSeparator)) {
1193 token += ":" + oneMore;
1194 } else {
1195 token += ":";
1196 lookahead = oneMore;
1197 }
1198 }
1199
1200
1201 } else {
1202
1203 lookahead = nextToken;
1204 }
1205 }
1206 }
1207 }
1208 return token;
1209 }
1210 }
1211
1212
1213
1214
1215
1216
1217
1218 protected static class JavadocOutputStreamConsumer extends CommandLineUtils.StringStreamConsumer {
1219 @Override
1220 public void consumeLine(String line) {
1221 if (!line.startsWith("Picked up ")) {
1222 super.consumeLine(line);
1223 }
1224 }
1225 }
1226
1227 static List<String> toList(String src) {
1228 return toList(src, null, null);
1229 }
1230
1231 static List<String> toList(String src, String elementPrefix, String elementSuffix) {
1232 if (StringUtils.isEmpty(src)) {
1233 return null;
1234 }
1235
1236 List<String> result = new ArrayList<>();
1237
1238 StringTokenizer st = new StringTokenizer(src, "[,:;]");
1239 StringBuilder sb = new StringBuilder(256);
1240 while (st.hasMoreTokens()) {
1241 sb.setLength(0);
1242 if (StringUtils.isNotEmpty(elementPrefix)) {
1243 sb.append(elementPrefix);
1244 }
1245
1246 sb.append(st.nextToken());
1247
1248 if (StringUtils.isNotEmpty(elementSuffix)) {
1249 sb.append(elementSuffix);
1250 }
1251
1252 result.add(sb.toString());
1253 }
1254
1255 return result;
1256 }
1257
1258 static <T> List<T> toList(T[] multiple) {
1259 return toList(null, multiple);
1260 }
1261
1262 static <T> List<T> toList(T single, T[] multiple) {
1263 if (single == null && (multiple == null || multiple.length < 1)) {
1264 return null;
1265 }
1266
1267 List<T> result = new ArrayList<>();
1268 if (single != null) {
1269 result.add(single);
1270 }
1271
1272 if (multiple != null && multiple.length > 0) {
1273 result.addAll(Arrays.asList(multiple));
1274 }
1275
1276 return result;
1277 }
1278
1279
1280 public static String toRelative(File basedir, String absolutePath) {
1281 String relative;
1282
1283 absolutePath = absolutePath.replace('\\', '/');
1284 String basedirPath = basedir.getAbsolutePath().replace('\\', '/');
1285
1286 if (absolutePath.startsWith(basedirPath)) {
1287 relative = absolutePath.substring(basedirPath.length());
1288 if (relative.startsWith("/")) {
1289 relative = relative.substring(1);
1290 }
1291 if (relative.length() <= 0) {
1292 relative = ".";
1293 }
1294 } else {
1295 relative = absolutePath;
1296 }
1297
1298 return relative;
1299 }
1300
1301
1302
1303
1304
1305
1306 public static boolean isNotEmpty(final Collection<?> collection) {
1307 return collection != null && !collection.isEmpty();
1308 }
1309
1310
1311
1312
1313
1314
1315 public static boolean isEmpty(final Collection<?> collection) {
1316 return collection == null || collection.isEmpty();
1317 }
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328 protected static URL getRedirectUrl(URL url, Settings settings) throws IOException {
1329 String protocol = url.getProtocol();
1330 if (!"http".equals(protocol) && !"https".equals(protocol)) {
1331 return url;
1332 }
1333
1334 try (CloseableHttpClient httpClient = createHttpClient(settings, url)) {
1335 HttpClientContext httpContext = HttpClientContext.create();
1336 HttpGet httpMethod = new HttpGet(url.toString());
1337 HttpResponse response = httpClient.execute(httpMethod, httpContext);
1338 int status = response.getStatusLine().getStatusCode();
1339 if (status != HttpStatus.SC_OK) {
1340 throw new FileNotFoundException(
1341 "Unexpected HTTP status code " + status + " getting resource " + url.toExternalForm() + ".");
1342 }
1343
1344 List<URI> redirects = httpContext.getRedirectLocations();
1345
1346 if (isEmpty(redirects)) {
1347 return url;
1348 } else {
1349 URI last = redirects.get(redirects.size() - 1);
1350
1351
1352
1353 String truncate = "index.html";
1354 if (last.getPath().endsWith("/" + truncate)) {
1355 try {
1356 String fixedPath =
1357 last.getPath().substring(0, last.getPath().length() - truncate.length());
1358 last = new URI(
1359 last.getScheme(), last.getAuthority(), fixedPath, last.getQuery(), last.getFragment());
1360 } catch (URISyntaxException ex) {
1361
1362 }
1363 }
1364 return last.toURL();
1365 }
1366 }
1367 }
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382 protected static boolean isValidPackageList(URL url, Settings settings, boolean validateContent)
1383 throws IOException {
1384 if (url == null) {
1385 throw new IllegalArgumentException("The url is null");
1386 }
1387
1388 try (BufferedReader reader = getReader(url, settings)) {
1389 if (validateContent) {
1390 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1391 if (!isValidPackageName(line)) {
1392 return false;
1393 }
1394 }
1395 }
1396 return true;
1397 }
1398 }
1399
1400 protected static boolean isValidElementList(URL url, Settings settings, boolean validateContent)
1401 throws IOException {
1402 if (url == null) {
1403 throw new IllegalArgumentException("The url is null");
1404 }
1405
1406 try (BufferedReader reader = getReader(url, settings)) {
1407 if (validateContent) {
1408 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1409 if (line.startsWith("module:")) {
1410 continue;
1411 }
1412
1413 if (!isValidPackageName(line)) {
1414 return false;
1415 }
1416 }
1417 }
1418 return true;
1419 }
1420 }
1421
1422 private static BufferedReader getReader(URL url, Settings settings) throws IOException {
1423 BufferedReader reader = null;
1424
1425 if ("file".equals(url.getProtocol())) {
1426
1427 reader = new BufferedReader(new InputStreamReader(url.openStream()));
1428 } else {
1429
1430 final CloseableHttpClient httpClient = createHttpClient(settings, url);
1431
1432 final HttpGet httpMethod = new HttpGet(url.toString());
1433
1434 HttpResponse response;
1435 HttpClientContext httpContext = HttpClientContext.create();
1436 try {
1437 response = httpClient.execute(httpMethod, httpContext);
1438 } catch (SocketTimeoutException e) {
1439
1440 response = httpClient.execute(httpMethod, httpContext);
1441 }
1442
1443 int status = response.getStatusLine().getStatusCode();
1444 if (status != HttpStatus.SC_OK) {
1445 throw new FileNotFoundException(
1446 "Unexpected HTTP status code " + status + " getting resource " + url.toExternalForm() + ".");
1447 } else {
1448 int pos = url.getPath().lastIndexOf('/');
1449 List<URI> redirects = httpContext.getRedirectLocations();
1450 if (pos >= 0 && isNotEmpty(redirects)) {
1451 URI location = redirects.get(redirects.size() - 1);
1452 String suffix = url.getPath().substring(pos);
1453
1454 if (!location.getPath().endsWith(suffix)) {
1455 throw new FileNotFoundException(url.toExternalForm() + " redirects to "
1456 + location.toURL().toExternalForm() + ".");
1457 }
1458 }
1459 }
1460
1461
1462 reader = new BufferedReader(
1463 new InputStreamReader(response.getEntity().getContent())) {
1464 @Override
1465 public void close() throws IOException {
1466 super.close();
1467
1468 if (httpMethod != null) {
1469 httpMethod.releaseConnection();
1470 }
1471 if (httpClient != null) {
1472 httpClient.close();
1473 }
1474 }
1475 };
1476 }
1477
1478 return reader;
1479 }
1480
1481 private static boolean isValidPackageName(String str) {
1482 if (StringUtils.isEmpty(str)) {
1483
1484 return true;
1485 }
1486
1487 int idx;
1488 while ((idx = str.indexOf('.')) != -1) {
1489 if (!isValidClassName(str.substring(0, idx))) {
1490 return false;
1491 }
1492
1493 str = str.substring(idx + 1);
1494 }
1495
1496 return isValidClassName(str);
1497 }
1498
1499 private static boolean isValidClassName(String str) {
1500 if (StringUtils.isEmpty(str) || !Character.isJavaIdentifierStart(str.charAt(0))) {
1501 return false;
1502 }
1503
1504 for (int i = str.length() - 1; i > 0; i--) {
1505 if (!Character.isJavaIdentifierPart(str.charAt(i))) {
1506 return false;
1507 }
1508 }
1509
1510 return true;
1511 }
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522 private static CloseableHttpClient createHttpClient(Settings settings, URL url) {
1523 HttpClientBuilder builder = HttpClients.custom();
1524
1525 Registry<ConnectionSocketFactory> csfRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
1526 .register("http", PlainConnectionSocketFactory.getSocketFactory())
1527 .register("https", SSLConnectionSocketFactory.getSystemSocketFactory())
1528 .build();
1529
1530 builder.setConnectionManager(new PoolingHttpClientConnectionManager(csfRegistry));
1531 builder.setDefaultRequestConfig(RequestConfig.custom()
1532 .setSocketTimeout(DEFAULT_TIMEOUT)
1533 .setConnectTimeout(DEFAULT_TIMEOUT)
1534 .setCircularRedirectsAllowed(true)
1535 .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
1536 .build());
1537
1538
1539 builder.setUserAgent("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
1540
1541
1542 builder.setDefaultHeaders(Arrays.asList(new BasicHeader(HttpHeaders.ACCEPT, "*/*")));
1543
1544 if (settings != null && settings.getActiveProxy() != null) {
1545 Proxy activeProxy = settings.getActiveProxy();
1546
1547 ProxyInfo proxyInfo = new ProxyInfo();
1548 proxyInfo.setNonProxyHosts(activeProxy.getNonProxyHosts());
1549
1550 if (StringUtils.isNotEmpty(activeProxy.getHost())
1551 && (url == null || !ProxyUtils.validateNonProxyHosts(proxyInfo, url.getHost()))) {
1552 HttpHost proxy = new HttpHost(activeProxy.getHost(), activeProxy.getPort());
1553 builder.setProxy(proxy);
1554
1555 if (StringUtils.isNotEmpty(activeProxy.getUsername()) && activeProxy.getPassword() != null) {
1556 Credentials credentials =
1557 new UsernamePasswordCredentials(activeProxy.getUsername(), activeProxy.getPassword());
1558
1559 CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
1560 credentialsProvider.setCredentials(AuthScope.ANY, credentials);
1561 builder.setDefaultCredentialsProvider(credentialsProvider);
1562 }
1563 }
1564 }
1565 return builder.build();
1566 }
1567
1568 static boolean equalsIgnoreCase(String value, String... strings) {
1569 for (String s : strings) {
1570 if (s.equalsIgnoreCase(value)) {
1571 return true;
1572 }
1573 }
1574 return false;
1575 }
1576
1577 static boolean equals(String value, String... strings) {
1578 for (String s : strings) {
1579 if (s.equals(value)) {
1580 return true;
1581 }
1582 }
1583 return false;
1584 }
1585 }