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 org.apache.maven.plugin.AbstractMojo;
23 import org.apache.maven.plugin.MojoExecutionException;
24 import org.apache.maven.plugin.MojoFailureException;
25 import org.apache.maven.plugin.invoker.model.BuildJob;
26 import org.apache.maven.plugin.invoker.model.io.xpp3.BuildJobXpp3Writer;
27 import org.apache.maven.model.Model;
28 import org.apache.maven.model.Profile;
29 import org.apache.maven.shared.invoker.InvocationRequest;
30 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
31 import org.apache.maven.shared.invoker.MavenCommandLineBuilder;
32 import org.apache.maven.shared.invoker.CommandLineConfigurationException;
33 import org.apache.maven.shared.invoker.InvocationResult;
34 import org.apache.maven.shared.invoker.MavenInvocationException;
35 import org.apache.maven.shared.invoker.Invoker;
36 import org.apache.maven.project.MavenProject;
37 import org.apache.maven.settings.Settings;
38 import org.codehaus.plexus.util.StringUtils;
39 import org.codehaus.plexus.util.ReaderFactory;
40 import org.codehaus.plexus.util.FileUtils;
41 import org.codehaus.plexus.util.DirectoryScanner;
42 import org.codehaus.plexus.util.IOUtil;
43 import org.codehaus.plexus.util.InterpolationFilterReader;
44 import org.codehaus.plexus.util.WriterFactory;
45 import org.codehaus.plexus.interpolation.Interpolator;
46 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
47 import org.codehaus.plexus.interpolation.MapBasedValueSource;
48 import org.codehaus.plexus.interpolation.InterpolationException;
49
50 import java.io.IOException;
51 import java.io.File;
52 import java.io.Reader;
53 import java.io.FileOutputStream;
54 import java.io.Writer;
55 import java.io.OutputStreamWriter;
56 import java.io.InputStream;
57 import java.io.FileInputStream;
58 import java.io.BufferedReader;
59 import java.util.Collection;
60 import java.util.LinkedHashSet;
61 import java.util.Iterator;
62 import java.util.List;
63 import java.util.ArrayList;
64 import java.util.Properties;
65 import java.util.TreeSet;
66 import java.util.Map;
67 import java.util.LinkedHashMap;
68 import java.util.Arrays;
69 import java.util.HashMap;
70 import java.util.StringTokenizer;
71 import java.util.Collections;
72 import java.util.Locale;
73 import java.text.DecimalFormat;
74 import java.text.DecimalFormatSymbols;
75
76
77
78
79
80
81
82 public abstract class AbstractInvokerMojo
83 extends AbstractMojo
84 {
85
86
87
88
89
90
91
92 private boolean skipInvocation;
93
94
95
96
97
98
99
100
101
102 protected boolean suppressSummaries;
103
104
105
106
107
108
109 private boolean streamLogs;
110
111
112
113
114
115
116
117
118 private File localRepositoryPath;
119
120
121
122
123
124
125 private File projectsDirectory;
126
127
128
129
130
131
132
133 private File reportsDirectory;
134
135
136
137
138
139
140
141 private boolean disableReports;
142
143
144
145
146
147
148
149
150
151 private File cloneProjectsTo;
152
153
154
155
156
157
158
159
160
161
162 private boolean cloneAllFiles;
163
164
165
166
167
168
169 private File pom;
170
171
172
173
174
175
176
177
178
179
180
181
182
183 private List pomIncludes = Collections.singletonList( "*/pom.xml" );
184
185
186
187
188
189
190
191
192 private List pomExcludes = Collections.EMPTY_LIST;
193
194
195
196
197
198
199
200
201
202
203
204 private List setupIncludes = Collections.singletonList( "setup*/pom.xml" );
205
206
207
208
209
210
211 private List goals = Collections.singletonList( "package" );
212
213
214
215
216
217
218
219
220 private String goalsFile;
221
222
223
224
225 private Invoker invoker;
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 private String selectorScript;
241
242
243
244
245
246
247
248
249
250
251
252 private String preBuildHookScript;
253
254
255
256
257
258
259
260
261
262
263 private String postBuildHookScript;
264
265
266
267
268
269
270 private String testPropertiesFile;
271
272
273
274
275
276
277
278 private Properties testProperties;
279
280
281
282
283
284
285
286 private Map properties;
287
288
289
290
291
292
293 private boolean showErrors;
294
295
296
297
298
299
300 private boolean debug;
301
302
303
304
305
306
307 private boolean noLog;
308
309
310
311
312
313
314
315 private List profiles;
316
317
318
319
320
321
322
323
324 private Properties interpolationsProperties;
325
326
327
328
329
330
331
332 private Map filterProperties;
333
334
335
336
337
338
339
340
341
342 private MavenProject project;
343
344
345
346
347
348
349
350
351
352
353
354
355 private String invokerTest;
356
357
358
359
360
361
362
363
364
365
366 private String profilesFile;
367
368
369
370
371
372
373
374
375
376 private File settingsFile;
377
378
379
380
381
382
383
384
385 private String mavenOpts;
386
387
388
389
390
391
392
393
394 private File mavenHome;
395
396
397
398
399
400
401
402
403 private File javaHome;
404
405
406
407
408
409
410
411 private String encoding;
412
413
414
415
416
417
418
419
420
421 private Settings settings;
422
423
424
425
426
427
428
429
430
431
432
433 private boolean addTestClassPath;
434
435
436
437
438
439
440
441 private List testClassPath;
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511 private String invokerPropertiesFile;
512
513
514
515
516
517
518 private boolean showVersion;
519
520
521
522
523 private ScriptRunner scriptRunner;
524
525
526
527
528
529
530 private String filteredPomPrefix = "interpolated-";
531
532
533
534
535 private final DecimalFormat secFormat = new DecimalFormat( "(0.0 s)", new DecimalFormatSymbols( Locale.ENGLISH ) );
536
537
538
539
540
541
542
543 public void execute()
544 throws MojoExecutionException, MojoFailureException
545 {
546 if ( skipInvocation )
547 {
548 getLog().info( "Skipping invocation per configuration."
549 + " If this is incorrect, ensure the skipInvocation parameter is not set to true." );
550 return;
551 }
552
553 BuildJob[] buildJobs;
554 if ( pom != null )
555 {
556 try
557 {
558 projectsDirectory = pom.getCanonicalFile().getParentFile();
559 }
560 catch ( IOException e )
561 {
562 throw new MojoExecutionException( "Failed to discover projectsDirectory from pom File parameter."
563 + " Reason: " + e.getMessage(), e );
564 }
565
566 buildJobs = new BuildJob[]{ new BuildJob( pom.getName(), BuildJob.Type.NORMAL )};
567 }
568 else
569 {
570 try
571 {
572 buildJobs = getBuildJobs();
573 }
574 catch ( final IOException e )
575 {
576 throw new MojoExecutionException( "Error retrieving POM list from includes, excludes, "
577 + "and projects directory. Reason: " + e.getMessage(), e );
578 }
579 }
580
581
582 if ( ( buildJobs == null ) || ( buildJobs.length < 1 ) )
583 {
584 getLog().info( "No projects were selected for execution." );
585 return;
586 }
587
588 if ( StringUtils.isEmpty( encoding ) )
589 {
590 getLog().warn(
591 "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
592 + ", i.e. build is platform dependent!" );
593 }
594
595 scriptRunner = new ScriptRunner( getLog() );
596 scriptRunner.setScriptEncoding( encoding );
597 scriptRunner.setGlobalVariable( "localRepositoryPath", localRepositoryPath );
598 scriptRunner.setClassPath( addTestClassPath ? testClassPath : null );
599
600 Collection collectedProjects = new LinkedHashSet();
601 for ( int i = 0; i < buildJobs.length; i++ )
602 {
603 collectProjects( projectsDirectory, buildJobs[i].getProject(), collectedProjects, true );
604 }
605
606 File projectsDir = projectsDirectory;
607
608 if ( cloneProjectsTo != null )
609 {
610 cloneProjects( collectedProjects );
611 projectsDir = cloneProjectsTo;
612 }
613 else
614 {
615 getLog().warn( "Filtering of parent/child POMs is not supported without cloning the projects" );
616 }
617
618 runBuilds( projectsDir, buildJobs );
619
620 processResults( new InvokerSession( buildJobs ) );
621 }
622
623
624
625
626
627
628
629
630 abstract void processResults( InvokerSession invokerSession )
631 throws MojoFailureException;
632
633
634
635
636
637
638
639
640 private Reader newReader( File file )
641 throws IOException
642 {
643 if ( StringUtils.isNotEmpty( encoding ) )
644 {
645 return ReaderFactory.newReader( file, encoding );
646 }
647 else
648 {
649 return ReaderFactory.newPlatformReader( file );
650 }
651 }
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667 private void collectProjects( File projectsDir, String projectPath, Collection projectPaths, boolean included )
668 throws MojoExecutionException
669 {
670 projectPath = projectPath.replace( '\\', '/' );
671 File pomFile = new File( projectsDir, projectPath );
672 if ( pomFile.isDirectory() )
673 {
674 pomFile = new File( pomFile, "pom.xml" );
675 if ( !pomFile.exists() )
676 {
677 if ( included )
678 {
679 projectPaths.add( projectPath );
680 }
681 return;
682 }
683 if ( !projectPath.endsWith( "/" ) )
684 {
685 projectPath += '/';
686 }
687 projectPath += "pom.xml";
688 }
689 else if ( !pomFile.isFile() )
690 {
691 return;
692 }
693 if ( !projectPaths.add( projectPath ) )
694 {
695 return;
696 }
697 getLog().debug( "Collecting parent/child projects of " + projectPath );
698
699 Model model = PomUtils.loadPom( pomFile );
700
701 try
702 {
703 String projectsRoot = projectsDir.getCanonicalPath();
704 String projectDir = pomFile.getParent();
705
706 String parentPath = "../pom.xml";
707 if ( model.getParent() != null && StringUtils.isNotEmpty( model.getParent().getRelativePath() ) )
708 {
709 parentPath = model.getParent().getRelativePath();
710 }
711 String parent = relativizePath( new File( projectDir, parentPath ), projectsRoot );
712 if ( parent != null )
713 {
714 collectProjects( projectsDir, parent, projectPaths, false );
715 }
716
717 Collection modulePaths = new LinkedHashSet();
718
719 modulePaths.addAll( model.getModules() );
720
721 for ( Iterator it = model.getProfiles().iterator(); it.hasNext(); )
722 {
723 Profile profile = (Profile) it.next();
724 modulePaths.addAll( profile.getModules() );
725 }
726
727 for ( Iterator it = modulePaths.iterator(); it.hasNext(); )
728 {
729 String modulePath = (String) it.next();
730 String module = relativizePath( new File( projectDir, modulePath ), projectsRoot );
731 if ( module != null )
732 {
733 collectProjects( projectsDir, module, projectPaths, false );
734 }
735 }
736 }
737 catch ( IOException e )
738 {
739 throw new MojoExecutionException( "Failed to analyze POM: " + pomFile, e );
740 }
741 }
742
743
744
745
746
747
748
749
750
751 private void cloneProjects( Collection projectPaths )
752 throws MojoExecutionException
753 {
754 cloneProjectsTo.mkdirs();
755
756
757 Collection dirs = new LinkedHashSet();
758 for ( Iterator it = projectPaths.iterator(); it.hasNext(); )
759 {
760 String projectPath = (String) it.next();
761 if ( !new File( projectsDirectory, projectPath ).isDirectory() )
762 {
763 projectPath = getParentPath( projectPath );
764 }
765 dirs.add( projectPath );
766 }
767
768 boolean filter = false;
769
770
771 try
772 {
773 filter = !cloneProjectsTo.getCanonicalFile().equals( projectsDirectory.getCanonicalFile() );
774
775 List clonedSubpaths = new ArrayList();
776
777 for ( Iterator it = dirs.iterator(); it.hasNext(); )
778 {
779 String subpath = (String) it.next();
780
781
782 if ( !".".equals( subpath ) && dirs.contains( getParentPath( subpath ) ) )
783 {
784 continue;
785 }
786
787
788 if ( !alreadyCloned( subpath, clonedSubpaths ) )
789 {
790
791 if ( ".".equals( subpath ) )
792 {
793 String cloneSubdir = relativizePath( cloneProjectsTo, projectsDirectory.getCanonicalPath() );
794
795
796 if ( cloneSubdir != null )
797 {
798 File temp = File.createTempFile( "pre-invocation-clone.", "" );
799 temp.delete();
800 temp.mkdirs();
801
802 copyDirectoryStructure( projectsDirectory, temp );
803
804 FileUtils.deleteDirectory( new File( temp, cloneSubdir ) );
805
806 copyDirectoryStructure( temp, cloneProjectsTo );
807 }
808 else
809 {
810 copyDirectoryStructure( projectsDirectory, cloneProjectsTo );
811 }
812 }
813 else
814 {
815 File srcDir = new File( projectsDirectory, subpath );
816 File dstDir = new File( cloneProjectsTo, subpath );
817 copyDirectoryStructure( srcDir, dstDir );
818 }
819
820 clonedSubpaths.add( subpath );
821 }
822 }
823 }
824 catch ( IOException e )
825 {
826 throw new MojoExecutionException( "Failed to clone projects from: " + projectsDirectory + " to: "
827 + cloneProjectsTo + ". Reason: " + e.getMessage(), e );
828 }
829
830
831 if ( filter )
832 {
833 for ( Iterator it = projectPaths.iterator(); it.hasNext(); )
834 {
835 String projectPath = (String) it.next();
836 File pomFile = new File( cloneProjectsTo, projectPath );
837 if ( pomFile.isFile() )
838 {
839 buildInterpolatedFile( pomFile, pomFile );
840 }
841 }
842 filteredPomPrefix = null;
843 }
844 }
845
846
847
848
849
850
851
852 private String getParentPath( String path )
853 {
854 int lastSep = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) );
855 return ( lastSep < 0 ) ? "." : path.substring( 0, lastSep );
856 }
857
858
859
860
861
862
863
864
865 private void copyDirectoryStructure( File sourceDir, File destDir )
866 throws IOException
867 {
868 DirectoryScanner scanner = new DirectoryScanner();
869 scanner.setBasedir( sourceDir );
870 if ( !cloneAllFiles )
871 {
872 scanner.addDefaultExcludes();
873 }
874 scanner.scan();
875
876
877
878
879 destDir.mkdirs();
880 String[] includedDirs = scanner.getIncludedDirectories();
881 for ( int i = 0; i < includedDirs.length; ++i )
882 {
883 File clonedDir = new File( destDir, includedDirs[i] );
884 clonedDir.mkdirs();
885 }
886
887 String[] includedFiles = scanner.getIncludedFiles();
888 for ( int i = 0; i < includedFiles.length; ++i )
889 {
890 File sourceFile = new File( sourceDir, includedFiles[i] );
891 File destFile = new File( destDir, includedFiles[i] );
892 FileUtils.copyFile( sourceFile, destFile );
893 }
894 }
895
896
897
898
899
900
901
902
903
904
905 static boolean alreadyCloned( String subpath, List clonedSubpaths )
906 {
907 for ( Iterator iter = clonedSubpaths.iterator(); iter.hasNext(); )
908 {
909 String path = (String) iter.next();
910
911 if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) )
912 {
913 return true;
914 }
915 }
916
917 return false;
918 }
919
920
921
922
923
924
925
926
927 private void runBuilds( File projectsDir, BuildJob[] buildJobs )
928 throws MojoExecutionException
929 {
930 if ( !localRepositoryPath.exists() )
931 {
932 localRepositoryPath.mkdirs();
933 }
934
935 File interpolatedSettingsFile = null;
936 if ( settingsFile != null )
937 {
938 if ( cloneProjectsTo != null )
939 {
940 interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() );
941 }
942 else
943 {
944 interpolatedSettingsFile =
945 new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() );
946 }
947 buildInterpolatedFile( settingsFile, interpolatedSettingsFile );
948 }
949
950 try
951 {
952 for ( int i = 0; i < buildJobs.length; i++ )
953 {
954 BuildJob project = buildJobs[i];
955 runBuild( projectsDir, project, interpolatedSettingsFile );
956 }
957 }
958 finally
959 {
960 if ( interpolatedSettingsFile != null && cloneProjectsTo == null )
961 {
962 interpolatedSettingsFile.delete();
963 }
964 }
965 }
966
967
968
969
970
971
972
973
974
975
976 private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile )
977 throws MojoExecutionException
978 {
979 File pomFile = new File( projectsDir, buildJob.getProject() );
980 File basedir;
981 if ( pomFile.isDirectory() )
982 {
983 basedir = pomFile;
984 pomFile = new File( basedir, "pom.xml" );
985 if ( !pomFile.exists() )
986 {
987 pomFile = null;
988 }
989 else
990 {
991 buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" );
992 }
993 }
994 else
995 {
996 basedir = pomFile.getParentFile();
997 }
998
999 getLog().info( "Building: " + buildJob.getProject() );
1000
1001 File interpolatedPomFile = null;
1002 if ( pomFile != null )
1003 {
1004 if ( filteredPomPrefix != null )
1005 {
1006 interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() );
1007 buildInterpolatedFile( pomFile, interpolatedPomFile );
1008 }
1009 else
1010 {
1011 interpolatedPomFile = pomFile;
1012 }
1013 }
1014
1015 InvokerProperties invokerProperties = getInvokerProperties( basedir );
1016
1017
1018 buildJob.setName( invokerProperties.getJobName() );
1019 buildJob.setDescription( invokerProperties.getJobDescription() );
1020
1021 try
1022 {
1023 if ( isSelected( invokerProperties ) )
1024 {
1025 long milliseconds = System.currentTimeMillis();
1026 boolean executed;
1027 try
1028 {
1029 executed = runBuild( basedir, interpolatedPomFile, settingsFile, invokerProperties );
1030 }
1031 finally
1032 {
1033 milliseconds = System.currentTimeMillis() - milliseconds;
1034 buildJob.setTime( milliseconds / 1000.0 );
1035 }
1036
1037 if ( executed )
1038 {
1039
1040 buildJob.setResult( BuildJob.Result.SUCCESS );
1041
1042 if ( !suppressSummaries )
1043 {
1044 getLog().info( "..SUCCESS " + formatTime( buildJob.getTime() ) );
1045 }
1046 }
1047 else
1048 {
1049 buildJob.setResult( BuildJob.Result.SKIPPED );
1050
1051 if ( !suppressSummaries )
1052 {
1053 getLog().info( "..SKIPPED " + formatTime( buildJob.getTime() ) );
1054 }
1055 }
1056 }
1057 else
1058 {
1059 buildJob.setResult( BuildJob.Result.SKIPPED );
1060
1061 if ( !suppressSummaries )
1062 {
1063 getLog().info( "..SKIPPED " );
1064 }
1065 }
1066 }
1067 catch ( BuildErrorException e )
1068 {
1069 buildJob.setResult( BuildJob.Result.ERROR );
1070 buildJob.setFailureMessage( e.getMessage() );
1071
1072 if ( !suppressSummaries )
1073 {
1074 getLog().info( "..ERROR " + formatTime( buildJob.getTime() ) );
1075 getLog().info( " " + e.getMessage() );
1076 }
1077 }
1078 catch ( BuildFailureException e )
1079 {
1080 buildJob.setResult( e.getType() );
1081 buildJob.setFailureMessage( e.getMessage() );
1082
1083 if ( !suppressSummaries )
1084 {
1085 getLog().info( "..FAILED " + formatTime( buildJob.getTime() ) );
1086 getLog().info( " " + e.getMessage() );
1087 }
1088 }
1089 finally
1090 {
1091 if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) )
1092 {
1093 interpolatedPomFile.delete();
1094 }
1095 writeBuildReport( buildJob );
1096 }
1097 }
1098
1099
1100
1101
1102
1103
1104
1105 private boolean isSelected( InvokerProperties invokerProperties )
1106 {
1107 if ( !SelectorUtils.isMavenVersion( invokerProperties.getMavenVersion() ) )
1108 {
1109 return false;
1110 }
1111
1112 if ( !SelectorUtils.isJreVersion( invokerProperties.getJreVersion() ) )
1113 {
1114 return false;
1115 }
1116
1117 if ( !SelectorUtils.isOsFamily( invokerProperties.getOsFamily() ) )
1118 {
1119 return false;
1120 }
1121
1122 return true;
1123 }
1124
1125
1126
1127
1128
1129
1130
1131 private void writeBuildReport( BuildJob buildJob )
1132 throws MojoExecutionException
1133 {
1134 if ( disableReports )
1135 {
1136 return;
1137 }
1138
1139 if ( !reportsDirectory.exists() )
1140 {
1141 reportsDirectory.mkdirs();
1142 }
1143
1144 String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' );
1145 if ( safeFileName.endsWith( "_pom.xml" ) )
1146 {
1147 safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() );
1148 }
1149
1150 File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );
1151 try
1152 {
1153 FileOutputStream fos = new FileOutputStream( reportFile );
1154 try
1155 {
1156 Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() );
1157 BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
1158 writer.write( osw, buildJob );
1159 osw.close();
1160 }
1161 finally
1162 {
1163 fos.close();
1164 }
1165 }
1166 catch ( IOException e )
1167 {
1168 throw new MojoExecutionException( "Failed to write build report " + reportFile, e );
1169 }
1170 }
1171
1172
1173
1174
1175
1176
1177
1178 private String formatTime( double seconds )
1179 {
1180 return secFormat.format( seconds );
1181 }
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196 private boolean runBuild( File basedir, File pomFile, File settingsFile, InvokerProperties invokerProperties )
1197 throws MojoExecutionException, BuildFailureException
1198 {
1199 if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() )
1200 {
1201 Properties props = invokerProperties.getProperties();
1202 getLog().debug( "Using invoker properties:" );
1203 for ( Iterator it = new TreeSet( props.keySet() ).iterator(); it.hasNext(); )
1204 {
1205 String key = (String) it.next();
1206 String value = props.getProperty( key );
1207 getLog().debug( " " + key + " = " + value );
1208 }
1209 }
1210
1211 List goals = getGoals( basedir );
1212
1213 List profiles = getProfiles( basedir );
1214
1215 Map context = new LinkedHashMap();
1216
1217 FileLogger logger = setupLogger( basedir );
1218 try
1219 {
1220 try
1221 {
1222 scriptRunner.run( "selector script", basedir, selectorScript, context, logger,
1223 BuildJob.Result.SKIPPED, false );
1224 }
1225 catch ( BuildErrorException e )
1226 {
1227 throw e;
1228 }
1229 catch ( BuildFailureException e )
1230 {
1231 return false;
1232 }
1233
1234 scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger,
1235 BuildJob.Result.FAILURE_PRE_HOOK, false );
1236
1237 final InvocationRequest request = new DefaultInvocationRequest();
1238
1239 request.setLocalRepositoryDirectory( localRepositoryPath );
1240
1241 request.setUserSettingsFile( settingsFile );
1242
1243 request.setInteractive( false );
1244
1245 request.setShowErrors( showErrors );
1246
1247 request.setDebug( debug );
1248
1249 request.setShowVersion( showVersion );
1250
1251 if ( logger != null )
1252 {
1253 request.setErrorHandler( logger );
1254
1255 request.setOutputHandler( logger );
1256 }
1257
1258 if ( mavenHome != null )
1259 {
1260 invoker.setMavenHome( mavenHome );
1261 request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() );
1262 }
1263
1264 if ( javaHome != null )
1265 {
1266 request.setJavaHome( javaHome );
1267 }
1268
1269 for ( int invocationIndex = 1;; invocationIndex++ )
1270 {
1271 if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) )
1272 {
1273 break;
1274 }
1275
1276 request.setBaseDirectory( basedir );
1277
1278 request.setPomFile( pomFile );
1279
1280 request.setGoals( goals );
1281
1282 request.setProfiles( profiles );
1283
1284 request.setMavenOpts( mavenOpts );
1285
1286 request.setOffline( false );
1287
1288 Properties systemProperties =
1289 getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) );
1290 request.setProperties( systemProperties );
1291
1292 invokerProperties.configureInvocation( request, invocationIndex );
1293
1294 if ( getLog().isDebugEnabled() )
1295 {
1296 try
1297 {
1298 getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() );
1299 getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) );
1300 }
1301 catch ( CommandLineConfigurationException e )
1302 {
1303 getLog().debug( "Failed to display command line: " + e.getMessage() );
1304 }
1305 }
1306
1307 InvocationResult result;
1308
1309 try
1310 {
1311 result = invoker.execute( request );
1312 }
1313 catch ( final MavenInvocationException e )
1314 {
1315 getLog().debug( "Error invoking Maven: " + e.getMessage(), e );
1316 throw new BuildFailureException( "Maven invocation failed. " + e.getMessage(),
1317 BuildJob.Result.FAILURE_BUILD );
1318 }
1319
1320 verify( result, invocationIndex, invokerProperties, logger );
1321 }
1322
1323 scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger,
1324 BuildJob.Result.FAILURE_POST_HOOK, true );
1325 }
1326 finally
1327 {
1328 if ( logger != null )
1329 {
1330 logger.close();
1331 }
1332 }
1333 return true;
1334 }
1335
1336
1337
1338
1339
1340
1341
1342
1343 private FileLogger setupLogger( File basedir )
1344 throws MojoExecutionException
1345 {
1346 FileLogger logger = null;
1347
1348 if ( !noLog )
1349 {
1350 File outputLog = new File( basedir, "build.log" );
1351 try
1352 {
1353 if ( streamLogs )
1354 {
1355 logger = new FileLogger( outputLog, getLog() );
1356 }
1357 else
1358 {
1359 logger = new FileLogger( outputLog );
1360 }
1361
1362 getLog().debug( "build log initialized in: " + outputLog );
1363 }
1364 catch ( IOException e )
1365 {
1366 throw new MojoExecutionException( "Error initializing build logfile in: " + outputLog, e );
1367 }
1368 }
1369
1370 return logger;
1371 }
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382 private Properties getSystemProperties( final File basedir, final String filename )
1383 throws MojoExecutionException
1384 {
1385 Properties collectedTestProperties = new Properties();
1386
1387 if ( testProperties != null )
1388 {
1389 collectedTestProperties.putAll( testProperties );
1390 }
1391
1392 if ( properties != null )
1393 {
1394 collectedTestProperties.putAll( properties );
1395 }
1396
1397 File propertiesFile = null;
1398 if ( filename != null )
1399 {
1400 propertiesFile = new File( basedir, filename );
1401 }
1402 else if ( testPropertiesFile != null )
1403 {
1404 propertiesFile = new File( basedir, testPropertiesFile );
1405 }
1406
1407 if ( propertiesFile != null && propertiesFile.isFile() )
1408 {
1409 InputStream fin = null;
1410 try
1411 {
1412 fin = new FileInputStream( propertiesFile );
1413
1414 Properties loadedProperties = new Properties();
1415 loadedProperties.load( fin );
1416 collectedTestProperties.putAll( loadedProperties );
1417 }
1418 catch ( IOException e )
1419 {
1420 throw new MojoExecutionException( "Error reading system properties from " + propertiesFile );
1421 }
1422 finally
1423 {
1424 IOUtil.close( fin );
1425 }
1426 }
1427
1428 return collectedTestProperties;
1429 }
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440 private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties,
1441 FileLogger logger )
1442 throws BuildFailureException
1443 {
1444 if ( result.getExecutionException() != null )
1445 {
1446 throw new BuildFailureException( "The Maven invocation failed. "
1447 + result.getExecutionException().getMessage(), BuildJob.Result.ERROR );
1448 }
1449 else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) )
1450 {
1451 StringBuffer buffer = new StringBuffer( 256 );
1452 buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " );
1453 if ( logger != null )
1454 {
1455 buffer.append( "See " );
1456 buffer.append( logger.getOutputFile().getAbsolutePath() );
1457 buffer.append( " for details." );
1458 }
1459 else
1460 {
1461 buffer.append( "See console output for details." );
1462 }
1463 throw new BuildFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD );
1464 }
1465 }
1466
1467
1468
1469
1470
1471
1472
1473
1474 List getGoals( final File basedir )
1475 throws MojoExecutionException
1476 {
1477 try
1478 {
1479 return getTokens( basedir, goalsFile, goals );
1480 }
1481 catch ( IOException e )
1482 {
1483 throw new MojoExecutionException( "error reading goals", e );
1484 }
1485 }
1486
1487
1488
1489
1490
1491
1492
1493
1494 List getProfiles( File basedir )
1495 throws MojoExecutionException
1496 {
1497 try
1498 {
1499 return getTokens( basedir, profilesFile, profiles );
1500 }
1501 catch ( IOException e )
1502 {
1503 throw new MojoExecutionException( "error reading profiles", e );
1504 }
1505 }
1506
1507
1508
1509
1510
1511
1512
1513 BuildJob[] getBuildJobs()
1514 throws IOException
1515 {
1516 BuildJob[] buildJobs;
1517
1518 if ( ( pom != null ) && pom.exists() )
1519 {
1520 buildJobs = new BuildJob[] { new BuildJob( pom.getAbsolutePath(), BuildJob.Type.NORMAL ) };
1521 }
1522 else if ( invokerTest != null )
1523 {
1524 String[] testRegexes = StringUtils.split( invokerTest, "," );
1525 List
1526
1527 for ( int i = 0, size = testRegexes.length; i < size; i++ )
1528 {
1529
1530 includes.add( testRegexes[i] );
1531 }
1532
1533
1534
1535 buildJobs = scanProjectsDirectory( includes, null, BuildJob.Type.DIRECT );
1536 }
1537 else
1538 {
1539 List excludes = ( pomExcludes != null ) ? new ArrayList( pomExcludes ) : new ArrayList();
1540 if ( this.settingsFile != null )
1541 {
1542 String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() );
1543 if ( exclude != null )
1544 {
1545 excludes.add( exclude.replace( '\\', '/' ) );
1546 getLog().debug( "Automatically excluded " + exclude + " from project scanning" );
1547 }
1548 }
1549
1550 BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
1551 getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) );
1552
1553 BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL );
1554
1555 Map uniquePoms = new LinkedHashMap();
1556 for ( int i = 0; i < setupPoms.length; i++ )
1557 {
1558 uniquePoms.put( setupPoms[i].getProject(), setupPoms[i] );
1559 }
1560 for ( int i = 0; i < normalPoms.length; i++ )
1561 {
1562 if ( !uniquePoms.containsKey( normalPoms[i].getProject() ) )
1563 {
1564 uniquePoms.put( normalPoms[i].getProject(), normalPoms[i] );
1565 }
1566 }
1567
1568 buildJobs = (BuildJob[]) uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] );
1569 }
1570
1571 relativizeProjectPaths( buildJobs );
1572
1573 return buildJobs;
1574 }
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588 private BuildJob[] scanProjectsDirectory( List includes, List excludes, String type )
1589 throws IOException
1590 {
1591 if ( !projectsDirectory.isDirectory() )
1592 {
1593 return new BuildJob[0];
1594 }
1595
1596 DirectoryScanner scanner = new DirectoryScanner();
1597 scanner.setBasedir( projectsDirectory.getCanonicalFile() );
1598 scanner.setFollowSymlinks( false );
1599 if ( includes != null )
1600 {
1601 scanner.setIncludes( (String[]) includes.toArray( new String[includes.size()] ) );
1602 }
1603 if ( excludes != null )
1604 {
1605 scanner.setExcludes( (String[]) excludes.toArray( new String[excludes.size()] ) );
1606 }
1607 scanner.addDefaultExcludes();
1608 scanner.scan();
1609
1610 Map matches = new LinkedHashMap();
1611
1612 String[] includedFiles = scanner.getIncludedFiles();
1613 for ( int i = 0; i < includedFiles.length; i++ )
1614 {
1615 matches.put( includedFiles[i], new BuildJob( includedFiles[i], type ) );
1616 }
1617
1618 String[] includedDirs = scanner.getIncludedDirectories();
1619 for ( int i = 0; i < includedDirs.length; i++ )
1620 {
1621 String includedFile = includedDirs[i] + File.separatorChar + "pom.xml";
1622 if ( new File( scanner.getBasedir(), includedFile ).isFile() )
1623 {
1624 matches.put( includedFile, new BuildJob( includedFile, type ) );
1625 }
1626 else
1627 {
1628 matches.put( includedDirs[i], new BuildJob( includedDirs[i], type ) );
1629 }
1630 }
1631
1632 return (BuildJob[]) matches.values().toArray( new BuildJob[matches.size()] );
1633 }
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644 private void relativizeProjectPaths( BuildJob[] buildJobs )
1645 throws IOException
1646 {
1647 String projectsDirPath = projectsDirectory.getCanonicalPath();
1648
1649 for ( int i = 0; i < buildJobs.length; i++ )
1650 {
1651 String projectPath = buildJobs[i].getProject();
1652
1653 File file = new File( projectPath );
1654
1655 if ( !file.isAbsolute() )
1656 {
1657 file = new File( projectsDirectory, projectPath );
1658 }
1659
1660 String relativizedPath = relativizePath( file, projectsDirPath );
1661
1662 if ( relativizedPath == null )
1663 {
1664 relativizedPath = projectPath;
1665 }
1666
1667 buildJobs[i].setProject( relativizedPath );
1668 }
1669 }
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681 private String relativizePath( File path, String basedir )
1682 throws IOException
1683 {
1684 String relativizedPath = path.getCanonicalPath();
1685
1686 if ( relativizedPath.startsWith( basedir ) )
1687 {
1688 relativizedPath = relativizedPath.substring( basedir.length() );
1689 if ( relativizedPath.startsWith( File.separator ) )
1690 {
1691 relativizedPath = relativizedPath.substring( File.separator.length() );
1692 }
1693
1694 return relativizedPath;
1695 }
1696 else
1697 {
1698 return null;
1699 }
1700 }
1701
1702
1703
1704
1705
1706
1707 private Map getInterpolationValueSource()
1708 {
1709 Map props = new HashMap();
1710 if ( interpolationsProperties != null )
1711 {
1712 props.putAll( interpolationsProperties );
1713 }
1714 if ( filterProperties != null )
1715 {
1716 props.putAll( filterProperties );
1717 }
1718 props.put( "basedir", this.project.getBasedir().getAbsolutePath() );
1719 props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) );
1720 if ( settings.getLocalRepository() != null )
1721 {
1722 props.put( "localRepository", settings.getLocalRepository() );
1723 props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) );
1724 }
1725 return new CompositeMap( this.project, props );
1726 }
1727
1728
1729
1730
1731
1732
1733
1734
1735 private static String toUrl( String filename )
1736 {
1737
1738
1739
1740
1741 String url = "file://" + new File( filename ).toURI().getPath();
1742 if ( url.endsWith( "/" ) )
1743 {
1744 url = url.substring( 0, url.length() - 1 );
1745 }
1746 return url;
1747 }
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761 private List getTokens( File basedir, String filename, List defaultTokens )
1762 throws IOException
1763 {
1764 List tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList();
1765
1766 if ( StringUtils.isNotEmpty( filename ) )
1767 {
1768 File tokenFile = new File( basedir, filename );
1769
1770 if ( tokenFile.exists() )
1771 {
1772 tokens = readTokens( tokenFile );
1773 }
1774 }
1775
1776 return tokens;
1777 }
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787 private List readTokens( final File tokenFile )
1788 throws IOException
1789 {
1790 List result = new ArrayList();
1791
1792 BufferedReader reader = null;
1793 try
1794 {
1795 Map composite = getInterpolationValueSource();
1796 reader = new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) );
1797
1798 String line = null;
1799 while ( ( line = reader.readLine() ) != null )
1800 {
1801 result.addAll( collectListFromCSV( line ) );
1802 }
1803 }
1804 finally
1805 {
1806 IOUtil.close( reader );
1807 }
1808
1809 return result;
1810 }
1811
1812
1813
1814
1815
1816
1817
1818 private List collectListFromCSV( final String csv )
1819 {
1820 final List result = new ArrayList();
1821
1822 if ( ( csv != null ) && ( csv.trim().length() > 0 ) )
1823 {
1824 final StringTokenizer st = new StringTokenizer( csv, "," );
1825
1826 while ( st.hasMoreTokens() )
1827 {
1828 result.add( st.nextToken().trim() );
1829 }
1830 }
1831
1832 return result;
1833 }
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844 void buildInterpolatedFile( File originalFile, File interpolatedFile )
1845 throws MojoExecutionException
1846 {
1847 getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() );
1848
1849 try
1850 {
1851 String xml;
1852
1853 Reader reader = null;
1854 try
1855 {
1856
1857 Map composite = getInterpolationValueSource();
1858 reader = ReaderFactory.newXmlReader( originalFile );
1859 reader = new InterpolationFilterReader( reader, composite, "@", "@" );
1860 xml = IOUtil.toString( reader );
1861 }
1862 finally
1863 {
1864 IOUtil.close( reader );
1865 }
1866
1867 Writer writer = null;
1868 try
1869 {
1870 interpolatedFile.getParentFile().mkdirs();
1871 writer = WriterFactory.newXmlWriter( interpolatedFile );
1872 writer.write( xml );
1873 writer.flush();
1874 }
1875 finally
1876 {
1877 IOUtil.close( writer );
1878 }
1879 }
1880 catch ( IOException e )
1881 {
1882 throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e );
1883 }
1884 }
1885
1886
1887
1888
1889
1890
1891
1892
1893 private InvokerProperties getInvokerProperties( final File projectDirectory )
1894 throws MojoExecutionException
1895 {
1896 Properties props = new Properties();
1897 if ( invokerPropertiesFile != null )
1898 {
1899 File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
1900 if ( propertiesFile.isFile() )
1901 {
1902 InputStream in = null;
1903 try
1904 {
1905 in = new FileInputStream( propertiesFile );
1906 props.load( in );
1907 }
1908 catch ( IOException e )
1909 {
1910 throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
1911 }
1912 finally
1913 {
1914 IOUtil.close( in );
1915 }
1916 }
1917
1918 Interpolator interpolator = new RegexBasedInterpolator();
1919 interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource() ) );
1920 for ( Iterator it = props.keySet().iterator(); it.hasNext(); )
1921 {
1922 String key = (String) it.next();
1923 String value = props.getProperty( key );
1924 try
1925 {
1926 value = interpolator.interpolate( value, "" );
1927 }
1928 catch ( InterpolationException e )
1929 {
1930 throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,
1931 e );
1932 }
1933 props.setProperty( key, value );
1934 }
1935 }
1936 return new InvokerProperties( props );
1937 }
1938
1939 }