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