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