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.jmod;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  
29  import org.apache.maven.execution.MavenSession;
30  import org.apache.maven.plugin.AbstractMojo;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugins.annotations.Parameter;
33  import org.apache.maven.project.MavenProject;
34  import org.apache.maven.shared.utils.Os;
35  import org.apache.maven.shared.utils.StringUtils;
36  import org.apache.maven.shared.utils.cli.CommandLineException;
37  import org.apache.maven.shared.utils.cli.CommandLineUtils;
38  import org.apache.maven.shared.utils.cli.Commandline;
39  import org.apache.maven.toolchain.Toolchain;
40  import org.apache.maven.toolchain.ToolchainManager;
41  
42  /**
43   * This contains the code to handle toolchains and the execution of the command which is similar to code in
44   * maven-jlink-plugin (maven-jdeps-plugin?). Later we need to think about a way to combine that code to reduce
45   * duplication.
46   *
47   * @author Karl Heinz Marbaise <a href="mailto:khmarbaise@apache.org">khmarbaise@apache.org</a>
48   */
49  public abstract class AbstractJModMojo extends AbstractMojo {
50  
51      @Parameter(defaultValue = "${project}", readonly = true, required = true)
52      private MavenProject project;
53  
54      @Parameter(defaultValue = "${session}", readonly = true, required = true)
55      private MavenSession session;
56  
57      /**
58       * <p>
59       * Specify the requirements for this JDK toolchain. This overrules the toolchain selected by the
60       * maven-toolchain-plugin.
61       * </p>
62       * <strong>note:</strong> requires at least Maven 3.3.1
63       */
64      @Parameter
65      private Map<String, String> jdkToolchain;
66  
67      private final ToolchainManager toolchainManager;
68  
69      protected AbstractJModMojo(ToolchainManager toolchainManager) {
70          this.toolchainManager = toolchainManager;
71      }
72  
73      // TODO: Check how to prevent code duplication in maven-jlink, maven-jmod and maven-jdeps plugin?
74      protected String getJModExecutable() throws IOException {
75          Toolchain tc = getToolchain();
76  
77          String jModExecutable = null;
78          if (tc != null) {
79              jModExecutable = tc.findTool("jmod");
80          }
81  
82          String jModCommand = "jmod" + (Os.isFamily(Os.FAMILY_WINDOWS) ? ".exe" : "");
83  
84          File jModExe;
85  
86          if (StringUtils.isNotEmpty(jModExecutable)) {
87              jModExe = new File(jModExecutable);
88  
89              if (jModExe.isDirectory()) {
90                  jModExe = new File(jModExe, jModCommand);
91              }
92  
93              if (Os.isFamily(Os.FAMILY_WINDOWS) && jModExe.getName().indexOf('.') < 0) {
94                  jModExe = new File(jModExe.getPath() + ".exe");
95              }
96  
97              if (!jModExe.isFile()) {
98                  throw new IOException("The jmod executable '" + jModExe + "' doesn't exist or is not a file.");
99              }
100             return jModExe.getAbsolutePath();
101         }
102 
103         // ----------------------------------------------------------------------
104         // Try to find jmod from System.getProperty( "java.home" )
105         // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME
106         // should be in the JDK_HOME
107         // ----------------------------------------------------------------------
108         jModExe =
109                 new File(System.getProperty("java.home") + File.separator + ".." + File.separator + "bin", jModCommand);
110 
111         // ----------------------------------------------------------------------
112         // Try to find jmod from JAVA_HOME environment variable
113         // ----------------------------------------------------------------------
114         if (!jModExe.exists() || !jModExe.isFile()) {
115             Properties env = CommandLineUtils.getSystemEnvVars();
116             String javaHome = env.getProperty("JAVA_HOME");
117             if (StringUtils.isEmpty(javaHome)) {
118                 throw new IOException("The environment variable JAVA_HOME is not correctly set.");
119             }
120             if ((!new File(javaHome).getCanonicalFile().exists())
121                     || (new File(javaHome).getCanonicalFile().isFile())) {
122                 throw new IOException("The environment variable JAVA_HOME=" + javaHome
123                         + " doesn't exist or is not a valid directory.");
124             }
125 
126             jModExe = new File(javaHome + File.separator + "bin", jModCommand);
127         }
128 
129         if (!jModExe.getCanonicalFile().exists() || !jModExe.getCanonicalFile().isFile()) {
130             throw new IOException("The jmod executable '" + jModExe
131                     + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable.");
132         }
133 
134         return jModExe.getAbsolutePath();
135     }
136 
137     protected boolean projectHasAlreadySetAnArtifact() {
138         if (getProject().getArtifact().getFile() != null) {
139             return getProject().getArtifact().getFile().isFile();
140         } else {
141             return false;
142         }
143     }
144 
145     protected void executeCommand(Commandline cmd, File outputDirectory) throws MojoExecutionException {
146         if (getLog().isDebugEnabled()) {
147             // no quoted arguments ???
148             getLog().debug(CommandLineUtils.toString(cmd.getCommandline()).replaceAll("'", ""));
149         }
150 
151         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
152         CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
153         try {
154             int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err);
155 
156             String output = (StringUtils.isEmpty(out.getOutput())
157                     ? null
158                     : '\n' + out.getOutput().trim());
159 
160             if (exitCode != 0) {
161                 if (StringUtils.isNotEmpty(output)) {
162                     // Reconsider to use WARN / ERROR ?
163                     getLog().info(output);
164                 }
165 
166                 StringBuilder msg = new StringBuilder("\nExit code: ");
167                 msg.append(exitCode);
168                 if (StringUtils.isNotEmpty(err.getOutput())) {
169                     msg.append(" - ").append(err.getOutput());
170                 }
171                 msg.append('\n');
172                 msg.append("Command line was: ").append(cmd).append('\n').append('\n');
173 
174                 throw new MojoExecutionException(msg.toString());
175             }
176 
177             if (StringUtils.isNotEmpty(output)) {
178                 String[] splitLines = StringUtils.split(output, "\n");
179                 for (String outputLine : splitLines) {
180                     getLog().info(outputLine);
181                 }
182             }
183         } catch (CommandLineException e) {
184             throw new MojoExecutionException("Unable to execute jmod command: " + e.getMessage(), e);
185         }
186     }
187 
188     /**
189      * Convert a list into a
190      *
191      * @param modules The list of modules.
192      * @return The string with the module list which is separated by {@code ,}.
193      */
194     protected String getCommaSeparatedList(List<String> modules) {
195         StringBuilder sb = new StringBuilder();
196         for (String module : modules) {
197             if (sb.length() > 0) {
198                 sb.append(',');
199             }
200             sb.append(module);
201         }
202         return sb.toString();
203     }
204 
205     protected Toolchain getToolchain() {
206         Toolchain tc = null;
207 
208         if (jdkToolchain != null) {
209             // Maven 3.3.1 has plugin execution scoped Toolchain Support
210             try {
211                 Method getToolchainsMethod = toolchainManager
212                         .getClass()
213                         .getMethod("getToolchains", MavenSession.class, String.class, Map.class);
214 
215                 @SuppressWarnings("unchecked")
216                 List<Toolchain> tcs =
217                         (List<Toolchain>) getToolchainsMethod.invoke(toolchainManager, session, "jdk", jdkToolchain);
218 
219                 if (tcs != null && tcs.size() > 0) {
220                     tc = tcs.get(0);
221                 }
222             } catch (NoSuchMethodException e) {
223                 // ignore
224             } catch (SecurityException e) {
225                 // ignore
226             } catch (IllegalAccessException e) {
227                 // ignore
228             } catch (IllegalArgumentException e) {
229                 // ignore
230             } catch (InvocationTargetException e) {
231                 // ignore
232             }
233         }
234 
235         if (tc == null) {
236             // TODO: Check if we should make the type configurable?
237             tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
238         }
239 
240         return tc;
241     }
242 
243     public MavenProject getProject() {
244         return project;
245     }
246 
247     public MavenSession getSession() {
248         return session;
249     }
250 }