View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.jlink;
20  
21  /*
22   * Licensed to the Apache Software Foundation (ASF) under one
23   * or more contributor license agreements.  See the NOTICE file
24   * distributed with this work for additional information
25   * regarding copyright ownership.  The ASF licenses this file
26   * to you under the Apache License, Version 2.0 (the
27   * "License"); you may not use this file except in compliance
28   * with the License.  You may obtain a copy of the License at
29   *
30   *   http://www.apache.org/licenses/LICENSE-2.0
31   *
32   * Unless required by applicable law or agreed to in writing,
33   * software distributed under the License is distributed on an
34   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
35   * KIND, either express or implied.  See the License for the
36   * specific language governing permissions and limitations
37   * under the License.
38   */
39  
40  import java.io.File;
41  import java.util.List;
42  import java.util.NoSuchElementException;
43  import java.util.Optional;
44  
45  import org.apache.commons.lang3.StringUtils;
46  import org.apache.commons.lang3.SystemUtils;
47  import org.apache.maven.plugin.MojoExecutionException;
48  import org.apache.maven.plugin.logging.Log;
49  import org.apache.maven.shared.utils.cli.CommandLineException;
50  import org.apache.maven.shared.utils.cli.CommandLineUtils;
51  import org.apache.maven.shared.utils.cli.Commandline;
52  import org.apache.maven.toolchain.Toolchain;
53  
54  abstract class AbstractJLinkToolchainExecutor extends AbstractJLinkExecutor {
55      private final Toolchain toolchain;
56  
57      AbstractJLinkToolchainExecutor(Toolchain toolchain, Log log) {
58          super(log);
59          this.toolchain = toolchain;
60      }
61  
62      protected Optional<Toolchain> getToolchain() {
63          return Optional.ofNullable(this.toolchain);
64      }
65  
66      /**
67       * Execute JLink via toolchain.
68       *
69       * @return the exit code ({@code 0} on success).
70       */
71      @Override
72      public int executeJlink(List<String> jlinkArgs) throws MojoExecutionException {
73          File jlinkExecutable = getJlinkExecutable();
74          getLog().info("Toolchain in maven-jlink-plugin: jlink [ " + jlinkExecutable + " ]");
75  
76          Commandline cmd = createJLinkCommandLine(jlinkArgs);
77          cmd.setExecutable(jlinkExecutable.getAbsolutePath());
78  
79          return executeCommand(cmd);
80      }
81  
82      private File getJlinkExecutable() {
83          return new File(getJLinkExecutable());
84      }
85  
86      @Override
87      public Optional<File> getJmodsFolder(/* nullable */ File sourceJdkModules) {
88          // Really Hacky...do we have a better solution to find the jmods directory of the JDK?
89          File jLinkParent = getJlinkExecutable().getParentFile().getParentFile();
90          File jmodsFolder;
91          if (sourceJdkModules != null && sourceJdkModules.isDirectory()) {
92              jmodsFolder = new File(sourceJdkModules, JMODS);
93          } else {
94              jmodsFolder = new File(jLinkParent, JMODS);
95          }
96  
97          getLog().debug(" Parent: " + jLinkParent.getAbsolutePath());
98          getLog().debug(" jmodsFolder: " + jmodsFolder.getAbsolutePath());
99  
100         return Optional.of(jmodsFolder);
101     }
102 
103     private Commandline createJLinkCommandLine(List<String> jlinkArgs) {
104         Commandline cmd = new Commandline();
105         jlinkArgs.forEach(arg -> cmd.createArg().setValue("\"" + arg + "\""));
106 
107         return cmd;
108     }
109 
110     private String getJLinkExecutable() {
111         Optional<Toolchain> toolchain = getToolchain();
112 
113         if (!toolchain.isPresent()) {
114             getLog().error("Either JDK9+ or a toolchain "
115                     + "pointing to a JDK9+ containing a jlink binary is required.");
116             getLog().info("See https://maven.apache.org/guides/mini/guide-using-toolchains.html "
117                     + "for mor information.");
118             throw new IllegalStateException("Running on JDK8 and no toolchain found.");
119         }
120 
121         String jLinkExecutable =
122                 toolchain.orElseThrow(NoSuchElementException::new).findTool("jlink");
123 
124         if (jLinkExecutable.isEmpty()) {
125             throw new IllegalStateException(
126                     "The jlink executable '" + jLinkExecutable + "' doesn't exist or is not a file.");
127         }
128 
129         // TODO: Check if there exist a more elegant way?
130         String jLinkCommand = "jlink" + (SystemUtils.IS_OS_WINDOWS ? ".exe" : "");
131 
132         File jLinkExe = new File(jLinkExecutable);
133 
134         if (jLinkExe.isDirectory()) {
135             jLinkExe = new File(jLinkExe, jLinkCommand);
136         }
137 
138         if (SystemUtils.IS_OS_WINDOWS && jLinkExe.getName().indexOf('.') < 0) {
139             jLinkExe = new File(jLinkExe.getPath() + ".exe");
140         }
141 
142         if (!jLinkExe.isFile()) {
143             throw new IllegalStateException("The jlink executable '" + jLinkExe + "' doesn't exist or is not a file.");
144         }
145         return jLinkExe.getAbsolutePath();
146     }
147 
148     private int executeCommand(Commandline cmd) throws MojoExecutionException {
149         if (getLog().isDebugEnabled()) {
150             // no quoted arguments ???
151             getLog().debug(CommandLineUtils.toString(cmd.getCommandline()).replaceAll("'", ""));
152         }
153 
154         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
155         CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
156         try {
157             int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err);
158 
159             String output = StringUtils.isEmpty(out.getOutput())
160                     ? null
161                     : '\n' + out.getOutput().trim();
162 
163             if (exitCode != 0) {
164 
165                 if (output != null && !output.isEmpty()) {
166                     // Reconsider to use WARN / ERROR ?
167                     //  getLog().error( output );
168                     for (String outputLine : output.split("\n")) {
169                         getLog().error(outputLine);
170                     }
171                 }
172 
173                 StringBuilder msg = new StringBuilder("\nExit code: ");
174                 msg.append(exitCode);
175                 if (StringUtils.isNotEmpty(err.getOutput())) {
176                     msg.append(" - ").append(err.getOutput());
177                 }
178                 msg.append('\n');
179                 msg.append("Command line was: ").append(cmd).append('\n').append('\n');
180 
181                 throw new MojoExecutionException(msg.toString());
182             }
183 
184             if (output != null && !output.isEmpty()) {
185                 // getLog().info( output );
186                 for (String outputLine : output.split("\n")) {
187                     getLog().info(outputLine);
188                 }
189             }
190 
191             return exitCode;
192         } catch (CommandLineException e) {
193             throw new MojoExecutionException("Unable to execute jlink command: " + e.getMessage(), e);
194         }
195     }
196 }