1 package org.apache.maven.plugins.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.artifact.Artifact;
23 import org.apache.maven.execution.MavenSession;
24 import org.apache.maven.model.Model;
25 import org.apache.maven.model.Profile;
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecution;
28 import org.apache.maven.plugin.MojoExecutionException;
29 import org.apache.maven.plugin.MojoFailureException;
30 import org.apache.maven.plugins.annotations.Component;
31 import org.apache.maven.plugins.annotations.Parameter;
32 import org.apache.maven.plugins.invoker.model.BuildJob;
33 import org.apache.maven.plugins.invoker.model.io.xpp3.BuildJobXpp3Writer;
34 import org.apache.maven.project.MavenProject;
35 import org.apache.maven.settings.Settings;
36 import org.apache.maven.settings.SettingsUtils;
37 import org.apache.maven.settings.TrackableBase;
38 import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
39 import org.apache.maven.settings.building.SettingsBuilder;
40 import org.apache.maven.settings.building.SettingsBuildingException;
41 import org.apache.maven.settings.building.SettingsBuildingRequest;
42 import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
43 import org.apache.maven.shared.invoker.CommandLineConfigurationException;
44 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
45 import org.apache.maven.shared.invoker.InvocationRequest;
46 import org.apache.maven.shared.invoker.InvocationResult;
47 import org.apache.maven.shared.invoker.Invoker;
48 import org.apache.maven.shared.invoker.MavenCommandLineBuilder;
49 import org.apache.maven.shared.invoker.MavenInvocationException;
50 import org.apache.maven.shared.scriptinterpreter.RunErrorException;
51 import org.apache.maven.shared.scriptinterpreter.RunFailureException;
52 import org.apache.maven.shared.scriptinterpreter.ScriptRunner;
53 import org.apache.maven.shared.utils.logging.MessageBuilder;
54 import org.apache.maven.toolchain.MisconfiguredToolchainException;
55 import org.apache.maven.toolchain.ToolchainManagerPrivate;
56 import org.apache.maven.toolchain.ToolchainPrivate;
57 import org.codehaus.plexus.interpolation.InterpolationException;
58 import org.codehaus.plexus.interpolation.Interpolator;
59 import org.codehaus.plexus.interpolation.MapBasedValueSource;
60 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
61 import org.codehaus.plexus.util.DirectoryScanner;
62 import org.codehaus.plexus.util.FileUtils;
63 import org.codehaus.plexus.util.IOUtil;
64 import org.codehaus.plexus.util.InterpolationFilterReader;
65 import org.codehaus.plexus.util.ReaderFactory;
66 import org.codehaus.plexus.util.ReflectionUtils;
67 import org.codehaus.plexus.util.StringUtils;
68 import org.codehaus.plexus.util.WriterFactory;
69 import org.codehaus.plexus.util.cli.CommandLineException;
70 import org.codehaus.plexus.util.cli.CommandLineUtils;
71 import org.codehaus.plexus.util.cli.Commandline;
72 import org.codehaus.plexus.util.cli.StreamConsumer;
73 import org.codehaus.plexus.util.xml.Xpp3Dom;
74 import org.codehaus.plexus.util.xml.Xpp3DomWriter;
75
76 import java.io.BufferedReader;
77 import java.io.BufferedWriter;
78 import java.io.File;
79 import java.io.FileInputStream;
80 import java.io.FileOutputStream;
81 import java.io.FileWriter;
82 import java.io.IOException;
83 import java.io.InputStream;
84 import java.io.OutputStreamWriter;
85 import java.io.Reader;
86 import java.io.Writer;
87 import java.nio.file.Files;
88 import java.nio.file.Path;
89 import java.nio.file.Paths;
90 import java.text.DecimalFormat;
91 import java.text.DecimalFormatSymbols;
92 import java.util.ArrayList;
93 import java.util.Arrays;
94 import java.util.Collection;
95 import java.util.Collections;
96 import java.util.Comparator;
97 import java.util.HashMap;
98 import java.util.HashSet;
99 import java.util.LinkedHashMap;
100 import java.util.LinkedHashSet;
101 import java.util.LinkedList;
102 import java.util.List;
103 import java.util.Locale;
104 import java.util.Map;
105 import java.util.Map.Entry;
106 import java.util.Properties;
107 import java.util.Set;
108 import java.util.StringTokenizer;
109 import java.util.TreeSet;
110 import java.util.concurrent.ExecutorService;
111 import java.util.concurrent.Executors;
112 import java.util.concurrent.TimeUnit;
113
114 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
115
116
117
118
119
120
121
122 public abstract class AbstractInvokerMojo
123 extends AbstractMojo
124 {
125
126
127
128 private static final int RESULT_COLUMN = 60;
129
130
131
132
133
134
135 @Parameter( property = "invoker.skip", defaultValue = "false" )
136 private boolean skipInvocation;
137
138
139
140
141
142
143
144 @Parameter( defaultValue = "false" )
145 protected boolean suppressSummaries;
146
147
148
149
150 @Parameter( property = "invoker.streamLogs", defaultValue = "false" )
151 private boolean streamLogs;
152
153
154
155
156
157
158 @Parameter( property = "invoker.localRepositoryPath", defaultValue = "${settings.localRepository}" )
159 private File localRepositoryPath;
160
161
162
163
164 @Parameter( property = "invoker.projectsDirectory", defaultValue = "${basedir}/src/it/" )
165 private File projectsDirectory;
166
167
168
169
170
171
172
173
174 @Parameter( property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports" )
175 private File reportsDirectory;
176
177
178
179
180
181
182 @Parameter( property = "invoker.disableReports", defaultValue = "false" )
183 private boolean disableReports;
184
185
186
187
188
189
190
191
192 @Parameter( property = "invoker.cloneProjectsTo" )
193 private File cloneProjectsTo;
194
195
196
197
198
199
200
201
202
203
204
205
206 @Parameter( defaultValue = "false" )
207 private boolean cloneAllFiles;
208
209
210
211
212
213
214
215 @Parameter( defaultValue = "true" )
216 private boolean cloneClean;
217
218
219
220
221 @Parameter( property = "invoker.pom" )
222 private File pom;
223
224
225
226
227
228
229
230
231
232
233
234 @Parameter
235 private List<String> pomIncludes = Collections.singletonList( "*/pom.xml" );
236
237
238
239
240
241
242 @Parameter
243 private List<String> pomExcludes = Collections.emptyList();
244
245
246
247
248
249
250
251
252
253
254 @Parameter
255 private List<String> setupIncludes = Collections.singletonList( "setup*/pom.xml" );
256
257
258
259
260 @Parameter
261 private List<String> goals = Collections.singletonList( "package" );
262
263
264
265 @Component
266 private Invoker invoker;
267
268 @Component
269 private SettingsBuilder settingsBuilder;
270
271 @Component
272 private ToolchainManagerPrivate toolchainManagerPrivate;
273
274
275
276
277
278
279
280
281
282
283
284
285
286 @Parameter( property = "invoker.selectorScript", defaultValue = "selector" )
287 private String selectorScript;
288
289
290
291
292
293
294
295
296
297 @Parameter( property = "invoker.preBuildHookScript", defaultValue = "prebuild" )
298 private String preBuildHookScript;
299
300
301
302
303
304
305
306
307 @Parameter( property = "invoker.postBuildHookScript", defaultValue = "postbuild" )
308 private String postBuildHookScript;
309
310
311
312
313 @Parameter( property = "invoker.testPropertiesFile", defaultValue = "test.properties" )
314 private String testPropertiesFile;
315
316
317
318
319
320
321 @Parameter
322 private Map<String, String> properties;
323
324
325
326
327 @Parameter( property = "invoker.showErrors", defaultValue = "false" )
328 private boolean showErrors;
329
330
331
332
333 @Parameter( property = "invoker.debug", defaultValue = "false" )
334 private boolean debug;
335
336
337
338
339 @Parameter( property = "invoker.noLog", defaultValue = "false" )
340 private boolean noLog;
341
342
343
344
345
346
347
348 @Parameter
349 private File logDirectory;
350
351
352
353
354
355
356 @Parameter
357 private List<String> profiles;
358
359
360
361
362
363
364 @Parameter
365 private Map<String, String> filterProperties;
366
367
368
369
370
371
372 @Parameter( defaultValue = "${project}", readonly = true, required = true )
373 private MavenProject project;
374
375 @Parameter( defaultValue = "${session}", readonly = true, required = true )
376 private MavenSession session;
377
378 @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true )
379 private MojoExecution mojoExecution;
380
381
382
383
384
385
386
387
388
389
390
391
392 @Parameter( property = "invoker.test" )
393 private String invokerTest;
394
395
396
397
398
399
400
401
402 @Parameter( property = "invoker.settingsFile" )
403 private File settingsFile;
404
405
406
407
408
409
410
411 @Parameter( property = "invoker.mavenOpts" )
412 private String mavenOpts;
413
414
415
416
417
418
419
420 @Parameter( property = "invoker.mavenHome" )
421 private File mavenHome;
422
423
424
425
426
427
428
429 @Parameter( property = "invoker.mavenExecutable" )
430 private String mavenExecutable;
431
432
433
434
435
436
437
438 @Parameter( property = "invoker.javaHome" )
439 private File javaHome;
440
441
442
443
444
445
446 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
447 private String encoding;
448
449
450
451
452
453
454 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
455 private Settings settings;
456
457
458
459
460
461
462
463
464
465
466 @Parameter( property = "invoker.addTestClassPath", defaultValue = "false" )
467 private boolean addTestClassPath;
468
469
470
471
472 @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true )
473 private List<String> testClassPath;
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
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580 @Parameter( property = "invoker.invokerPropertiesFile", defaultValue = "invoker.properties" )
581 private String invokerPropertiesFile;
582
583
584
585
586
587
588 @Parameter( property = "invoker.showVersion", defaultValue = "false" )
589 private boolean showVersion;
590
591
592
593
594
595
596 @Parameter( property = "invoker.parallelThreads", defaultValue = "1" )
597 private int parallelThreads;
598
599
600
601
602 @Parameter( property = "plugin.artifacts", required = true, readonly = true )
603 private List<Artifact> pluginArtifacts;
604
605
606
607
608
609
610
611 @Parameter( property = "invoker.mergeUserSettings", defaultValue = "false" )
612 private boolean mergeUserSettings;
613
614
615
616
617
618
619 @Parameter
620 private Map<String, String> environmentVariables;
621
622
623
624
625
626
627 @Parameter
628 private Map<String, String> scriptVariables;
629
630
631
632
633
634 @Parameter( defaultValue = "0", property = "invoker.timeoutInSeconds" )
635 private int timeoutInSeconds;
636
637
638
639
640
641 @Parameter( defaultValue = "false", property = "invoker.writeJunitReport" )
642 private boolean writeJunitReport;
643
644
645
646
647
648 @Parameter( defaultValue = "maven.invoker.it", property = "invoker.junitPackageName" )
649 private String junitPackageName = "maven.invoker.it";
650
651
652
653
654 private ScriptRunner scriptRunner;
655
656
657
658
659
660
661 private String filteredPomPrefix = "interpolated-";
662
663
664
665
666 private final DecimalFormat secFormat = new DecimalFormat( "(0.0 s)", new DecimalFormatSymbols( Locale.ENGLISH ) );
667
668
669
670
671 private String actualMavenVersion;
672
673
674
675
676
677
678
679 public void execute()
680 throws MojoExecutionException, MojoFailureException
681 {
682 if ( skipInvocation )
683 {
684 getLog().info( "Skipping invocation per configuration."
685 + " If this is incorrect, ensure the skipInvocation parameter is not set to true." );
686 return;
687 }
688
689 if ( StringUtils.isEmpty( encoding ) )
690 {
691 getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
692 + ", i.e. build is platform dependent!" );
693 }
694
695
696 if ( !disableReports )
697 {
698 setupReportsFolder();
699 }
700
701 List<BuildJob> buildJobs;
702 if ( pom == null )
703 {
704 try
705 {
706 buildJobs = getBuildJobs();
707 }
708 catch ( final IOException e )
709 {
710 throw new MojoExecutionException( "Error retrieving POM list from includes, "
711 + "excludes, and projects directory. Reason: " + e.getMessage(), e );
712 }
713 }
714 else
715 {
716 try
717 {
718 projectsDirectory = pom.getCanonicalFile().getParentFile();
719 }
720 catch ( IOException e )
721 {
722 throw new MojoExecutionException( "Failed to discover projectsDirectory from "
723 + "pom File parameter. Reason: " + e.getMessage(), e );
724 }
725
726 buildJobs = Collections.singletonList( new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) );
727 }
728
729 if ( buildJobs.isEmpty() )
730 {
731 doFailIfNoProjects();
732
733 getLog().info( "No projects were selected for execution." );
734 return;
735 }
736
737 handleScriptRunnerWithScriptClassPath();
738
739 Collection<String> collectedProjects = new LinkedHashSet<>();
740 for ( BuildJob buildJob : buildJobs )
741 {
742 collectProjects( projectsDirectory, buildJob.getProject(), collectedProjects, true );
743 }
744
745 File projectsDir = projectsDirectory;
746
747 if ( cloneProjectsTo != null )
748 {
749 cloneProjects( collectedProjects );
750 projectsDir = cloneProjectsTo;
751 }
752 else if ( cloneProjectsTo == null && "maven-plugin".equals( project.getPackaging() ) )
753 {
754 cloneProjectsTo = new File( project.getBuild().getDirectory(), "its" );
755 cloneProjects( collectedProjects );
756 projectsDir = cloneProjectsTo;
757 }
758 else
759 {
760 getLog().warn( "Filtering of parent/child POMs is not supported without cloning the projects" );
761 }
762
763
764 List<BuildJob> setupBuildJobs = null;
765 try
766 {
767 setupBuildJobs = getSetupBuildJobsFromFolders();
768 }
769 catch ( IOException e )
770 {
771 getLog().error( "Failure during scanning of folders.", e );
772
773 }
774
775 if ( !setupBuildJobs.isEmpty() )
776 {
777
778
779
780
781 getLog().info( "Running " + setupBuildJobs.size() + " setup job"
782 + ( ( setupBuildJobs.size() < 2 ) ? "" : "s" ) + ":" );
783 runBuilds( projectsDir, setupBuildJobs, 1 );
784 getLog().info( "Setup done." );
785 }
786
787
788 List<BuildJob> nonSetupBuildJobs = getNonSetupJobs( buildJobs );
789
790
791 runBuilds( projectsDir, nonSetupBuildJobs, parallelThreads );
792
793 writeSummaryFile( nonSetupBuildJobs );
794
795 processResults( new InvokerSession( nonSetupBuildJobs ) );
796
797 }
798
799
800
801
802
803
804 private void setupReportsFolder()
805 throws MojoExecutionException
806 {
807
808 if ( reportsDirectory.exists() )
809 {
810 try
811 {
812 FileUtils.deleteDirectory( reportsDirectory );
813 }
814 catch ( IOException e )
815 {
816 throw new MojoExecutionException( "Failure while trying to delete "
817 + reportsDirectory.getAbsolutePath(), e );
818 }
819 }
820 if ( !reportsDirectory.mkdirs() )
821 {
822 throw new MojoExecutionException( "Failure while creating the " + reportsDirectory.getAbsolutePath() );
823 }
824 }
825
826 private List<BuildJob> getNonSetupJobs( List<BuildJob> buildJobs )
827 {
828 List<BuildJob> result = new LinkedList<>();
829 for ( BuildJob buildJob : buildJobs )
830 {
831 if ( !buildJob.getType().equals( BuildJob.Type.SETUP ) )
832 {
833 result.add( buildJob );
834 }
835 }
836 return result;
837 }
838
839 private void handleScriptRunnerWithScriptClassPath()
840 {
841 final List<String> scriptClassPath;
842 if ( addTestClassPath )
843 {
844 scriptClassPath = new ArrayList<>( testClassPath );
845 for ( Artifact pluginArtifact : pluginArtifacts )
846 {
847 scriptClassPath.remove( pluginArtifact.getFile().getAbsolutePath() );
848 }
849 }
850 else
851 {
852 scriptClassPath = null;
853 }
854 scriptRunner = new ScriptRunner( getLog() );
855 scriptRunner.setScriptEncoding( encoding );
856 scriptRunner.setGlobalVariable( "localRepositoryPath", localRepositoryPath );
857 if ( scriptVariables != null )
858 {
859 for ( Entry<String, String> entry : scriptVariables.entrySet() )
860 {
861 scriptRunner.setGlobalVariable( entry.getKey(), entry.getValue() );
862 }
863 }
864 scriptRunner.setClassPath( scriptClassPath );
865 }
866
867 private void writeSummaryFile( List<BuildJob> buildJobs )
868 throws MojoExecutionException
869 {
870
871 File summaryReportFile = new File( reportsDirectory, "invoker-summary.txt" );
872
873 try ( Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) ) )
874 {
875 for ( BuildJob buildJob : buildJobs )
876 {
877 if ( !buildJob.getResult().equals( BuildJob.Result.SUCCESS ) )
878 {
879 writer.append( buildJob.getResult() );
880 writer.append( " [" );
881 writer.append( buildJob.getProject() );
882 writer.append( "] " );
883 if ( buildJob.getFailureMessage() != null )
884 {
885 writer.append( " " );
886 writer.append( buildJob.getFailureMessage() );
887 }
888 writer.append( "\n" );
889 }
890 }
891 }
892 catch ( IOException e )
893 {
894 throw new MojoExecutionException( "Failed to write summary report " + summaryReportFile, e );
895 }
896 }
897
898 protected void doFailIfNoProjects()
899 throws MojoFailureException
900 {
901
902 }
903
904
905
906
907
908
909
910
911 abstract void processResults( InvokerSession invokerSession )
912 throws MojoFailureException;
913
914
915
916
917
918
919
920
921 private Reader newReader( File file )
922 throws IOException
923 {
924 if ( StringUtils.isNotEmpty( encoding ) )
925 {
926 return ReaderFactory.newReader( file, encoding );
927 }
928 else
929 {
930 return ReaderFactory.newPlatformReader( file );
931 }
932 }
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948 private void collectProjects( File projectsDir, String projectPath, Collection<String> projectPaths,
949 boolean included )
950 throws MojoExecutionException
951 {
952 projectPath = projectPath.replace( '\\', '/' );
953 File pomFile = new File( projectsDir, projectPath );
954 if ( pomFile.isDirectory() )
955 {
956 pomFile = new File( pomFile, "pom.xml" );
957 if ( !pomFile.exists() )
958 {
959 if ( included )
960 {
961 projectPaths.add( projectPath );
962 }
963 return;
964 }
965 if ( !projectPath.endsWith( "/" ) )
966 {
967 projectPath += '/';
968 }
969 projectPath += "pom.xml";
970 }
971 else if ( !pomFile.isFile() )
972 {
973 return;
974 }
975 if ( !projectPaths.add( projectPath ) )
976 {
977 return;
978 }
979 getLog().debug( "Collecting parent/child projects of " + projectPath );
980
981 Model model = PomUtils.loadPom( pomFile );
982
983 try
984 {
985 String projectsRoot = projectsDir.getCanonicalPath();
986 String projectDir = pomFile.getParent();
987
988 String parentPath = "../pom.xml";
989 if ( model.getParent() != null && StringUtils.isNotEmpty( model.getParent().getRelativePath() ) )
990 {
991 parentPath = model.getParent().getRelativePath();
992 }
993 String parent = relativizePath( new File( projectDir, parentPath ), projectsRoot );
994 if ( parent != null )
995 {
996 collectProjects( projectsDir, parent, projectPaths, false );
997 }
998
999 Collection<String> modulePaths = new LinkedHashSet<>();
1000
1001 modulePaths.addAll( model.getModules() );
1002
1003 for ( Profile profile : model.getProfiles() )
1004 {
1005 modulePaths.addAll( profile.getModules() );
1006 }
1007
1008 for ( String modulePath : modulePaths )
1009 {
1010 String module = relativizePath( new File( projectDir, modulePath ), projectsRoot );
1011 if ( module != null )
1012 {
1013 collectProjects( projectsDir, module, projectPaths, false );
1014 }
1015 }
1016 }
1017 catch ( IOException e )
1018 {
1019 throw new MojoExecutionException( "Failed to analyze POM: " + pomFile, e );
1020 }
1021 }
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031 private void cloneProjects( Collection<String> projectPaths )
1032 throws MojoExecutionException
1033 {
1034 if ( !cloneProjectsTo.mkdirs() && cloneClean )
1035 {
1036 try
1037 {
1038 FileUtils.cleanDirectory( cloneProjectsTo );
1039 }
1040 catch ( IOException e )
1041 {
1042 throw new MojoExecutionException( "Could not clean the cloneProjectsTo directory. Reason: "
1043 + e.getMessage(), e );
1044 }
1045 }
1046
1047
1048 Collection<String> dirs = new LinkedHashSet<>();
1049 for ( String projectPath : projectPaths )
1050 {
1051 if ( !new File( projectsDirectory, projectPath ).isDirectory() )
1052 {
1053 projectPath = getParentPath( projectPath );
1054 }
1055 dirs.add( projectPath );
1056 }
1057
1058 boolean filter;
1059
1060
1061 try
1062 {
1063 filter = !cloneProjectsTo.getCanonicalFile().equals( projectsDirectory.getCanonicalFile() );
1064
1065 List<String> clonedSubpaths = new ArrayList<>();
1066
1067 for ( String subpath : dirs )
1068 {
1069
1070 if ( !".".equals( subpath ) && dirs.contains( getParentPath( subpath ) ) )
1071 {
1072 continue;
1073 }
1074
1075
1076 if ( !alreadyCloned( subpath, clonedSubpaths ) )
1077 {
1078
1079 if ( ".".equals( subpath ) )
1080 {
1081 String cloneSubdir = relativizePath( cloneProjectsTo, projectsDirectory.getCanonicalPath() );
1082
1083
1084 if ( cloneSubdir != null )
1085 {
1086 File temp = File.createTempFile( "pre-invocation-clone.", "" );
1087 temp.delete();
1088 temp.mkdirs();
1089
1090 copyDirectoryStructure( projectsDirectory, temp );
1091
1092 FileUtils.deleteDirectory( new File( temp, cloneSubdir ) );
1093
1094 copyDirectoryStructure( temp, cloneProjectsTo );
1095 }
1096 else
1097 {
1098 copyDirectoryStructure( projectsDirectory, cloneProjectsTo );
1099 }
1100 }
1101 else
1102 {
1103 File srcDir = new File( projectsDirectory, subpath );
1104 File dstDir = new File( cloneProjectsTo, subpath );
1105 copyDirectoryStructure( srcDir, dstDir );
1106 }
1107
1108 clonedSubpaths.add( subpath );
1109 }
1110 }
1111 }
1112 catch ( IOException e )
1113 {
1114 throw new MojoExecutionException( "Failed to clone projects from: " + projectsDirectory + " to: "
1115 + cloneProjectsTo + ". Reason: " + e.getMessage(), e );
1116 }
1117
1118
1119 if ( filter )
1120 {
1121 for ( String projectPath : projectPaths )
1122 {
1123 File pomFile = new File( cloneProjectsTo, projectPath );
1124 if ( pomFile.isFile() )
1125 {
1126 buildInterpolatedFile( pomFile, pomFile );
1127 }
1128
1129
1130
1131
1132 File baseDir = pomFile.getParentFile();
1133 File mvnDir = new File( baseDir, ".mvn" );
1134 if ( mvnDir.isDirectory() )
1135 {
1136 File extensionsFile = new File( mvnDir, "extensions.xml" );
1137 if ( extensionsFile.isFile() )
1138 {
1139 buildInterpolatedFile( extensionsFile, extensionsFile );
1140 }
1141 }
1142
1143 }
1144 filteredPomPrefix = null;
1145 }
1146 }
1147
1148
1149
1150
1151
1152
1153
1154 private String getParentPath( String path )
1155 {
1156 int lastSep = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) );
1157 return ( lastSep < 0 ) ? "." : path.substring( 0, lastSep );
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167 private void copyDirectoryStructure( File sourceDir, File destDir )
1168 throws IOException
1169 {
1170 DirectoryScanner scanner = new DirectoryScanner();
1171 scanner.setBasedir( sourceDir );
1172 if ( !cloneAllFiles )
1173 {
1174 scanner.addDefaultExcludes();
1175 }
1176 scanner.scan();
1177
1178
1179
1180
1181 destDir.mkdirs();
1182
1183 FileUtils.mkDirs( sourceDir, scanner.getIncludedDirectories(), destDir );
1184
1185 for ( String includedFile : scanner.getIncludedFiles() )
1186 {
1187 File sourceFile = new File( sourceDir, includedFile );
1188 File destFile = new File( destDir, includedFile );
1189 FileUtils.copyFile( sourceFile, destFile );
1190
1191
1192 destFile.setWritable( true );
1193 }
1194 }
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205 static boolean alreadyCloned( String subpath, List<String> clonedSubpaths )
1206 {
1207 for ( String path : clonedSubpaths )
1208 {
1209 if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) )
1210 {
1211 return true;
1212 }
1213 }
1214
1215 return false;
1216 }
1217
1218
1219
1220
1221
1222
1223
1224
1225 private void runBuilds( final File projectsDir, List<BuildJob> buildJobs, int runWithParallelThreads )
1226 throws MojoExecutionException
1227 {
1228 if ( !localRepositoryPath.exists() )
1229 {
1230 localRepositoryPath.mkdirs();
1231 }
1232
1233
1234
1235
1236
1237 File interpolatedSettingsFile = interpolateSettings( settingsFile );
1238
1239 final File mergedSettingsFile = mergeSettings( interpolatedSettingsFile );
1240
1241 if ( mavenHome != null )
1242 {
1243 actualMavenVersion = SelectorUtils.getMavenVersion( mavenHome );
1244 }
1245 else
1246 {
1247 actualMavenVersion = SelectorUtils.getMavenVersion();
1248 }
1249 scriptRunner.setGlobalVariable( "mavenVersion", actualMavenVersion );
1250
1251 final CharSequence actualJreVersion;
1252
1253 if ( javaHome != null )
1254 {
1255 actualJreVersion = resolveExternalJreVersion();
1256 }
1257 else
1258 {
1259 actualJreVersion = SelectorUtils.getJreVersion();
1260 }
1261
1262 final Path projectsPath = this.projectsDirectory.toPath();
1263
1264 Set<Path> folderGroupSet = new HashSet<>();
1265 folderGroupSet.add( Paths.get( "." ) );
1266 for ( BuildJob buildJob : buildJobs )
1267 {
1268 Path p = Paths.get( buildJob.getProject() );
1269
1270 if ( Files.isRegularFile( projectsPath.resolve( p ) ) )
1271 {
1272 p = p.getParent();
1273 }
1274
1275 if ( p != null )
1276 {
1277 p = p.getParent();
1278 }
1279
1280 while ( p != null && folderGroupSet.add( p ) )
1281 {
1282 p = p.getParent();
1283 }
1284 }
1285
1286 List<Path> folderGroup = new ArrayList<>( folderGroupSet );
1287 Collections.sort( folderGroup );
1288
1289 final Map<Path, Properties> globalInvokerProperties = new HashMap<>();
1290
1291 for ( Path path : folderGroup )
1292 {
1293 Properties ancestorProperties = globalInvokerProperties.get( projectsPath.resolve( path ).getParent() );
1294
1295 Path currentInvokerProperties = projectsPath.resolve( path ).resolve( invokerPropertiesFile );
1296
1297 Properties currentProperties;
1298 if ( Files.isRegularFile( currentInvokerProperties ) )
1299 {
1300 if ( ancestorProperties != null )
1301 {
1302 currentProperties = new Properties( ancestorProperties );
1303
1304 }
1305 else
1306 {
1307 currentProperties = new Properties();
1308 }
1309 }
1310 else
1311 {
1312 currentProperties = ancestorProperties;
1313 }
1314
1315 if ( Files.isRegularFile( currentInvokerProperties ) )
1316 {
1317 try ( InputStream in = new FileInputStream( currentInvokerProperties.toFile() ) )
1318 {
1319 currentProperties.load( in );
1320 }
1321 catch ( IOException e )
1322 {
1323 throw new MojoExecutionException( "Failed to read invoker properties: "
1324 + currentInvokerProperties );
1325 }
1326 }
1327
1328 if ( currentProperties != null )
1329 {
1330 globalInvokerProperties.put( projectsPath.resolve( path ).normalize(), currentProperties );
1331 }
1332 }
1333
1334 try
1335 {
1336 if ( runWithParallelThreads > 1 )
1337 {
1338 getLog().info( "use parallelThreads " + runWithParallelThreads );
1339
1340 ExecutorService executorService = Executors.newFixedThreadPool( runWithParallelThreads );
1341 for ( final BuildJob job : buildJobs )
1342 {
1343 executorService.execute( new Runnable()
1344 {
1345 public void run()
1346 {
1347 try
1348 {
1349 Path ancestorFolder = getAncestorFolder( projectsPath.resolve( job.getProject() ) );
1350
1351 runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion,
1352 globalInvokerProperties.get( ancestorFolder ) );
1353 }
1354 catch ( MojoExecutionException e )
1355 {
1356 throw new RuntimeException( e.getMessage(), e );
1357 }
1358 }
1359 } );
1360 }
1361
1362 try
1363 {
1364 executorService.shutdown();
1365
1366 executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
1367 }
1368 catch ( InterruptedException e )
1369 {
1370 throw new MojoExecutionException( e.getMessage(), e );
1371 }
1372 }
1373 else
1374 {
1375 for ( BuildJob job : buildJobs )
1376 {
1377 Path ancestorFolder = getAncestorFolder( projectsPath.resolve( job.getProject() ) );
1378
1379 runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion,
1380 globalInvokerProperties.get( ancestorFolder ) );
1381 }
1382 }
1383 }
1384 finally
1385 {
1386 if ( interpolatedSettingsFile != null && cloneProjectsTo == null )
1387 {
1388 interpolatedSettingsFile.delete();
1389 }
1390 if ( mergedSettingsFile != null && mergedSettingsFile.exists() )
1391 {
1392 mergedSettingsFile.delete();
1393 }
1394 }
1395 }
1396
1397 private Path getAncestorFolder( Path p )
1398 {
1399 Path ancestor = p;
1400 if ( Files.isRegularFile( ancestor ) )
1401 {
1402 ancestor = ancestor.getParent();
1403 }
1404 if ( ancestor != null )
1405 {
1406 ancestor = ancestor.getParent();
1407 }
1408 return ancestor;
1409 }
1410
1411
1412
1413
1414
1415
1416
1417
1418 private File interpolateSettings( File settingsFile )
1419 throws MojoExecutionException
1420 {
1421 File interpolatedSettingsFile = null;
1422 if ( settingsFile != null )
1423 {
1424 if ( cloneProjectsTo != null )
1425 {
1426 interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() );
1427 }
1428 else
1429 {
1430 interpolatedSettingsFile =
1431 new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() );
1432 }
1433 buildInterpolatedFile( settingsFile, interpolatedSettingsFile );
1434 }
1435 return interpolatedSettingsFile;
1436 }
1437
1438
1439
1440
1441
1442
1443
1444
1445 private File mergeSettings( File interpolatedSettingsFile )
1446 throws MojoExecutionException
1447 {
1448 File mergedSettingsFile;
1449 Settings mergedSettings = this.settings;
1450 if ( mergeUserSettings )
1451 {
1452 if ( interpolatedSettingsFile != null )
1453 {
1454
1455 try
1456 {
1457 SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
1458 request.setGlobalSettingsFile( interpolatedSettingsFile );
1459
1460 Settings dominantSettings = settingsBuilder.build( request ).getEffectiveSettings();
1461 Settings recessiveSettings = cloneSettings();
1462 SettingsUtils.merge( dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL );
1463
1464 mergedSettings = dominantSettings;
1465 getLog().debug( "Merged specified settings file with settings of invoking process" );
1466 }
1467 catch ( SettingsBuildingException e )
1468 {
1469 throw new MojoExecutionException( "Could not read specified settings file", e );
1470 }
1471 }
1472 }
1473
1474 if ( this.settingsFile != null && !mergeUserSettings )
1475 {
1476 mergedSettingsFile = interpolatedSettingsFile;
1477 }
1478 else
1479 {
1480 try
1481 {
1482 mergedSettingsFile = writeMergedSettingsFile( mergedSettings );
1483 }
1484 catch ( IOException e )
1485 {
1486 throw new MojoExecutionException( "Could not create temporary file for invoker settings.xml", e );
1487 }
1488 }
1489 return mergedSettingsFile;
1490 }
1491
1492 private File writeMergedSettingsFile( Settings mergedSettings )
1493 throws IOException
1494 {
1495 File mergedSettingsFile;
1496 mergedSettingsFile = File.createTempFile( "invoker-settings", ".xml" );
1497
1498 SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
1499
1500
1501 try ( FileWriter fileWriter = new FileWriter( mergedSettingsFile ) )
1502 {
1503 settingsWriter.write( fileWriter, mergedSettings );
1504 }
1505
1506 if ( getLog().isDebugEnabled() )
1507 {
1508 getLog().debug( "Created temporary file for invoker settings.xml: "
1509 + mergedSettingsFile.getAbsolutePath() );
1510 }
1511 return mergedSettingsFile;
1512 }
1513
1514 private Settings cloneSettings()
1515 {
1516 Settings recessiveSettings = SettingsUtils.copySettings( this.settings );
1517
1518
1519 resetSourceLevelSet( recessiveSettings );
1520 for ( org.apache.maven.settings.Mirror mirror : recessiveSettings.getMirrors() )
1521 {
1522 resetSourceLevelSet( mirror );
1523 }
1524 for ( org.apache.maven.settings.Server server : recessiveSettings.getServers() )
1525 {
1526 resetSourceLevelSet( server );
1527 }
1528 for ( org.apache.maven.settings.Proxy proxy : recessiveSettings.getProxies() )
1529 {
1530 resetSourceLevelSet( proxy );
1531 }
1532 for ( org.apache.maven.settings.Profile profile : recessiveSettings.getProfiles() )
1533 {
1534 resetSourceLevelSet( profile );
1535 }
1536
1537 return recessiveSettings;
1538 }
1539
1540 private void resetSourceLevelSet( org.apache.maven.settings.TrackableBase trackable )
1541 {
1542 try
1543 {
1544 ReflectionUtils.setVariableValueInObject( trackable, "sourceLevelSet", Boolean.FALSE );
1545 getLog().debug( "sourceLevelSet: "
1546 + ReflectionUtils.getValueIncludingSuperclasses( "sourceLevelSet", trackable ) );
1547 }
1548 catch ( IllegalAccessException e )
1549 {
1550
1551 }
1552 }
1553
1554 private CharSequence resolveExternalJreVersion()
1555 {
1556 Artifact pluginArtifact = mojoExecution.getMojoDescriptor().getPluginDescriptor().getPluginArtifact();
1557 pluginArtifact.getFile();
1558
1559 Commandline commandLine = new Commandline();
1560 commandLine.setExecutable( new File( javaHome, "bin/java" ).getAbsolutePath() );
1561 commandLine.createArg().setValue( "-cp" );
1562 commandLine.createArg().setFile( pluginArtifact.getFile() );
1563 commandLine.createArg().setValue( SystemPropertyPrinter.class.getName() );
1564 commandLine.createArg().setValue( "java.version" );
1565
1566 final StringBuilder actualJreVersion = new StringBuilder();
1567 StreamConsumer consumer = new StreamConsumer()
1568 {
1569 public void consumeLine( String line )
1570 {
1571 actualJreVersion.append( line );
1572 }
1573 };
1574 try
1575 {
1576 CommandLineUtils.executeCommandLine( commandLine, consumer, null );
1577 }
1578 catch ( CommandLineException e )
1579 {
1580 getLog().warn( e.getMessage() );
1581 }
1582 return actualJreVersion;
1583 }
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594 private File interpolatePomFile( File pomFile, File basedir )
1595 throws MojoExecutionException
1596 {
1597 File interpolatedPomFile = null;
1598 if ( pomFile != null )
1599 {
1600 if ( StringUtils.isNotEmpty( filteredPomPrefix ) )
1601 {
1602 interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() );
1603 buildInterpolatedFile( pomFile, interpolatedPomFile );
1604 }
1605 else
1606 {
1607 interpolatedPomFile = pomFile;
1608 }
1609 }
1610 return interpolatedPomFile;
1611 }
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623 private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome,
1624 CharSequence actualJreVersion, Properties globalInvokerProperties )
1625 throws MojoExecutionException
1626 {
1627
1628 File pomFile = new File( projectsDir, buildJob.getProject() );
1629 File basedir;
1630 if ( pomFile.isDirectory() )
1631 {
1632 basedir = pomFile;
1633 pomFile = new File( basedir, "pom.xml" );
1634 if ( !pomFile.exists() )
1635 {
1636 pomFile = null;
1637 }
1638 else
1639 {
1640 buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" );
1641 }
1642 }
1643 else
1644 {
1645 basedir = pomFile.getParentFile();
1646 }
1647
1648 File interpolatedPomFile = interpolatePomFile( pomFile, basedir );
1649
1650
1651 getLog().info( buffer().a( "Building: " ).strong( buildJob.getProject() ).toString() );
1652
1653 InvokerProperties invokerProperties = getInvokerProperties( basedir, globalInvokerProperties );
1654
1655
1656 buildJob.setName( invokerProperties.getJobName() );
1657 buildJob.setDescription( invokerProperties.getJobDescription() );
1658 ExecutionResult executionResult = null;
1659
1660 try
1661 {
1662 int selection = getSelection( invokerProperties, actualJreVersion );
1663 if ( selection == 0 )
1664 {
1665 long milliseconds = System.currentTimeMillis();
1666 boolean executed;
1667 try
1668 {
1669
1670 executionResult =
1671 runBuild( basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties );
1672
1673 executed = executionResult.executed;
1674 }
1675 finally
1676 {
1677 milliseconds = System.currentTimeMillis() - milliseconds;
1678 buildJob.setTime( milliseconds / 1000.0 );
1679 }
1680
1681 if ( executed )
1682 {
1683 buildJob.setResult( BuildJob.Result.SUCCESS );
1684
1685 if ( !suppressSummaries )
1686 {
1687 getLog().info( pad( buildJob ).success( "SUCCESS" ).a( ' ' )
1688 + formatTime( buildJob.getTime() ) );
1689 }
1690 }
1691 else
1692 {
1693 buildJob.setResult( BuildJob.Result.SKIPPED );
1694
1695 if ( !suppressSummaries )
1696 {
1697 getLog().info( pad( buildJob ).warning( "SKIPPED" ).a( ' ' )
1698 + formatTime( buildJob.getTime() ) );
1699 }
1700 }
1701 }
1702 else
1703 {
1704 buildJob.setResult( BuildJob.Result.SKIPPED );
1705
1706 StringBuilder message = new StringBuilder();
1707 if ( selection == Selector.SELECTOR_MULTI )
1708 {
1709 message.append( "non-matching selectors" );
1710 }
1711 else
1712 {
1713 if ( ( selection & Selector.SELECTOR_MAVENVERSION ) != 0 )
1714 {
1715 message.append( "Maven version" );
1716 }
1717 if ( ( selection & Selector.SELECTOR_JREVERSION ) != 0 )
1718 {
1719 if ( message.length() > 0 )
1720 {
1721 message.append( ", " );
1722 }
1723 message.append( "JRE version" );
1724 }
1725 if ( ( selection & Selector.SELECTOR_OSFAMILY ) != 0 )
1726 {
1727 if ( message.length() > 0 )
1728 {
1729 message.append( ", " );
1730 }
1731 message.append( "OS" );
1732 }
1733 if ( ( selection & Selector.SELECTOR_TOOLCHAIN ) != 0 )
1734 {
1735 if ( message.length() > 0 )
1736 {
1737 message.append( ", " );
1738 }
1739 message.append( "Toolchain" );
1740 }
1741 }
1742
1743 if ( !suppressSummaries )
1744 {
1745 getLog().info( pad( buildJob ).warning( "SKIPPED" ) + " due to " + message.toString() );
1746 }
1747
1748
1749
1750 buildJob.setFailureMessage( "Skipped due to " + message.toString() );
1751 }
1752 }
1753 catch ( RunErrorException e )
1754 {
1755 buildJob.setResult( BuildJob.Result.ERROR );
1756 buildJob.setFailureMessage( e.getMessage() );
1757
1758 if ( !suppressSummaries )
1759 {
1760 getLog().info( " " + e.getMessage() );
1761 getLog().info( pad( buildJob ).failure( "ERROR" ).a( ' ' ) + formatTime( buildJob.getTime() ) );
1762 }
1763 }
1764 catch ( RunFailureException e )
1765 {
1766 buildJob.setResult( e.getType() );
1767 buildJob.setFailureMessage( e.getMessage() );
1768
1769 if ( !suppressSummaries )
1770 {
1771 getLog().info( " " + e.getMessage() );
1772 getLog().info( pad( buildJob ).failure( "FAILED" ).a( ' ' ) + formatTime( buildJob.getTime() ) );
1773 }
1774 }
1775 finally
1776 {
1777 deleteInterpolatedPomFile( interpolatedPomFile );
1778 writeBuildReport( buildJob, executionResult );
1779 }
1780 }
1781
1782 private MessageBuilder pad( BuildJob buildJob )
1783 {
1784 MessageBuilder buffer = buffer( 128 );
1785
1786 buffer.a( " " );
1787 buffer.a( buildJob.getProject() );
1788
1789 int l = 10 + buildJob.getProject().length();
1790
1791 if ( l < RESULT_COLUMN )
1792 {
1793 buffer.a( ' ' );
1794 l++;
1795
1796 if ( l < RESULT_COLUMN )
1797 {
1798 for ( int i = RESULT_COLUMN - l; i > 0; i-- )
1799 {
1800 buffer.a( '.' );
1801 }
1802 }
1803 }
1804
1805 return buffer.a( ' ' );
1806 }
1807
1808
1809
1810
1811
1812
1813 private void deleteInterpolatedPomFile( File interpolatedPomFile )
1814 {
1815 if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) )
1816 {
1817 interpolatedPomFile.delete();
1818 }
1819 }
1820
1821
1822
1823
1824
1825
1826
1827
1828 private int getSelection( InvokerProperties invokerProperties, CharSequence actualJreVersion )
1829 {
1830 return new Selector( actualMavenVersion, actualJreVersion.toString(),
1831 getToolchainPrivateManager() ).getSelection( invokerProperties );
1832 }
1833
1834 private ToolchainPrivateManager getToolchainPrivateManager()
1835 {
1836 return new ToolchainPrivateManager( toolchainManagerPrivate, session );
1837 }
1838
1839
1840
1841
1842
1843
1844
1845 private void writeBuildReport( BuildJob buildJob, ExecutionResult executionResult )
1846 throws MojoExecutionException
1847 {
1848 if ( disableReports )
1849 {
1850 return;
1851 }
1852
1853 String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' );
1854 if ( safeFileName.endsWith( "_pom.xml" ) )
1855 {
1856 safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() );
1857 }
1858
1859 File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );
1860 try ( FileOutputStream fos = new FileOutputStream( reportFile );
1861 Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )
1862 {
1863 BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
1864
1865 writer.write( osw, buildJob );
1866 }
1867 catch ( IOException e )
1868 {
1869 throw new MojoExecutionException( "Failed to write build report " + reportFile, e );
1870 }
1871
1872 if ( writeJunitReport )
1873 {
1874 writeJunitReport( buildJob, safeFileName, executionResult );
1875 }
1876 }
1877
1878 private void writeJunitReport( BuildJob buildJob, String safeFileName, ExecutionResult executionResult )
1879 throws MojoExecutionException
1880 {
1881 File reportFile = new File( reportsDirectory, "TEST-" + safeFileName + ".xml" );
1882 Xpp3Dom testsuite = new Xpp3Dom( "testsuite" );
1883 testsuite.setAttribute( "name", junitPackageName + "." + safeFileName );
1884 testsuite.setAttribute( "time", Double.toString( buildJob.getTime() ) );
1885
1886
1887 testsuite.setAttribute( "tests", "1" );
1888 testsuite.setAttribute( "errors", "0" );
1889 testsuite.setAttribute( "skipped", "0" );
1890 testsuite.setAttribute( "failures", "0" );
1891
1892 Xpp3Dom testcase = new Xpp3Dom( "testcase" );
1893 testsuite.addChild( testcase );
1894 switch ( buildJob.getResult() )
1895 {
1896 case BuildJob.Result.SUCCESS:
1897 break;
1898 case BuildJob.Result.SKIPPED:
1899 testsuite.setAttribute( "skipped", "1" );
1900
1901 Xpp3Dom skipped = new Xpp3Dom( "skipped" );
1902 testcase.addChild( skipped );
1903 skipped.setValue( buildJob.getFailureMessage() );
1904 break;
1905 case BuildJob.Result.ERROR:
1906 testsuite.setAttribute( "errors", "1" );
1907 break;
1908 default:
1909 testsuite.setAttribute( "failures", "1" );
1910
1911 Xpp3Dom failure = new Xpp3Dom( "failure" );
1912 testcase.addChild( failure );
1913 failure.setAttribute( "message", buildJob.getFailureMessage() );
1914 }
1915 testcase.setAttribute( "classname", junitPackageName + "." + safeFileName );
1916 testcase.setAttribute( "name", safeFileName );
1917 testcase.setAttribute( "time", Double.toString( buildJob.getTime() ) );
1918 Xpp3Dom systemOut = new Xpp3Dom( "system-out" );
1919 testcase.addChild( systemOut );
1920
1921 if ( executionResult != null && executionResult.fileLogger != null )
1922 {
1923 getLog().info( "fileLogger:" + executionResult.fileLogger.getOutputFile() );
1924 try
1925 {
1926 systemOut.setValue( FileUtils.fileRead( executionResult.fileLogger.getOutputFile() ) );
1927 }
1928 catch ( IOException e )
1929 {
1930 throw new MojoExecutionException( "Failed to read logfile " + executionResult.fileLogger.getOutputFile()
1931 , e );
1932 }
1933 }
1934 else
1935 {
1936 getLog().info( safeFileName + ", executionResult:" + executionResult );
1937 }
1938 try ( FileOutputStream fos = new FileOutputStream( reportFile );
1939 Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )
1940 {
1941 Xpp3DomWriter.write( osw, testsuite );
1942 } catch ( IOException e )
1943 {
1944 throw new MojoExecutionException( "Failed to write JUnit build report " + reportFile, e );
1945 }
1946 }
1947
1948
1949
1950
1951
1952
1953
1954 private String formatTime( double seconds )
1955 {
1956 return secFormat.format( seconds );
1957 }
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973 private ExecutionResult runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome,
1974 InvokerProperties invokerProperties )
1975 throws MojoExecutionException, RunFailureException
1976 {
1977 if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() )
1978 {
1979 Properties props = invokerProperties.getProperties();
1980 getLog().debug( "Using invoker properties:" );
1981 for ( String key : new TreeSet<String>( props.stringPropertyNames() ) )
1982 {
1983 String value = props.getProperty( key );
1984 getLog().debug( " " + key + " = " + value );
1985 }
1986 }
1987
1988 List<String> goals = getGoals( basedir );
1989
1990 List<String> profiles = getProfiles( basedir );
1991
1992 Map<String, Object> context = new LinkedHashMap<>();
1993
1994 FileLogger logger = setupBuildLogFile( basedir );
1995 boolean selectorResult = true;
1996 ExecutionResult executionResult = new ExecutionResult();
1997 try
1998 {
1999 try
2000 {
2001 scriptRunner.run( "selector script", basedir, selectorScript, context, logger, BuildJob.Result.SKIPPED,
2002 false );
2003 }
2004 catch ( RunErrorException e )
2005 {
2006 selectorResult = false;
2007 throw e;
2008 }
2009 catch ( RunFailureException e )
2010 {
2011 selectorResult = false;
2012 executionResult.executed = false;
2013 return executionResult;
2014 }
2015
2016 scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger,
2017 BuildJob.Result.FAILURE_PRE_HOOK, false );
2018
2019 final InvocationRequest request = new DefaultInvocationRequest();
2020
2021 request.setLocalRepositoryDirectory( localRepositoryPath );
2022
2023 request.setBatchMode( true );
2024
2025 request.setShowErrors( showErrors );
2026
2027 request.setDebug( debug );
2028
2029 request.setShowVersion( showVersion );
2030
2031 setupLoggerForBuildJob( logger, request );
2032
2033 if ( mavenHome != null )
2034 {
2035 invoker.setMavenHome( mavenHome );
2036
2037 request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() );
2038 }
2039
2040 if ( mavenExecutable != null )
2041 {
2042 invoker.setMavenExecutable( new File( mavenExecutable ) );
2043 }
2044
2045 if ( actualJavaHome != null )
2046 {
2047 request.setJavaHome( actualJavaHome );
2048 }
2049
2050 if ( environmentVariables != null )
2051 {
2052 for ( Map.Entry<String, String> variable : environmentVariables.entrySet() )
2053 {
2054 request.addShellEnvironment( variable.getKey(), variable.getValue() );
2055 }
2056 }
2057
2058 for ( int invocationIndex = 1;; invocationIndex++ )
2059 {
2060 if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) )
2061 {
2062 break;
2063 }
2064
2065 request.setBaseDirectory( basedir );
2066
2067 request.setPomFile( pomFile );
2068
2069 request.setGoals( goals );
2070
2071 request.setProfiles( profiles );
2072
2073 request.setMavenOpts( mavenOpts );
2074
2075 request.setOffline( false );
2076
2077 int timeOut = invokerProperties.getTimeoutInSeconds( invocationIndex );
2078
2079 request.setTimeoutInSeconds( timeOut < 0 ? timeoutInSeconds : timeOut );
2080
2081 String customSettingsFile = invokerProperties.getSettingsFile( invocationIndex );
2082 if ( customSettingsFile != null )
2083 {
2084 File interpolateSettingsFile = interpolateSettings( new File( customSettingsFile ) );
2085 File mergeSettingsFile = mergeSettings( interpolateSettingsFile );
2086
2087 request.setUserSettingsFile( mergeSettingsFile );
2088 }
2089 else
2090 {
2091 request.setUserSettingsFile( settingsFile );
2092 }
2093
2094 Properties systemProperties =
2095 getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) );
2096 request.setProperties( systemProperties );
2097
2098 invokerProperties.configureInvocation( request, invocationIndex );
2099
2100 if ( getLog().isDebugEnabled() )
2101 {
2102 try
2103 {
2104 getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() );
2105 getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) );
2106 }
2107 catch ( CommandLineConfigurationException e )
2108 {
2109 getLog().debug( "Failed to display command line: " + e.getMessage() );
2110 }
2111 }
2112
2113 try
2114 {
2115 InvocationResult result = invoker.execute( request );
2116 verify( result, invocationIndex, invokerProperties, logger );
2117 }
2118 catch ( final MavenInvocationException e )
2119 {
2120 getLog().debug( "Error invoking Maven: " + e.getMessage(), e );
2121 throw new RunFailureException( "Maven invocation failed. " + e.getMessage(),
2122 BuildJob.Result.FAILURE_BUILD );
2123 }
2124 }
2125 }
2126 catch ( IOException e )
2127 {
2128 throw new MojoExecutionException( e.getMessage(), e );
2129 }
2130 finally
2131 {
2132 if ( selectorResult )
2133 {
2134 runPostBuildHook( basedir, context, logger );
2135 }
2136 if ( logger != null )
2137 {
2138 logger.close();
2139 }
2140 }
2141 executionResult.executed = true;
2142 executionResult. fileLogger = logger;
2143 return executionResult;
2144 }
2145
2146 private static class ExecutionResult
2147 {
2148 boolean executed;
2149 FileLogger fileLogger;
2150
2151 @Override
2152 public String toString()
2153 {
2154 return "ExecutionResult{" + "executed=" + executed + ", fileLogger=" + fileLogger + '}';
2155 }
2156 }
2157
2158 private void runPostBuildHook( File basedir, Map<String, Object> context, FileLogger logger )
2159 throws MojoExecutionException, RunFailureException
2160 {
2161 try
2162 {
2163 scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger,
2164 BuildJob.Result.FAILURE_POST_HOOK, true );
2165 }
2166 catch ( IOException e )
2167 {
2168 throw new MojoExecutionException( e.getMessage(), e );
2169 }
2170 }
2171 private void setupLoggerForBuildJob( FileLogger logger, final InvocationRequest request )
2172 {
2173 if ( logger != null )
2174 {
2175 request.setErrorHandler( logger );
2176
2177 request.setOutputHandler( logger );
2178 }
2179 }
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189 private FileLogger setupBuildLogFile( File basedir )
2190 throws MojoExecutionException
2191 {
2192 FileLogger logger = null;
2193
2194 if ( !noLog )
2195 {
2196 Path projectLogDirectory;
2197 if ( logDirectory == null )
2198 {
2199 projectLogDirectory = basedir.toPath();
2200 }
2201 else if ( cloneProjectsTo != null )
2202 {
2203 projectLogDirectory =
2204 logDirectory.toPath().resolve( cloneProjectsTo.toPath().relativize( basedir.toPath() ) );
2205 }
2206 else
2207 {
2208 projectLogDirectory =
2209 logDirectory.toPath().resolve( projectsDirectory.toPath().relativize( basedir.toPath() ) );
2210 }
2211
2212 try
2213 {
2214 if ( streamLogs )
2215 {
2216 logger = new FileLogger( projectLogDirectory.resolve( "build.log" ).toFile(), getLog() );
2217 }
2218 else
2219 {
2220 logger = new FileLogger( projectLogDirectory.resolve( "build.log" ).toFile() );
2221 }
2222
2223 getLog().debug( "Build log initialized in: " + projectLogDirectory );
2224 }
2225 catch ( IOException e )
2226 {
2227 throw new MojoExecutionException( "Error initializing build logfile in: " + projectLogDirectory, e );
2228 }
2229 }
2230
2231 return logger;
2232 }
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243 private Properties getSystemProperties( final File basedir, final String filename )
2244 throws MojoExecutionException
2245 {
2246 Properties collectedTestProperties = new Properties();
2247
2248 if ( properties != null )
2249 {
2250
2251 for ( Map.Entry<String, String> entry : properties.entrySet() )
2252 {
2253 if ( entry.getValue() != null )
2254 {
2255 collectedTestProperties.put( entry.getKey(), entry.getValue() );
2256 }
2257 }
2258 }
2259
2260 File propertiesFile = null;
2261 if ( filename != null )
2262 {
2263 propertiesFile = new File( basedir, filename );
2264 }
2265 else if ( testPropertiesFile != null )
2266 {
2267 propertiesFile = new File( basedir, testPropertiesFile );
2268 }
2269
2270 if ( propertiesFile != null && propertiesFile.isFile() )
2271 {
2272
2273 try ( InputStream fin = new FileInputStream( propertiesFile ) )
2274 {
2275 Properties loadedProperties = new Properties();
2276 loadedProperties.load( fin );
2277 collectedTestProperties.putAll( loadedProperties );
2278 }
2279 catch ( IOException e )
2280 {
2281 throw new MojoExecutionException( "Error reading system properties from " + propertiesFile );
2282 }
2283 }
2284
2285 return collectedTestProperties;
2286 }
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298 private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties,
2299 FileLogger logger )
2300 throws RunFailureException
2301 {
2302 if ( result.getExecutionException() != null )
2303 {
2304 throw new RunFailureException( "The Maven invocation failed. "
2305 + result.getExecutionException().getMessage(), BuildJob.Result.ERROR );
2306 }
2307 else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) )
2308 {
2309 StringBuilder buffer = new StringBuilder( 256 );
2310 buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " );
2311 if ( logger != null )
2312 {
2313 buffer.append( "See " );
2314 buffer.append( logger.getOutputFile().getAbsolutePath() );
2315 buffer.append( " for details." );
2316 }
2317 else
2318 {
2319 buffer.append( "See console output for details." );
2320 }
2321 throw new RunFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD );
2322 }
2323 }
2324
2325
2326
2327
2328
2329
2330
2331
2332 List<String> getGoals( final File basedir )
2333 throws MojoExecutionException
2334 {
2335 try
2336 {
2337
2338
2339 return getTokens( basedir, null, goals );
2340 }
2341 catch ( IOException e )
2342 {
2343 throw new MojoExecutionException( "error reading goals", e );
2344 }
2345 }
2346
2347
2348
2349
2350
2351
2352
2353
2354 List<String> getProfiles( File basedir )
2355 throws MojoExecutionException
2356 {
2357 try
2358 {
2359 return getTokens( basedir, null, profiles );
2360 }
2361 catch ( IOException e )
2362 {
2363 throw new MojoExecutionException( "error reading profiles", e );
2364 }
2365 }
2366
2367 private List<String> calculateExcludes()
2368 throws IOException
2369 {
2370 List<String> excludes =
2371 ( pomExcludes != null ) ? new ArrayList<>( pomExcludes ) : new ArrayList<String>();
2372 if ( this.settingsFile != null )
2373 {
2374 String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() );
2375 if ( exclude != null )
2376 {
2377 excludes.add( exclude.replace( '\\', '/' ) );
2378 getLog().debug( "Automatically excluded " + exclude + " from project scanning" );
2379 }
2380 }
2381 return excludes;
2382
2383 }
2384
2385
2386
2387
2388
2389
2390 private List<BuildJob> getSetupBuildJobsFromFolders()
2391 throws IOException, MojoExecutionException
2392 {
2393 List<String> excludes = calculateExcludes();
2394
2395 List<BuildJob> setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
2396 if ( getLog().isDebugEnabled() )
2397 {
2398 getLog().debug( "Setup projects: " + setupPoms );
2399 }
2400
2401 return setupPoms;
2402 }
2403
2404 private static class OrdinalComparator implements Comparator
2405 {
2406 private static final OrdinalComparator INSTANCE = new OrdinalComparator();
2407
2408 @Override
2409 public int compare( Object o1, Object o2 )
2410 {
2411 return Integer.compare( ( ( BuildJob ) o2 ).getOrdinal(), ( ( BuildJob ) o1 ).getOrdinal() );
2412 }
2413 }
2414
2415
2416
2417
2418
2419
2420
2421 List<BuildJob> getBuildJobs()
2422 throws IOException, MojoExecutionException
2423 {
2424 List<BuildJob> buildJobs;
2425
2426 if ( invokerTest == null )
2427 {
2428 List<String> excludes = calculateExcludes();
2429
2430 List<BuildJob> setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
2431 if ( getLog().isDebugEnabled() )
2432 {
2433 getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) );
2434 }
2435
2436 List<BuildJob> normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL );
2437
2438 Map<String, BuildJob> uniquePoms = new LinkedHashMap<>();
2439 for ( BuildJob setupPom : setupPoms )
2440 {
2441 uniquePoms.put( setupPom.getProject(), setupPom );
2442 }
2443 for ( BuildJob normalPom : normalPoms )
2444 {
2445 if ( !uniquePoms.containsKey( normalPom.getProject() ) )
2446 {
2447 uniquePoms.put( normalPom.getProject(), normalPom );
2448 }
2449 }
2450
2451 buildJobs = new ArrayList<>( uniquePoms.values() );
2452 }
2453 else
2454 {
2455 String[] testRegexes = StringUtils.split( invokerTest, "," );
2456 List<String> includes = new ArrayList<>( testRegexes.length );
2457 List<String> excludes = new ArrayList<>();
2458
2459 for ( String regex : testRegexes )
2460 {
2461
2462 if ( regex.startsWith( "!" ) )
2463 {
2464 excludes.add( regex.substring( 1 ) );
2465 }
2466 else
2467 {
2468 includes.add( regex );
2469 }
2470 }
2471
2472
2473
2474 buildJobs = scanProjectsDirectory( includes, excludes, BuildJob.Type.DIRECT );
2475 }
2476
2477 relativizeProjectPaths( buildJobs );
2478
2479 return buildJobs;
2480 }
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494 private List<BuildJob> scanProjectsDirectory( List<String> includes, List<String> excludes, String type )
2495 throws IOException, MojoExecutionException
2496 {
2497 if ( !projectsDirectory.isDirectory() )
2498 {
2499 return Collections.emptyList();
2500 }
2501
2502 DirectoryScanner scanner = new DirectoryScanner();
2503 scanner.setBasedir( projectsDirectory.getCanonicalFile() );
2504 scanner.setFollowSymlinks( false );
2505 if ( includes != null )
2506 {
2507 scanner.setIncludes( includes.toArray( new String[includes.size()] ) );
2508 }
2509 if ( excludes != null )
2510 {
2511 scanner.setExcludes( excludes.toArray( new String[excludes.size()] ) );
2512 }
2513 scanner.addDefaultExcludes();
2514 scanner.scan();
2515
2516 Map<String, BuildJob> matches = new LinkedHashMap<>();
2517
2518 for ( String includedFile : scanner.getIncludedFiles() )
2519 {
2520 matches.put( includedFile, new BuildJob( includedFile, type ) );
2521 }
2522
2523 for ( String includedDir : scanner.getIncludedDirectories() )
2524 {
2525 String includedFile = includedDir + File.separatorChar + "pom.xml";
2526 if ( new File( scanner.getBasedir(), includedFile ).isFile() )
2527 {
2528 matches.put( includedFile, new BuildJob( includedFile, type ) );
2529 }
2530 else
2531 {
2532 matches.put( includedDir, new BuildJob( includedDir, type ) );
2533 }
2534 }
2535
2536 List<BuildJob> projects = new ArrayList<>( matches.size() );
2537
2538
2539 for ( BuildJob buildJob : matches.values() )
2540 {
2541 InvokerProperties invokerProperties =
2542 getInvokerProperties( new File( projectsDirectory, buildJob.getProject() ).getParentFile(),
2543 null );
2544 buildJob.setOrdinal( invokerProperties.getOrdinal() );
2545 projects.add( buildJob );
2546 }
2547 Collections.sort( projects, OrdinalComparator.INSTANCE );
2548 return projects;
2549 }
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560 private void relativizeProjectPaths( List<BuildJob> buildJobs )
2561 throws IOException
2562 {
2563 String projectsDirPath = projectsDirectory.getCanonicalPath();
2564
2565 for ( BuildJob buildJob : buildJobs )
2566 {
2567 String projectPath = buildJob.getProject();
2568
2569 File file = new File( projectPath );
2570
2571 if ( !file.isAbsolute() )
2572 {
2573 file = new File( projectsDirectory, projectPath );
2574 }
2575
2576 String relativizedPath = relativizePath( file, projectsDirPath );
2577
2578 if ( relativizedPath == null )
2579 {
2580 relativizedPath = projectPath;
2581 }
2582
2583 buildJob.setProject( relativizedPath );
2584 }
2585 }
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597 private String relativizePath( File path, String basedir )
2598 throws IOException
2599 {
2600 String relativizedPath = path.getCanonicalPath();
2601
2602 if ( relativizedPath.startsWith( basedir ) )
2603 {
2604 relativizedPath = relativizedPath.substring( basedir.length() );
2605 if ( relativizedPath.startsWith( File.separator ) )
2606 {
2607 relativizedPath = relativizedPath.substring( File.separator.length() );
2608 }
2609
2610 return relativizedPath;
2611 }
2612 else
2613 {
2614 return null;
2615 }
2616 }
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626 private Map<String, Object> getInterpolationValueSource( final boolean escapeXml )
2627 {
2628 Map<String, Object> props = new HashMap<>();
2629
2630 if ( filterProperties != null )
2631 {
2632 props.putAll( filterProperties );
2633 }
2634 props.put( "basedir", this.project.getBasedir().getAbsolutePath() );
2635 props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) );
2636 if ( settings.getLocalRepository() != null )
2637 {
2638 props.put( "localRepository", settings.getLocalRepository() );
2639 props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) );
2640 }
2641
2642 return new CompositeMap( this.project, props, escapeXml );
2643 }
2644
2645
2646
2647
2648
2649
2650
2651
2652 private static String toUrl( String filename )
2653 {
2654
2655
2656
2657
2658 String url = "file://" + new File( filename ).toURI().getPath();
2659 if ( url.endsWith( "/" ) )
2660 {
2661 url = url.substring( 0, url.length() - 1 );
2662 }
2663 return url;
2664 }
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678 private List<String> getTokens( File basedir, String filename, List<String> defaultTokens )
2679 throws IOException
2680 {
2681 List<String> tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList<String>();
2682
2683 if ( StringUtils.isNotEmpty( filename ) )
2684 {
2685 File tokenFile = new File( basedir, filename );
2686
2687 if ( tokenFile.exists() )
2688 {
2689 tokens = readTokens( tokenFile );
2690 }
2691 }
2692
2693 return tokens;
2694 }
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704 private List<String> readTokens( final File tokenFile )
2705 throws IOException
2706 {
2707 List<String> result = new ArrayList<>();
2708
2709 Map<String, Object> composite = getInterpolationValueSource( false );
2710
2711 try ( BufferedReader reader =
2712 new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) ) )
2713 {
2714 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
2715 {
2716 result.addAll( collectListFromCSV( line ) );
2717 }
2718 }
2719
2720 return result;
2721 }
2722
2723
2724
2725
2726
2727
2728
2729 private List<String> collectListFromCSV( final String csv )
2730 {
2731 final List<String> result = new ArrayList<>();
2732
2733 if ( ( csv != null ) && ( csv.trim().length() > 0 ) )
2734 {
2735 final StringTokenizer st = new StringTokenizer( csv, "," );
2736
2737 while ( st.hasMoreTokens() )
2738 {
2739 result.add( st.nextToken().trim() );
2740 }
2741 }
2742
2743 return result;
2744 }
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759 void buildInterpolatedFile( File originalFile, File interpolatedFile )
2760 throws MojoExecutionException
2761 {
2762 getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() );
2763
2764 try
2765 {
2766 String xml;
2767
2768 Map<String, Object> composite = getInterpolationValueSource( true );
2769
2770
2771 try ( Reader reader =
2772 new InterpolationFilterReader( ReaderFactory.newXmlReader( originalFile ), composite, "@", "@" ) )
2773 {
2774 xml = IOUtil.toString( reader );
2775 }
2776
2777 try ( Writer writer = WriterFactory.newXmlWriter( interpolatedFile ) )
2778 {
2779 interpolatedFile.getParentFile().mkdirs();
2780
2781 writer.write( xml );
2782 }
2783 }
2784 catch ( IOException e )
2785 {
2786 throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e );
2787 }
2788 }
2789
2790
2791
2792
2793
2794
2795
2796
2797 private InvokerProperties getInvokerProperties( final File projectDirectory, Properties globalInvokerProperties )
2798 throws MojoExecutionException
2799 {
2800 Properties props;
2801 if ( globalInvokerProperties != null )
2802 {
2803 props = new Properties( globalInvokerProperties );
2804 }
2805 else
2806 {
2807 props = new Properties();
2808 }
2809
2810 File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
2811 if ( propertiesFile.isFile() )
2812 {
2813 try ( InputStream in = new FileInputStream( propertiesFile ) )
2814 {
2815 props.load( in );
2816 }
2817 catch ( IOException e )
2818 {
2819 throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
2820 }
2821 }
2822
2823 Interpolator interpolator = new RegexBasedInterpolator();
2824 interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) );
2825
2826 for ( String key : props.stringPropertyNames() )
2827 {
2828 String value = props.getProperty( key );
2829 try
2830 {
2831 value = interpolator.interpolate( value, "" );
2832 }
2833 catch ( InterpolationException e )
2834 {
2835 throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,
2836 e );
2837 }
2838 props.setProperty( key, value );
2839 }
2840 return new InvokerProperties( props );
2841 }
2842
2843 protected boolean isParallelRun()
2844 {
2845 return parallelThreads > 1;
2846 }
2847
2848 static class ToolchainPrivateManager
2849 {
2850 private ToolchainManagerPrivate manager;
2851
2852 private MavenSession session;
2853
2854 ToolchainPrivateManager( ToolchainManagerPrivate manager, MavenSession session )
2855 {
2856 this.manager = manager;
2857 this.session = session;
2858 }
2859
2860 ToolchainPrivate[] getToolchainPrivates( String type ) throws MisconfiguredToolchainException
2861 {
2862 return manager.getToolchainsForType( type, session );
2863 }
2864 }
2865 }