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