1 package org.apache.maven.plugins.invoker;
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.BufferedWriter;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStreamWriter;
31 import java.io.Reader;
32 import java.io.Writer;
33 import java.text.DecimalFormat;
34 import java.text.DecimalFormatSymbols;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.LinkedHashMap;
41 import java.util.LinkedHashSet;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Locale;
45 import java.util.Map;
46 import java.util.Map.Entry;
47 import java.util.Properties;
48 import java.util.StringTokenizer;
49 import java.util.TreeSet;
50 import java.util.concurrent.ExecutorService;
51 import java.util.concurrent.Executors;
52 import java.util.concurrent.TimeUnit;
53 import org.apache.maven.artifact.Artifact;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.model.Profile;
56 import org.apache.maven.plugin.AbstractMojo;
57 import org.apache.maven.plugin.MojoExecution;
58 import org.apache.maven.plugin.MojoExecutionException;
59 import org.apache.maven.plugin.MojoFailureException;
60 import org.apache.maven.plugins.invoker.model.BuildJob;
61 import org.apache.maven.plugins.invoker.model.io.xpp3.BuildJobXpp3Writer;
62 import org.apache.maven.plugins.annotations.Component;
63 import org.apache.maven.plugins.annotations.Parameter;
64 import org.apache.maven.project.MavenProject;
65 import org.apache.maven.settings.Settings;
66 import org.apache.maven.settings.SettingsUtils;
67 import org.apache.maven.settings.TrackableBase;
68 import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
69 import org.apache.maven.settings.building.SettingsBuilder;
70 import org.apache.maven.settings.building.SettingsBuildingException;
71 import org.apache.maven.settings.building.SettingsBuildingRequest;
72 import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
73 import org.apache.maven.shared.invoker.CommandLineConfigurationException;
74 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
75 import org.apache.maven.shared.invoker.InvocationRequest;
76 import org.apache.maven.shared.invoker.InvocationResult;
77 import org.apache.maven.shared.invoker.Invoker;
78 import org.apache.maven.shared.invoker.MavenCommandLineBuilder;
79 import org.apache.maven.shared.invoker.MavenInvocationException;
80 import org.apache.maven.shared.scriptinterpreter.RunErrorException;
81 import org.apache.maven.shared.scriptinterpreter.RunFailureException;
82 import org.apache.maven.shared.scriptinterpreter.ScriptRunner;
83 import org.apache.maven.shared.utils.logging.MessageBuilder;
84 import org.codehaus.plexus.interpolation.InterpolationException;
85 import org.codehaus.plexus.interpolation.Interpolator;
86 import org.codehaus.plexus.interpolation.MapBasedValueSource;
87 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
88 import org.codehaus.plexus.util.DirectoryScanner;
89 import org.codehaus.plexus.util.FileUtils;
90 import org.codehaus.plexus.util.IOUtil;
91 import org.codehaus.plexus.util.InterpolationFilterReader;
92 import org.codehaus.plexus.util.ReaderFactory;
93 import org.codehaus.plexus.util.ReflectionUtils;
94 import org.codehaus.plexus.util.StringUtils;
95 import org.codehaus.plexus.util.WriterFactory;
96 import org.codehaus.plexus.util.cli.CommandLineException;
97 import org.codehaus.plexus.util.cli.CommandLineUtils;
98 import org.codehaus.plexus.util.cli.Commandline;
99 import org.codehaus.plexus.util.cli.StreamConsumer;
100 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
101
102
103
104
105
106
107
108 public abstract class AbstractInvokerMojo
109 extends AbstractMojo
110 {
111
112
113
114 private static final int RESULT_COLUMN = 60;
115
116
117
118
119
120
121 @Parameter( property = "invoker.skip", defaultValue = "false" )
122 private boolean skipInvocation;
123
124
125
126
127
128
129
130 @Parameter( defaultValue = "false" )
131 protected boolean suppressSummaries;
132
133
134
135
136 @Parameter( property = "invoker.streamLogs", defaultValue = "false" )
137 private boolean streamLogs;
138
139
140
141
142
143
144 @Parameter( property = "invoker.localRepositoryPath", defaultValue = "${settings.localRepository}" )
145 private File localRepositoryPath;
146
147
148
149
150 @Parameter( property = "invoker.projectsDirectory", defaultValue = "${basedir}/src/it/" )
151 private File projectsDirectory;
152
153
154
155
156
157
158
159
160 @Parameter( property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports" )
161 private File reportsDirectory;
162
163
164
165
166
167
168 @Parameter( property = "invoker.disableReports", defaultValue = "false" )
169 private boolean disableReports;
170
171
172
173
174
175
176
177
178 @Parameter( defaultValue = "${project.build.directory}/its" )
179 private File cloneProjectsTo;
180
181
182
183
184
185
186
187
188
189
190
191
192 @Parameter( defaultValue = "false" )
193 private boolean cloneAllFiles;
194
195
196
197
198
199
200
201 @Parameter( defaultValue = "true" )
202 private boolean cloneClean;
203
204
205
206
207 @Parameter( property = "invoker.pom" )
208 private File pom;
209
210
211
212
213
214
215
216
217
218
219
220 @Parameter
221 private List<String> pomIncludes = Collections.singletonList( "*/pom.xml" );
222
223
224
225
226
227
228 @Parameter
229 private List<String> pomExcludes = Collections.emptyList();
230
231
232
233
234
235
236
237
238
239
240 @Parameter
241 private List<String> setupIncludes = Collections.singletonList( "setup*/pom.xml" );
242
243
244
245
246 @Parameter
247 private List<String> goals = Collections.singletonList( "package" );
248
249
250
251 @Component
252 private Invoker invoker;
253
254 @Component
255 private SettingsBuilder settingsBuilder;
256
257
258
259
260
261
262
263
264
265
266
267
268
269 @Parameter( property = "invoker.selectorScript", defaultValue = "selector" )
270 private String selectorScript;
271
272
273
274
275
276
277
278
279
280 @Parameter( property = "invoker.preBuildHookScript", defaultValue = "prebuild" )
281 private String preBuildHookScript;
282
283
284
285
286
287
288
289
290 @Parameter( property = "invoker.postBuildHookScript", defaultValue = "postbuild" )
291 private String postBuildHookScript;
292
293
294
295
296 @Parameter( property = "invoker.testPropertiesFile", defaultValue = "test.properties" )
297 private String testPropertiesFile;
298
299
300
301
302
303
304 @Parameter
305 private Map<String, String> properties;
306
307
308
309
310 @Parameter( property = "invoker.showErrors", defaultValue = "false" )
311 private boolean showErrors;
312
313
314
315
316 @Parameter( property = "invoker.debug", defaultValue = "false" )
317 private boolean debug;
318
319
320
321
322 @Parameter( property = "invoker.noLog", defaultValue = "false" )
323 private boolean noLog;
324
325
326
327
328
329
330 @Parameter
331 private List<String> profiles;
332
333
334
335
336
337
338 @Parameter
339 private Map<String, String> filterProperties;
340
341
342
343
344
345
346 @Parameter( defaultValue = "${project}", readonly = true, required = true )
347 private MavenProject project;
348
349 @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true )
350 private MojoExecution mojoExecution;
351
352
353
354
355
356
357
358
359
360
361
362
363 @Parameter( property = "invoker.test" )
364 private String invokerTest;
365
366
367
368
369
370
371
372
373 @Parameter( property = "invoker.settingsFile" )
374 private File settingsFile;
375
376
377
378
379
380
381
382 @Parameter( property = "invoker.mavenOpts" )
383 private String mavenOpts;
384
385
386
387
388
389
390
391 @Parameter( property = "invoker.mavenHome" )
392 private File mavenHome;
393
394
395
396
397
398
399
400 @Parameter( property = "invoker.mavenExecutable" )
401 private String mavenExecutable;
402
403
404
405
406
407
408
409 @Parameter( property = "invoker.javaHome" )
410 private File javaHome;
411
412
413
414
415
416
417 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
418 private String encoding;
419
420
421
422
423
424
425 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
426 private Settings settings;
427
428
429
430
431
432
433
434
435
436
437 @Parameter( property = "invoker.addTestClassPath", defaultValue = "false" )
438 private boolean addTestClassPath;
439
440
441
442
443 @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true )
444 private List<String> testClassPath;
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535 @Parameter( property = "invoker.invokerPropertiesFile", defaultValue = "invoker.properties" )
536 private String invokerPropertiesFile;
537
538
539
540
541
542
543 @Parameter( property = "invoker.showVersion", defaultValue = "false" )
544 private boolean showVersion;
545
546
547
548
549
550
551 @Parameter( property = "invoker.parallelThreads", defaultValue = "1" )
552 private int parallelThreads;
553
554
555
556
557 @Parameter( property = "plugin.artifacts", required = true, readonly = true )
558 private List<Artifact> pluginArtifacts;
559
560
561
562
563
564
565
566 @Parameter( property = "invoker.mergeUserSettings", defaultValue = "false" )
567 private boolean mergeUserSettings;
568
569
570
571
572
573
574 @Parameter
575 private Map<String, String> environmentVariables;
576
577
578
579
580
581
582 @Parameter
583 private Map<String, String> scriptVariables;
584
585
586
587
588 private ScriptRunner scriptRunner;
589
590
591
592
593
594
595 private String filteredPomPrefix = "interpolated-";
596
597
598
599
600 private final DecimalFormat secFormat = new DecimalFormat( "(0.0 s)", new DecimalFormatSymbols( Locale.ENGLISH ) );
601
602
603
604
605 private String actualMavenVersion;
606
607
608
609
610
611
612
613 public void execute()
614 throws MojoExecutionException, MojoFailureException
615 {
616 if ( skipInvocation )
617 {
618 getLog().info( "Skipping invocation per configuration."
619 + " If this is incorrect, ensure the skipInvocation parameter is not set to true." );
620 return;
621 }
622
623 if ( StringUtils.isEmpty( encoding ) )
624 {
625 getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
626 + ", i.e. build is platform dependent!" );
627 }
628
629
630 if ( !disableReports )
631 {
632 setupReportsFolder();
633 }
634
635 BuildJob[] buildJobs;
636 if ( pom == null )
637 {
638 try
639 {
640 buildJobs = getBuildJobs();
641 }
642 catch ( final IOException e )
643 {
644 throw new MojoExecutionException( "Error retrieving POM list from includes, "
645 + "excludes, and projects directory. Reason: " + e.getMessage(), e );
646 }
647 }
648 else
649 {
650 try
651 {
652 projectsDirectory = pom.getCanonicalFile().getParentFile();
653 }
654 catch ( IOException e )
655 {
656 throw new MojoExecutionException( "Failed to discover projectsDirectory from "
657 + "pom File parameter. Reason: " + e.getMessage(), e );
658 }
659
660 buildJobs = new BuildJob[] { new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) };
661 }
662
663 if ( ( buildJobs == null ) || ( buildJobs.length < 1 ) )
664 {
665 doFailIfNoProjects();
666
667 getLog().info( "No projects were selected for execution." );
668 return;
669 }
670
671 handleScriptRunnerWithScriptClassPath();
672
673 Collection<String> collectedProjects = new LinkedHashSet<String>();
674 for ( BuildJob buildJob : buildJobs )
675 {
676 collectProjects( projectsDirectory, buildJob.getProject(), collectedProjects, true );
677 }
678
679 File projectsDir = projectsDirectory;
680
681 if ( cloneProjectsTo != null )
682 {
683 cloneProjects( collectedProjects );
684 projectsDir = cloneProjectsTo;
685 }
686 else
687 {
688 getLog().warn( "Filtering of parent/child POMs is not supported without cloning the projects" );
689 }
690
691
692 BuildJob[] setupBuildJobs = null;
693 try
694 {
695 setupBuildJobs = getSetupBuildJobsFromFolders();
696 }
697 catch ( IOException e )
698 {
699 getLog().error( "Failure during scanning of folders.", e );
700
701 }
702
703 if ( setupBuildJobs != null )
704 {
705
706
707
708
709 getLog().info( "Running Setup Jobs" );
710 runBuilds( projectsDir, setupBuildJobs, 1 );
711 }
712
713
714 BuildJob[] nonSetupBuildJobs = getNonSetupJobs( buildJobs );
715
716
717 runBuilds( projectsDir, nonSetupBuildJobs, parallelThreads );
718
719 writeSummaryFile( nonSetupBuildJobs );
720
721 processResults( new InvokerSession( nonSetupBuildJobs ) );
722
723 }
724
725
726
727
728
729
730 private void setupReportsFolder()
731 throws MojoExecutionException
732 {
733
734 if ( reportsDirectory.exists() )
735 {
736 try
737 {
738 FileUtils.deleteDirectory( reportsDirectory );
739 }
740 catch ( IOException e )
741 {
742 throw new MojoExecutionException( "Failure while trying to delete "
743 + reportsDirectory.getAbsolutePath(), e );
744 }
745 }
746 if ( !reportsDirectory.mkdirs() )
747 {
748 throw new MojoExecutionException( "Failure while creating the " + reportsDirectory.getAbsolutePath() );
749 }
750 }
751
752 private BuildJob[] getNonSetupJobs( BuildJob[] buildJobs )
753 {
754 List<BuildJob> result = new LinkedList<BuildJob>();
755 for ( int i = 0; i < buildJobs.length; i++ )
756 {
757 if ( !buildJobs[i].getType().equals( BuildJob.Type.SETUP ) )
758 {
759 result.add( buildJobs[i] );
760 }
761 }
762 BuildJob[] buildNonSetupJobs = result.toArray( new BuildJob[result.size()] );
763 return buildNonSetupJobs;
764 }
765
766 private void handleScriptRunnerWithScriptClassPath()
767 {
768 final List<String> scriptClassPath;
769 if ( addTestClassPath )
770 {
771 scriptClassPath = new ArrayList<String>( testClassPath );
772 for ( Artifact pluginArtifact : pluginArtifacts )
773 {
774 scriptClassPath.remove( pluginArtifact.getFile().getAbsolutePath() );
775 }
776 }
777 else
778 {
779 scriptClassPath = null;
780 }
781 scriptRunner = new ScriptRunner( getLog() );
782 scriptRunner.setScriptEncoding( encoding );
783 scriptRunner.setGlobalVariable( "localRepositoryPath", localRepositoryPath );
784 if ( scriptVariables != null )
785 {
786 for ( Entry<String, String> entry : scriptVariables.entrySet() )
787 {
788 scriptRunner.setGlobalVariable( entry.getKey(), entry.getValue() );
789 }
790 }
791 scriptRunner.setClassPath( scriptClassPath );
792 }
793
794 private void writeSummaryFile( BuildJob[] buildJobs )
795 throws MojoExecutionException
796 {
797
798 File summaryReportFile = new File( reportsDirectory, "invoker-summary.txt" );
799
800 try
801 {
802 Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) );
803
804 for ( int i = 0; i < buildJobs.length; i++ )
805 {
806 BuildJob buildJob = buildJobs[i];
807 if ( !buildJob.getResult().equals( BuildJob.Result.SUCCESS ) )
808 {
809 writer.append( buildJob.getResult() );
810 writer.append( " [" );
811 writer.append( buildJob.getProject() );
812 writer.append( "] " );
813 if ( buildJob.getFailureMessage() != null )
814 {
815 writer.append( " " );
816 writer.append( buildJob.getFailureMessage() );
817 }
818 writer.append( "\n" );
819 }
820 }
821
822 writer.close();
823 }
824 catch ( IOException e )
825 {
826 throw new MojoExecutionException( "Failed to write summary report " + summaryReportFile, e );
827 }
828 }
829
830 protected void doFailIfNoProjects()
831 throws MojoFailureException
832 {
833
834 }
835
836
837
838
839
840
841
842
843 abstract void processResults( InvokerSession invokerSession )
844 throws MojoFailureException;
845
846
847
848
849
850
851
852
853 private Reader newReader( File file )
854 throws IOException
855 {
856 if ( StringUtils.isNotEmpty( encoding ) )
857 {
858 return ReaderFactory.newReader( file, encoding );
859 }
860 else
861 {
862 return ReaderFactory.newPlatformReader( file );
863 }
864 }
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880 private void collectProjects( File projectsDir, String projectPath, Collection<String> projectPaths,
881 boolean included )
882 throws MojoExecutionException
883 {
884 projectPath = projectPath.replace( '\\', '/' );
885 File pomFile = new File( projectsDir, projectPath );
886 if ( pomFile.isDirectory() )
887 {
888 pomFile = new File( pomFile, "pom.xml" );
889 if ( !pomFile.exists() )
890 {
891 if ( included )
892 {
893 projectPaths.add( projectPath );
894 }
895 return;
896 }
897 if ( !projectPath.endsWith( "/" ) )
898 {
899 projectPath += '/';
900 }
901 projectPath += "pom.xml";
902 }
903 else if ( !pomFile.isFile() )
904 {
905 return;
906 }
907 if ( !projectPaths.add( projectPath ) )
908 {
909 return;
910 }
911 getLog().debug( "Collecting parent/child projects of " + projectPath );
912
913 Model model = PomUtils.loadPom( pomFile );
914
915 try
916 {
917 String projectsRoot = projectsDir.getCanonicalPath();
918 String projectDir = pomFile.getParent();
919
920 String parentPath = "../pom.xml";
921 if ( model.getParent() != null && StringUtils.isNotEmpty( model.getParent().getRelativePath() ) )
922 {
923 parentPath = model.getParent().getRelativePath();
924 }
925 String parent = relativizePath( new File( projectDir, parentPath ), projectsRoot );
926 if ( parent != null )
927 {
928 collectProjects( projectsDir, parent, projectPaths, false );
929 }
930
931 Collection<String> modulePaths = new LinkedHashSet<String>();
932
933 modulePaths.addAll( model.getModules() );
934
935 for ( Profile profile : model.getProfiles() )
936 {
937 modulePaths.addAll( profile.getModules() );
938 }
939
940 for ( String modulePath : modulePaths )
941 {
942 String module = relativizePath( new File( projectDir, modulePath ), projectsRoot );
943 if ( module != null )
944 {
945 collectProjects( projectsDir, module, projectPaths, false );
946 }
947 }
948 }
949 catch ( IOException e )
950 {
951 throw new MojoExecutionException( "Failed to analyze POM: " + pomFile, e );
952 }
953 }
954
955
956
957
958
959
960
961
962
963 private void cloneProjects( Collection<String> projectPaths )
964 throws MojoExecutionException
965 {
966 if ( !cloneProjectsTo.mkdirs() && cloneClean )
967 {
968 try
969 {
970 FileUtils.cleanDirectory( cloneProjectsTo );
971 }
972 catch ( IOException e )
973 {
974 throw new MojoExecutionException( "Could not clean the cloneProjectsTo directory. Reason: "
975 + e.getMessage(), e );
976 }
977 }
978
979
980 Collection<String> dirs = new LinkedHashSet<String>();
981 for ( String projectPath : projectPaths )
982 {
983 if ( !new File( projectsDirectory, projectPath ).isDirectory() )
984 {
985 projectPath = getParentPath( projectPath );
986 }
987 dirs.add( projectPath );
988 }
989
990 boolean filter;
991
992
993 try
994 {
995 filter = !cloneProjectsTo.getCanonicalFile().equals( projectsDirectory.getCanonicalFile() );
996
997 List<String> clonedSubpaths = new ArrayList<String>();
998
999 for ( String subpath : dirs )
1000 {
1001
1002 if ( !".".equals( subpath ) && dirs.contains( getParentPath( subpath ) ) )
1003 {
1004 continue;
1005 }
1006
1007
1008 if ( !alreadyCloned( subpath, clonedSubpaths ) )
1009 {
1010
1011 if ( ".".equals( subpath ) )
1012 {
1013 String cloneSubdir = relativizePath( cloneProjectsTo, projectsDirectory.getCanonicalPath() );
1014
1015
1016 if ( cloneSubdir != null )
1017 {
1018 File temp = File.createTempFile( "pre-invocation-clone.", "" );
1019 temp.delete();
1020 temp.mkdirs();
1021
1022 copyDirectoryStructure( projectsDirectory, temp );
1023
1024 FileUtils.deleteDirectory( new File( temp, cloneSubdir ) );
1025
1026 copyDirectoryStructure( temp, cloneProjectsTo );
1027 }
1028 else
1029 {
1030 copyDirectoryStructure( projectsDirectory, cloneProjectsTo );
1031 }
1032 }
1033 else
1034 {
1035 File srcDir = new File( projectsDirectory, subpath );
1036 File dstDir = new File( cloneProjectsTo, subpath );
1037 copyDirectoryStructure( srcDir, dstDir );
1038 }
1039
1040 clonedSubpaths.add( subpath );
1041 }
1042 }
1043 }
1044 catch ( IOException e )
1045 {
1046 throw new MojoExecutionException( "Failed to clone projects from: " + projectsDirectory + " to: "
1047 + cloneProjectsTo + ". Reason: " + e.getMessage(), e );
1048 }
1049
1050
1051 if ( filter )
1052 {
1053 for ( String projectPath : projectPaths )
1054 {
1055 File pomFile = new File( cloneProjectsTo, projectPath );
1056 if ( pomFile.isFile() )
1057 {
1058 buildInterpolatedFile( pomFile, pomFile );
1059 }
1060
1061
1062
1063
1064 File baseDir = pomFile.getParentFile();
1065 File mvnDir = new File( baseDir, ".mvn" );
1066 if ( mvnDir.isDirectory() )
1067 {
1068 File extensionsFile = new File( mvnDir, "extensions.xml" );
1069 if ( extensionsFile.isFile() )
1070 {
1071 buildInterpolatedFile( extensionsFile, extensionsFile );
1072 }
1073 }
1074
1075 }
1076 filteredPomPrefix = null;
1077 }
1078 }
1079
1080
1081
1082
1083
1084
1085
1086 private String getParentPath( String path )
1087 {
1088 int lastSep = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) );
1089 return ( lastSep < 0 ) ? "." : path.substring( 0, lastSep );
1090 }
1091
1092
1093
1094
1095
1096
1097
1098
1099 private void copyDirectoryStructure( File sourceDir, File destDir )
1100 throws IOException
1101 {
1102 DirectoryScanner scanner = new DirectoryScanner();
1103 scanner.setBasedir( sourceDir );
1104 if ( !cloneAllFiles )
1105 {
1106 scanner.addDefaultExcludes();
1107 }
1108 scanner.scan();
1109
1110
1111
1112
1113 destDir.mkdirs();
1114
1115 FileUtils.mkDirs( sourceDir, scanner.getIncludedDirectories(), destDir );
1116
1117 for ( String includedFile : scanner.getIncludedFiles() )
1118 {
1119 File sourceFile = new File( sourceDir, includedFile );
1120 File destFile = new File( destDir, includedFile );
1121 FileUtils.copyFile( sourceFile, destFile );
1122
1123
1124 destFile.setWritable( true );
1125 }
1126 }
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137 static boolean alreadyCloned( String subpath, List<String> clonedSubpaths )
1138 {
1139 for ( String path : clonedSubpaths )
1140 {
1141 if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) )
1142 {
1143 return true;
1144 }
1145 }
1146
1147 return false;
1148 }
1149
1150
1151
1152
1153
1154
1155
1156
1157 private void runBuilds( final File projectsDir, BuildJob[] buildJobs, int runWithParallelThreads )
1158 throws MojoExecutionException
1159 {
1160 if ( !localRepositoryPath.exists() )
1161 {
1162 localRepositoryPath.mkdirs();
1163 }
1164
1165
1166
1167
1168
1169 File interpolatedSettingsFile = interpolateSettings( settingsFile );
1170
1171 final File mergedSettingsFile = mergeSettings( interpolatedSettingsFile );
1172
1173 if ( mavenHome != null )
1174 {
1175 actualMavenVersion = SelectorUtils.getMavenVersion( mavenHome );
1176 }
1177 else
1178 {
1179 actualMavenVersion = SelectorUtils.getMavenVersion();
1180 }
1181 scriptRunner.setGlobalVariable( "mavenVersion", actualMavenVersion );
1182
1183 final CharSequence actualJreVersion;
1184
1185 if ( javaHome != null )
1186 {
1187 actualJreVersion = resolveExternalJreVersion();
1188 }
1189 else
1190 {
1191 actualJreVersion = SelectorUtils.getJreVersion();
1192 }
1193
1194 try
1195 {
1196 if ( runWithParallelThreads > 1 )
1197 {
1198 getLog().info( "use parallelThreads " + runWithParallelThreads );
1199
1200 ExecutorService executorService = Executors.newFixedThreadPool( runWithParallelThreads );
1201 for ( final BuildJob job : buildJobs )
1202 {
1203 executorService.execute( new Runnable()
1204 {
1205 public void run()
1206 {
1207 try
1208 {
1209 runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion );
1210 }
1211 catch ( MojoExecutionException e )
1212 {
1213 throw new RuntimeException( e.getMessage(), e );
1214 }
1215 }
1216 } );
1217 }
1218
1219 try
1220 {
1221 executorService.shutdown();
1222
1223 executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
1224 }
1225 catch ( InterruptedException e )
1226 {
1227 throw new MojoExecutionException( e.getMessage(), e );
1228 }
1229 }
1230 else
1231 {
1232 for ( BuildJob job : buildJobs )
1233 {
1234 runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion );
1235 }
1236 }
1237 }
1238 finally
1239 {
1240 if ( interpolatedSettingsFile != null && cloneProjectsTo == null )
1241 {
1242 interpolatedSettingsFile.delete();
1243 }
1244 if ( mergedSettingsFile != null && mergedSettingsFile.exists() )
1245 {
1246 mergedSettingsFile.delete();
1247 }
1248 }
1249 }
1250
1251
1252
1253
1254
1255
1256
1257
1258 private File interpolateSettings( File settingsFile )
1259 throws MojoExecutionException
1260 {
1261 File interpolatedSettingsFile = null;
1262 if ( settingsFile != null )
1263 {
1264 if ( cloneProjectsTo != null )
1265 {
1266 interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() );
1267 }
1268 else
1269 {
1270 interpolatedSettingsFile =
1271 new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() );
1272 }
1273 buildInterpolatedFile( settingsFile, interpolatedSettingsFile );
1274 }
1275 return interpolatedSettingsFile;
1276 }
1277
1278
1279
1280
1281
1282
1283
1284
1285 private File mergeSettings( File interpolatedSettingsFile )
1286 throws MojoExecutionException
1287 {
1288 File mergedSettingsFile;
1289 Settings mergedSettings = this.settings;
1290 if ( mergeUserSettings )
1291 {
1292 if ( interpolatedSettingsFile != null )
1293 {
1294
1295 try
1296 {
1297 SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
1298 request.setGlobalSettingsFile( interpolatedSettingsFile );
1299
1300 Settings dominantSettings = settingsBuilder.build( request ).getEffectiveSettings();
1301 Settings recessiveSettings = cloneSettings();
1302 SettingsUtils.merge( dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL );
1303
1304 mergedSettings = dominantSettings;
1305 getLog().debug( "Merged specified settings file with settings of invoking process" );
1306 }
1307 catch ( SettingsBuildingException e )
1308 {
1309 throw new MojoExecutionException( "Could not read specified settings file", e );
1310 }
1311 }
1312 }
1313
1314 if ( this.settingsFile != null && !mergeUserSettings )
1315 {
1316 mergedSettingsFile = interpolatedSettingsFile;
1317 }
1318 else
1319 {
1320 try
1321 {
1322 mergedSettingsFile = writeMergedSettingsFile( mergedSettings );
1323 }
1324 catch ( IOException e )
1325 {
1326 throw new MojoExecutionException( "Could not create temporary file for invoker settings.xml", e );
1327 }
1328 }
1329 return mergedSettingsFile;
1330 }
1331
1332 private File writeMergedSettingsFile( Settings mergedSettings )
1333 throws IOException
1334 {
1335 File mergedSettingsFile;
1336 mergedSettingsFile = File.createTempFile( "invoker-settings", ".xml" );
1337
1338 SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
1339
1340 FileWriter fileWriter = null;
1341 try
1342 {
1343 fileWriter = new FileWriter( mergedSettingsFile );
1344 settingsWriter.write( fileWriter, mergedSettings );
1345 fileWriter.close();
1346 fileWriter = null;
1347 }
1348 finally
1349 {
1350 IOUtil.close( fileWriter );
1351 }
1352
1353 if ( getLog().isDebugEnabled() )
1354 {
1355 getLog().debug( "Created temporary file for invoker settings.xml: "
1356 + mergedSettingsFile.getAbsolutePath() );
1357 }
1358 return mergedSettingsFile;
1359 }
1360
1361 private Settings cloneSettings()
1362 {
1363 Settings recessiveSettings = SettingsUtils.copySettings( this.settings );
1364
1365
1366 resetSourceLevelSet( recessiveSettings );
1367 for ( org.apache.maven.settings.Mirror mirror : recessiveSettings.getMirrors() )
1368 {
1369 resetSourceLevelSet( mirror );
1370 }
1371 for ( org.apache.maven.settings.Server server : recessiveSettings.getServers() )
1372 {
1373 resetSourceLevelSet( server );
1374 }
1375 for ( org.apache.maven.settings.Proxy proxy : recessiveSettings.getProxies() )
1376 {
1377 resetSourceLevelSet( proxy );
1378 }
1379 for ( org.apache.maven.settings.Profile profile : recessiveSettings.getProfiles() )
1380 {
1381 resetSourceLevelSet( profile );
1382 }
1383
1384 return recessiveSettings;
1385 }
1386
1387 private void resetSourceLevelSet( org.apache.maven.settings.TrackableBase trackable )
1388 {
1389 try
1390 {
1391 ReflectionUtils.setVariableValueInObject( trackable, "sourceLevelSet", Boolean.FALSE );
1392 getLog().debug( "sourceLevelSet: "
1393 + ReflectionUtils.getValueIncludingSuperclasses( "sourceLevelSet", trackable ) );
1394 }
1395 catch ( IllegalAccessException e )
1396 {
1397
1398 }
1399 }
1400
1401 private CharSequence resolveExternalJreVersion()
1402 {
1403 Artifact pluginArtifact = mojoExecution.getMojoDescriptor().getPluginDescriptor().getPluginArtifact();
1404 pluginArtifact.getFile();
1405
1406 Commandline commandLine = new Commandline();
1407 commandLine.setExecutable( new File( javaHome, "bin/java" ).getAbsolutePath() );
1408 commandLine.createArg().setValue( "-cp" );
1409 commandLine.createArg().setFile( pluginArtifact.getFile() );
1410 commandLine.createArg().setValue( SystemPropertyPrinter.class.getName() );
1411 commandLine.createArg().setValue( "java.version" );
1412
1413 final StringBuilder actualJreVersion = new StringBuilder();
1414 StreamConsumer consumer = new StreamConsumer()
1415 {
1416 public void consumeLine( String line )
1417 {
1418 actualJreVersion.append( line );
1419 }
1420 };
1421 try
1422 {
1423 CommandLineUtils.executeCommandLine( commandLine, consumer, null );
1424 }
1425 catch ( CommandLineException e )
1426 {
1427 getLog().warn( e.getMessage() );
1428 }
1429 return actualJreVersion;
1430 }
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441 private File interpolatePomFile( File pomFile, File basedir )
1442 throws MojoExecutionException
1443 {
1444 File interpolatedPomFile = null;
1445 if ( pomFile != null )
1446 {
1447 if ( StringUtils.isNotEmpty( filteredPomPrefix ) )
1448 {
1449 interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() );
1450 buildInterpolatedFile( pomFile, interpolatedPomFile );
1451 }
1452 else
1453 {
1454 interpolatedPomFile = pomFile;
1455 }
1456 }
1457 return interpolatedPomFile;
1458 }
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469 private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome,
1470 CharSequence actualJreVersion )
1471 throws MojoExecutionException
1472 {
1473
1474 File pomFile = new File( projectsDir, buildJob.getProject() );
1475 File basedir;
1476 if ( pomFile.isDirectory() )
1477 {
1478 basedir = pomFile;
1479 pomFile = new File( basedir, "pom.xml" );
1480 if ( !pomFile.exists() )
1481 {
1482 pomFile = null;
1483 }
1484 else
1485 {
1486 buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" );
1487 }
1488 }
1489 else
1490 {
1491 basedir = pomFile.getParentFile();
1492 }
1493
1494 File interpolatedPomFile = interpolatePomFile( pomFile, basedir );
1495
1496
1497 getLog().info( buffer().a( "Building: " ).strong( buildJob.getProject() ).toString() );
1498
1499 InvokerProperties invokerProperties = getInvokerProperties( basedir );
1500
1501
1502 buildJob.setName( invokerProperties.getJobName() );
1503 buildJob.setDescription( invokerProperties.getJobDescription() );
1504
1505 try
1506 {
1507 int selection = getSelection( invokerProperties, actualJreVersion );
1508 if ( selection == 0 )
1509 {
1510 long milliseconds = System.currentTimeMillis();
1511 boolean executed;
1512 try
1513 {
1514
1515 executed =
1516 runBuild( basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties );
1517
1518 }
1519 finally
1520 {
1521 milliseconds = System.currentTimeMillis() - milliseconds;
1522 buildJob.setTime( milliseconds / 1000.0 );
1523 }
1524
1525 if ( executed )
1526 {
1527 buildJob.setResult( BuildJob.Result.SUCCESS );
1528
1529 if ( !suppressSummaries )
1530 {
1531 getLog().info( pad( buildJob ).success( "SUCCESS" ).a( ' ' )
1532 + formatTime( buildJob.getTime() ) );
1533 }
1534 }
1535 else
1536 {
1537 buildJob.setResult( BuildJob.Result.SKIPPED );
1538
1539 if ( !suppressSummaries )
1540 {
1541 getLog().info( pad( buildJob ).warning( "SKIPPED" ).a( ' ' )
1542 + formatTime( buildJob.getTime() ) );
1543 }
1544 }
1545 }
1546 else
1547 {
1548 buildJob.setResult( BuildJob.Result.SKIPPED );
1549
1550 StringBuilder message = new StringBuilder();
1551 if ( selection == Selector.SELECTOR_MULTI )
1552 {
1553 message.append( "non-matching selectors" );
1554 }
1555 else
1556 {
1557 if ( ( selection & Selector.SELECTOR_MAVENVERSION ) != 0 )
1558 {
1559 message.append( "Maven version" );
1560 }
1561 if ( ( selection & Selector.SELECTOR_JREVERSION ) != 0 )
1562 {
1563 if ( message.length() > 0 )
1564 {
1565 message.append( ", " );
1566 }
1567 message.append( "JRE version" );
1568 }
1569 if ( ( selection & Selector.SELECTOR_OSFAMILY ) != 0 )
1570 {
1571 if ( message.length() > 0 )
1572 {
1573 message.append( ", " );
1574 }
1575 message.append( "OS" );
1576 }
1577 }
1578
1579 if ( !suppressSummaries )
1580 {
1581 getLog().info( pad( buildJob ).warning( "SKIPPED" ) + " due to " + message.toString() );
1582 }
1583
1584
1585
1586 buildJob.setFailureMessage( "Skipped due to " + message.toString() );
1587 }
1588 }
1589 catch ( RunErrorException e )
1590 {
1591 buildJob.setResult( BuildJob.Result.ERROR );
1592 buildJob.setFailureMessage( e.getMessage() );
1593
1594 if ( !suppressSummaries )
1595 {
1596 getLog().info( pad( buildJob ).failure( "ERROR" ).a( ' ' ) + formatTime( buildJob.getTime() ) );
1597 getLog().info( " " + e.getMessage() );
1598 }
1599 }
1600 catch ( RunFailureException e )
1601 {
1602 buildJob.setResult( e.getType() );
1603 buildJob.setFailureMessage( e.getMessage() );
1604
1605 if ( !suppressSummaries )
1606 {
1607 getLog().info( pad( buildJob ).failure( "FAILED" ).a( ' ' ) + formatTime( buildJob.getTime() ) );
1608 getLog().info( " " + e.getMessage() );
1609 }
1610 }
1611 finally
1612 {
1613 deleteInterpolatedPomFile( interpolatedPomFile );
1614 writeBuildReport( buildJob );
1615 }
1616 }
1617
1618 private MessageBuilder pad( BuildJob buildJob )
1619 {
1620 MessageBuilder buffer = buffer( 128 );
1621
1622 buffer.a( " " );
1623 buffer.a( buildJob.getProject() );
1624
1625 int l = 10 + buildJob.getProject().length();
1626
1627 if ( l < RESULT_COLUMN )
1628 {
1629 buffer.a( ' ' );
1630 l++;
1631
1632 if ( l < RESULT_COLUMN )
1633 {
1634 for ( int i = RESULT_COLUMN - l; i > 0; i-- )
1635 {
1636 buffer.a( '.' );
1637 }
1638 }
1639 }
1640
1641 return buffer.a( ' ' );
1642 }
1643
1644
1645
1646
1647
1648
1649 private void deleteInterpolatedPomFile( File interpolatedPomFile )
1650 {
1651 if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) )
1652 {
1653 interpolatedPomFile.delete();
1654 }
1655 }
1656
1657
1658
1659
1660
1661
1662
1663
1664 private int getSelection( InvokerProperties invokerProperties, CharSequence actualJreVersion )
1665 {
1666 return new Selector( actualMavenVersion, actualJreVersion.toString() ).getSelection( invokerProperties );
1667 }
1668
1669
1670
1671
1672
1673
1674
1675 private void writeBuildReport( BuildJob buildJob )
1676 throws MojoExecutionException
1677 {
1678 if ( disableReports )
1679 {
1680 return;
1681 }
1682
1683 String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' );
1684 if ( safeFileName.endsWith( "_pom.xml" ) )
1685 {
1686 safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() );
1687 }
1688
1689 File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );
1690 try
1691 {
1692 FileOutputStream fos = new FileOutputStream( reportFile );
1693 try
1694 {
1695 Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() );
1696 BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
1697 writer.write( osw, buildJob );
1698 osw.close();
1699 }
1700 finally
1701 {
1702 fos.close();
1703 }
1704 }
1705 catch ( IOException e )
1706 {
1707 throw new MojoExecutionException( "Failed to write build report " + reportFile, e );
1708 }
1709 }
1710
1711
1712
1713
1714
1715
1716
1717 private String formatTime( double seconds )
1718 {
1719 return secFormat.format( seconds );
1720 }
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736 private boolean runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome,
1737 InvokerProperties invokerProperties )
1738 throws MojoExecutionException, RunFailureException
1739 {
1740 if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() )
1741 {
1742 Properties props = invokerProperties.getProperties();
1743 getLog().debug( "Using invoker properties:" );
1744 for ( String key : new TreeSet<String>( props.stringPropertyNames() ) )
1745 {
1746 String value = props.getProperty( key );
1747 getLog().debug( " " + key + " = " + value );
1748 }
1749 }
1750
1751 List<String> goals = getGoals( basedir );
1752
1753 List<String> profiles = getProfiles( basedir );
1754
1755 Map<String, Object> context = new LinkedHashMap<String, Object>();
1756
1757 FileLogger logger = setupBuildLogFile( basedir );
1758 boolean selectorResult = true;
1759 try
1760 {
1761 try
1762 {
1763 scriptRunner.run( "selector script", basedir, selectorScript, context, logger, BuildJob.Result.SKIPPED,
1764 false );
1765 }
1766 catch ( RunErrorException e )
1767 {
1768 selectorResult = false;
1769 throw e;
1770 }
1771 catch ( RunFailureException e )
1772 {
1773 selectorResult = false;
1774 return false;
1775 }
1776
1777 scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger,
1778 BuildJob.Result.FAILURE_PRE_HOOK, false );
1779
1780 final InvocationRequest request = new DefaultInvocationRequest();
1781
1782 request.setLocalRepositoryDirectory( localRepositoryPath );
1783
1784 request.setBatchMode( true );
1785
1786 request.setShowErrors( showErrors );
1787
1788 request.setDebug( debug );
1789
1790 request.setShowVersion( showVersion );
1791
1792 setupLoggerForBuildJob( logger, request );
1793
1794 if ( mavenHome != null )
1795 {
1796 invoker.setMavenHome( mavenHome );
1797
1798 request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() );
1799 }
1800
1801 if ( mavenExecutable != null )
1802 {
1803 invoker.setMavenExecutable( new File( mavenExecutable ) );
1804 }
1805
1806 if ( actualJavaHome != null )
1807 {
1808 request.setJavaHome( actualJavaHome );
1809 }
1810
1811 if ( environmentVariables != null )
1812 {
1813 for ( Map.Entry<String, String> variable : environmentVariables.entrySet() )
1814 {
1815 request.addShellEnvironment( variable.getKey(), variable.getValue() );
1816 }
1817 }
1818
1819 for ( int invocationIndex = 1;; invocationIndex++ )
1820 {
1821 if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) )
1822 {
1823 break;
1824 }
1825
1826 request.setBaseDirectory( basedir );
1827
1828 request.setPomFile( pomFile );
1829
1830 request.setGoals( goals );
1831
1832 request.setProfiles( profiles );
1833
1834 request.setMavenOpts( mavenOpts );
1835
1836 request.setOffline( false );
1837
1838 String customSettingsFile = invokerProperties.getSettingsFile( invocationIndex );
1839 if ( customSettingsFile != null )
1840 {
1841 File interpolateSettingsFile = interpolateSettings( new File( customSettingsFile ) );
1842 File mergeSettingsFile = mergeSettings( interpolateSettingsFile );
1843
1844 request.setUserSettingsFile( mergeSettingsFile );
1845 }
1846 else
1847 {
1848 request.setUserSettingsFile( settingsFile );
1849 }
1850
1851 Properties systemProperties =
1852 getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) );
1853 request.setProperties( systemProperties );
1854
1855 invokerProperties.configureInvocation( request, invocationIndex );
1856
1857 if ( getLog().isDebugEnabled() )
1858 {
1859 try
1860 {
1861 getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() );
1862 getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) );
1863 }
1864 catch ( CommandLineConfigurationException e )
1865 {
1866 getLog().debug( "Failed to display command line: " + e.getMessage() );
1867 }
1868 }
1869
1870 InvocationResult result;
1871
1872 try
1873 {
1874 result = invoker.execute( request );
1875 }
1876 catch ( final MavenInvocationException e )
1877 {
1878 getLog().debug( "Error invoking Maven: " + e.getMessage(), e );
1879 throw new RunFailureException( "Maven invocation failed. " + e.getMessage(),
1880 BuildJob.Result.FAILURE_BUILD );
1881 }
1882 verify( result, invocationIndex, invokerProperties, logger );
1883 }
1884 }
1885 catch ( IOException e )
1886 {
1887 throw new MojoExecutionException( e.getMessage(), e );
1888 }
1889 finally
1890 {
1891 if ( selectorResult )
1892 {
1893 runPostBuildHook( basedir, context, logger );
1894 }
1895 if ( logger != null )
1896 {
1897 logger.close();
1898 }
1899 }
1900 return true;
1901 }
1902
1903 private void runPostBuildHook( File basedir, Map<String, Object> context, FileLogger logger )
1904 throws MojoExecutionException, RunFailureException
1905 {
1906 try
1907 {
1908 scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger,
1909 BuildJob.Result.FAILURE_POST_HOOK, true );
1910 }
1911 catch ( IOException e )
1912 {
1913 throw new MojoExecutionException( e.getMessage(), e );
1914 }
1915 }
1916 private void setupLoggerForBuildJob( FileLogger logger, final InvocationRequest request )
1917 {
1918 if ( logger != null )
1919 {
1920 request.setErrorHandler( logger );
1921
1922 request.setOutputHandler( logger );
1923 }
1924 }
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934 private FileLogger setupBuildLogFile( File basedir )
1935 throws MojoExecutionException
1936 {
1937 FileLogger logger = null;
1938
1939 if ( !noLog )
1940 {
1941 File outputLog = new File( basedir, "build.log" );
1942 try
1943 {
1944 if ( streamLogs )
1945 {
1946 logger = new FileLogger( outputLog, getLog() );
1947 }
1948 else
1949 {
1950 logger = new FileLogger( outputLog );
1951 }
1952
1953 getLog().debug( "Build log initialized in: " + outputLog );
1954 }
1955 catch ( IOException e )
1956 {
1957 throw new MojoExecutionException( "Error initializing build logfile in: " + outputLog, e );
1958 }
1959 }
1960
1961 return logger;
1962 }
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973 private Properties getSystemProperties( final File basedir, final String filename )
1974 throws MojoExecutionException
1975 {
1976 Properties collectedTestProperties = new Properties();
1977
1978 if ( properties != null )
1979 {
1980
1981 for ( Map.Entry<String, String> entry : properties.entrySet() )
1982 {
1983 if ( entry.getValue() != null )
1984 {
1985 collectedTestProperties.put( entry.getKey(), entry.getValue() );
1986 }
1987 }
1988 }
1989
1990 File propertiesFile = null;
1991 if ( filename != null )
1992 {
1993 propertiesFile = new File( basedir, filename );
1994 }
1995 else if ( testPropertiesFile != null )
1996 {
1997 propertiesFile = new File( basedir, testPropertiesFile );
1998 }
1999
2000 if ( propertiesFile != null && propertiesFile.isFile() )
2001 {
2002 InputStream fin = null;
2003 try
2004 {
2005 fin = new FileInputStream( propertiesFile );
2006
2007 Properties loadedProperties = new Properties();
2008 loadedProperties.load( fin );
2009 fin.close();
2010 fin = null;
2011 collectedTestProperties.putAll( loadedProperties );
2012 }
2013 catch ( IOException e )
2014 {
2015 throw new MojoExecutionException( "Error reading system properties from " + propertiesFile );
2016 }
2017 finally
2018 {
2019 IOUtil.close( fin );
2020 }
2021 }
2022
2023 return collectedTestProperties;
2024 }
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036 private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties,
2037 FileLogger logger )
2038 throws RunFailureException
2039 {
2040 if ( result.getExecutionException() != null )
2041 {
2042 throw new RunFailureException( "The Maven invocation failed. "
2043 + result.getExecutionException().getMessage(), BuildJob.Result.ERROR );
2044 }
2045 else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) )
2046 {
2047 StringBuilder buffer = new StringBuilder( 256 );
2048 buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " );
2049 if ( logger != null )
2050 {
2051 buffer.append( "See " );
2052 buffer.append( logger.getOutputFile().getAbsolutePath() );
2053 buffer.append( " for details." );
2054 }
2055 else
2056 {
2057 buffer.append( "See console output for details." );
2058 }
2059 throw new RunFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD );
2060 }
2061 }
2062
2063
2064
2065
2066
2067
2068
2069
2070 List<String> getGoals( final File basedir )
2071 throws MojoExecutionException
2072 {
2073 try
2074 {
2075
2076
2077 return getTokens( basedir, null, goals );
2078 }
2079 catch ( IOException e )
2080 {
2081 throw new MojoExecutionException( "error reading goals", e );
2082 }
2083 }
2084
2085
2086
2087
2088
2089
2090
2091
2092 List<String> getProfiles( File basedir )
2093 throws MojoExecutionException
2094 {
2095 try
2096 {
2097 return getTokens( basedir, null, profiles );
2098 }
2099 catch ( IOException e )
2100 {
2101 throw new MojoExecutionException( "error reading profiles", e );
2102 }
2103 }
2104
2105 private List<String> calculateExcludes()
2106 throws IOException
2107 {
2108 List<String> excludes =
2109 ( pomExcludes != null ) ? new ArrayList<String>( pomExcludes ) : new ArrayList<String>();
2110 if ( this.settingsFile != null )
2111 {
2112 String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() );
2113 if ( exclude != null )
2114 {
2115 excludes.add( exclude.replace( '\\', '/' ) );
2116 getLog().debug( "Automatically excluded " + exclude + " from project scanning" );
2117 }
2118 }
2119 return excludes;
2120
2121 }
2122
2123
2124
2125
2126
2127
2128 private BuildJob[] getSetupBuildJobsFromFolders()
2129 throws IOException
2130 {
2131 List<String> excludes = calculateExcludes();
2132
2133 BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
2134 if ( getLog().isDebugEnabled() )
2135 {
2136 getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) );
2137 }
2138
2139 return setupPoms;
2140 }
2141
2142
2143
2144
2145
2146
2147
2148 BuildJob[] getBuildJobs()
2149 throws IOException
2150 {
2151 BuildJob[] buildJobs;
2152
2153 if ( invokerTest == null )
2154 {
2155 List<String> excludes = calculateExcludes();
2156
2157 BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
2158 if ( getLog().isDebugEnabled() )
2159 {
2160 getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) );
2161 }
2162
2163 BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL );
2164
2165 Map<String, BuildJob> uniquePoms = new LinkedHashMap<String, BuildJob>();
2166 for ( BuildJob setupPom : setupPoms )
2167 {
2168 uniquePoms.put( setupPom.getProject(), setupPom );
2169 }
2170 for ( BuildJob normalPom : normalPoms )
2171 {
2172 if ( !uniquePoms.containsKey( normalPom.getProject() ) )
2173 {
2174 uniquePoms.put( normalPom.getProject(), normalPom );
2175 }
2176 }
2177
2178 buildJobs = uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] );
2179 }
2180 else
2181 {
2182 String[] testRegexes = StringUtils.split( invokerTest, "," );
2183 List<String> includes = new ArrayList<String>( testRegexes.length );
2184 List<String> excludes = new ArrayList<String>();
2185
2186 for ( String regex : testRegexes )
2187 {
2188
2189 if ( regex.startsWith( "!" ) )
2190 {
2191 excludes.add( regex.substring( 1 ) );
2192 }
2193 else
2194 {
2195 includes.add( regex );
2196 }
2197 }
2198
2199
2200
2201 buildJobs = scanProjectsDirectory( includes, excludes, BuildJob.Type.DIRECT );
2202 }
2203
2204 relativizeProjectPaths( buildJobs );
2205
2206 return buildJobs;
2207 }
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221 private BuildJob[] scanProjectsDirectory( List<String> includes, List<String> excludes, String type )
2222 throws IOException
2223 {
2224 if ( !projectsDirectory.isDirectory() )
2225 {
2226 return new BuildJob[0];
2227 }
2228
2229 DirectoryScanner scanner = new DirectoryScanner();
2230 scanner.setBasedir( projectsDirectory.getCanonicalFile() );
2231 scanner.setFollowSymlinks( false );
2232 if ( includes != null )
2233 {
2234 scanner.setIncludes( includes.toArray( new String[includes.size()] ) );
2235 }
2236 if ( excludes != null )
2237 {
2238 scanner.setExcludes( excludes.toArray( new String[excludes.size()] ) );
2239 }
2240 scanner.addDefaultExcludes();
2241 scanner.scan();
2242
2243 Map<String, BuildJob> matches = new LinkedHashMap<String, BuildJob>();
2244
2245 for ( String includedFile : scanner.getIncludedFiles() )
2246 {
2247 matches.put( includedFile, new BuildJob( includedFile, type ) );
2248 }
2249
2250 for ( String includedDir : scanner.getIncludedDirectories() )
2251 {
2252 String includedFile = includedDir + File.separatorChar + "pom.xml";
2253 if ( new File( scanner.getBasedir(), includedFile ).isFile() )
2254 {
2255 matches.put( includedFile, new BuildJob( includedFile, type ) );
2256 }
2257 else
2258 {
2259 matches.put( includedDir, new BuildJob( includedDir, type ) );
2260 }
2261 }
2262
2263 return matches.values().toArray( new BuildJob[matches.size()] );
2264 }
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275 private void relativizeProjectPaths( BuildJob[] buildJobs )
2276 throws IOException
2277 {
2278 String projectsDirPath = projectsDirectory.getCanonicalPath();
2279
2280 for ( BuildJob buildJob : buildJobs )
2281 {
2282 String projectPath = buildJob.getProject();
2283
2284 File file = new File( projectPath );
2285
2286 if ( !file.isAbsolute() )
2287 {
2288 file = new File( projectsDirectory, projectPath );
2289 }
2290
2291 String relativizedPath = relativizePath( file, projectsDirPath );
2292
2293 if ( relativizedPath == null )
2294 {
2295 relativizedPath = projectPath;
2296 }
2297
2298 buildJob.setProject( relativizedPath );
2299 }
2300 }
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312 private String relativizePath( File path, String basedir )
2313 throws IOException
2314 {
2315 String relativizedPath = path.getCanonicalPath();
2316
2317 if ( relativizedPath.startsWith( basedir ) )
2318 {
2319 relativizedPath = relativizedPath.substring( basedir.length() );
2320 if ( relativizedPath.startsWith( File.separator ) )
2321 {
2322 relativizedPath = relativizedPath.substring( File.separator.length() );
2323 }
2324
2325 return relativizedPath;
2326 }
2327 else
2328 {
2329 return null;
2330 }
2331 }
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341 private Map<String, Object> getInterpolationValueSource( final boolean escapeXml )
2342 {
2343 Map<String, Object> props = new HashMap<String, Object>();
2344
2345 if ( filterProperties != null )
2346 {
2347 props.putAll( filterProperties );
2348 }
2349 props.put( "basedir", this.project.getBasedir().getAbsolutePath() );
2350 props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) );
2351 if ( settings.getLocalRepository() != null )
2352 {
2353 props.put( "localRepository", settings.getLocalRepository() );
2354 props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) );
2355 }
2356
2357 return new CompositeMap( this.project, props, escapeXml );
2358 }
2359
2360
2361
2362
2363
2364
2365
2366
2367 private static String toUrl( String filename )
2368 {
2369
2370
2371
2372
2373 String url = "file://" + new File( filename ).toURI().getPath();
2374 if ( url.endsWith( "/" ) )
2375 {
2376 url = url.substring( 0, url.length() - 1 );
2377 }
2378 return url;
2379 }
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393 private List<String> getTokens( File basedir, String filename, List<String> defaultTokens )
2394 throws IOException
2395 {
2396 List<String> tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList<String>();
2397
2398 if ( StringUtils.isNotEmpty( filename ) )
2399 {
2400 File tokenFile = new File( basedir, filename );
2401
2402 if ( tokenFile.exists() )
2403 {
2404 tokens = readTokens( tokenFile );
2405 }
2406 }
2407
2408 return tokens;
2409 }
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419 private List<String> readTokens( final File tokenFile )
2420 throws IOException
2421 {
2422 List<String> result = new ArrayList<String>();
2423
2424 BufferedReader reader = null;
2425 try
2426 {
2427 Map<String, Object> composite = getInterpolationValueSource( false );
2428 reader = new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) );
2429
2430 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
2431 {
2432 result.addAll( collectListFromCSV( line ) );
2433 }
2434
2435 reader.close();
2436 reader = null;
2437 }
2438 finally
2439 {
2440 IOUtil.close( reader );
2441 }
2442
2443 return result;
2444 }
2445
2446
2447
2448
2449
2450
2451
2452 private List<String> collectListFromCSV( final String csv )
2453 {
2454 final List<String> result = new ArrayList<String>();
2455
2456 if ( ( csv != null ) && ( csv.trim().length() > 0 ) )
2457 {
2458 final StringTokenizer st = new StringTokenizer( csv, "," );
2459
2460 while ( st.hasMoreTokens() )
2461 {
2462 result.add( st.nextToken().trim() );
2463 }
2464 }
2465
2466 return result;
2467 }
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482 void buildInterpolatedFile( File originalFile, File interpolatedFile )
2483 throws MojoExecutionException
2484 {
2485 getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() );
2486
2487 try
2488 {
2489 String xml;
2490
2491 Reader reader = null;
2492 try
2493 {
2494
2495 Map<String, Object> composite = getInterpolationValueSource( true );
2496 reader =
2497 new InterpolationFilterReader( ReaderFactory.newXmlReader( originalFile ), composite, "@", "@" );
2498
2499 xml = IOUtil.toString( reader );
2500
2501 reader.close();
2502 reader = null;
2503 }
2504 finally
2505 {
2506 IOUtil.close( reader );
2507 }
2508
2509 Writer writer = null;
2510 try
2511 {
2512 interpolatedFile.getParentFile().mkdirs();
2513 writer = WriterFactory.newXmlWriter( interpolatedFile );
2514 writer.write( xml );
2515 writer.close();
2516 writer = null;
2517 }
2518 finally
2519 {
2520 IOUtil.close( writer );
2521 }
2522 }
2523 catch ( IOException e )
2524 {
2525 throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e );
2526 }
2527 }
2528
2529
2530
2531
2532
2533
2534
2535
2536 private InvokerProperties getInvokerProperties( final File projectDirectory )
2537 throws MojoExecutionException
2538 {
2539 Properties props = new Properties();
2540 if ( invokerPropertiesFile != null )
2541 {
2542 File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
2543 if ( propertiesFile.isFile() )
2544 {
2545 InputStream in = null;
2546 try
2547 {
2548 in = new FileInputStream( propertiesFile );
2549 props.load( in );
2550 in.close();
2551 in = null;
2552 }
2553 catch ( IOException e )
2554 {
2555 throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
2556 }
2557 finally
2558 {
2559 IOUtil.close( in );
2560 }
2561 }
2562
2563 Interpolator interpolator = new RegexBasedInterpolator();
2564 interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) );
2565
2566 for ( String key : props.stringPropertyNames() )
2567 {
2568 String value = props.getProperty( key );
2569 try
2570 {
2571 value = interpolator.interpolate( value, "" );
2572 }
2573 catch ( InterpolationException e )
2574 {
2575 throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,
2576 e );
2577 }
2578 props.setProperty( key, value );
2579 }
2580
2581 }
2582 return new InvokerProperties( props );
2583 }
2584
2585 protected boolean isParallelRun()
2586 {
2587 return parallelThreads > 1;
2588 }
2589
2590 }