View Javadoc
1   package org.apache.maven.shared.utils.cli.javatool;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.shared.utils.Os;
23  import org.apache.maven.shared.utils.StringUtils;
24  import org.apache.maven.shared.utils.cli.CommandLineException;
25  import org.apache.maven.shared.utils.cli.CommandLineUtils;
26  import org.apache.maven.shared.utils.cli.Commandline;
27  import org.apache.maven.shared.utils.cli.StreamConsumer;
28  import org.codehaus.plexus.logging.AbstractLogEnabled;
29  
30  import java.io.File;
31  import java.io.InputStream;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.util.Map;
35  
36  /**
37   * Abstract implementation of a {@link JavaTool}.
38   *
39   * @author Tony Chemit <chemit@codelutin.com>
40   * @since 0.5
41   * @param <Request>
42   */
43  public abstract class AbstractJavaTool<Request extends JavaToolRequest>
44      extends AbstractLogEnabled
45      implements JavaTool<Request>
46  {
47  
48      /**
49       * The java tool name to find out in the jdk.
50       */
51      private final String javaToolName;
52  
53      /**
54       * The location of the java tool executable file.
55       */
56      private String javaToolFile;
57  
58      /**
59       * Optional toolChain used to find java tool executable file.
60       */
61      private Object toolchain;
62  
63      /**
64       * @param javaToolName The name of the java tool.
65       */
66      protected AbstractJavaTool( String javaToolName )
67      {
68          this.javaToolName = javaToolName;
69      }
70  
71      /**
72       * Create the command line object given the request.
73       *
74       * @param request      User request on the java tool
75       * @param javaToolFileLocation Location of the java tool file to use
76       * @return the command line
77       * @throws JavaToolException if could not create the command line from the request
78       */
79      protected abstract Commandline createCommandLine( Request request, String javaToolFileLocation )
80          throws JavaToolException;
81  
82      /**
83       * {@inheritDoc}
84       */
85      public String getJavaToolName()
86      {
87          return javaToolName;
88      }
89  
90      /**
91       * {@inheritDoc}
92       */
93      public void setToolchain( Object toolchain )
94      {
95          this.toolchain = toolchain;
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     public JavaToolResult execute( Request request )
102         throws JavaToolException
103     {
104 
105         if ( javaToolFile == null )
106         {
107 
108             // find the java tool file to use
109             try
110             {
111                 javaToolFile = findJavaToolExecutable();
112             }
113             catch ( Exception e )
114             {
115                 throw new JavaToolException( "Error finding " + javaToolName + " executable. Reason: " + e.getMessage(),
116                                              e );
117             }
118         }
119 
120         // creates the command line from the given request
121         Commandline cli = createCommandLine( request, javaToolFile );
122 
123         // execute it
124         JavaToolResult result = executeCommandLine( cli, request );
125 
126         // return result
127         return result;
128     }
129 
130     /**
131      * @return {@link InputStream}
132      */
133     protected InputStream createSystemInputStream()
134     {
135         InputStream systemIn = new InputStream()
136         {
137 
138             /**
139              * {@inheritDoc}
140              */
141             public int read()
142             {
143                 return -1;
144             }
145 
146         };
147         return systemIn;
148     }
149 
150     /**
151      * @param cli {@link Commandline}
152      * @param request The request.
153      * @return {@link JavaToolRequest}
154      */
155     protected JavaToolResult executeCommandLine( Commandline cli, Request request )
156     {
157         if ( getLogger().isDebugEnabled() )
158         {
159             getLogger().debug( "Executing: " + cli );
160         }
161 
162         JavaToolResult result = createResult();
163 
164         result.setCommandline( cli );
165 
166         InputStream systemIn = createSystemInputStream();
167 
168         StreamConsumer systemOut = createSystemOutStreamConsumer( request );
169 
170         StreamConsumer systemErr = createSystemErrorStreamConsumer( request );
171 
172         try
173         {
174             int resultCode = CommandLineUtils.executeCommandLine( cli, systemIn, systemOut, systemErr );
175 
176             result.setExitCode( resultCode );
177         }
178         catch ( CommandLineException e )
179         {
180             result.setExecutionException( e );
181         }
182 
183         return result;
184     }
185 
186     /**
187      * @param request The request.
188      * @return {@link StreamConsumer}
189      */
190     protected StreamConsumer createSystemErrorStreamConsumer( Request request )
191     {
192         StreamConsumer systemErr = request.getSystemErrorStreamConsumer();
193 
194         if ( systemErr == null )
195         {
196             systemErr = new StreamConsumer()
197             {
198 
199                 /**
200                  * {@inheritDoc}
201                  */
202                 public void consumeLine( final String line )
203                 {
204                     getLogger().warn( line );
205                 }
206 
207             };
208         }
209         return systemErr;
210     }
211 
212     /**
213      * @param request The request.
214      * @return {@link StreamConsumer}
215      */
216     protected StreamConsumer createSystemOutStreamConsumer( Request request )
217     {
218         StreamConsumer systemOut = request.getSystemOutStreamConsumer();
219 
220         if ( systemOut == null )
221         {
222 
223             systemOut = new StreamConsumer()
224             {
225 
226                 /**
227                  * {@inheritDoc}
228                  */
229                 public void consumeLine( final String line )
230                 {
231                     getLogger().info( line );
232 
233                 }
234 
235             };
236         }
237         return systemOut;
238     }
239 
240     /**
241      * @return The JavaToolResult.
242      */
243     protected JavaToolResult createResult()
244     {
245         return new JavaToolResult();
246     }
247 
248     /**
249      * @return The location of the java tool executable.
250      */
251     protected String findJavaToolExecutable()
252     {
253         String executable = null;
254 
255         if ( toolchain != null )
256         {
257             executable = findToolchainExecutable();
258         }
259 
260         String command = javaToolName + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" );
261 
262         if ( executable == null )
263         {
264             executable = findExecutable( command, System.getProperty( "java.home" ), "../bin", "bin", "../sh" );
265         }
266 
267         if ( executable == null )
268         {
269 
270             Map<String, String> env = System.getenv();
271 
272             String[] variables = { "JDK_HOME", "JAVA_HOME" };
273 
274             for ( String variable : variables )
275             {
276                 executable = findExecutable( command, env.get( variable ), "bin", "sh" );
277                 if ( executable != null )
278                 {
279                     break;
280                 }
281             }
282         }
283 
284         if ( executable == null )
285         {
286             executable = command;
287         }
288 
289         return executable;
290     }
291 
292     /**
293      * Run toolchain.findTool( javaToolName ); through reflection to avoid compile dependency on
294      * Maven core.
295      */
296     private String findToolchainExecutable()
297     {
298         try
299         {
300             Method m = toolchain.getClass().getMethod( "findTool", String.class );
301             return (String) m.invoke( toolchain, javaToolName );
302         }
303         catch ( NoSuchMethodException e )
304         {
305             // should not happen if toolchain is really a Toolchain object
306             getLogger().warn( "unexpected NoSuchMethodException", e );
307         }
308         catch ( SecurityException e )
309         {
310             // should not happen
311             getLogger().warn( "unexpected SecurityException", e );
312         }
313         catch ( IllegalAccessException e )
314         {
315             // should not happen
316             getLogger().warn( "unexpected IllegalAccessException", e );
317         }
318         catch ( IllegalArgumentException e )
319         {
320             // should not happen: parameter is the right type
321             getLogger().warn( "unexpected IllegalArgumentException", e );
322         }
323         catch ( InvocationTargetException e )
324         {
325             // not expected...
326             getLogger().warn( "unexpected InvocationTargetException", e );
327         }
328         return null;
329     }
330 
331     /**
332      * Finds the specified command in any of the given sub directories of the specified JDK/JRE home directory.
333      *
334      * @param command The command to find, must not be <code>null</code>.
335      * @param homeDir The home directory to search in, may be <code>null</code>.
336      * @param subDirs The sub directories of the home directory to search in, must not be <code>null</code>.
337      * @return The (absolute) path to the command if found, <code>null</code> otherwise.
338      */
339     private String findExecutable( String command, String homeDir, String... subDirs )
340     {
341         String result = null;
342         if ( StringUtils.isNotEmpty( homeDir ) )
343         {
344             for ( String subDir : subDirs )
345             {
346                 File file = new File( new File( homeDir, subDir ), command );
347 
348                 if ( file.isFile() )
349                 {
350                     result = file.getAbsolutePath();
351                     break;
352                 }
353             }
354         }
355 
356         return result;
357     }
358 }