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.commons.lang3.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.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.util.DirectoryScanner;
52 import org.codehaus.plexus.util.FileUtils;
53 import org.codehaus.plexus.util.IOUtil;
54 import org.codehaus.plexus.util.Os;
55 import org.codehaus.plexus.util.StringUtils;
56 import org.codehaus.plexus.util.cli.CommandLineException;
57 import org.codehaus.plexus.util.cli.CommandLineUtils;
58 import org.codehaus.plexus.util.cli.Commandline;
59
60 import java.io.BufferedReader;
61 import java.io.ByteArrayOutputStream;
62 import java.io.File;
63 import java.io.FileInputStream;
64 import java.io.FileNotFoundException;
65 import java.io.FileOutputStream;
66 import java.io.IOException;
67 import java.io.InputStream;
68 import java.io.InputStreamReader;
69 import java.io.OutputStream;
70 import java.io.OutputStreamWriter;
71 import java.io.PrintStream;
72 import java.io.UnsupportedEncodingException;
73 import java.lang.reflect.Modifier;
74 import java.net.SocketTimeoutException;
75 import java.net.URL;
76 import java.net.URLClassLoader;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.Collection;
80 import java.util.Collections;
81 import java.util.LinkedHashSet;
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 Collection<String> pruneDirs( MavenProject project, Collection<String> dirs )
121 {
122 Set<String> pruned = new LinkedHashSet<>( 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() )
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( Collection<String> files )
153 {
154 List<String> pruned = new ArrayList<>( 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( Collection<String> sourcePaths, String[] subpackagesList,
197 String[] excludedPackages )
198 {
199 List<String> excludedNames = new ArrayList<>();
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
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(), "," ),
319 null, 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
345 protected static List<String> getIncludedFiles( File sourceDirectory, String[] fileList, String[] excludePackages )
346 {
347 List<String> files = new ArrayList<>();
348
349 for ( String aFileList : fileList )
350 {
351 boolean include = true;
352 for ( int k = 0; k < excludePackages.length && include; k++ )
353 {
354
355 String[] excludeName = excludePackages[k].split( "[*]" );
356
357 if ( excludeName.length == 0 )
358 {
359 continue;
360 }
361
362 if ( excludeName.length > 1 )
363 {
364 int u = 0;
365 while ( include && u < excludeName.length )
366 {
367 if ( !"".equals( excludeName[u].trim() ) && aFileList.contains( excludeName[u] ) )
368 {
369 include = false;
370 }
371 u++;
372 }
373 }
374 else
375 {
376 if ( aFileList.startsWith( sourceDirectory.toString() + File.separatorChar + excludeName[0] ) )
377 {
378 if ( excludeName[0].endsWith( String.valueOf( File.separatorChar ) ) )
379 {
380 int i = aFileList.lastIndexOf( File.separatorChar );
381 String packageName = aFileList.substring( 0, i + 1 );
382 File currentPackage = new File( packageName );
383 File excludedPackage = new File( sourceDirectory, excludeName[0] );
384 if ( currentPackage.equals( excludedPackage )
385 && aFileList.substring( i ).contains( ".java" ) )
386 {
387 include = true;
388 }
389 else
390 {
391 include = false;
392 }
393 }
394 else
395 {
396 include = false;
397 }
398 }
399 }
400 }
401
402 if ( include )
403 {
404 files.add( quotedPathArgument( aFileList ) );
405 }
406 }
407
408 return files;
409 }
410
411
412
413
414
415
416
417
418
419 protected static List<String> getExcludedPackages( String sourceDirectory, String[] excludePackagenames )
420 {
421 List<String> files = new ArrayList<>();
422 for ( String excludePackagename : excludePackagenames )
423 {
424 String[] fileList = FileUtils.getFilesFromExtension( sourceDirectory, new String[] { "java" } );
425 for ( String aFileList : fileList )
426 {
427 String[] excludeName = excludePackagename.split( "[*]" );
428 int u = 0;
429 while ( u < excludeName.length )
430 {
431 if ( !"".equals( excludeName[u].trim() ) && aFileList.contains( excludeName[u] )
432 && !sourceDirectory.contains( excludeName[u] ) )
433 {
434 files.add( aFileList );
435 }
436 u++;
437 }
438 }
439 }
440
441 List<String> excluded = new ArrayList<>();
442 for ( String file : files )
443 {
444 int idx = file.lastIndexOf( File.separatorChar );
445 String tmpStr = file.substring( 0, idx );
446 tmpStr = tmpStr.replace( '\\', '/' );
447 String[] srcSplit = tmpStr.split( Pattern.quote( sourceDirectory.replace( '\\', '/' ) + '/' ) );
448 String excludedPackage = srcSplit[1].replace( '/', '.' );
449
450 if ( !excluded.contains( excludedPackage ) )
451 {
452 excluded.add( excludedPackage );
453 }
454 }
455
456 return excluded;
457 }
458
459
460
461
462
463
464
465
466
467
468 protected static void addFilesFromSource( List<String> files, File sourceDirectory,
469 List<String> sourceFileIncludes,
470 List<String> sourceFileExcludes,
471 String[] excludePackages )
472 {
473 DirectoryScanner ds = new DirectoryScanner();
474 if ( sourceFileIncludes == null )
475 {
476 sourceFileIncludes = Collections.singletonList( "**/*.java" );
477 }
478 ds.setIncludes( sourceFileIncludes.toArray( new String[sourceFileIncludes.size()] ) );
479 if ( sourceFileExcludes != null && sourceFileExcludes.size() > 0 )
480 {
481 ds.setExcludes( sourceFileExcludes.toArray( new String[sourceFileExcludes.size()] ) );
482 }
483 ds.setBasedir( sourceDirectory );
484 ds.scan();
485
486 String[] fileList = ds.getIncludedFiles();
487 String[] pathList = new String[fileList.length];
488 for ( int x = 0; x < fileList.length; x++ )
489 {
490 pathList[x] = new File( sourceDirectory, fileList[x] ).getAbsolutePath();
491 }
492
493
494 if ( pathList.length != 0 )
495 {
496 List<String> tmpFiles = getIncludedFiles( sourceDirectory, pathList, excludePackages );
497 files.addAll( tmpFiles );
498 }
499 }
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515 protected static JavadocVersion getJavadocVersion( File javadocExe )
516 throws IOException, CommandLineException, IllegalArgumentException
517 {
518 if ( ( javadocExe == null ) || ( !javadocExe.exists() ) || ( !javadocExe.isFile() ) )
519 {
520 throw new IOException( "The javadoc executable '" + javadocExe + "' doesn't exist or is not a file. " );
521 }
522
523 Commandline cmd = new Commandline();
524 cmd.setExecutable( javadocExe.getAbsolutePath() );
525 cmd.setWorkingDirectory( javadocExe.getParentFile() );
526 cmd.createArg().setValue( "-J-version" );
527
528 CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
529 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
530
531 int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
532
533 if ( exitCode != 0 )
534 {
535 StringBuilder msg = new StringBuilder( "Exit code: " + exitCode + " - " + err.getOutput() );
536 msg.append( '\n' );
537 msg.append( "Command line was:" + CommandLineUtils.toString( cmd.getCommandline() ) );
538 throw new CommandLineException( msg.toString() );
539 }
540
541 if ( StringUtils.isNotEmpty( err.getOutput() ) )
542 {
543 return JavadocVersion.parse( extractJavadocVersion( err.getOutput() ) );
544 }
545 else if ( StringUtils.isNotEmpty( out.getOutput() ) )
546 {
547 return JavadocVersion.parse( extractJavadocVersion( out.getOutput() ) );
548 }
549
550 throw new IllegalArgumentException( "No output found from the command line 'javadoc -J-version'" );
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
591
592
593
594 protected static String extractJavadocVersion( String output )
595 throws IllegalArgumentException
596 {
597 if ( StringUtils.isEmpty( output ) )
598 {
599 throw new IllegalArgumentException( "The output could not be null." );
600 }
601
602 Pattern pattern = Pattern.compile( "(?s).*?[^a-zA-Z](([0-9]+\\.?[0-9]*)(\\.[0-9]+)?).*" );
603
604 Matcher matcher = pattern.matcher( output );
605 if ( !matcher.matches() )
606 {
607 throw new PatternSyntaxException( "Unrecognized version of Javadoc: '" + output + "'", pattern.pattern(),
608 pattern.toString().length() - 1 );
609 }
610
611 return matcher.group( 1 );
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
639
640
641
642
643 protected static String parseJavadocMemory( String memory )
644 throws IllegalArgumentException
645 {
646 if ( StringUtils.isEmpty( memory ) )
647 {
648 throw new IllegalArgumentException( "The memory could not be null." );
649 }
650
651 Pattern p = Pattern.compile( "^\\s*(\\d+)\\s*?\\s*$" );
652 Matcher m = p.matcher( memory );
653 if ( m.matches() )
654 {
655 return m.group( 1 ) + "m";
656 }
657
658 p = Pattern.compile( "^\\s*(\\d+)\\s*k(b)?\\s*$", Pattern.CASE_INSENSITIVE );
659 m = p.matcher( memory );
660 if ( m.matches() )
661 {
662 return m.group( 1 ) + "k";
663 }
664
665 p = Pattern.compile( "^\\s*(\\d+)\\s*m(b)?\\s*$", Pattern.CASE_INSENSITIVE );
666 m = p.matcher( memory );
667 if ( m.matches() )
668 {
669 return m.group( 1 ) + "m";
670 }
671
672 p = Pattern.compile( "^\\s*(\\d+)\\s*g(b)?\\s*$", Pattern.CASE_INSENSITIVE );
673 m = p.matcher( memory );
674 if ( m.matches() )
675 {
676 return ( Integer.parseInt( m.group( 1 ) ) * 1024 ) + "m";
677 }
678
679 p = Pattern.compile( "^\\s*(\\d+)\\s*t(b)?\\s*$", Pattern.CASE_INSENSITIVE );
680 m = p.matcher( memory );
681 if ( m.matches() )
682 {
683 return ( Integer.parseInt( m.group( 1 ) ) * 1024 * 1024 ) + "m";
684 }
685
686 throw new IllegalArgumentException( "Could convert not to a memory size: " + memory );
687 }
688
689
690
691
692
693
694
695 protected static boolean validateEncoding( String charsetName )
696 {
697 if ( StringUtils.isEmpty( charsetName ) )
698 {
699 return false;
700 }
701
702 OutputStream ost = new ByteArrayOutputStream();
703 OutputStreamWriter osw = null;
704 try
705 {
706 osw = new OutputStreamWriter( ost, charsetName );
707 osw.close();
708 osw = null;
709 }
710 catch ( IOException exc )
711 {
712 return false;
713 }
714 finally
715 {
716 IOUtil.close( osw );
717 }
718
719 return true;
720 }
721
722
723
724
725
726
727
728
729
730 protected static String hideProxyPassword( String cmdLine, Settings settings )
731 {
732 if ( cmdLine == null )
733 {
734 throw new IllegalArgumentException( "cmdLine could not be null" );
735 }
736
737 if ( settings == null )
738 {
739 return cmdLine;
740 }
741
742 Proxy activeProxy = settings.getActiveProxy();
743 if ( activeProxy != null && StringUtils.isNotEmpty( activeProxy.getHost() )
744 && StringUtils.isNotEmpty( activeProxy.getUsername() )
745 && StringUtils.isNotEmpty( activeProxy.getPassword() ) )
746 {
747 String pass = "-J-Dhttp.proxyPassword=\"" + activeProxy.getPassword() + "\"";
748 String hidepass =
749 "-J-Dhttp.proxyPassword=\"" + StringUtils.repeat( "*", activeProxy.getPassword().length() ) + "\"";
750
751 return StringUtils.replace( cmdLine, pass, hidepass );
752 }
753
754 return cmdLine;
755 }
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771 protected static List<String> getTagletClassNames( File jarFile )
772 throws IOException, ClassNotFoundException, NoClassDefFoundError
773 {
774 List<String> classes = getClassNamesFromJar( jarFile );
775 ClassLoader cl;
776
777
778 File tools = new File( System.getProperty( "java.home" ), "../lib/tools.jar" );
779 if ( tools.exists() && tools.isFile() )
780 {
781 cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL(), tools.toURI().toURL() }, null );
782 }
783 else
784 {
785 cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL() }, ClassLoader.getSystemClassLoader() );
786 }
787
788 List<String> tagletClasses = new ArrayList<>();
789
790 Class<?> tagletClass;
791
792 try
793 {
794 tagletClass = cl.loadClass( "com.sun.tools.doclets.Taglet" );
795 }
796 catch ( ClassNotFoundException e )
797 {
798 tagletClass = cl.loadClass( "jdk.javadoc.doclet.Taglet" );
799 }
800
801 for ( String s : classes )
802 {
803 Class<?> c = cl.loadClass( s );
804
805 if ( tagletClass.isAssignableFrom( c ) && !Modifier.isAbstract( c.getModifiers() ) )
806 {
807 tagletClasses.add( c.getName() );
808 }
809 }
810
811 return tagletClasses;
812 }
813
814
815
816
817
818
819
820
821
822 protected static void copyResource( URL url, File file )
823 throws IOException
824 {
825 if ( file == null )
826 {
827 throw new IOException( "The file can't be null." );
828 }
829 if ( url == null )
830 {
831 throw new IOException( "The url could not be null." );
832 }
833
834 if ( !file.getParentFile().exists() )
835 {
836 file.getParentFile().mkdirs();
837 }
838
839 InputStream in = null;
840 OutputStream out = null;
841 try
842 {
843 in = url.openStream();
844
845 if ( in == null )
846 {
847 throw new IOException( "The resource " + url + " doesn't exists." );
848 }
849
850 out = new FileOutputStream( file );
851
852 IOUtil.copy( in, out );
853
854 out.close();
855 out = null;
856 in.close();
857 in = null;
858 }
859 finally
860 {
861 IOUtil.close( in );
862 IOUtil.close( out );
863 }
864 }
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882 protected static void invokeMaven( Log log, File localRepositoryDir, File projectFile, List<String> goals,
883 Properties properties, File invokerLog )
884 throws MavenInvocationException
885 {
886 if ( projectFile == null )
887 {
888 throw new IllegalArgumentException( "projectFile should be not null." );
889 }
890 if ( !projectFile.isFile() )
891 {
892 throw new IllegalArgumentException( projectFile.getAbsolutePath() + " is not a file." );
893 }
894 if ( goals == null || goals.size() == 0 )
895 {
896 throw new IllegalArgumentException( "goals should be not empty." );
897 }
898 if ( localRepositoryDir == null || !localRepositoryDir.isDirectory() )
899 {
900 throw new IllegalArgumentException( "localRepositoryDir '" + localRepositoryDir
901 + "' should be a directory." );
902 }
903
904 String mavenHome = getMavenHome( log );
905 if ( StringUtils.isEmpty( mavenHome ) )
906 {
907 String msg =
908 "Could NOT invoke Maven because no Maven Home is defined. You need to have set the M2_HOME "
909 + "system env variable or a maven.home Java system properties.";
910 if ( log != null )
911 {
912 log.error( msg );
913 }
914 else
915 {
916 System.err.println( msg );
917 }
918 return;
919 }
920
921 Invoker invoker = new DefaultInvoker();
922 invoker.setMavenHome( new File( mavenHome ) );
923 invoker.setLocalRepositoryDirectory( localRepositoryDir );
924
925 InvocationRequest request = new DefaultInvocationRequest();
926 request.setBaseDirectory( projectFile.getParentFile() );
927 request.setPomFile( projectFile );
928 if ( log != null )
929 {
930 request.setDebug( log.isDebugEnabled() );
931 }
932 else
933 {
934 request.setDebug( true );
935 }
936 request.setGoals( goals );
937 if ( properties != null )
938 {
939 request.setProperties( properties );
940 }
941 File javaHome = getJavaHome( log );
942 if ( javaHome != null )
943 {
944 request.setJavaHome( javaHome );
945 }
946
947 if ( log != null && log.isDebugEnabled() )
948 {
949 log.debug( "Invoking Maven for the goals: " + goals + " with "
950 + ( properties == null ? "no properties" : "properties=" + properties ) );
951 }
952 InvocationResult result = invoke( log, invoker, request, invokerLog, goals, properties, null );
953
954 if ( result.getExitCode() != 0 )
955 {
956 String invokerLogContent = readFile( invokerLog, "UTF-8" );
957
958
959 if ( invokerLogContent != null
960 && ( !invokerLogContent.contains( "Scanning for projects..." )
961 || invokerLogContent.contains( OutOfMemoryError.class.getName() ) ) )
962 {
963 if ( log != null )
964 {
965 log.error( "Error occurred during initialization of VM, trying to use an empty MAVEN_OPTS..." );
966
967 if ( log.isDebugEnabled() )
968 {
969 log.debug( "Reinvoking Maven for the goals: " + goals + " with an empty MAVEN_OPTS..." );
970 }
971 }
972 result = invoke( log, invoker, request, invokerLog, goals, properties, "" );
973 }
974 }
975
976 if ( result.getExitCode() != 0 )
977 {
978 String invokerLogContent = readFile( invokerLog, "UTF-8" );
979
980
981 if ( invokerLogContent != null
982 && ( !invokerLogContent.contains( "Scanning for projects..." )
983 || invokerLogContent.contains( OutOfMemoryError.class.getName() ) ) )
984 {
985 throw new MavenInvocationException( ERROR_INIT_VM );
986 }
987
988 throw new MavenInvocationException( "Error when invoking Maven, consult the invoker log file: "
989 + invokerLog.getAbsolutePath() );
990 }
991 }
992
993
994
995
996
997
998
999
1000
1001
1002 protected static String readFile( final File javaFile, final String encoding )
1003 {
1004 try
1005 {
1006 return FileUtils.fileRead( javaFile, encoding );
1007 }
1008 catch ( IOException e )
1009 {
1010 return null;
1011 }
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029 protected static String[] splitPath( final String path )
1030 {
1031 if ( path == null )
1032 {
1033 return null;
1034 }
1035
1036 List<String> subpaths = new ArrayList<>();
1037 PathTokenizer pathTokenizer = new PathTokenizer( path );
1038 while ( pathTokenizer.hasMoreTokens() )
1039 {
1040 subpaths.add( pathTokenizer.nextToken() );
1041 }
1042
1043 return subpaths.toArray( new String[subpaths.size()] );
1044 }
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062 protected static String unifyPathSeparator( final String path )
1063 {
1064 if ( path == null )
1065 {
1066 return null;
1067 }
1068
1069 return StringUtils.join( splitPath( path ), File.pathSeparator );
1070 }
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081 private static List<String> getClassNamesFromJar( File jarFile )
1082 throws IOException
1083 {
1084 if ( jarFile == null || !jarFile.exists() || !jarFile.isFile() )
1085 {
1086 throw new IOException( "The jar '" + jarFile + "' doesn't exist or is not a file." );
1087 }
1088
1089 List<String> classes = new ArrayList<>();
1090 JarInputStream jarStream = null;
1091
1092 try
1093 {
1094 jarStream = new JarInputStream( new FileInputStream( jarFile ) );
1095
1096 for ( JarEntry jarEntry = jarStream.getNextJarEntry(); jarEntry != null;
1097 jarEntry = jarStream.getNextJarEntry() )
1098 {
1099 if ( jarEntry.getName().toLowerCase( Locale.ENGLISH ).endsWith( ".class" ) )
1100 {
1101 String name = jarEntry.getName().substring( 0, jarEntry.getName().indexOf( "." ) );
1102
1103 classes.add( name.replaceAll( "/", "\\." ) );
1104 }
1105
1106 jarStream.closeEntry();
1107 }
1108
1109 jarStream.close();
1110 jarStream = null;
1111 }
1112 finally
1113 {
1114 IOUtil.close( jarStream );
1115 }
1116
1117 return classes;
1118 }
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132 private static InvocationResult invoke( Log log, Invoker invoker, InvocationRequest request, File invokerLog,
1133 List<String> goals, Properties properties, String mavenOpts )
1134 throws MavenInvocationException
1135 {
1136 PrintStream ps;
1137 OutputStream os = null;
1138 if ( invokerLog != null )
1139 {
1140 if ( log != null && log.isDebugEnabled() )
1141 {
1142 log.debug( "Using " + invokerLog.getAbsolutePath() + " to log the invoker" );
1143 }
1144
1145 try
1146 {
1147 if ( !invokerLog.exists() )
1148 {
1149
1150 invokerLog.getParentFile().mkdirs();
1151 }
1152 os = new FileOutputStream( invokerLog );
1153 ps = new PrintStream( os, true, "UTF-8" );
1154 }
1155 catch ( FileNotFoundException e )
1156 {
1157 if ( log != null && log.isErrorEnabled() )
1158 {
1159 log.error( "FileNotFoundException: " + e.getMessage() + ". Using System.out to log the invoker." );
1160 }
1161 ps = System.out;
1162 }
1163 catch ( UnsupportedEncodingException e )
1164 {
1165 if ( log != null && log.isErrorEnabled() )
1166 {
1167 log.error( "UnsupportedEncodingException: " + e.getMessage()
1168 + ". Using System.out to log the invoker." );
1169 }
1170 ps = System.out;
1171 }
1172 }
1173 else
1174 {
1175 if ( log != null && log.isDebugEnabled() )
1176 {
1177 log.debug( "Using System.out to log the invoker." );
1178 }
1179
1180 ps = System.out;
1181 }
1182
1183 if ( mavenOpts != null )
1184 {
1185 request.setMavenOpts( mavenOpts );
1186 }
1187
1188 InvocationOutputHandler outputHandler = new PrintStreamHandler( ps, false );
1189 request.setOutputHandler( outputHandler );
1190
1191 outputHandler.consumeLine( "Invoking Maven for the goals: " + goals + " with "
1192 + ( properties == null ? "no properties" : "properties=" + properties ) );
1193 outputHandler.consumeLine( "" );
1194 outputHandler.consumeLine( "M2_HOME=" + getMavenHome( log ) );
1195 outputHandler.consumeLine( "MAVEN_OPTS=" + getMavenOpts( log ) );
1196 outputHandler.consumeLine( "JAVA_HOME=" + getJavaHome( log ) );
1197 outputHandler.consumeLine( "JAVA_OPTS=" + getJavaOpts( log ) );
1198 outputHandler.consumeLine( "" );
1199
1200 try
1201 {
1202 return invoker.execute( request );
1203 }
1204 finally
1205 {
1206 IOUtil.close( os );
1207 }
1208 }
1209
1210
1211
1212
1213
1214
1215
1216 private static String getMavenHome( Log log )
1217 {
1218 String mavenHome = System.getProperty( "maven.home" );
1219 if ( mavenHome == null )
1220 {
1221 try
1222 {
1223 mavenHome = CommandLineUtils.getSystemEnvVars().getProperty( "M2_HOME" );
1224 }
1225 catch ( IOException e )
1226 {
1227 if ( log != null && log.isDebugEnabled() )
1228 {
1229 log.debug( "IOException: " + e.getMessage() );
1230 }
1231 }
1232 }
1233
1234 File m2Home = new File( mavenHome );
1235 if ( !m2Home.exists() )
1236 {
1237 if ( log != null && log.isErrorEnabled() )
1238 {
1239 log
1240 .error( "Cannot find Maven application directory. Either specify \'maven.home\' system property, or "
1241 + "M2_HOME environment variable." );
1242 }
1243 }
1244
1245 return mavenHome;
1246 }
1247
1248
1249
1250
1251
1252
1253 private static String getMavenOpts( Log log )
1254 {
1255 String mavenOpts = null;
1256 try
1257 {
1258 mavenOpts = CommandLineUtils.getSystemEnvVars().getProperty( "MAVEN_OPTS" );
1259 }
1260 catch ( IOException e )
1261 {
1262 if ( log != null && log.isDebugEnabled() )
1263 {
1264 log.debug( "IOException: " + e.getMessage() );
1265 }
1266 }
1267
1268 return mavenOpts;
1269 }
1270
1271
1272
1273
1274
1275
1276
1277
1278 private static File getJavaHome( Log log )
1279 {
1280 File javaHome = null;
1281
1282
1283
1284 if ( System.getProperty( "maven.home" ) == null )
1285 {
1286
1287 if ( ( SystemUtils.IS_OS_MAC_OSX
1288 || JavadocVersion.parse( SystemUtils.JAVA_VERSION ).compareTo( JavadocVersion.parse( "9" ) ) >= 0 ) )
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 try
1301 {
1302 javaHome = new File( CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_HOME" ) );
1303 }
1304 catch ( IOException e )
1305 {
1306 if ( log != null && log.isDebugEnabled() )
1307 {
1308 log.debug( "IOException: " + e.getMessage() );
1309 }
1310 }
1311 }
1312
1313 if ( javaHome == null || !javaHome.exists() )
1314 {
1315 if ( log != null && log.isErrorEnabled() )
1316 {
1317 log.error( "Cannot find Java application directory. Either specify \'java.home\' system property, or "
1318 + "JAVA_HOME environment variable." );
1319 }
1320 }
1321
1322 return javaHome;
1323 }
1324
1325
1326
1327
1328
1329
1330 private static String getJavaOpts( Log log )
1331 {
1332 String javaOpts = null;
1333 try
1334 {
1335 javaOpts = CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_OPTS" );
1336 }
1337 catch ( IOException e )
1338 {
1339 if ( log != null && log.isDebugEnabled() )
1340 {
1341 log.debug( "IOException: " + e.getMessage() );
1342 }
1343 }
1344
1345 return javaOpts;
1346 }
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358 private static class PathTokenizer
1359 {
1360
1361
1362
1363 private StringTokenizer tokenizer;
1364
1365
1366
1367
1368
1369 private String lookahead = null;
1370
1371
1372
1373
1374
1375
1376 private boolean onNetWare = Os.isFamily( "netware" );
1377
1378
1379
1380
1381
1382 private boolean dosStyleFilesystem;
1383
1384
1385
1386
1387
1388
1389 public PathTokenizer( String path )
1390 {
1391 if ( onNetWare )
1392 {
1393
1394
1395 tokenizer = new StringTokenizer( path, ":;", true );
1396 }
1397 else
1398 {
1399
1400
1401 tokenizer = new StringTokenizer( path, ":;", false );
1402 }
1403 dosStyleFilesystem = File.pathSeparatorChar == ';';
1404 }
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414 public boolean hasMoreTokens()
1415 {
1416 return lookahead != null || tokenizer.hasMoreTokens();
1417
1418 }
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428 public String nextToken()
1429 throws NoSuchElementException
1430 {
1431 String token;
1432 if ( lookahead != null )
1433 {
1434 token = lookahead;
1435 lookahead = null;
1436 }
1437 else
1438 {
1439 token = tokenizer.nextToken().trim();
1440 }
1441
1442 if ( !onNetWare )
1443 {
1444 if ( token.length() == 1 && Character.isLetter( token.charAt( 0 ) ) && dosStyleFilesystem
1445 && tokenizer.hasMoreTokens() )
1446 {
1447
1448
1449 String nextToken = tokenizer.nextToken().trim();
1450 if ( nextToken.startsWith( "\\" ) || nextToken.startsWith( "/" ) )
1451 {
1452
1453
1454
1455 token += ":" + nextToken;
1456 }
1457 else
1458 {
1459
1460 lookahead = nextToken;
1461 }
1462 }
1463 }
1464 else
1465 {
1466
1467
1468 if ( token.equals( File.pathSeparator ) || token.equals( ":" ) )
1469 {
1470
1471 token = tokenizer.nextToken().trim();
1472 }
1473
1474 if ( tokenizer.hasMoreTokens() )
1475 {
1476
1477 String nextToken = tokenizer.nextToken().trim();
1478
1479
1480 if ( !nextToken.equals( File.pathSeparator ) )
1481 {
1482 if ( nextToken.equals( ":" ) )
1483 {
1484 if ( !token.startsWith( "/" ) && !token.startsWith( "\\" ) && !token.startsWith( "." )
1485 && !token.startsWith( ".." ) )
1486 {
1487
1488 String oneMore = tokenizer.nextToken().trim();
1489 if ( !oneMore.equals( File.pathSeparator ) )
1490 {
1491 token += ":" + oneMore;
1492 }
1493 else
1494 {
1495 token += ":";
1496 lookahead = oneMore;
1497 }
1498 }
1499
1500
1501 }
1502 else
1503 {
1504
1505 lookahead = nextToken;
1506 }
1507 }
1508 }
1509 }
1510 return token;
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 public static boolean isNotEmpty( final Collection<?> collection )
1609 {
1610 return collection != null && !collection.isEmpty();
1611 }
1612
1613
1614
1615
1616 public static boolean isEmpty( final Collection<?> collection )
1617 {
1618 return collection == null || collection.isEmpty();
1619 }
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638 protected static boolean isValidPackageList( URL url, Settings settings, boolean validateContent )
1639 throws IOException
1640 {
1641 if ( url == null )
1642 {
1643 throw new IllegalArgumentException( "The url is null" );
1644 }
1645
1646 BufferedReader reader = null;
1647 HttpGet httpMethod = null;
1648 HttpClient httpClient = null;
1649
1650 try
1651 {
1652 if ( "file".equals( url.getProtocol() ) )
1653 {
1654
1655 reader = new BufferedReader( new InputStreamReader( url.openStream() ) );
1656 }
1657 else
1658 {
1659
1660 httpClient = createHttpClient( settings, url );
1661
1662 httpMethod = new HttpGet( url.toString() );
1663 HttpResponse response;
1664 try
1665 {
1666 response = httpClient.execute( httpMethod );
1667 }
1668 catch ( SocketTimeoutException e )
1669 {
1670
1671 response = httpClient.execute( httpMethod );
1672 }
1673
1674 int status = response.getStatusLine().getStatusCode();
1675 if ( status != HttpStatus.SC_OK )
1676 {
1677 throw new FileNotFoundException(
1678 "Unexpected HTTP status code " + status + " getting resource " + url.toExternalForm() + "." );
1679 }
1680
1681
1682 reader = new BufferedReader( new InputStreamReader( response.getEntity().getContent() ) );
1683 }
1684
1685 if ( validateContent )
1686 {
1687 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
1688 {
1689 if ( !isValidPackageName( line ) )
1690 {
1691 return false;
1692 }
1693 }
1694 }
1695
1696 reader.close();
1697 reader = null;
1698
1699 return true;
1700 }
1701 finally
1702 {
1703 IOUtil.close( reader );
1704
1705 if ( httpMethod != null )
1706 {
1707 httpMethod.releaseConnection();
1708 }
1709 if ( httpClient != null )
1710 {
1711 httpClient.getConnectionManager().shutdown();
1712 }
1713 }
1714 }
1715
1716 private static boolean isValidPackageName( String str )
1717 {
1718 if ( StringUtils.isEmpty( str ) )
1719 {
1720
1721 return true;
1722 }
1723
1724 int idx;
1725 while ( ( idx = str.indexOf( '.' ) ) != -1 )
1726 {
1727 if ( !isValidClassName( str.substring( 0, idx ) ) )
1728 {
1729 return false;
1730 }
1731
1732 str = str.substring( idx + 1 );
1733 }
1734
1735 return isValidClassName( str );
1736 }
1737
1738 private static boolean isValidClassName( String str )
1739 {
1740 if ( StringUtils.isEmpty( str ) || !Character.isJavaIdentifierStart( str.charAt( 0 ) ) )
1741 {
1742 return false;
1743 }
1744
1745 for ( int i = str.length() - 1; i > 0; i-- )
1746 {
1747 if ( !Character.isJavaIdentifierPart( str.charAt( i ) ) )
1748 {
1749 return false;
1750 }
1751 }
1752
1753 return true;
1754 }
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767 private static HttpClient createHttpClient( Settings settings, URL url )
1768 {
1769 DefaultHttpClient httpClient = new DefaultHttpClient( new PoolingClientConnectionManager() );
1770 httpClient.getParams().setIntParameter( CoreConnectionPNames.SO_TIMEOUT, DEFAULT_TIMEOUT );
1771 httpClient.getParams().setIntParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, DEFAULT_TIMEOUT );
1772 httpClient.getParams().setBooleanParameter( ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true );
1773
1774
1775 httpClient.getParams().setParameter( CoreProtocolPNames.USER_AGENT,
1776 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" );
1777
1778 if ( settings != null && settings.getActiveProxy() != null )
1779 {
1780 Proxy activeProxy = settings.getActiveProxy();
1781
1782 ProxyInfo proxyInfo = new ProxyInfo();
1783 proxyInfo.setNonProxyHosts( activeProxy.getNonProxyHosts() );
1784
1785 if ( StringUtils.isNotEmpty( activeProxy.getHost() )
1786 && ( url == null || !ProxyUtils.validateNonProxyHosts( proxyInfo, url.getHost() ) ) )
1787 {
1788 HttpHost proxy = new HttpHost( activeProxy.getHost(), activeProxy.getPort() );
1789 httpClient.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, proxy );
1790
1791 if ( StringUtils.isNotEmpty( activeProxy.getUsername() ) && activeProxy.getPassword() != null )
1792 {
1793 Credentials credentials =
1794 new UsernamePasswordCredentials( activeProxy.getUsername(), activeProxy.getPassword() );
1795
1796 httpClient.getCredentialsProvider().setCredentials( AuthScope.ANY, credentials );
1797 }
1798 }
1799 }
1800
1801 return httpClient;
1802 }
1803
1804 static boolean equalsIgnoreCase( String value, String... strings )
1805 {
1806 for ( String s : strings )
1807 {
1808 if ( s.equalsIgnoreCase( value ) )
1809 {
1810 return true;
1811 }
1812 }
1813 return false;
1814 }
1815
1816 static boolean equals( String value, String... strings )
1817 {
1818 for ( String s : strings )
1819 {
1820 if ( s.equals( value ) )
1821 {
1822 return true;
1823 }
1824 }
1825 return false;
1826 }
1827 }