blob: 75606e78c8a3448365e82b00b43932f34b5c3143 [file] [log] [blame]
package org.apache.maven.shared.utils.cli;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.maven.shared.utils.Os;
import org.apache.maven.shared.utils.StringUtils;
import org.apache.maven.shared.utils.cli.shell.BourneShell;
import org.apache.maven.shared.utils.cli.shell.CmdShell;
import org.apache.maven.shared.utils.cli.shell.CommandShell;
import org.apache.maven.shared.utils.cli.shell.Shell;
/**
* <p/>
* Commandline objects help handling command lines specifying processes to
* execute.
* </p>
* <p/>
* The class can be used to define a command line as nested elements or as a
* helper to define a command line by an application.
* </p>
* <p/>
* <code>
* &lt;someelement&gt;<br>
* &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
* &nbsp;&nbsp;&lt;/acommandline&gt;<br>
* &lt;/someelement&gt;<br>
* </code>
* </p>
* <p/>
* The element <code>someelement</code> must provide a method
* <code>createAcommandline</code> which returns an instance of this class.
* </p>
*
* @author thomas.haas@softwired-inc.com
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class Commandline
implements Cloneable
{
private final List<Argument> arguments = new ArrayList<>();
//protected Vector envVars = new Vector();
// synchronized added to preserve synchronize of Vector class
private final Map<String, String> envVars = Collections.synchronizedMap( new LinkedHashMap<String, String>() );
private Shell shell;
/**
* Create a new command line object.
* Shell is autodetected from operating system
* @param shell The shell instance.
*/
public Commandline( Shell shell )
{
this.shell = shell;
}
/**
* Create a new command line object.
* Shell is autodetected from operating system
*
* @param toProcess The command to process
*/
public Commandline( String toProcess ) throws CommandLineException
{
setDefaultShell();
String[] tmp = CommandLineUtils.translateCommandline( toProcess );
if ( tmp.length > 0 )
{
setExecutable( tmp[0] );
for ( int i = 1; i < tmp.length; i++ )
{
createArg().setValue( tmp[i] );
}
}
}
/**
* Create a new command line object.
* Shell is autodetected from operating system
*/
public Commandline()
{
setDefaultShell();
}
/**
* <p>Sets the shell or command-line interpretor for the detected operating system,
* and the shell arguments.</p>
*/
private void setDefaultShell()
{
//If this is windows set the shell to command.com or cmd.exe with correct arguments.
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
if ( Os.isFamily( Os.FAMILY_WIN9X ) )
{
setShell( new CommandShell() );
}
else
{
setShell( new CmdShell() );
}
}
else
{
setShell( new BourneShell() );
}
}
/**
* Creates an argument object.
* <p/>
* <p>Each commandline object has at most one instance of the
* argument class. This method calls
* <code>this.createArgument(false)</code>.</p>
*
* @return the argument object.
*/
public Arg createArg()
{
return this.createArg( false );
}
/**
* Creates an argument object and adds it to our list of args.
* <p/>
* <p>Each commandline object has at most one instance of the
* argument class.</p>
*
* @param insertAtStart if true, the argument is inserted at the
* beginning of the list of args, otherwise it is appended.
* @return The arguments.
*/
public Arg createArg( boolean insertAtStart )
{
Argument argument = new Argument();
if ( insertAtStart )
{
arguments.add( 0, argument );
}
else
{
arguments.add( argument );
}
return argument;
}
/**
* Sets the executable to run.
* @param executable The executable.
*/
public void setExecutable( String executable )
{
shell.setExecutable( executable );
}
/**
* @return The executable.
*/
public String getExecutable()
{
return shell.getExecutable();
}
/**
* @param line The arguments.
*/
public void addArguments( String... line )
{
for ( String aLine : line )
{
createArg().setValue( aLine );
}
}
/**
* Add an environment variable
* @param name The name of the environment variable.
* @param value The appropriate value.
*/
public void addEnvironment( String name, String value )
{
//envVars.add( name + "=" + value );
envVars.put( name, value );
}
/**
* Add system environment variables
*/
public void addSystemEnvironment()
{
Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
for ( Object o : systemEnvVars.keySet() )
{
String key = (String) o;
if ( !envVars.containsKey( key ) )
{
addEnvironment( key, systemEnvVars.getProperty( key ) );
}
}
}
/**
* Return the list of environment variables
* @return an array of all environment variables.
*/
public String[] getEnvironmentVariables()
{
addSystemEnvironment();
String[] environmentVars = new String[envVars.size()];
int i = 0;
for ( String name : envVars.keySet() )
{
String value = envVars.get( name );
environmentVars[i] = name + "=" + value;
i++;
}
return environmentVars;
}
/**
* Returns the executable and all defined arguments.
* @return an array of all arguments incl. executable.
*/
public String[] getCommandline()
{
final String[] args = getArguments();
String executable = getExecutable();
if ( executable == null )
{
return args;
}
final String[] result = new String[args.length + 1];
result[0] = executable;
System.arraycopy( args, 0, result, 1, args.length );
return result;
}
/**
* @return the shell, executable and all defined arguments without masking any arguments.
*/
private String[] getShellCommandline()
{
return getShellCommandline( false ) ;
}
/**
* @param mask flag to mask any arguments (having his {@code mask} field to {@code true}).
* @return the shell, executable and all defined arguments with masking some arguments if
* {@code mask} parameter is on
*/
private String[] getShellCommandline( boolean mask )
{
List<String> shellCommandLine = getShell().getShellCommandLine( getArguments( mask ) );
return shellCommandLine.toArray( new String[shellCommandLine.size()] );
}
/**
* Returns all arguments defined by <code>addLine</code>,
* <code>addValue</code> or the argument object.
* @return an array of arguments.
*/
public String[] getArguments()
{
return getArguments( false );
}
/**
* Returns all arguments defined by <code>addLine</code>,
* <code>addValue</code> or the argument object.
*
* @param mask flag to mask any arguments (having his {@code mask} field to {@code true}).
* @return an array of arguments.
*/
public String[] getArguments( boolean mask )
{
List<String> result = new ArrayList<>( arguments.size() * 2 );
for ( Argument arg : arguments )
{
String[] s = arg.getParts();
if ( s != null )
{
if ( mask && ( arg.isMask() ) )
{
// should be a key-pair argument
if ( s.length > 0 )
{
// use a masked copy
String[] copy = new String[s.length];
Arrays.fill( copy, "*****" );
s = copy;
}
}
Collections.addAll( result, s );
}
}
return result.toArray( new String[result.size()] );
}
@Override
public String toString()
{
return StringUtils.join( getShellCommandline( true ), " " );
}
@Override
public Object clone()
{
throw new RuntimeException( "Do we ever clone a commandline?" );
/* Commandline c = new Commandline( (Shell) shell.clone() );
c.addArguments( getArguments() );
return c;*/
}
/**
* Sets working directory.
* @param path The to be set as working directory.
*/
public void setWorkingDirectory( String path )
{
shell.setWorkingDirectory( path );
}
/**
* Sets execution directory.
* @param workingDirectory The working directory.
*/
public void setWorkingDirectory( File workingDirectory )
{
shell.setWorkingDirectory( workingDirectory );
}
/**
* @return The working directory.
*/
public File getWorkingDirectory()
{
return shell.getWorkingDirectory();
}
/**
* Clear out the arguments but leave the executable in place for another operation.
*/
public void clearArgs()
{
arguments.clear();
}
/**
* Executes the command.
* @return The process.
* @throws CommandLineException in case of errors.
*/
public Process execute()
throws CommandLineException
{
Process process;
//addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" );
String[] environment = getEnvironmentVariables();
File workingDir = shell.getWorkingDirectory();
try
{
if ( workingDir == null )
{
process = Runtime.getRuntime().exec( getShellCommandline(), environment );
}
else
{
if ( !workingDir.exists() )
{
throw new CommandLineException(
"Working directory \"" + workingDir.getPath() + "\" does not exist!" );
}
else if ( !workingDir.isDirectory() )
{
throw new CommandLineException(
"Path \"" + workingDir.getPath() + "\" does not specify a directory." );
}
process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir );
}
}
catch ( IOException ex )
{
throw new CommandLineException( "Error while executing process.", ex );
}
return process;
}
/**
* Allows to set the shell to be used in this command line.
*
* @param shell the shell
*/
void setShell( Shell shell )
{
this.shell = shell;
}
/**
* Get the shell to be used in this command line.
* @return the shell.
*/
public Shell getShell()
{
return shell;
}
/**
*
*/
public static class Argument
implements Arg
{
private String[] parts;
private boolean mask;
@Override
public void setValue( String value )
{
if ( value != null )
{
parts = new String[]{ value };
}
}
@Override
public void setLine( String line ) throws CommandLineException
{
if ( line == null )
{
return;
}
try
{
parts = CommandLineUtils.translateCommandline( line );
}
catch ( CommandLineException e )
{
System.err.println( "Error translating Commandline." );
throw( e );
}
}
@Override
public void setFile( File value )
{
parts = new String[]{ value.getAbsolutePath() };
}
@Override
public void setMask( boolean mask )
{
this.mask = mask;
}
/**
* @return The parts.
*/
private String[] getParts()
{
return parts;
}
/**
* @return true/false
*/
public boolean isMask()
{
return mask;
}
}
}