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                 @Override
203                 public void consumeLine( final String line )
204                 {
205                     getLogger().warn( line );
206                 }
207 
208             };
209         }
210         return systemErr;
211     }
212 
213     /**
214      * @param request The request.
215      * @return {@link StreamConsumer}
216      */
217     protected StreamConsumer createSystemOutStreamConsumer( Request request )
218     {
219         StreamConsumer systemOut = request.getSystemOutStreamConsumer();
220 
221         if ( systemOut == null )
222         {
223 
224             systemOut = new StreamConsumer()
225             {
226 
227                 /**
228                  * {@inheritDoc}
229                  */
230                 @Override
231                 public void consumeLine( final String line )
232                 {
233                     getLogger().info( line );
234 
235                 }
236 
237             };
238         }
239         return systemOut;
240     }
241 
242     /**
243      * @return The JavaToolResult.
244      */
245     protected JavaToolResult createResult()
246     {
247         return new JavaToolResult();
248     }
249 
250     /**
251      * @return The location of the java tool executable.
252      */
253     protected String findJavaToolExecutable()
254     {
255         String executable = null;
256 
257         if ( toolchain != null )
258         {
259             executable = findToolchainExecutable();
260         }
261 
262         String command = javaToolName + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" );
263 
264         if ( executable == null )
265         {
266             executable = findExecutable( command, System.getProperty( "java.home" ), "../bin", "bin", "../sh" );
267         }
268 
269         if ( executable == null )
270         {
271 
272             Map<String, String> env = System.getenv();
273 
274             String[] variables = { "JDK_HOME", "JAVA_HOME" };
275 
276             for ( String variable : variables )
277             {
278                 executable = findExecutable( command, env.get( variable ), "bin", "sh" );
279                 if ( executable != null )
280                 {
281                     break;
282                 }
283             }
284         }
285 
286         if ( executable == null )
287         {
288             executable = command;
289         }
290 
291         return executable;
292     }
293 
294     /**
295      * Run toolchain.findTool( javaToolName ); through reflection to avoid compile dependency on
296      * Maven core.
297      */
298     private String findToolchainExecutable()
299     {
300         try
301         {
302             Method m = toolchain.getClass().getMethod( "findTool", String.class );
303             return (String) m.invoke( toolchain, javaToolName );
304         }
305         catch ( NoSuchMethodException e )
306         {
307             // should not happen if toolchain is really a Toolchain object
308             getLogger().warn( "unexpected NoSuchMethodException", e );
309         }
310         catch ( SecurityException e )
311         {
312             // should not happen
313             getLogger().warn( "unexpected SecurityException", e );
314         }
315         catch ( IllegalAccessException e )
316         {
317             // should not happen
318             getLogger().warn( "unexpected IllegalAccessException", e );
319         }
320         catch ( IllegalArgumentException e )
321         {
322             // should not happen: parameter is the right type
323             getLogger().warn( "unexpected IllegalArgumentException", e );
324         }
325         catch ( InvocationTargetException e )
326         {
327             // not expected...
328             getLogger().warn( "unexpected InvocationTargetException", e );
329         }
330         return null;
331     }
332 
333     /**
334      * Finds the specified command in any of the given sub directories of the specified JDK/JRE home directory.
335      *
336      * @param command The command to find, must not be <code>null</code>.
337      * @param homeDir The home directory to search in, may be <code>null</code>.
338      * @param subDirs The sub directories of the home directory to search in, must not be <code>null</code>.
339      * @return The (absolute) path to the command if found, <code>null</code> otherwise.
340      */
341     private String findExecutable( String command, String homeDir, String... subDirs )
342     {
343         String result = null;
344         if ( StringUtils.isNotEmpty( homeDir ) )
345         {
346             for ( String subDir : subDirs )
347             {
348                 File file = new File( new File( homeDir, subDir ), command );
349 
350                 if ( file.isFile() )
351                 {
352                     result = file.getAbsolutePath();
353                     break;
354                 }
355             }
356         }
357 
358         return result;
359     }
360 }