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.shared.utils.cli.javatool;
20  
21  import java.io.File;
22  import java.io.InputStream;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.util.Map;
26  
27  import org.apache.maven.shared.utils.Os;
28  import org.apache.maven.shared.utils.StringUtils;
29  import org.apache.maven.shared.utils.cli.CommandLineException;
30  import org.apache.maven.shared.utils.cli.CommandLineUtils;
31  import org.apache.maven.shared.utils.cli.Commandline;
32  import org.apache.maven.shared.utils.cli.StreamConsumer;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  /**
37   * Abstract implementation of a {@link JavaTool}.
38   *
39   * @author <a href="mailto:chemit@codelutin.com">Tony Chemit</a>
40   * @since 0.5
41   * @param <Request> Tool-specific request type
42   */
43  public abstract class AbstractJavaTool<Request extends JavaToolRequest> implements JavaTool<Request> {
44      private final Logger logger = LoggerFactory.getLogger(getClass());
45  
46      /**
47       * The java tool name to find out in the jdk.
48       */
49      private final String javaToolName;
50  
51      /**
52       * The location of the java tool executable file.
53       */
54      private String javaToolFile;
55  
56      /**
57       * Optional toolChain used to find java tool executable file.
58       */
59      private Object toolchain;
60  
61      /**
62       * @param javaToolName The name of the java tool.
63       */
64      protected AbstractJavaTool(String javaToolName) {
65          this.javaToolName = javaToolName;
66      }
67  
68      /**
69       * Create the command line object given the request.
70       *
71       * @param request      User request on the java tool
72       * @param javaToolFileLocation Location of the java tool file to use
73       * @return the command line
74       * @throws JavaToolException if could not create the command line from the request
75       */
76      protected abstract Commandline createCommandLine(Request request, String javaToolFileLocation)
77              throws JavaToolException;
78  
79      protected Logger getLogger() {
80          return logger;
81      }
82  
83      /**
84       * {@inheritDoc}
85       */
86      public String getJavaToolName() {
87          return javaToolName;
88      }
89  
90      /**
91       * {@inheritDoc}
92       */
93      public void setToolchain(Object toolchain) {
94          this.toolchain = toolchain;
95      }
96  
97      /**
98       * {@inheritDoc}
99       */
100     public JavaToolResult execute(Request request) throws JavaToolException {
101 
102         if (javaToolFile == null) {
103 
104             // find the java tool file to use
105             try {
106                 javaToolFile = findJavaToolExecutable();
107             } catch (Exception e) {
108                 throw new JavaToolException(
109                         "Error finding " + javaToolName + " executable. Reason: " + e.getMessage(), e);
110             }
111         }
112 
113         // creates the command line from the given request
114         Commandline cli = createCommandLine(request, javaToolFile);
115 
116         // execute it
117         JavaToolResult result = executeCommandLine(cli, request);
118 
119         // return result
120         return result;
121     }
122 
123     /**
124      * @return {@link InputStream}
125      */
126     protected InputStream createSystemInputStream() {
127         InputStream systemIn = new InputStream() {
128 
129             /**
130              * {@inheritDoc}
131              */
132             public int read() {
133                 return -1;
134             }
135         };
136         return systemIn;
137     }
138 
139     /**
140      * @param cli {@link Commandline}
141      * @param request The request.
142      * @return {@link JavaToolRequest}
143      */
144     protected JavaToolResult executeCommandLine(Commandline cli, Request request) {
145         if (getLogger().isDebugEnabled()) {
146             getLogger().debug("Executing: " + cli);
147         }
148 
149         JavaToolResult result = createResult();
150 
151         result.setCommandline(cli);
152 
153         InputStream systemIn = createSystemInputStream();
154 
155         StreamConsumer systemOut = createSystemOutStreamConsumer(request);
156 
157         StreamConsumer systemErr = createSystemErrorStreamConsumer(request);
158 
159         try {
160             int resultCode = CommandLineUtils.executeCommandLine(cli, systemIn, systemOut, systemErr);
161 
162             result.setExitCode(resultCode);
163         } catch (CommandLineException e) {
164             result.setExecutionException(e);
165         }
166 
167         return result;
168     }
169 
170     /**
171      * @param request The request.
172      * @return {@link StreamConsumer}
173      */
174     protected StreamConsumer createSystemErrorStreamConsumer(Request request) {
175         StreamConsumer systemErr = request.getSystemErrorStreamConsumer();
176 
177         if (systemErr == null) {
178             systemErr = new StreamConsumer() {
179 
180                 /**
181                  * {@inheritDoc}
182                  */
183                 @Override
184                 public void consumeLine(final String line) {
185                     getLogger().warn(line);
186                 }
187             };
188         }
189         return systemErr;
190     }
191 
192     /**
193      * @param request The request.
194      * @return {@link StreamConsumer}
195      */
196     protected StreamConsumer createSystemOutStreamConsumer(Request request) {
197         StreamConsumer systemOut = request.getSystemOutStreamConsumer();
198 
199         if (systemOut == null) {
200 
201             systemOut = new StreamConsumer() {
202 
203                 /**
204                  * {@inheritDoc}
205                  */
206                 @Override
207                 public void consumeLine(final String line) {
208                     getLogger().info(line);
209                 }
210             };
211         }
212         return systemOut;
213     }
214 
215     /**
216      * @return The JavaToolResult.
217      */
218     protected JavaToolResult createResult() {
219         return new JavaToolResult();
220     }
221 
222     /**
223      * @return The location of the java tool executable.
224      */
225     protected String findJavaToolExecutable() {
226         String executable = null;
227 
228         if (toolchain != null) {
229             executable = findToolchainExecutable();
230         }
231 
232         String command = javaToolName + (Os.isFamily(Os.FAMILY_WINDOWS) ? ".exe" : "");
233 
234         if (executable == null) {
235             executable = findExecutable(command, System.getProperty("java.home"), "../bin", "bin", "../sh");
236         }
237 
238         if (executable == null) {
239 
240             Map<String, String> env = System.getenv();
241 
242             String[] variables = {"JDK_HOME", "JAVA_HOME"};
243 
244             for (String variable : variables) {
245                 executable = findExecutable(command, env.get(variable), "bin", "sh");
246                 if (executable != null) {
247                     break;
248                 }
249             }
250         }
251 
252         if (executable == null) {
253             executable = command;
254         }
255 
256         return executable;
257     }
258 
259     /**
260      * Run toolchain.findTool( javaToolName ); through reflection to avoid compile dependency on
261      * Maven core.
262      */
263     private String findToolchainExecutable() {
264         try {
265             Method m = toolchain.getClass().getMethod("findTool", String.class);
266             return (String) m.invoke(toolchain, javaToolName);
267         } catch (NoSuchMethodException e) {
268             // should not happen if toolchain is really a Toolchain object
269             getLogger().warn("unexpected NoSuchMethodException", e);
270         } catch (SecurityException e) {
271             // should not happen
272             getLogger().warn("unexpected SecurityException", e);
273         } catch (IllegalAccessException e) {
274             // should not happen
275             getLogger().warn("unexpected IllegalAccessException", e);
276         } catch (IllegalArgumentException e) {
277             // should not happen: parameter is the right type
278             getLogger().warn("unexpected IllegalArgumentException", e);
279         } catch (InvocationTargetException e) {
280             // not expected...
281             getLogger().warn("unexpected InvocationTargetException", e);
282         }
283         return null;
284     }
285 
286     /**
287      * Finds the specified command in any of the given sub directories of the specified JDK/JRE home directory.
288      *
289      * @param command The command to find, must not be <code>null</code>.
290      * @param homeDir The home directory to search in, may be <code>null</code>.
291      * @param subDirs The sub directories of the home directory to search in, must not be <code>null</code>.
292      * @return The (absolute) path to the command if found, <code>null</code> otherwise.
293      */
294     private String findExecutable(String command, String homeDir, String... subDirs) {
295         String result = null;
296         if (StringUtils.isNotEmpty(homeDir)) {
297             for (String subDir : subDirs) {
298                 File file = new File(new File(homeDir, subDir), command);
299 
300                 if (file.isFile()) {
301                     result = file.getAbsolutePath();
302                     break;
303                 }
304             }
305         }
306 
307         return result;
308     }
309 }