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