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