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 destFile.setWritable( true );
1076 }
1077 }
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088 static boolean alreadyCloned( String subpath, List<String> clonedSubpaths )
1089 {
1090 for ( String path : clonedSubpaths )
1091 {
1092 if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) )
1093 {
1094 return true;
1095 }
1096 }
1097
1098 return false;
1099 }
1100
1101
1102
1103
1104
1105
1106
1107
1108 private void runBuilds( final File projectsDir, BuildJob[] buildJobs )
1109 throws MojoExecutionException
1110 {
1111 if ( !localRepositoryPath.exists() )
1112 {
1113 localRepositoryPath.mkdirs();
1114 }
1115
1116
1117
1118
1119
1120 File interpolatedSettingsFile = null;
1121 if ( settingsFile != null )
1122 {
1123 if ( cloneProjectsTo != null )
1124 {
1125 interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() );
1126 }
1127 else
1128 {
1129 interpolatedSettingsFile =
1130 new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() );
1131 }
1132 buildInterpolatedFile( settingsFile, interpolatedSettingsFile );
1133 }
1134
1135
1136
1137
1138
1139 SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
1140
1141 File mergedSettingsFile;
1142 Settings mergedSettings = this.settings;
1143 if ( mergeUserSettings )
1144 {
1145 if ( interpolatedSettingsFile != null )
1146 {
1147
1148 try
1149 {
1150 Settings dominantSettings = settingsBuilder.buildSettings( interpolatedSettingsFile, false );
1151 Settings recessiveSettings = cloneSettings();
1152 SettingsUtils.merge( dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL );
1153
1154 mergedSettings = dominantSettings;
1155 getLog().debug( "Merged specified settings file with settings of invoking process" );
1156 }
1157 catch ( XmlPullParserException e )
1158 {
1159 throw new MojoExecutionException( "Could not read specified settings file", e );
1160 }
1161 catch ( IOException e )
1162 {
1163 throw new MojoExecutionException( "Could not read specified settings file", e );
1164 }
1165 }
1166 }
1167 if ( this.settingsFile != null && !mergeUserSettings )
1168 {
1169 mergedSettingsFile = interpolatedSettingsFile;
1170 }
1171 else
1172 {
1173 try
1174 {
1175 mergedSettingsFile = File.createTempFile( "invoker-settings", ".xml" );
1176
1177 FileWriter fileWriter = null;
1178 try
1179 {
1180 fileWriter = new FileWriter( mergedSettingsFile );
1181 settingsWriter.write( fileWriter, mergedSettings );
1182 }
1183 finally
1184 {
1185 IOUtil.close( fileWriter );
1186 }
1187
1188 if ( getLog().isDebugEnabled() )
1189 {
1190 getLog().debug( "Created temporary file for invoker settings.xml: "
1191 + mergedSettingsFile.getAbsolutePath() );
1192 }
1193 }
1194 catch ( IOException e )
1195 {
1196 throw new MojoExecutionException( "Could not create temporary file for invoker settings.xml", e );
1197 }
1198 }
1199 final File finalSettingsFile = mergedSettingsFile;
1200
1201 if ( mavenHome != null )
1202 {
1203 actualMavenVersion = SelectorUtils.getMavenVersion( mavenHome );
1204 }
1205 else
1206 {
1207 actualMavenVersion = SelectorUtils.getMavenVersion();
1208 }
1209 scriptRunner.setGlobalVariable( "mavenVersion", actualMavenVersion );
1210
1211 final CharSequence actualJreVersion;
1212
1213 if ( javaHome != null )
1214 {
1215 actualJreVersion = resolveExternalJreVersion();
1216 }
1217 else
1218 {
1219 actualJreVersion = SelectorUtils.getJreVersion();
1220 }
1221
1222 try
1223 {
1224 if ( isParallelRun() )
1225 {
1226 getLog().info( "use parallelThreads " + parallelThreads );
1227
1228 ExecutorService executorService = Executors.newFixedThreadPool( parallelThreads );
1229 for ( final BuildJob job : buildJobs )
1230 {
1231 executorService.execute( new Runnable()
1232 {
1233 public void run()
1234 {
1235 try
1236 {
1237 runBuild( projectsDir, job, finalSettingsFile, javaHome, actualJreVersion );
1238 }
1239 catch ( MojoExecutionException e )
1240 {
1241 throw new RuntimeException( e.getMessage(), e );
1242 }
1243 }
1244 } );
1245 }
1246
1247 try
1248 {
1249 executorService.shutdown();
1250
1251 executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
1252 }
1253 catch ( InterruptedException e )
1254 {
1255 throw new MojoExecutionException( e.getMessage(), e );
1256 }
1257 }
1258 else
1259 {
1260 for ( BuildJob job : buildJobs )
1261 {
1262 runBuild( projectsDir, job, finalSettingsFile, javaHome, actualJreVersion );
1263 }
1264 }
1265 }
1266 finally
1267 {
1268 if ( interpolatedSettingsFile != null && cloneProjectsTo == null )
1269 {
1270 interpolatedSettingsFile.delete();
1271 }
1272 if ( mergedSettingsFile != null && mergedSettingsFile.exists() )
1273 {
1274 mergedSettingsFile.delete();
1275 }
1276 }
1277 }
1278
1279 private Settings cloneSettings()
1280 {
1281 Settings recessiveSettings = SettingsUtils.copySettings( this.settings );
1282
1283
1284 resetSourceLevelSet( recessiveSettings );
1285 for ( org.apache.maven.settings.Mirror mirror : recessiveSettings.getMirrors() )
1286 {
1287 resetSourceLevelSet( mirror );
1288 }
1289 for ( org.apache.maven.settings.Server server : recessiveSettings.getServers() )
1290 {
1291 resetSourceLevelSet( server );
1292 }
1293 for ( org.apache.maven.settings.Proxy proxy : recessiveSettings.getProxies() )
1294 {
1295 resetSourceLevelSet( proxy );
1296 }
1297 for ( org.apache.maven.settings.Profile profile : recessiveSettings.getProfiles() )
1298 {
1299 resetSourceLevelSet( profile );
1300 }
1301
1302 return recessiveSettings;
1303 }
1304
1305 private void resetSourceLevelSet( org.apache.maven.settings.TrackableBase trackable )
1306 {
1307 try
1308 {
1309 ReflectionUtils.setVariableValueInObject( trackable, "sourceLevelSet", Boolean.FALSE );
1310 getLog().debug( "sourceLevelSet: "
1311 + ReflectionUtils.getValueIncludingSuperclasses( "sourceLevelSet", trackable ) );
1312 }
1313 catch ( IllegalAccessException e )
1314 {
1315
1316 }
1317 }
1318
1319 private CharSequence resolveExternalJreVersion()
1320 {
1321 Artifact pluginArtifact = mojoExecution.getMojoDescriptor().getPluginDescriptor().getPluginArtifact();
1322 pluginArtifact.getFile();
1323
1324 Commandline commandLine = new Commandline();
1325 commandLine.setExecutable( new File( javaHome, "bin/java" ).getAbsolutePath() );
1326 commandLine.createArg().setValue( "-cp" );
1327 commandLine.createArg().setFile( pluginArtifact.getFile() );
1328 commandLine.createArg().setValue( SystemPropertyPrinter.class.getName() );
1329 commandLine.createArg().setValue( "java.version" );
1330
1331 final StringBuilder actualJreVersion = new StringBuilder();
1332 StreamConsumer consumer = new StreamConsumer()
1333 {
1334 public void consumeLine( String line )
1335 {
1336 actualJreVersion.append( line );
1337 }
1338 };
1339 try
1340 {
1341 CommandLineUtils.executeCommandLine( commandLine, consumer, null );
1342 }
1343 catch ( CommandLineException e )
1344 {
1345 getLog().warn( e.getMessage() );
1346 }
1347 return actualJreVersion;
1348 }
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359 private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome,
1360 CharSequence actualJreVersion )
1361 throws MojoExecutionException
1362 {
1363 File pomFile = new File( projectsDir, buildJob.getProject() );
1364 File basedir;
1365 if ( pomFile.isDirectory() )
1366 {
1367 basedir = pomFile;
1368 pomFile = new File( basedir, "pom.xml" );
1369 if ( !pomFile.exists() )
1370 {
1371 pomFile = null;
1372 }
1373 else
1374 {
1375 buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" );
1376 }
1377 }
1378 else
1379 {
1380 basedir = pomFile.getParentFile();
1381 }
1382
1383 getLog().info( "Building: " + buildJob.getProject() );
1384
1385 File interpolatedPomFile = null;
1386 if ( pomFile != null )
1387 {
1388 if ( filteredPomPrefix != null )
1389 {
1390 interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() );
1391 buildInterpolatedFile( pomFile, interpolatedPomFile );
1392 }
1393 else
1394 {
1395 interpolatedPomFile = pomFile;
1396 }
1397 }
1398
1399 InvokerProperties invokerProperties = getInvokerProperties( basedir );
1400
1401
1402 buildJob.setName( invokerProperties.getJobName() );
1403 buildJob.setDescription( invokerProperties.getJobDescription() );
1404
1405 try
1406 {
1407 int selection = getSelection( invokerProperties, actualJreVersion );
1408 if ( selection == 0 )
1409 {
1410 long milliseconds = System.currentTimeMillis();
1411 boolean executed;
1412 try
1413 {
1414
1415 executed = runBuild( basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties );
1416
1417 }
1418 finally
1419 {
1420 milliseconds = System.currentTimeMillis() - milliseconds;
1421 buildJob.setTime( milliseconds / 1000.0 );
1422 }
1423
1424 if ( executed )
1425 {
1426 buildJob.setResult( BuildJob.Result.SUCCESS );
1427
1428 if ( !suppressSummaries )
1429 {
1430 getLog().info( "..SUCCESS " + formatTime( buildJob.getTime() ) );
1431 }
1432 }
1433 else
1434 {
1435 buildJob.setResult( BuildJob.Result.SKIPPED );
1436
1437 if ( !suppressSummaries )
1438 {
1439 getLog().info( "..SKIPPED " + formatTime( buildJob.getTime() ) );
1440 }
1441 }
1442 }
1443 else
1444 {
1445 buildJob.setResult( BuildJob.Result.SKIPPED );
1446
1447 StringBuilder message = new StringBuilder();
1448 if ( ( selection & SELECTOR_MAVENVERSION ) != 0 )
1449 {
1450 message.append( "Maven version" );
1451 }
1452 if ( ( selection & SELECTOR_JREVERSION ) != 0 )
1453 {
1454 if ( message.length() > 0 )
1455 {
1456 message.append( ", " );
1457 }
1458 message.append( "JRE version" );
1459 }
1460 if ( ( selection & SELECTOR_OSFAMILY ) != 0 )
1461 {
1462 if ( message.length() > 0 )
1463 {
1464 message.append( ", " );
1465 }
1466 message.append( "OS" );
1467 }
1468
1469 if ( !suppressSummaries )
1470 {
1471 getLog().info( "..SKIPPED due to " + message.toString() );
1472 }
1473
1474
1475
1476 buildJob.setFailureMessage( "Skipped due to " + message.toString() );
1477 }
1478 }
1479 catch ( RunErrorException e )
1480 {
1481 buildJob.setResult( BuildJob.Result.ERROR );
1482 buildJob.setFailureMessage( e.getMessage() );
1483
1484 if ( !suppressSummaries )
1485 {
1486 getLog().info( "..ERROR " + formatTime( buildJob.getTime() ) );
1487 getLog().info( " " + e.getMessage() );
1488 }
1489 }
1490 catch ( RunFailureException e )
1491 {
1492 buildJob.setResult( e.getType() );
1493 buildJob.setFailureMessage( e.getMessage() );
1494
1495 if ( !suppressSummaries )
1496 {
1497 getLog().info( "..FAILED " + formatTime( buildJob.getTime() ) );
1498 getLog().info( " " + e.getMessage() );
1499 }
1500 }
1501 finally
1502 {
1503 if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) )
1504 {
1505 interpolatedPomFile.delete();
1506 }
1507 writeBuildReport( buildJob );
1508 }
1509 }
1510
1511
1512
1513
1514
1515
1516
1517
1518 private int getSelection( InvokerProperties invokerProperties, CharSequence actualJreVersion )
1519 {
1520 int selection = 0;
1521 if ( !SelectorUtils.isMavenVersion( invokerProperties.getMavenVersion(), actualMavenVersion ) )
1522 {
1523 selection |= SELECTOR_MAVENVERSION;
1524 }
1525
1526 if ( !SelectorUtils.isJreVersion( invokerProperties.getJreVersion(), actualJreVersion.toString() ) )
1527 {
1528 selection |= SELECTOR_JREVERSION;
1529 }
1530
1531 if ( !SelectorUtils.isOsFamily( invokerProperties.getOsFamily() ) )
1532 {
1533 selection |= SELECTOR_OSFAMILY;
1534 }
1535
1536 return selection;
1537 }
1538
1539
1540
1541
1542
1543
1544
1545 private void writeBuildReport( BuildJob buildJob )
1546 throws MojoExecutionException
1547 {
1548 if ( disableReports )
1549 {
1550 return;
1551 }
1552
1553 String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' );
1554 if ( safeFileName.endsWith( "_pom.xml" ) )
1555 {
1556 safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() );
1557 }
1558
1559 File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );
1560 try
1561 {
1562 FileOutputStream fos = new FileOutputStream( reportFile );
1563 try
1564 {
1565 Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() );
1566 BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
1567 writer.write( osw, buildJob );
1568 osw.close();
1569 }
1570 finally
1571 {
1572 fos.close();
1573 }
1574 }
1575 catch ( IOException e )
1576 {
1577 throw new MojoExecutionException( "Failed to write build report " + reportFile, e );
1578 }
1579 }
1580
1581
1582
1583
1584
1585
1586
1587 private String formatTime( double seconds )
1588 {
1589 return secFormat.format( seconds );
1590 }
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606 private boolean runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome,
1607 InvokerProperties invokerProperties )
1608 throws MojoExecutionException, RunFailureException
1609 {
1610 if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() )
1611 {
1612 Properties props = invokerProperties.getProperties();
1613 getLog().debug( "Using invoker properties:" );
1614 for ( String key : new TreeSet<String>( (Set) props.keySet() ) )
1615 {
1616 String value = props.getProperty( key );
1617 getLog().debug( " " + key + " = " + value );
1618 }
1619 }
1620
1621 List<String> goals = getGoals( basedir );
1622
1623 List<String> profiles = getProfiles( basedir );
1624
1625 Map<String, Object> context = new LinkedHashMap<String, Object>();
1626
1627 FileLogger logger = setupLogger( basedir );
1628 try
1629 {
1630 try
1631 {
1632 scriptRunner.run( "selector script", basedir, selectorScript, context, logger, BuildJob.Result.SKIPPED,
1633 false );
1634 }
1635 catch ( RunErrorException e )
1636 {
1637 throw e;
1638 }
1639 catch ( RunFailureException e )
1640 {
1641 return false;
1642 }
1643
1644 scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger,
1645 BuildJob.Result.FAILURE_PRE_HOOK, false );
1646
1647 final InvocationRequest request = new DefaultInvocationRequest();
1648
1649 request.setLocalRepositoryDirectory( localRepositoryPath );
1650
1651 request.setInteractive( false );
1652
1653 request.setShowErrors( showErrors );
1654
1655 request.setDebug( debug );
1656
1657 request.setShowVersion( showVersion );
1658
1659 if ( logger != null )
1660 {
1661 request.setErrorHandler( logger );
1662
1663 request.setOutputHandler( logger );
1664 }
1665
1666 if ( mavenHome != null )
1667 {
1668 invoker.setMavenHome( mavenHome );
1669 request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() );
1670 }
1671
1672 if ( mavenExecutable != null )
1673 {
1674 invoker.setMavenExecutable( new File( mavenExecutable ) );
1675 }
1676
1677 if ( actualJavaHome != null )
1678 {
1679 request.setJavaHome( actualJavaHome );
1680 }
1681
1682 if ( environmentVariables != null )
1683 {
1684 for ( Map.Entry<String, String> variable : environmentVariables.entrySet() )
1685 {
1686 request.addShellEnvironment( variable.getKey(), variable.getValue() );
1687 }
1688 }
1689
1690 for ( int invocationIndex = 1;; invocationIndex++ )
1691 {
1692 if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) )
1693 {
1694 break;
1695 }
1696
1697 request.setBaseDirectory( basedir );
1698
1699 request.setPomFile( pomFile );
1700
1701 request.setGoals( goals );
1702
1703 request.setProfiles( profiles );
1704
1705 request.setMavenOpts( mavenOpts );
1706
1707 request.setOffline( false );
1708
1709 request.setUserSettingsFile( settingsFile );
1710
1711 Properties systemProperties =
1712 getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) );
1713 request.setProperties( systemProperties );
1714
1715 invokerProperties.configureInvocation( request, invocationIndex );
1716
1717 if ( getLog().isDebugEnabled() )
1718 {
1719 try
1720 {
1721 getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() );
1722 getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) );
1723 }
1724 catch ( CommandLineConfigurationException e )
1725 {
1726 getLog().debug( "Failed to display command line: " + e.getMessage() );
1727 }
1728 }
1729
1730 InvocationResult result;
1731
1732 try
1733 {
1734 result = invoker.execute( request );
1735 }
1736 catch ( final MavenInvocationException e )
1737 {
1738 getLog().debug( "Error invoking Maven: " + e.getMessage(), e );
1739 throw new RunFailureException( "Maven invocation failed. " + e.getMessage(),
1740 BuildJob.Result.FAILURE_BUILD );
1741 }
1742
1743 verify( result, invocationIndex, invokerProperties, logger );
1744 }
1745
1746 scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger,
1747 BuildJob.Result.FAILURE_POST_HOOK, true );
1748 }
1749 catch ( IOException e )
1750 {
1751 throw new MojoExecutionException( e.getMessage(), e );
1752 }
1753 finally
1754 {
1755 if ( logger != null )
1756 {
1757 logger.close();
1758 }
1759 }
1760 return true;
1761 }
1762
1763
1764
1765
1766
1767
1768
1769
1770 private FileLogger setupLogger( File basedir )
1771 throws MojoExecutionException
1772 {
1773 FileLogger logger = null;
1774
1775 if ( !noLog )
1776 {
1777 File outputLog = new File( basedir, "build.log" );
1778 try
1779 {
1780 if ( streamLogs )
1781 {
1782 logger = new FileLogger( outputLog, getLog() );
1783 }
1784 else
1785 {
1786 logger = new FileLogger( outputLog );
1787 }
1788
1789 getLog().debug( "Build log initialized in: " + outputLog );
1790 }
1791 catch ( IOException e )
1792 {
1793 throw new MojoExecutionException( "Error initializing build logfile in: " + outputLog, e );
1794 }
1795 }
1796
1797 return logger;
1798 }
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809 private Properties getSystemProperties( final File basedir, final String filename )
1810 throws MojoExecutionException
1811 {
1812 Properties collectedTestProperties = new Properties();
1813
1814 if ( testProperties != null )
1815 {
1816 collectedTestProperties.putAll( testProperties );
1817 }
1818
1819 if ( properties != null )
1820 {
1821
1822 for ( Map.Entry<String, String> entry : properties.entrySet() )
1823 {
1824 if ( entry.getValue() != null )
1825 {
1826 collectedTestProperties.put( entry.getKey(), entry.getValue() );
1827 }
1828 }
1829 }
1830
1831 File propertiesFile = null;
1832 if ( filename != null )
1833 {
1834 propertiesFile = new File( basedir, filename );
1835 }
1836 else if ( testPropertiesFile != null )
1837 {
1838 propertiesFile = new File( basedir, testPropertiesFile );
1839 }
1840
1841 if ( propertiesFile != null && propertiesFile.isFile() )
1842 {
1843 InputStream fin = null;
1844 try
1845 {
1846 fin = new FileInputStream( propertiesFile );
1847
1848 Properties loadedProperties = new Properties();
1849 loadedProperties.load( fin );
1850 collectedTestProperties.putAll( loadedProperties );
1851 }
1852 catch ( IOException e )
1853 {
1854 throw new MojoExecutionException( "Error reading system properties from " + propertiesFile );
1855 }
1856 finally
1857 {
1858 IOUtil.close( fin );
1859 }
1860 }
1861
1862 return collectedTestProperties;
1863 }
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875 private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties,
1876 FileLogger logger )
1877 throws RunFailureException
1878 {
1879 if ( result.getExecutionException() != null )
1880 {
1881 throw new RunFailureException( "The Maven invocation failed. "
1882 + result.getExecutionException().getMessage(), BuildJob.Result.ERROR );
1883 }
1884 else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) )
1885 {
1886 StringBuilder buffer = new StringBuilder( 256 );
1887 buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " );
1888 if ( logger != null )
1889 {
1890 buffer.append( "See " );
1891 buffer.append( logger.getOutputFile().getAbsolutePath() );
1892 buffer.append( " for details." );
1893 }
1894 else
1895 {
1896 buffer.append( "See console output for details." );
1897 }
1898 throw new RunFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD );
1899 }
1900 }
1901
1902
1903
1904
1905
1906
1907
1908
1909 List<String> getGoals( final File basedir )
1910 throws MojoExecutionException
1911 {
1912 try
1913 {
1914 return getTokens( basedir, goalsFile, goals );
1915 }
1916 catch ( IOException e )
1917 {
1918 throw new MojoExecutionException( "error reading goals", e );
1919 }
1920 }
1921
1922
1923
1924
1925
1926
1927
1928
1929 List<String> getProfiles( File basedir )
1930 throws MojoExecutionException
1931 {
1932 try
1933 {
1934 return getTokens( basedir, profilesFile, profiles );
1935 }
1936 catch ( IOException e )
1937 {
1938 throw new MojoExecutionException( "error reading profiles", e );
1939 }
1940 }
1941
1942
1943
1944
1945
1946
1947
1948 BuildJob[] getBuildJobs()
1949 throws IOException
1950 {
1951 BuildJob[] buildJobs;
1952
1953 if ( ( pom != null ) && pom.exists() )
1954 {
1955 buildJobs = new BuildJob[] { new BuildJob( pom.getAbsolutePath(), BuildJob.Type.NORMAL ) };
1956 }
1957 else if ( invokerTest != null )
1958 {
1959 String[] testRegexes = StringUtils.split( invokerTest, "," );
1960 List<String> includes = new ArrayList<String>( testRegexes.length );
1961 List<String> excludes = new ArrayList<String>();
1962
1963 for ( String regex : testRegexes )
1964 {
1965
1966 if ( regex.startsWith( "!" ) )
1967 {
1968 excludes.add( regex.substring( 1 ) );
1969 }
1970 else
1971 {
1972 includes.add( regex );
1973 }
1974 }
1975
1976
1977
1978 buildJobs = scanProjectsDirectory( includes, excludes, BuildJob.Type.DIRECT );
1979 }
1980 else
1981 {
1982 List<String> excludes =
1983 ( pomExcludes != null ) ? new ArrayList<String>( pomExcludes ) : new ArrayList<String>();
1984 if ( this.settingsFile != null )
1985 {
1986 String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() );
1987 if ( exclude != null )
1988 {
1989 excludes.add( exclude.replace( '\\', '/' ) );
1990 getLog().debug( "Automatically excluded " + exclude + " from project scanning" );
1991 }
1992 }
1993
1994 BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
1995 if ( getLog().isDebugEnabled() )
1996 {
1997 getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) );
1998 }
1999
2000 BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL );
2001
2002 Map<String, BuildJob> uniquePoms = new LinkedHashMap<String, BuildJob>();
2003 for ( BuildJob setupPom : setupPoms )
2004 {
2005 uniquePoms.put( setupPom.getProject(), setupPom );
2006 }
2007 for ( BuildJob normalPom : normalPoms )
2008 {
2009 if ( !uniquePoms.containsKey( normalPom.getProject() ) )
2010 {
2011 uniquePoms.put( normalPom.getProject(), normalPom );
2012 }
2013 }
2014
2015 buildJobs = uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] );
2016 }
2017
2018 relativizeProjectPaths( buildJobs );
2019
2020 return buildJobs;
2021 }
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035 private BuildJob[] scanProjectsDirectory( List<String> includes, List<String> excludes, String type )
2036 throws IOException
2037 {
2038 if ( !projectsDirectory.isDirectory() )
2039 {
2040 return new BuildJob[0];
2041 }
2042
2043 DirectoryScanner scanner = new DirectoryScanner();
2044 scanner.setBasedir( projectsDirectory.getCanonicalFile() );
2045 scanner.setFollowSymlinks( false );
2046 if ( includes != null )
2047 {
2048 scanner.setIncludes( includes.toArray( new String[includes.size()] ) );
2049 }
2050 if ( excludes != null )
2051 {
2052 scanner.setExcludes( excludes.toArray( new String[excludes.size()] ) );
2053 }
2054 scanner.addDefaultExcludes();
2055 scanner.scan();
2056
2057 Map<String, BuildJob> matches = new LinkedHashMap<String, BuildJob>();
2058
2059 for ( String includedFile : scanner.getIncludedFiles() )
2060 {
2061 matches.put( includedFile, new BuildJob( includedFile, type ) );
2062 }
2063
2064 for ( String includedDir : scanner.getIncludedDirectories() )
2065 {
2066 String includedFile = includedDir + File.separatorChar + "pom.xml";
2067 if ( new File( scanner.getBasedir(), includedFile ).isFile() )
2068 {
2069 matches.put( includedFile, new BuildJob( includedFile, type ) );
2070 }
2071 else
2072 {
2073 matches.put( includedDir, new BuildJob( includedDir, type ) );
2074 }
2075 }
2076
2077 return matches.values().toArray( new BuildJob[matches.size()] );
2078 }
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089 private void relativizeProjectPaths( BuildJob[] buildJobs )
2090 throws IOException
2091 {
2092 String projectsDirPath = projectsDirectory.getCanonicalPath();
2093
2094 for ( BuildJob buildJob : buildJobs )
2095 {
2096 String projectPath = buildJob.getProject();
2097
2098 File file = new File( projectPath );
2099
2100 if ( !file.isAbsolute() )
2101 {
2102 file = new File( projectsDirectory, projectPath );
2103 }
2104
2105 String relativizedPath = relativizePath( file, projectsDirPath );
2106
2107 if ( relativizedPath == null )
2108 {
2109 relativizedPath = projectPath;
2110 }
2111
2112 buildJob.setProject( relativizedPath );
2113 }
2114 }
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126 private String relativizePath( File path, String basedir )
2127 throws IOException
2128 {
2129 String relativizedPath = path.getCanonicalPath();
2130
2131 if ( relativizedPath.startsWith( basedir ) )
2132 {
2133 relativizedPath = relativizedPath.substring( basedir.length() );
2134 if ( relativizedPath.startsWith( File.separator ) )
2135 {
2136 relativizedPath = relativizedPath.substring( File.separator.length() );
2137 }
2138
2139 return relativizedPath;
2140 }
2141 else
2142 {
2143 return null;
2144 }
2145 }
2146
2147
2148
2149
2150
2151
2152 private Map<String, Object> getInterpolationValueSource()
2153 {
2154 Map<String, Object> props = new HashMap<String, Object>();
2155 if ( interpolationsProperties != null )
2156 {
2157 props.putAll( (Map) interpolationsProperties );
2158 }
2159 if ( filterProperties != null )
2160 {
2161 props.putAll( filterProperties );
2162 }
2163 props.put( "basedir", this.project.getBasedir().getAbsolutePath() );
2164 props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) );
2165 if ( settings.getLocalRepository() != null )
2166 {
2167 props.put( "localRepository", settings.getLocalRepository() );
2168 props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) );
2169 }
2170 return new CompositeMap( this.project, props );
2171 }
2172
2173
2174
2175
2176
2177
2178
2179
2180 private static String toUrl( String filename )
2181 {
2182
2183
2184
2185
2186 String url = "file://" + new File( filename ).toURI().getPath();
2187 if ( url.endsWith( "/" ) )
2188 {
2189 url = url.substring( 0, url.length() - 1 );
2190 }
2191 return url;
2192 }
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206 private List<String> getTokens( File basedir, String filename, List<String> defaultTokens )
2207 throws IOException
2208 {
2209 List<String> tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList<String>();
2210
2211 if ( StringUtils.isNotEmpty( filename ) )
2212 {
2213 File tokenFile = new File( basedir, filename );
2214
2215 if ( tokenFile.exists() )
2216 {
2217 tokens = readTokens( tokenFile );
2218 }
2219 }
2220
2221 return tokens;
2222 }
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232 private List<String> readTokens( final File tokenFile )
2233 throws IOException
2234 {
2235 List<String> result = new ArrayList<String>();
2236
2237 BufferedReader reader = null;
2238 try
2239 {
2240 Map<String, Object> composite = getInterpolationValueSource();
2241 reader = new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) );
2242
2243 String line;
2244 while ( ( line = reader.readLine() ) != null )
2245 {
2246 result.addAll( collectListFromCSV( line ) );
2247 }
2248 }
2249 finally
2250 {
2251 IOUtil.close( reader );
2252 }
2253
2254 return result;
2255 }
2256
2257
2258
2259
2260
2261
2262
2263 private List<String> collectListFromCSV( final String csv )
2264 {
2265 final List<String> result = new ArrayList<String>();
2266
2267 if ( ( csv != null ) && ( csv.trim().length() > 0 ) )
2268 {
2269 final StringTokenizer st = new StringTokenizer( csv, "," );
2270
2271 while ( st.hasMoreTokens() )
2272 {
2273 result.add( st.nextToken().trim() );
2274 }
2275 }
2276
2277 return result;
2278 }
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289 void buildInterpolatedFile( File originalFile, File interpolatedFile )
2290 throws MojoExecutionException
2291 {
2292 getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() );
2293
2294 try
2295 {
2296 String xml;
2297
2298 Reader reader = null;
2299 try
2300 {
2301
2302 Map<String, Object> composite = getInterpolationValueSource();
2303 reader = ReaderFactory.newXmlReader( originalFile );
2304 reader = new InterpolationFilterReader( reader, composite, "@", "@" );
2305 xml = IOUtil.toString( reader );
2306 }
2307 finally
2308 {
2309 IOUtil.close( reader );
2310 }
2311
2312 Writer writer = null;
2313 try
2314 {
2315 interpolatedFile.getParentFile().mkdirs();
2316 writer = WriterFactory.newXmlWriter( interpolatedFile );
2317 writer.write( xml );
2318 writer.flush();
2319 }
2320 finally
2321 {
2322 IOUtil.close( writer );
2323 }
2324 }
2325 catch ( IOException e )
2326 {
2327 throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e );
2328 }
2329 }
2330
2331
2332
2333
2334
2335
2336
2337
2338 private InvokerProperties getInvokerProperties( final File projectDirectory )
2339 throws MojoExecutionException
2340 {
2341 Properties props = new Properties();
2342 if ( invokerPropertiesFile != null )
2343 {
2344 File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
2345 if ( propertiesFile.isFile() )
2346 {
2347 InputStream in = null;
2348 try
2349 {
2350 in = new FileInputStream( propertiesFile );
2351 props.load( in );
2352 }
2353 catch ( IOException e )
2354 {
2355 throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
2356 }
2357 finally
2358 {
2359 IOUtil.close( in );
2360 }
2361 }
2362
2363 Interpolator interpolator = new RegexBasedInterpolator();
2364 interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource() ) );
2365
2366 for ( String key : (Set<String>) ( (Map) props ).keySet() )
2367 {
2368 String value = props.getProperty( key );
2369 try
2370 {
2371 value = interpolator.interpolate( value, "" );
2372 }
2373 catch ( InterpolationException e )
2374 {
2375 throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile, e );
2376 }
2377 props.setProperty( key, value );
2378 }
2379
2380 }
2381 return new InvokerProperties( props );
2382 }
2383
2384 protected boolean isParallelRun()
2385 {
2386 return parallelThreads > 1;
2387 }
2388
2389 }