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