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