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