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