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 }