View Javadoc
1   /*
2     Copyright (C) 2020 - 2022 Alexander Kapitman
3   
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15  */
16  
17  package ru.akman.maven.plugins;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.nio.charset.Charset;
22  import java.nio.file.Files;
23  import java.nio.file.Path;
24  import java.nio.file.Paths;
25  import java.text.MessageFormat;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.Properties;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  import java.util.stream.Collectors;
32  import java.util.stream.Stream;
33  import org.apache.commons.lang3.JavaVersion;
34  import org.apache.commons.lang3.StringUtils;
35  import org.apache.commons.lang3.SystemUtils;
36  import org.apache.maven.execution.MavenSession;
37  import org.apache.maven.plugin.AbstractMojo;
38  import org.apache.maven.plugin.BuildPluginManager;
39  import org.apache.maven.plugin.MojoExecutionException;
40  import org.apache.maven.plugins.annotations.Component;
41  import org.apache.maven.plugins.annotations.Parameter;
42  import org.apache.maven.project.MavenProject;
43  import org.apache.maven.shared.model.fileset.util.FileSetManager;
44  import org.apache.maven.toolchain.Toolchain;
45  import org.apache.maven.toolchain.ToolchainManager;
46  import org.codehaus.plexus.util.cli.CommandLineException;
47  import org.codehaus.plexus.util.cli.CommandLineUtils;
48  import org.codehaus.plexus.util.cli.Commandline;
49  
50  /**
51   * Base class for creating a CLI tool Mojos.
52   */
53  public abstract class BaseToolMojo extends AbstractMojo {
54  
55    /**
56     * The name of JDK toolchain.
57     */
58    private static final String JDK = "jdk";
59  
60    /**
61     * The name of the system environment JAVA_HOME variable.
62     */
63    private static final String JAVA_HOME = "JAVA_HOME";
64  
65    /**
66     * The value for older major versions of Java.
67     */
68    private static final int OLD_MAJOR = 1;
69  
70    /**
71     * The value from which new major versions of Java begin.
72     */
73    private static final int NEW_MAJOR = 9;
74  
75    /**
76     * The value from which the most recent major versions of Java begin.
77     */
78    private static final int NEW_RECENT = 14;
79  
80    /**
81     * The value for android major versions of Java.
82     */
83    private static final int ANDROID_MAJOR = 0;
84  
85    /**
86     * The value for android minor versions of Java.
87     */
88    private static final int ANDROID_MINOR = 9;
89  
90    /**
91     * The name of the subdirectory under JAVA_HOME where executables live.
92     */
93    private static final String JAVA_HOME_BIN = "bin";
94  
95    /**
96     * The name of the system environment PATH variable.
97     */
98    private static final String PATH = "PATH";
99  
100   /**
101    * The name of the system environment PATHEXT variable.
102    */
103   private static final String PATHEXT = "PATHEXT";
104 
105   /**
106    * The version string pattern of CLI tool.
107    */
108   private static final String VERSION_PATTERN = "^(\\d+)(\\.(\\d+))?.*";
109 
110   /**
111    * The version option of CLI tool.
112    */
113   private static final String VERSION_OPTION = "--version";
114 
115   /**
116    * Project base directory (that containing the pom.xml file).
117    */
118   private File baseDir;
119   
120   /**
121    * Project build directory (${project.basedir}/target).
122    */
123   private File buildDir;
124 
125   /**
126    * Project output directory (${project.build.directory}/classes).
127    */
128   private File outputDir;
129 
130   /**
131    * Project properties.
132    */
133   private Properties properties;
134 
135   /**
136    * Default charset (${project.build.sourceEncoding}).
137    */
138   private Charset sourceEncoding = Charset.defaultCharset();
139 
140   /**
141    * Fileset manager.
142    */
143   private FileSetManager fileSetManager;
144 
145   /**
146    * All JDK toolchains available in user settings
147    * independently from maven-toolchains-plugin.
148    */
149   private List<Toolchain> toolchains;
150 
151   /**
152    * JDK toolchain from build context,
153    * i.e. the toolchain selected by maven-toolchains-plugin.
154    */
155   private Toolchain toolchain;
156 
157   /**
158    * Tool home directory.
159    */
160   private File toolHomeDirectory;
161 
162   /**
163    * Tool executable.
164    */
165   private File toolExecutable;
166 
167   /**
168    * Tool version.
169    */
170   private String toolVersion;
171 
172   /**
173    * Tool corresponding java version.
174    */
175   private JavaVersion toolJavaVersion;
176 
177   /**
178    * Toolchain manager.
179    */
180   @Component
181   private ToolchainManager toolchainManager;
182 
183   /**
184    * Build plugin manager.
185    */
186   @Component
187   private BuildPluginManager pluginManager;
188 
189   /**
190    * Maven project.
191    */
192   @Parameter(
193       defaultValue = "${project}",
194       readonly = true,
195       required = true
196   )
197   private MavenProject project;
198 
199   /**
200    * Maven session.
201    */
202   @Parameter(
203       defaultValue = "${session}",
204       readonly = true,
205       required = true
206   )
207   private MavenSession session;
208 
209   /**
210    * Get tool executable path from tool home.
211    *
212    * @param toolName the name of the tool (without extension)
213    * @param toolHomeDir the tool home directory
214    * @param toolBinDirName the name of subdirectory where the tool live
215    *
216    * @return tool executable path from tool home directory specified in
217    *         configuration as toolhome parameter or null
218    */
219   private Path getExecutableFromToolHome(final String toolName,
220       final File toolHomeDir, final String toolBinDirName) {
221     Path executablePath = toolHomeDir == null
222         ? null
223         : resolveToolPath(toolName, toolHomeDir.toPath(), toolBinDirName);
224     if (executablePath != null) {
225       try {
226         executablePath = executablePath.toRealPath();
227         toolHomeDirectory = toolHomeDir;
228         if (getLog().isDebugEnabled()) {
229           getLog().debug(MessageFormat.format(
230               "Executable (toolhome) for [{0}]: {1}", toolName,
231               executablePath));
232           getLog().debug(MessageFormat.format(
233               "Home directory (toolhome) for [{0}]: {1}", toolName,
234               toolHomeDirectory));
235         }
236       } catch (IOException ex) {
237         if (getLog().isWarnEnabled()) {
238           getLog().warn(MessageFormat.format(
239               "Unable to resolve executable (toolhome) for [{0}]: {1}",
240               toolName, executablePath), ex);
241         }
242       }
243     }
244     return executablePath;
245   }
246 
247   /**
248    * Get tool executable path from default JDK toolchain.
249    *
250    * @param toolName the name of the tool (without extension)
251    *
252    * @return tool executable path from JDK toolchain specified in
253    *         configuration by toolchain plugin or null
254    */
255   @SuppressWarnings("deprecation") // DefaultJavaToolChain
256   private Path getExecutableFromToolchain(final String toolName) {
257     final String tcJavaHome = toolchain == null
258         ? null
259         : org.apache.maven.toolchain.java.DefaultJavaToolChain.class.cast(
260             toolchain).getJavaHome();
261     final String tcToolExecutable = toolchain == null
262         ? null
263         : toolchain.findTool(toolName);
264     Path executablePath = null;
265     if (!StringUtils.isBlank(tcJavaHome)
266         && !StringUtils.isBlank(tcToolExecutable)) {
267       try {
268         executablePath = Paths.get(tcToolExecutable).toRealPath();
269         toolHomeDirectory = new File(tcJavaHome);
270         if (getLog().isDebugEnabled()) {
271           getLog().debug(MessageFormat.format(
272               "Executable (toolchain) for [{0}]: {1}", toolName,
273               executablePath));
274           getLog().debug(MessageFormat.format(
275               "Home directory (toolchain) for [{0}]: {1}", toolName,
276               toolHomeDirectory));
277         }
278       } catch (IOException ex) {
279         if (getLog().isWarnEnabled()) {
280           getLog().warn(MessageFormat.format(
281               "Unable to resolve executable (toolchain) for [{0}]: {1}",
282               toolName, executablePath), ex);
283         }
284       }
285     }
286     return executablePath;
287   }
288 
289   /**
290    * Get tool executable path from java home.
291    *
292    * @param toolName the name of the tool (without extension)
293    *
294    * @return tool executable path from JDK home directory specified in
295    *              the system environment variable JAVA_HOME or null
296    */
297   private Path getExecutableFromJavaHome(final String toolName) {
298     final File javaHomeDir = getJavaHome();
299     Path executablePath = javaHomeDir == null
300         ? null
301         : resolveToolPath(toolName, javaHomeDir.toPath(), JAVA_HOME_BIN);
302     if (executablePath != null) {
303       try {
304         executablePath = executablePath.toRealPath();
305         toolHomeDirectory = javaHomeDir;
306         if (getLog().isDebugEnabled()) {
307           getLog().debug(MessageFormat.format(
308               "Executable (javahome) for [{0}]: {1}", toolName,
309               executablePath));
310           getLog().debug(MessageFormat.format(
311               "Home directory (javahome) for [{0}]: {1}", toolName,
312               toolHomeDirectory));
313         }
314       } catch (IOException ex) {
315         if (getLog().isWarnEnabled()) {
316           getLog().warn(MessageFormat.format(
317               "Unable to resolve executable (javahome) for [{0}]: {1}",
318               toolName, executablePath), ex);
319         }
320       }
321     }
322     return executablePath;
323   }
324 
325   /**
326    * Get tool executable path from system path.
327    *
328    * @param toolName the name of the tool (without extension)
329    *
330    * @return tool executable path from paths specified in
331    *              the system environment variable PATH or null
332    */
333   private Path getExecutableFromSystemPath(final String toolName) {
334     final List<Path> systemPath = getSystemPath();
335     Path executablePath = null;
336     for (final Path path : systemPath) {
337       executablePath = resolveToolPath(toolName, path, null);
338       if (executablePath != null) {
339         break;
340       }
341     }
342     if (executablePath != null) {
343       try {
344         final Path toolHomePath = executablePath.getParent();
345         toolHomeDirectory = toolHomePath == null
346             ? null : toolHomePath.toRealPath().toFile();
347         executablePath = executablePath.toRealPath();
348         if (getLog().isDebugEnabled()) {
349           getLog().debug(MessageFormat.format(
350               "Executable (systempath) for [{0}]: {1}", toolName,
351               executablePath));
352           getLog().debug(MessageFormat.format(
353               "Home directory (systempath) for [{0}]: {1}", toolName,
354               toolHomeDirectory));
355         }
356       } catch (IOException ex) {
357         if (getLog().isWarnEnabled()) {
358           getLog().warn(MessageFormat.format(
359               "Unable to resolve executable (systempath) for [{0}]: {1}",
360               toolName, executablePath), ex);
361         }
362       }
363     }
364     return executablePath;
365   }
366 
367   /**
368    * Get tool executable path.
369    *
370    * <p>
371    * Find tool executable in following order:
372    * - toolhome (user specified tool home directory in configuration)
373    * - toolchain (user specified JDK home directory by toolchains-plugin)
374    * - javahome (JDK home directory specified by system variable JAVA_HOME)
375    * - systempath (system path)
376    * </p>
377    *
378    * @param toolName the name of the tool (without extension)
379    * @param toolHomeDir the tool home directory
380    * @param toolBinDirName the name of subdirectory where the tool live
381    *
382    * @return tool executable path from tool home directory specified in
383    *         configuration or by toolchain plugin or by system variable
384    *         JAVA_HOME or null
385    */
386   private Path getToolExecutablePath(final String toolName,
387       final File toolHomeDir, final String toolBinDirName) {
388     Path executablePath =
389         getExecutableFromToolHome(toolName, toolHomeDir, toolBinDirName);
390     if (executablePath != null) {
391       return executablePath;
392     }
393     if (getLog().isDebugEnabled()) {
394       getLog().debug(MessageFormat.format(
395           "Executable (toolhome) for [{0}] not found", toolName));
396     }
397     executablePath = getExecutableFromToolchain(toolName);
398     if (executablePath != null) {
399       return executablePath;
400     }
401     if (getLog().isDebugEnabled()) {
402       getLog().debug(MessageFormat.format(
403           "Executable (toolchain) for [{0}] not found", toolName));
404     }
405     executablePath = getExecutableFromJavaHome(toolName);
406     if (executablePath != null) {
407       return executablePath;
408     }
409     if (getLog().isDebugEnabled()) {
410       getLog().debug(MessageFormat.format(
411           "Executable (javahome) for [{0}] not found", toolName));
412     }
413     executablePath = getExecutableFromSystemPath(toolName);
414     if (executablePath != null) {
415       return executablePath;
416     }
417     if (getLog().isDebugEnabled()) {
418       getLog().debug(MessageFormat.format(
419           "Executable (systempath) for [{0}] not found", toolName));
420     }
421     return executablePath;
422   }
423 
424   /**
425    * Resolve the tool path against the specified home dir.
426    *
427    * @param toolName the name of the tool (without extension)
428    * @param toolHomeDir the home path of the tool
429    * @param toolBinDirName the name of subdirectory where the tool live
430    *
431    * @return tool executable path or null
432    */
433   private Path resolveToolPath(final String toolName, final Path toolHomeDir,
434       final String toolBinDirName) {
435     if (toolHomeDir == null || StringUtils.isBlank(toolName)) {
436       return null;
437     }
438     Path toolBinDir = toolHomeDir;
439     if (!StringUtils.isBlank(toolBinDirName)) {
440       toolBinDir = toolHomeDir.resolve(toolBinDirName);
441     }
442     if (!Files.exists(toolBinDir) || !Files.isDirectory(toolBinDir)) {
443       return null;
444     }
445     return findToolExecutable(toolName, List.of(toolBinDir));
446   }
447 
448   /**
449    * Find tool executable under specified paths.
450    *
451    * @param toolName the name of the tool (without extension)
452    * @param paths the list of path under which the tool will be find
453    *
454    * @return tool executable path or null if it not found
455    */
456   private Path findToolExecutable(final String toolName,
457       final List<Path> paths) {
458     Path executablePath = null;
459     Path toolFile = null;
460     final List<String> exts = getPathExt();
461     for (final Path path : paths) {
462       if (SystemUtils.IS_OS_WINDOWS) {
463         for (final String ext : exts) {
464           toolFile = path.resolve(toolName.concat(ext));
465           if (Files.isExecutable(toolFile)
466               && !Files.isDirectory(toolFile)) {
467             executablePath = toolFile;
468             break;
469           }
470         }
471       } else {
472         toolFile = path.resolve(toolName);
473         if (Files.isExecutable(toolFile)
474             && !Files.isDirectory(toolFile)) {
475           executablePath = toolFile;
476           break;
477         }
478       }
479     }
480     return executablePath;
481   }
482 
483   /**
484    * Get path from the system environment variable JAVA_HOME.
485    *
486    * @return path from the system environment variable JAVA_HOME
487    */
488   private File getJavaHome() {
489     final String javaHome = StringUtils.stripToEmpty(System.getenv(JAVA_HOME));
490     return StringUtils.isBlank(javaHome) ? null : new File(javaHome);
491   }
492 
493   /**
494    * Get list of the paths registered in the system environment variable PATH.
495    *
496    * @return list of the paths registered in the system
497    *         environment variable PATH.
498    */
499   private List<Path> getSystemPath() {
500     final String systemPath = StringUtils.stripToEmpty(System.getenv(PATH));
501     if (StringUtils.isBlank(systemPath)) {
502       return new ArrayList<Path>();
503     }
504     return Stream.of(systemPath.split(File.pathSeparator))
505         .filter(s -> !StringUtils.isBlank(s))
506         .map(s -> Paths.get(StringUtils.stripToEmpty(s)))
507         .collect(Collectors.toList());
508   }
509 
510   /**
511    * Get list of the registered path extensions from
512    * the system environment variable PATHEXT.
513    *
514    * @return list of the registered path extensions from the system
515    *         environment variable PATHEXT
516    */
517   private List<String> getPathExt() {
518     if (SystemUtils.IS_OS_WINDOWS) {
519       final String systemPathExt =
520           StringUtils.stripToEmpty(System.getenv(PATHEXT));
521       if (!StringUtils.isBlank(systemPathExt)) {
522         return Stream.of(systemPathExt.split(File.pathSeparator))
523             .filter(s -> !StringUtils.isBlank(s))
524             .map(s -> StringUtils.stripToEmpty(s))
525             .collect(Collectors.toList());
526       }
527     }
528     return new ArrayList<String>();
529   }
530 
531   /**
532    * Obtain the tool version.
533    *
534    * @return the tool version or null
535    *
536    * @throws CommandLineException if any errors occurred while processing
537    *                              command line
538    */
539   private String obtainToolVersion(final Path executablePath)
540       throws CommandLineException {
541     final Commandline cmdLine = new Commandline();
542     cmdLine.setExecutable(executablePath.toString());
543     cmdLine.createArg().setValue(VERSION_OPTION);
544     final CommandLineUtils.StringStreamConsumer out =
545         new CommandLineUtils.StringStreamConsumer();
546     final CommandLineUtils.StringStreamConsumer err =
547         new CommandLineUtils.StringStreamConsumer();
548     return execCmdLine(cmdLine, out, err) == 0
549         ? StringUtils.stripToEmpty(out.getOutput())
550         : null;
551   }
552 
553   /**
554    * Get Java version corresponding to the tool version passed in.
555    *
556    * @param version the tool version, not null
557    *
558    * @return the corresponding Java version matching the tool version
559    */
560   private JavaVersion getCorrespondingJavaVersion(final String version) {
561     JavaVersion resolvedVersion = null;
562     if (version != null) {
563       final Matcher versionMatcher = Pattern.compile(VERSION_PATTERN)
564           .matcher(version);
565       if (versionMatcher.matches()) {
566         // always present
567         final String majorVersionPart = versionMatcher.group(1);
568         final int majorVersion = Integer.parseInt(majorVersionPart);
569         // optional part
570         final String minorVersionPart = versionMatcher.group(3);
571         final int minorVersion = StringUtils.isBlank(minorVersionPart)
572             ? 0 : Integer.parseInt(minorVersionPart);
573         if (majorVersion >= NEW_MAJOR) {
574           if (majorVersion >= NEW_RECENT) {
575             resolvedVersion = JavaVersion.JAVA_RECENT;
576           } else {
577             resolvedVersion = JavaVersion.valueOf("JAVA_" + majorVersion);
578           }
579         } else {
580           // JAVA_1_1 - JAVA_1_9 || JAVA_0_9 (android)
581           if (majorVersion == OLD_MAJOR
582               && minorVersion > 0
583               && minorVersion <= NEW_MAJOR
584               || majorVersion == ANDROID_MAJOR
585               && minorVersion == ANDROID_MINOR) {
586             resolvedVersion = JavaVersion.valueOf("JAVA_" + majorVersion
587                 + "_" + minorVersion);
588           }
589         }
590       }
591     }
592     return resolvedVersion;
593   }
594 
595   /**
596    * Get JDK toolchain specified in toolchains-plugin for
597    * current build context.
598    *
599    * @return JDK toolchain
600    */
601   @SuppressWarnings("deprecation") // DefaultJavaToolChain
602   private Toolchain getDefaultJavaToolchain() {
603     final Toolchain ctxToolchain =
604         getToolchainManager().getToolchainFromBuildContext(JDK, getSession());
605     return ctxToolchain == null || !(ctxToolchain
606         instanceof org.apache.maven.toolchain.java.DefaultJavaToolChain)
607         ? null : ctxToolchain;
608   }
609 
610   /**
611    * Log result of the commandline execution.
612    *
613    * @param cmdLine the command line
614    * @param exitCode the exit code
615    * @param stdout the standard output
616    * @param stderr the standard error
617    */
618   private void logCommandLineExecution(final Commandline cmdLine,
619       final int exitCode, final String stdout, final String stderr) {
620     if (exitCode == 0) {
621       if (getLog().isDebugEnabled()) {
622         if (!StringUtils.isBlank(stdout)) {
623           getLog().debug(System.lineSeparator() + stdout);
624         }
625         if (!StringUtils.isBlank(stderr)) {
626           getLog().debug(System.lineSeparator() + stderr);
627         }
628       }
629     } else {
630       if (getLog().isErrorEnabled()) {
631         getLog().error(System.lineSeparator() + "Exit code: " + exitCode);
632         if (!StringUtils.isBlank(stdout)) {
633           getLog().error(System.lineSeparator() + stdout);
634         }
635         if (!StringUtils.isBlank(stderr)) {
636           getLog().error(System.lineSeparator() + stderr);
637         }
638         getLog().error(System.lineSeparator()
639             + "Command line was: "
640             + CommandLineUtils.toString(cmdLine.getCommandline()));
641       }
642     }
643   }
644 
645   /**
646    * Get project base directory.
647    *
648    * @return project base directory (that containing the pom.xml file)
649    */
650   protected File getBaseDir() {
651     return baseDir;
652   }
653 
654   /**
655    * Get project build directory.
656    *
657    * @return project build directory (${project.basedir}/target)
658    */
659   protected File getBuildDir() {
660     return buildDir;
661   }
662 
663   /**
664    * Get project output directory.
665    *
666    * @return project output directory (${project.build.directory}/classes)
667    */
668   protected File getOutputDir() {
669     return outputDir;
670   }
671 
672   /**
673    * Get project properties.
674    *
675    * @return project properties
676    */
677   protected Properties getProperties() {
678     return properties;
679   }
680 
681   /**
682    * Get default charset.
683    *
684    * @return default charset (${project.build.sourceEncoding})
685    */
686   protected Charset getCharset() {
687     return sourceEncoding;
688   }
689 
690   /**
691    * Get fileset manager.
692    *
693    * @return fileset manager
694    */
695   protected FileSetManager getFileSetManager() {
696     return fileSetManager;
697   }
698 
699   /**
700    * Get list of all JDK toolchains available in user settings
701    * independently from maven-toolchains-plugin.
702    *
703    * @return list of all JDK toolchains available in user settings
704    */
705   protected List<Toolchain> getToolchains() {
706     return toolchains;
707   }
708 
709   /**
710    * Get JDK toolchain from build context,
711    * i.e. the toolchain selected by maven-toolchains-plugin.
712    *
713    * @return JDK toolchain from build context
714    */
715   protected Toolchain getToolchain() {
716     return toolchain;
717   }
718 
719   /**
720    * Get tool home directory.
721    *
722    * @return tool home directory
723    */
724   protected File getToolHomeDirectory() {
725     return toolHomeDirectory;
726   }
727 
728   /**
729    * Get tool executable.
730    *
731    * @return tool executable
732    */
733   protected File getToolExecutable() {
734     return toolExecutable;
735   }
736 
737   /**
738    * Get tool version.
739    *
740    * @return tool version
741    */
742   protected String getToolVersion() {
743     return toolVersion;
744   }
745 
746   /**
747    * Get tool corresponding java version.
748    *
749    * @return tool corresponding java version
750    */
751   protected JavaVersion getToolJavaVersion() {
752     return toolJavaVersion;
753   }
754 
755   /**
756    * Get toolchain manager.
757    *
758    * @return toolchain manager
759    */
760   protected ToolchainManager getToolchainManager() {
761     return toolchainManager;
762   }
763 
764   /**
765    * Get plugin manager.
766    *
767    * @return plugin manager
768    */
769   protected BuildPluginManager getPluginManager() {
770     return pluginManager;
771   }
772 
773   /**
774    * Get maven project.
775    *
776    * @return maven project
777    */
778   protected MavenProject getProject() {
779     return project;
780   }
781 
782   /**
783    * Get maven session.
784    *
785    * @return maven session
786    */
787   protected MavenSession getSession() {
788     return session;
789   }
790 
791   /**
792    * Execute command line.
793    *
794    * @param cmdLine command line
795    *
796    * @return exit code
797    *
798    * @throws CommandLineException if any errors occurred while processing
799    *                              command line
800    */
801   protected int execCmdLine(final Commandline cmdLine)
802       throws CommandLineException {
803     return execCmdLine(cmdLine, null, null);
804   }
805 
806   /**
807    * Execute command line with defined standard output/error streams.
808    *
809    * @param cmdLine command line
810    * @param out standard output, can be null
811    * @param err standard error, can be null
812    *
813    * @return exit code
814    *
815    * @throws CommandLineException if any errors occurred while processing
816    *                              command line
817    */
818   protected int execCmdLine(final Commandline cmdLine,
819       final CommandLineUtils.StringStreamConsumer out,
820       final CommandLineUtils.StringStreamConsumer err)
821       throws CommandLineException {
822     if (getLog().isDebugEnabled()) {
823       getLog().debug(CommandLineUtils.toString(cmdLine.getCommandline()));
824     }
825     final CommandLineUtils.StringStreamConsumer stdout = out == null
826         ? new CommandLineUtils.StringStreamConsumer()
827         : out;
828     final CommandLineUtils.StringStreamConsumer stderr = err == null
829         ? new CommandLineUtils.StringStreamConsumer()
830         : err;
831     final int exitCode =
832         CommandLineUtils.executeCommandLine(cmdLine, stdout, err);
833     logCommandLineExecution(cmdLine, exitCode, stdout.getOutput(),
834         stderr.getOutput());
835     return exitCode;
836   }
837 
838   /**
839    * Init Mojo.
840    *
841    * @param toolName the name of the tool (without extension)
842    * @param toolHomeDir the tool home directory
843    * @param toolBinDirName the name of subdirectory where the tool live
844    *                       relative to the tool home directory
845    *
846    * @throws MojoExecutionException if any errors occurred while processing
847    *                                configuration parameters
848    */
849   protected void init(final String toolName, final File toolHomeDir,
850       final String toolBinDirName) throws MojoExecutionException {
851     if (getProject() == null) {
852       throw new MojoExecutionException(
853           "Error: The predefined variable ${project} is not defined");
854     }
855 
856     if (getSession() == null) {
857       throw new MojoExecutionException(
858           "Error: The predefined variable ${session} is not defined");
859     }
860 
861     baseDir = getProject().getBasedir();
862     if (baseDir == null) {
863       throw new MojoExecutionException(
864           "Error: The predefined variable ${project.basedir} is not defined");
865     }
866 
867     buildDir = new File(getProject().getBuild().getDirectory());
868     if (buildDir == null) {
869       throw new MojoExecutionException(
870           "Error: The predefined variable ${project.build.directory} is not defined");
871     }
872 
873     outputDir = new File(getProject().getBuild().getOutputDirectory());
874     if (outputDir == null) {
875       throw new MojoExecutionException(
876           "Error: The predefined variable ${project.build.outputDirectory} is not defined");
877     }
878 
879     properties = getProject().getProperties();
880     if (properties == null) {
881       throw new MojoExecutionException(
882           "Error: Unable to read project properties");
883     }
884 
885     fileSetManager = new FileSetManager();
886     if (fileSetManager == null) {
887       throw new MojoExecutionException(
888           "Error: Unable to create file set manager");
889     }
890 
891     // Get charset to write files
892     final String encoding =
893         properties.getProperty("project.build.sourceEncoding");
894     try {
895       sourceEncoding = Charset.forName(encoding);
896     } catch (IllegalArgumentException ex) {
897       if (getLog().isWarnEnabled()) {
898         getLog().warn("Unable to read ${project.build.sourceEncoding}");
899       }
900     }
901     if (getLog().isDebugEnabled()) {
902       getLog().debug(MessageFormat.format(
903           "Using source encoding: [{0}] to write files", sourceEncoding));
904     }
905 
906     // Resolve all available jdk toolchains
907     toolchains = getToolchainManager().getToolchains(getSession(), JDK, null);
908     if (toolchains == null) {
909       if (getLog().isDebugEnabled()) {
910         getLog().debug("No toolchains found");
911       }
912     } else {
913       toolchains.forEach(tc -> {
914         if (getLog().isDebugEnabled()) {
915           getLog().debug("Found toolchain: " + tc);
916         }
917       });
918     }
919 
920     // Retrieve jdk toolchain from build context,
921     // i.e. the toolchain selected by maven-toolchains-plugin
922     toolchain = getDefaultJavaToolchain();
923     if (toolchain == null) {
924       if (getLog().isDebugEnabled()) {
925         getLog().debug("Toolchain not specified");
926       }
927     } else {
928       if (getLog().isInfoEnabled()) {
929         getLog().info("Using toolchain: " + toolchain);
930       }
931     }
932 
933     // Resolve the tool home directory and executable file
934     final Path executablePath =
935         getToolExecutablePath(toolName, toolHomeDir, toolBinDirName);
936     if (executablePath == null) {
937       throw new MojoExecutionException(MessageFormat.format(
938           "Error: Executable for [{0}] not found", toolName));
939     }
940     toolExecutable = executablePath.toFile();
941 
942     // Obtain the tool version
943     try {
944       toolVersion = obtainToolVersion(executablePath);
945     } catch (CommandLineException ex) {
946       throw new MojoExecutionException(MessageFormat.format(
947           "Error: Unable to obtain version of [{0}]", toolName), ex);
948     }
949     if (toolVersion == null) {
950       if (getLog().isWarnEnabled()) {
951         getLog().warn(MessageFormat.format(
952             "Unable to resolve version of [{0}]", toolName));
953       }
954     } else {
955       if (getLog().isInfoEnabled()) {
956         getLog().info(MessageFormat.format("Version of [{0}]: {1}", toolName,
957             toolVersion));
958       }
959     }
960 
961     // Obtain the corresponding java version matching the tool version
962     toolJavaVersion = getCorrespondingJavaVersion(toolVersion);
963     if (toolJavaVersion == null) {
964       if (getLog().isWarnEnabled()) {
965         getLog().warn(MessageFormat.format(
966             "Unable to resolve corresponding java version of [{0}]",
967             toolName));
968       }
969     } else {
970       if (getLog().isDebugEnabled()) {
971         getLog().debug(MessageFormat.format(
972             "Version (corresponding java version) of [{0}]: {1}", toolName,
973             toolJavaVersion));
974       }
975     }
976 
977   }
978 
979 }