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