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