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