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 }