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