View Javadoc
1   package org.apache.maven.shared.utils.cli;
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 java.io.File;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.Vector;
32  
33  import org.apache.maven.shared.utils.Os;
34  import org.apache.maven.shared.utils.StringUtils;
35  import org.apache.maven.shared.utils.cli.shell.BourneShell;
36  import org.apache.maven.shared.utils.cli.shell.CmdShell;
37  import org.apache.maven.shared.utils.cli.shell.CommandShell;
38  import org.apache.maven.shared.utils.cli.shell.Shell;
39  
40  /**
41   * <p/>
42   * Commandline objects help handling command lines specifying processes to
43   * execute.
44   * </p>
45   * <p/>
46   * The class can be used to define a command line as nested elements or as a
47   * helper to define a command line by an application.
48   * </p>
49   * <p/>
50   * <code>
51   * &lt;someelement&gt;<br>
52   * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
53   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
54   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
55   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
56   * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
57   * &lt;/someelement&gt;<br>
58   * </code>
59   * </p>
60   * <p/>
61   * The element <code>someelement</code> must provide a method
62   * <code>createAcommandline</code> which returns an instance of this class.
63   * </p>
64   *
65   * @author thomas.haas@softwired-inc.com
66   * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
67   */
68  public class Commandline
69      implements Cloneable
70  {
71      private final List<Arg> arguments = new Vector<Arg>();
72  
73      //protected Vector envVars = new Vector();
74      // synchronized added to preserve synchronize of Vector class
75      private final Map<String, String> envVars = Collections.synchronizedMap( new LinkedHashMap<String, String>() );
76  
77      private Shell shell;
78  
79      /**
80       * Create a new command line object.
81       * Shell is autodetected from operating system
82       * @param shell The shell instance.
83       */
84      public Commandline( Shell shell )
85      {
86          this.shell = shell;
87      }
88  
89      /**
90       * Create a new command line object.
91       * Shell is autodetected from operating system
92       *
93       * @param toProcess The command to process
94       */
95      public Commandline( String toProcess ) throws CommandLineException
96      {
97          setDefaultShell();
98          String[] tmp = CommandLineUtils.translateCommandline( toProcess );
99          if ( ( tmp.length > 0 ) )
100         {
101             setExecutable( tmp[0] );
102             for ( int i = 1; i < tmp.length; i++ )
103             {
104                 createArg().setValue( tmp[i] );
105             }
106         }
107     }
108 
109     /**
110      * Create a new command line object.
111      * Shell is autodetected from operating system
112      */
113     public Commandline()
114     {
115         setDefaultShell();
116     }
117 
118     /**
119      * <p>Sets the shell or command-line interpretor for the detected operating system,
120      * and the shell arguments.</p>
121      */
122     private void setDefaultShell()
123     {
124         //If this is windows set the shell to command.com or cmd.exe with correct arguments.
125         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
126         {
127             if ( Os.isFamily( Os.FAMILY_WIN9X ) )
128             {
129                 setShell( new CommandShell() );
130             }
131             else
132             {
133                 setShell( new CmdShell() );
134             }
135         }
136         else
137         {
138             setShell( new BourneShell() );
139         }
140     }
141 
142     /**
143      * Creates an argument object.
144      * <p/>
145      * <p>Each commandline object has at most one instance of the
146      * argument class.  This method calls
147      * <code>this.createArgument(false)</code>.</p>
148      *
149      * @return the argument object.
150      */
151     public Arg createArg()
152     {
153         return this.createArg( false );
154     }
155 
156     /**
157      * Creates an argument object and adds it to our list of args.
158      * <p/>
159      * <p>Each commandline object has at most one instance of the
160      * argument class.</p>
161      *
162      * @param insertAtStart if true, the argument is inserted at the
163      *                      beginning of the list of args, otherwise it is appended.
164      * @return The arguments.
165      */
166     public Arg createArg( boolean insertAtStart )
167     {
168         Arg argument = new Argument();
169         if ( insertAtStart )
170         {
171             arguments.add( 0, argument );
172         }
173         else
174         {
175             arguments.add( argument );
176         }
177         return argument;
178     }
179 
180     /**
181      * Sets the executable to run.
182      * @param executable The executable.
183      */
184     public void setExecutable( String executable )
185     {
186         shell.setExecutable( executable );
187     }
188 
189     /**
190      * @return The executable.
191      */
192     public String getExecutable()
193     {
194 
195         return shell.getExecutable();
196     }
197 
198     /**
199      * @param line The arguments.
200      */
201     public void addArguments( String... line )
202     {
203         for ( String aLine : line )
204         {
205             createArg().setValue( aLine );
206         }
207     }
208 
209     /**
210      * Add an environment variable
211      * @param name The name of the environment variable.
212      * @param value The appropriate value.
213      */
214     public void addEnvironment( String name, String value )
215     {
216         //envVars.add( name + "=" + value );
217         envVars.put( name, value );
218     }
219 
220     /**
221      * Add system environment variables
222      */
223     public void addSystemEnvironment()
224     {
225         Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
226 
227         for ( Object o : systemEnvVars.keySet() )
228         {
229             String key = (String) o;
230             if ( !envVars.containsKey( key ) )
231             {
232                 addEnvironment( key, systemEnvVars.getProperty( key ) );
233             }
234         }
235     }
236 
237     /**
238      * Return the list of environment variables
239      * @return an array of all environment variables.
240      */
241     public String[] getEnvironmentVariables()
242     {
243         addSystemEnvironment();
244         String[] environmentVars = new String[envVars.size()];
245         int i = 0;
246         for ( String name : envVars.keySet() )
247         {
248             String value = envVars.get( name );
249             environmentVars[i] = name + "=" + value;
250             i++;
251         }
252         return environmentVars;
253     }
254 
255     /**
256      * Returns the executable and all defined arguments.
257      * @return an array of all arguments incl. executable.
258      */
259     public String[] getCommandline()
260     {
261         final String[] args = getArguments();
262         String executable = getExecutable();
263 
264         if ( executable == null )
265         {
266             return args;
267         }
268         final String[] result = new String[args.length + 1];
269         result[0] = executable;
270         System.arraycopy( args, 0, result, 1, args.length );
271         return result;
272     }
273 
274     /**
275      * @return the shell, executable and all defined arguments without masking any arguments.
276      */
277     private String[] getShellCommandline()
278     {
279         return getShellCommandline( false ) ;
280     }
281 
282     /**
283      * @param mask flag to mask any arguments (having his {@code mask} field to {@code true}).
284      * @return the shell, executable and all defined arguments with masking some arguments if
285      * {@code mask} parameter is on
286      */
287     private String[] getShellCommandline( boolean mask )
288     {
289         List<String> shellCommandLine = getShell().getShellCommandLine( getArguments( mask ) );
290         return shellCommandLine.toArray( new String[shellCommandLine.size()] );
291     }
292 
293     /**
294      * Returns all arguments defined by <code>addLine</code>,
295      * <code>addValue</code> or the argument object.
296      * @return an array of arguments.
297      */
298     public String[] getArguments()
299     {
300         return getArguments( false );
301     }
302 
303     /**
304      * Returns all arguments defined by <code>addLine</code>,
305      * <code>addValue</code> or the argument object.
306      *
307      * @param mask flag to mask any arguments (having his {@code mask} field to {@code true}).
308      * @return an array of arguments.
309      */
310     public String[] getArguments( boolean mask )
311     {
312         List<String> result = new ArrayList<String>( arguments.size() * 2 );
313         for ( Arg argument : arguments )
314         {
315             Argument arg = (Argument) argument;
316             String[] s = arg.getParts();
317             if ( s != null )
318             {
319                 if ( mask && ( arg.isMask() ) )
320                 {
321                     // should be a key-pair argument
322                     if ( s.length > 0 )
323                     {
324 
325                         // use a masked copy
326                         String[] copy = new String[s.length];
327                         Arrays.fill( copy, "*****" );
328                         s = copy;
329                     }
330                 }
331                 Collections.addAll( result, s );
332             }
333         }
334 
335         return result.toArray( new String[result.size()] );
336     }
337 
338     /** {@inheritDoc}
339      */
340     public String toString()
341     {
342         return StringUtils.join( getShellCommandline( true ), " " );
343     }
344 
345 
346     /** {@inheritDoc}
347      */
348     public Object clone()
349     {
350         throw new RuntimeException( "Do we ever clone a commandline?" );
351 /*        Commandline c = new Commandline( (Shell) shell.clone() );
352        c.addArguments( getArguments() );
353         return c;*/
354     }
355 
356     /**
357      * Sets working directory.
358      * @param path The to be set as working directory.
359      */
360     public void setWorkingDirectory( String path )
361     {
362         shell.setWorkingDirectory( path );
363     }
364 
365     /**
366      * Sets execution directory.
367      * @param workingDirectory The working directory.
368      */
369     public void setWorkingDirectory( File workingDirectory )
370     {
371         shell.setWorkingDirectory( workingDirectory );
372     }
373 
374     /**
375      * @return The working directory.
376      */
377     public File getWorkingDirectory()
378     {
379         return shell.getWorkingDirectory();
380     }
381 
382     /**
383      * Clear out the arguments but leave the executable in place for another operation.
384      */
385     public void clearArgs()
386     {
387         arguments.clear();
388     }
389 
390     /**
391      * Executes the command.
392      * @return The process.
393      * @throws CommandLineException in case of errors.
394      */
395     public Process execute()
396         throws CommandLineException
397     {
398         Process process;
399 
400         //addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" );
401 
402         String[] environment = getEnvironmentVariables();
403 
404         File workingDir = shell.getWorkingDirectory();
405 
406         try
407         {
408             if ( workingDir == null )
409             {
410                 process = Runtime.getRuntime().exec( getShellCommandline(), environment );
411             }
412             else
413             {
414                 if ( !workingDir.exists() )
415                 {
416                     throw new CommandLineException(
417                         "Working directory \"" + workingDir.getPath() + "\" does not exist!" );
418                 }
419                 else if ( !workingDir.isDirectory() )
420                 {
421                     throw new CommandLineException(
422                         "Path \"" + workingDir.getPath() + "\" does not specify a directory." );
423                 }
424 
425                 process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir );
426             }
427         }
428         catch ( IOException ex )
429         {
430             throw new CommandLineException( "Error while executing process.", ex );
431         }
432 
433         return process;
434     }
435 
436     /**
437      * Allows to set the shell to be used in this command line.
438      *
439      * @param shell the shell
440      */
441     void setShell( Shell shell )
442     {
443         this.shell = shell;
444     }
445 
446     /**
447      * Get the shell to be used in this command line.
448      * @return the shell.
449      */
450     public Shell getShell()
451     {
452         return shell;
453     }
454 
455     /**
456      * 
457      */
458     public static class Argument
459         implements Arg
460     {
461         private String[] parts;
462 
463         private boolean mask;
464 
465         /**
466          * {@inheritDoc}
467          */
468         public void setValue( String value )
469         {
470             if ( value != null )
471             {
472                 parts = new String[]{ value };
473             }
474         }
475 
476         /**
477          * {@inheritDoc}
478          */
479         public void setLine( String line ) throws CommandLineException
480         {
481             if ( line == null )
482             {
483                 return;
484             }
485             try
486             {
487                 parts = CommandLineUtils.translateCommandline( line );
488             }
489             catch ( CommandLineException e )
490             {
491                 System.err.println( "Error translating Commandline." );
492                 throw( e );
493             }
494         }
495 
496         /**
497          * {@inheritDoc}
498          */
499         public void setFile( File value )
500         {
501             parts = new String[]{ value.getAbsolutePath() };
502         }
503 
504         /**
505          * {@inheritDoc}
506          */
507         public void setMask( boolean mask )
508         {
509             this.mask = mask;
510         }
511 
512         /**
513          * @return The parts.
514          */
515         private String[] getParts()
516         {
517             return parts;
518         }
519 
520         /**
521          * @return true/false
522          */
523         public boolean isMask()
524         {
525             return mask;
526         }
527     }
528 }