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