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