donaldp 01/12/17 01:49:30
Added: proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec
ExecTask.java Execute.java
ExecuteStreamHandler.java ExecuteWatchdog.java
LogOutputStream.java LogStreamHandler.java
ProcessDestroyer.java PumpStreamHandler.java
StreamPumper.java
Log:
Move the execute related classes into a new package.
Revision Changes Path
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/ExecTask.java
Index: ExecTask.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import org.apache.myrmidon.api.TaskException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Environment;
/**
* Executes a given command if the os platform is appropriate.
*
* @author [EMAIL PROTECTED]
* @author [EMAIL PROTECTED]
* @author [EMAIL PROTECTED]
* @author <a href="mailto:[EMAIL PROTECTED]">Stefan Bodewig</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Mariusz Nowostawski</a>
*/
public class ExecTask extends Task
{
private static String lSep = System.getProperty( "line.separator" );
protected boolean failOnError = false;
protected boolean newEnvironment = false;
private Integer timeout = null;
private Environment env = new Environment();
protected Commandline cmdl = new Commandline();
private FileOutputStream fos = null;
private ByteArrayOutputStream baos = null;
private boolean failIfExecFails = true;
/**
* Controls whether the VM (1.3 and above) is used to execute the command
*/
private boolean vmLauncher = true;
private File dir;
private String os;
private File out;
private String outputprop;
private String resultProperty;
/**
* The working directory of the process
*
* @param d The new Dir value
*/
public void setDir( File d )
throws TaskException
{
this.dir = d;
}
/**
* The command to execute.
*
* @param value The new Executable value
*/
public void setExecutable( String value )
throws TaskException
{
cmdl.setExecutable( value );
}
/**
* ant attribute
*
* @param flag The new FailIfExecutionFails value
*/
public void setFailIfExecutionFails( boolean flag )
{
failIfExecFails = flag;
}
/**
* Throw a TaskException if process returns non 0.
*
* @param fail The new Failonerror value
*/
public void setFailonerror( boolean fail )
{
failOnError = fail;
}
/**
* Use a completely new environment
*
* @param newenv The new Newenvironment value
*/
public void setNewenvironment( boolean newenv )
{
newEnvironment = newenv;
}
/**
* Only execute the process if <code>os.name</code> is included in this
* string.
*
* @param os The new Os value
*/
public void setOs( String os )
{
this.os = os;
}
/**
* File the output of the process is redirected to.
*
* @param out The new Output value
*/
public void setOutput( File out )
{
this.out = out;
}
/**
* Property name whose value should be set to the output of the process
*
* @param outputprop The new Outputproperty value
*/
public void setOutputproperty( String outputprop )
{
this.outputprop = outputprop;
}
/**
* fill a property in with a result. when no property is defined: failure
to
* execute
*
* @param resultProperty The new ResultProperty value
* @since 1.5
*/
public void setResultProperty( String resultProperty )
{
this.resultProperty = resultProperty;
}
/**
* Timeout in milliseconds after which the process will be killed.
*
* @param value The new Timeout value
*/
public void setTimeout( Integer value )
{
timeout = value;
}
/**
* Control whether the VM is used to launch the new process or whether the
* OS's shell is used.
*
* @param vmLauncher The new VMLauncher value
*/
public void setVMLauncher( boolean vmLauncher )
{
this.vmLauncher = vmLauncher;
}
/**
* Add a nested env element - an environment variable.
*
* @param var The feature to be added to the Env attribute
*/
public void addEnv( Environment.Variable var )
{
env.addVariable( var );
}
/**
* Add a nested arg element - a command line argument.
*
* @return Description of the Returned Value
*/
public Commandline.Argument createArg()
{
return cmdl.createArgument();
}
/**
* Do the work.
*
* @exception TaskException Description of Exception
*/
public void execute()
throws TaskException
{
checkConfiguration();
if( isValidOs() )
{
runExec( prepareExec() );
}
}
/**
* Is this the OS the user wanted?
*
* @return The ValidOs value
*/
protected boolean isValidOs()
{
// test if os match
String myos = System.getProperty( "os.name" );
log( "Current OS is " + myos, Project.MSG_VERBOSE );
if( ( os != null ) && ( os.indexOf( myos ) < 0 ) )
{
// this command will be executed only on the specified OS
log( "This OS, " + myos + " was not found in the specified list
of valid OSes: " + os, Project.MSG_VERBOSE );
return false;
}
return true;
}
/**
* A Utility method for this classes and subclasses to run an Execute
* instance (an external command).
*
* @param exe Description of Parameter
* @exception IOException Description of Exception
*/
protected final void runExecute( Execute exe )
throws IOException, TaskException
{
int err = -1;// assume the worst
err = exe.execute();
//test for and handle a forced process death
if( exe.killedProcess() )
{
log( "Timeout: killed the sub-process", Project.MSG_WARN );
}
maybeSetResultPropertyValue( err );
if( err != 0 )
{
if( failOnError )
{
throw new TaskException( getName() + " returned: " + err );
}
else
{
log( "Result: " + err, Project.MSG_ERR );
}
}
if( baos != null )
{
BufferedReader in =
new BufferedReader( new StringReader( baos.toString() ) );
String line = null;
StringBuffer val = new StringBuffer();
while( ( line = in.readLine() ) != null )
{
if( val.length() != 0 )
{
val.append( lSep );
}
val.append( line );
}
setProperty( outputprop, val.toString() );
}
}
/**
* Has the user set all necessary attributes?
*
* @exception TaskException Description of Exception
*/
protected void checkConfiguration()
throws TaskException
{
if( cmdl.getExecutable() == null )
{
throw new TaskException( "no executable specified" );
}
if( dir != null && !dir.exists() )
{
throw new TaskException( "The directory you specified does not
exist" );
}
if( dir != null && !dir.isDirectory() )
{
throw new TaskException( "The directory you specified is not a
directory" );
}
}
/**
* Create the StreamHandler to use with our Execute instance.
*
* @return Description of the Returned Value
* @exception TaskException Description of Exception
*/
protected ExecuteStreamHandler createHandler()
throws TaskException
{
if( out != null )
{
try
{
fos = new FileOutputStream( out );
log( "Output redirected to " + out, Project.MSG_VERBOSE );
return new PumpStreamHandler( fos );
}
catch( FileNotFoundException fne )
{
throw new TaskException( "Cannot write to " + out, fne );
}
catch( IOException ioe )
{
throw new TaskException( "Cannot write to " + out, ioe );
}
}
else if( outputprop != null )
{
baos = new ByteArrayOutputStream();
log( "Output redirected to ByteArray", Project.MSG_VERBOSE );
return new PumpStreamHandler( baos );
}
else
{
return new LogStreamHandler( this,
Project.MSG_INFO, Project.MSG_WARN );
}
}
/**
* Create the Watchdog to kill a runaway process.
*
* @return Description of the Returned Value
* @exception TaskException Description of Exception
*/
protected ExecuteWatchdog createWatchdog()
throws TaskException
{
if( timeout == null )
return null;
return new ExecuteWatchdog( timeout.intValue() );
}
/**
* Flush the output stream - if there is one.
*/
protected void logFlush()
{
try
{
if( fos != null )
fos.close();
if( baos != null )
baos.close();
}
catch( IOException io )
{
}
}
/**
* helper method to set result property to the passed in value if
* appropriate
*
* @param result Description of Parameter
*/
protected void maybeSetResultPropertyValue( int result )
throws TaskException
{
String res = Integer.toString( result );
if( resultProperty != null )
{
setProperty( resultProperty, res );
}
}
/**
* Create an Execute instance with the correct working directory set.
*
* @return Description of the Returned Value
* @exception TaskException Description of Exception
*/
protected Execute prepareExec()
throws TaskException
{
// default directory to the project's base directory
if( dir == null ) dir = getBaseDirectory();
// show the command
log( cmdl.toString(), Project.MSG_VERBOSE );
Execute exe = new Execute( createHandler(), createWatchdog() );
exe.setAntRun( project );
exe.setWorkingDirectory( dir );
exe.setVMLauncher( vmLauncher );
String[] environment = env.getVariables();
if( environment != null )
{
for( int i = 0; i < environment.length; i++ )
{
log( "Setting environment variable: " + environment[ i ],
Project.MSG_VERBOSE );
}
}
exe.setNewenvironment( newEnvironment );
exe.setEnvironment( environment );
return exe;
}
/**
* Run the command using the given Execute instance. This may be overidden
* by subclasses
*
* @param exe Description of Parameter
* @exception TaskException Description of Exception
*/
protected void runExec( Execute exe )
throws TaskException
{
exe.setCommandline( cmdl.getCommandline() );
try
{
runExecute( exe );
}
catch( IOException e )
{
if( failIfExecFails )
{
throw new TaskException( "Execute failed: " + e.toString(), e
);
}
else
{
log( "Execute failed: " + e.toString(), Project.MSG_ERR );
}
}
finally
{
// close the output file if required
logFlush();
}
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/Execute.java
Index: Execute.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Vector;
import org.apache.myrmidon.api.TaskException;
import org.apache.myrmidon.framework.Os;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.util.FileUtils;
/**
* Runs an external program.
*
* @author [EMAIL PROTECTED]
*/
public class Execute
{
/**
* Invalid exit code.
*/
public final static int INVALID = Integer.MAX_VALUE;
private static String antWorkingDirectory = System.getProperty(
"user.dir" );
private static CommandLauncher vmLauncher;
private static CommandLauncher shellLauncher;
private static Vector procEnvironment;
/**
* Used to destroy processes when the VM exits.
*/
private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
private String[] cmdl = null;
private String[] env = null;
private int exitValue = INVALID;
private File workingDirectory = null;
private Project project = null;
private boolean newEnvironment = false;
/**
* Controls whether the VM is used to launch commands, where possible
*/
private boolean useVMLauncher = true;
private ExecuteStreamHandler streamHandler;
private ExecuteWatchdog watchdog;
/**
* Builds a command launcher for the OS and JVM we are running under
*/
static
{
try
{
// Try using a JDK 1.3 launcher
try
{
vmLauncher = new Java13CommandLauncher();
}
catch( NoSuchMethodException exc )
{
// Ignore and keep try
}
if( Os.isFamily( "mac" ) )
{
// Mac
shellLauncher = new MacCommandLauncher( new CommandLauncher()
);
}
else if( Os.isFamily( "os/2" ) )
{
// OS/2 - use same mechanism as Windows 2000
shellLauncher = new WinNTCommandLauncher( new
CommandLauncher() );
}
else if( Os.isFamily( "windows" ) )
{
// Windows. Need to determine which JDK we're running in
CommandLauncher baseLauncher;
if( System.getProperty( "java.version" ).startsWith( "1.1" ) )
{
// JDK 1.1
baseLauncher = new Java11CommandLauncher();
}
else
{
// JDK 1.2
baseLauncher = new CommandLauncher();
}
// Determine if we're running under 2000/NT or 98/95
String osname =
System.getProperty( "os.name" ).toLowerCase( Locale.US );
if( osname.indexOf( "nt" ) >= 0 || osname.indexOf( "2000" )
>= 0 )
{
// Windows 2000/NT
shellLauncher = new WinNTCommandLauncher( baseLauncher );
}
else
{
// Windows 98/95 - need to use an auxiliary script
shellLauncher = new ScriptCommandLauncher(
"bin/antRun.bat", baseLauncher );
}
}
else if( ( new Os( "netware" ) ).eval() )
{
// NetWare. Need to determine which JDK we're running in
CommandLauncher baseLauncher;
if( System.getProperty( "java.version" ).startsWith( "1.1" ) )
{
// JDK 1.1
baseLauncher = new Java11CommandLauncher();
}
else
{
// JDK 1.2
baseLauncher = new CommandLauncher();
}
shellLauncher = new PerlScriptCommandLauncher(
"bin/antRun.pl", baseLauncher );
}
else
{
// Generic
shellLauncher = new ScriptCommandLauncher( "bin/antRun", new
CommandLauncher() );
}
}
catch( TaskException e )
{
e.printStackTrace();
}
}
/**
* Creates a new execute object using <code>PumpStreamHandler</code> for
* stream handling.
*/
public Execute()
{
this( new PumpStreamHandler(), null );
}
/**
* Creates a new execute object.
*
* @param streamHandler the stream handler used to handle the input and
* output streams of the subprocess.
*/
public Execute( ExecuteStreamHandler streamHandler )
{
this( streamHandler, null );
}
/**
* Creates a new execute object.
*
* @param streamHandler the stream handler used to handle the input and
* output streams of the subprocess.
* @param watchdog a watchdog for the subprocess or <code>null</code> to
to
* disable a timeout for the subprocess.
*/
public Execute( ExecuteStreamHandler streamHandler, ExecuteWatchdog
watchdog )
{
this.streamHandler = streamHandler;
this.watchdog = watchdog;
}
/**
* Find the list of environment variables for this process.
*
* @return The ProcEnvironment value
*/
public static synchronized Vector getProcEnvironment()
throws TaskException
{
if( procEnvironment != null )
return procEnvironment;
procEnvironment = new Vector();
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
Execute exe = new Execute( new PumpStreamHandler( out ) );
exe.setCommandline( getProcEnvCommand() );
// Make sure we do not recurse forever
exe.setNewenvironment( true );
int retval = exe.execute();
if( retval != 0 )
{
// Just try to use what we got
}
BufferedReader in =
new BufferedReader( new StringReader( out.toString() ) );
String var = null;
String line;
String lineSep = System.getProperty( "line.separator" );
while( ( line = in.readLine() ) != null )
{
if( line.indexOf( '=' ) == -1 )
{
// Chunk part of previous env var (UNIX env vars can
// contain embedded new lines).
if( var == null )
{
var = lineSep + line;
}
else
{
var += lineSep + line;
}
}
else
{
// New env var...append the previous one if we have it.
if( var != null )
{
procEnvironment.addElement( var );
}
var = line;
}
}
// Since we "look ahead" before adding, there's one last env var.
procEnvironment.addElement( var );
}
catch( IOException exc )
{
exc.printStackTrace();
// Just try to see how much we got
}
return procEnvironment;
}
/**
* A utility method that runs an external command. Writes the output and
* error streams of the command to the project log.
*
* @param task The task that the command is part of. Used for logging
* @param cmdline The command to execute.
* @throws TaskException if the command does not return 0.
*/
public static void runCommand( Task task, String[] cmdline )
throws TaskException
{
try
{
task.log( Commandline.toString( cmdline ), Project.MSG_VERBOSE );
Execute exe = new Execute( new LogStreamHandler( task,
Project.MSG_INFO,
Project.MSG_ERR
) );
exe.setAntRun( task.getProject() );
exe.setCommandline( cmdline );
int retval = exe.execute();
if( retval != 0 )
{
throw new TaskException( cmdline[ 0 ] + " failed with return
code " + retval );
}
}
catch( IOException exc )
{
throw new TaskException( "Could not launch " + cmdline[ 0 ] + ":
" + exc );
}
}
private static String[] getProcEnvCommand()
{
if( Os.isFamily( "os/2" ) )
{
// OS/2 - use same mechanism as Windows 2000
// Not sure
String[] cmd = {"cmd", "/c", "set"};
return cmd;
}
else if( Os.isFamily( "windows" ) )
{
String osname =
System.getProperty( "os.name" ).toLowerCase( Locale.US );
// Determine if we're running under 2000/NT or 98/95
if( osname.indexOf( "nt" ) >= 0 || osname.indexOf( "2000" ) >= 0 )
{
// Windows 2000/NT
String[] cmd = {"cmd", "/c", "set"};
return cmd;
}
else
{
// Windows 98/95 - need to use an auxiliary script
String[] cmd = {"command.com", "/c", "set"};
return cmd;
}
}
else if( Os.isFamily( "unix" ) )
{
// Generic UNIX
// Alternatively one could use: /bin/sh -c env
String[] cmd = {"/usr/bin/env"};
return cmd;
}
else if( Os.isFamily( "netware" ) )
{
String[] cmd = {"env"};
return cmd;
}
else
{
// MAC OS 9 and previous
// TODO: I have no idea how to get it, someone must fix it
String[] cmd = null;
return cmd;
}
}
/**
* Set the name of the antRun script using the project's value.
*
* @param project the current project.
* @exception TaskException Description of Exception
*/
public void setAntRun( Project project )
throws TaskException
{
this.project = project;
}
/**
* Sets the commandline of the subprocess to launch.
*
* @param commandline the commandline of the subprocess to launch
*/
public void setCommandline( String[] commandline )
{
cmdl = commandline;
}
/**
* Sets the environment variables for the subprocess to launch.
*
* @param env The new Environment value
*/
public void setEnvironment( String[] env )
{
this.env = env;
}
/**
* Set whether to propagate the default environment or not.
*
* @param newenv whether to propagate the process environment.
*/
public void setNewenvironment( boolean newenv )
{
newEnvironment = newenv;
}
/**
* Launch this execution through the VM, where possible, rather than
through
* the OS's shell. In some cases and operating systems using the shell
will
* allow the shell to perform additional processing such as associating an
* executable with a script, etc
*
* @param useVMLauncher The new VMLauncher value
*/
public void setVMLauncher( boolean useVMLauncher )
{
this.useVMLauncher = useVMLauncher;
}
/**
* Sets the working directory of the process to execute. <p>
*
* This is emulated using the antRun scripts unless the OS is Windows NT
in
* which case a cmd.exe is spawned, or MRJ and setting user.dir works, or
* JDK 1.3 and there is official support in java.lang.Runtime.
*
* @param wd the working directory of the process.
*/
public void setWorkingDirectory( File wd )
{
if( wd == null || wd.getAbsolutePath().equals( antWorkingDirectory ) )
workingDirectory = null;
else
workingDirectory = wd;
}
/**
* Returns the commandline used to create a subprocess.
*
* @return the commandline used to create a subprocess
*/
public String[] getCommandline()
{
return cmdl;
}
/**
* Returns the environment used to create a subprocess.
*
* @return the environment used to create a subprocess
*/
public String[] getEnvironment()
throws TaskException
{
if( env == null || newEnvironment )
return env;
return patchEnvironment();
}
/**
* query the exit value of the process.
*
* @return the exit value, 1 if the process was killed, or Project.INVALID
* if no exit value has been received
*/
public int getExitValue()
{
return exitValue;
}
/**
* Runs a process defined by the command line and returns its exit status.
*
* @return the exit status of the subprocess or <code>INVALID</code>
* @exception IOException Description of Exception
*/
public int execute()
throws IOException, TaskException
{
CommandLauncher launcher = vmLauncher != null ? vmLauncher :
shellLauncher;
if( !useVMLauncher )
{
launcher = shellLauncher;
}
final Process process = launcher.exec( project, getCommandline(),
getEnvironment(), workingDirectory );
try
{
streamHandler.setProcessInputStream( process.getOutputStream() );
streamHandler.setProcessOutputStream( process.getInputStream() );
streamHandler.setProcessErrorStream( process.getErrorStream() );
}
catch( IOException e )
{
process.destroy();
throw e;
}
streamHandler.start();
// add the process to the list of those to destroy if the VM exits
//
processDestroyer.add( process );
if( watchdog != null )
watchdog.start( process );
waitFor( process );
// remove the process to the list of those to destroy if the VM exits
//
processDestroyer.remove( process );
if( watchdog != null )
watchdog.stop();
streamHandler.stop();
if( watchdog != null )
watchdog.checkException();
return getExitValue();
}
/**
* test for an untimely death of the process
*
* @return true iff a watchdog had to kill the process
* @since 1.5
*/
public boolean killedProcess()
{
return watchdog != null && watchdog.killedProcess();
}
protected void setExitValue( int value )
{
exitValue = value;
}
protected void waitFor( Process process )
{
try
{
process.waitFor();
setExitValue( process.exitValue() );
}
catch( InterruptedException e )
{
}
}
/**
* Patch the current environment with the new values from the user.
*
* @return the patched environment
*/
private String[] patchEnvironment()
throws TaskException
{
Vector osEnv = (Vector)getProcEnvironment().clone();
for( int i = 0; i < env.length; i++ )
{
int pos = env[ i ].indexOf( '=' );
// Get key including "="
String key = env[ i ].substring( 0, pos + 1 );
int size = osEnv.size();
for( int j = 0; j < size; j++ )
{
if( ( (String)osEnv.elementAt( j ) ).startsWith( key ) )
{
osEnv.removeElementAt( j );
break;
}
}
osEnv.addElement( env[ i ] );
}
String[] result = new String[ osEnv.size() ];
osEnv.copyInto( result );
return result;
}
/**
* A command launcher for a particular JVM/OS platform. This class is a
* general purpose command launcher which can only launch commands in the
* current working directory.
*
* @author RT
*/
private static class CommandLauncher
{
/**
* Launches the given command in a new process.
*
* @param project The project that the command is part of
* @param cmd The command to execute
* @param env The environment for the new process. If null, the
* environment of the current proccess is used.
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env )
throws IOException, TaskException
{
if( project != null )
{
project.log( "Execute:CommandLauncher: " +
Commandline.toString( cmd ), Project.MSG_DEBUG );
}
return Runtime.getRuntime().exec( cmd, env );
}
/**
* Launches the given command in a new process, in the given working
* directory.
*
* @param project The project that the command is part of
* @param cmd The command to execute
* @param env The environment for the new process. If null, the
* environment of the current proccess is used.
* @param workingDir The directory to start the command in. If null,
the
* current directory is used
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env,
File workingDir )
throws IOException, TaskException
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
throw new IOException( "Cannot execute a process in different
directory under this JVM" );
}
}
/**
* A command launcher that proxies another command launcher. Sub-classes
* override exec(args, env, workdir)
*
* @author RT
*/
private static class CommandLauncherProxy extends CommandLauncher
{
private CommandLauncher _launcher;
CommandLauncherProxy( CommandLauncher launcher )
{
_launcher = launcher;
}
/**
* Launches the given command in a new process. Delegates this method
to
* the proxied launcher
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env )
throws IOException, TaskException
{
return _launcher.exec( project, cmd, env );
}
}
/**
* A command launcher for JDK/JRE 1.1 under Windows. Fixes quoting
problems
* in Runtime.exec(). Can only launch commands in the current working
* directory
*
* @author RT
*/
private static class Java11CommandLauncher extends CommandLauncher
{
/**
* Launches the given command in a new process. Needs to quote
arguments
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env )
throws IOException, TaskException
{
// Need to quote arguments with spaces, and to escape quote
characters
String[] newcmd = new String[ cmd.length ];
for( int i = 0; i < cmd.length; i++ )
{
newcmd[ i ] = Commandline.quoteArgument( cmd[ i ] );
}
if( project != null )
{
project.log( "Execute:Java11CommandLauncher: " +
Commandline.toString( newcmd ),
Project.MSG_DEBUG );
}
return Runtime.getRuntime().exec( newcmd, env );
}
}
/**
* A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
* Runtime.exec() command
*
* @author RT
*/
private static class Java13CommandLauncher extends CommandLauncher
{
private Method _execWithCWD;
public Java13CommandLauncher()
throws NoSuchMethodException
{
// Locate method Runtime.exec(String[] cmdarray, String[] envp,
File dir)
_execWithCWD = Runtime.class.getMethod( "exec", new
Class[]{String[].class, String[].class, File.class} );
}
/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env,
File workingDir )
throws IOException, TaskException
{
try
{
if( project != null )
{
project.log( "Execute:Java13CommandLauncher: " +
Commandline.toString( cmd ),
Project.MSG_DEBUG );
}
Object[] arguments = {cmd, env, workingDir};
return (Process)_execWithCWD.invoke( Runtime.getRuntime(),
arguments );
}
catch( InvocationTargetException exc )
{
Throwable realexc = exc.getTargetException();
if( realexc instanceof ThreadDeath )
{
throw (ThreadDeath)realexc;
}
else if( realexc instanceof IOException )
{
throw (IOException)realexc;
}
else
{
throw new TaskException( "Unable to execute command",
realexc );
}
}
catch( Exception exc )
{
// IllegalAccess, IllegalArgument, ClassCast
throw new TaskException( "Unable to execute command", exc );
}
}
}
/**
* A command launcher for Mac that uses a dodgy mechanism to change
working
* directory before launching commands.
*
* @author RT
*/
private static class MacCommandLauncher extends CommandLauncherProxy
{
MacCommandLauncher( CommandLauncher launcher )
{
super( launcher );
}
/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env,
File workingDir )
throws IOException, TaskException
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
System.getProperties().put( "user.dir",
workingDir.getAbsolutePath() );
try
{
return exec( project, cmd, env );
}
finally
{
System.getProperties().put( "user.dir", antWorkingDirectory );
}
}
}
/**
* A command launcher that uses an auxiliary perl script to launch
commands
* in directories other than the current working directory.
*
* @author RT
*/
private static class PerlScriptCommandLauncher extends
CommandLauncherProxy
{
private String _script;
PerlScriptCommandLauncher( String script, CommandLauncher launcher )
{
super( launcher );
_script = script;
}
/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env,
File workingDir )
throws IOException, TaskException
{
if( project == null )
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
throw new IOException( "Cannot locate antRun script: No
project provided" );
}
// Locate the auxiliary script
String antHome = project.getProperty( "ant.home" );
if( antHome == null )
{
throw new IOException( "Cannot locate antRun script: Property
'ant.home' not found" );
}
String antRun = FileUtils.newFileUtils().
resolveFile( project.getBaseDir(), antHome + File.separator +
_script ).toString();
// Build the command
File commandDir = workingDir;
if( workingDir == null && project != null )
{
commandDir = project.getBaseDir();
}
String[] newcmd = new String[ cmd.length + 3 ];
newcmd[ 0 ] = "perl";
newcmd[ 1 ] = antRun;
newcmd[ 2 ] = commandDir.getAbsolutePath();
System.arraycopy( cmd, 0, newcmd, 3, cmd.length );
return exec( project, newcmd, env );
}
}
/**
* A command launcher that uses an auxiliary script to launch commands in
* directories other than the current working directory.
*
* @author RT
*/
private static class ScriptCommandLauncher extends CommandLauncherProxy
{
private String _script;
ScriptCommandLauncher( String script, CommandLauncher launcher )
{
super( launcher );
_script = script;
}
/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env,
File workingDir )
throws IOException, TaskException
{
if( project == null )
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
throw new IOException( "Cannot locate antRun script: No
project provided" );
}
// Locate the auxiliary script
String antHome = project.getProperty( "ant.home" );
if( antHome == null )
{
throw new IOException( "Cannot locate antRun script: Property
'ant.home' not found" );
}
String antRun = FileUtils.newFileUtils().
resolveFile( project.getBaseDir(), antHome + File.separator +
_script ).toString();
// Build the command
File commandDir = workingDir;
if( workingDir == null && project != null )
{
commandDir = project.getBaseDir();
}
String[] newcmd = new String[ cmd.length + 2 ];
newcmd[ 0 ] = antRun;
newcmd[ 1 ] = commandDir.getAbsolutePath();
System.arraycopy( cmd, 0, newcmd, 2, cmd.length );
return exec( project, newcmd, env );
}
}
/**
* A command launcher for Windows 2000/NT that uses 'cmd.exe' when
launching
* commands in directories other than the current working directory.
*
* @author RT
*/
private static class WinNTCommandLauncher extends CommandLauncherProxy
{
WinNTCommandLauncher( CommandLauncher launcher )
{
super( launcher );
}
/**
* Launches the given command in a new process, in the given working
* directory.
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env,
File workingDir )
throws IOException, TaskException
{
File commandDir = workingDir;
if( workingDir == null )
{
if( project != null )
{
commandDir = project.getBaseDir();
}
else
{
return exec( project, cmd, env );
}
}
// Use cmd.exe to change to the specified directory before running
// the command
final int preCmdLength = 6;
String[] newcmd = new String[ cmd.length + preCmdLength ];
newcmd[ 0 ] = "cmd";
newcmd[ 1 ] = "/c";
newcmd[ 2 ] = "cd";
newcmd[ 3 ] = "/d";
newcmd[ 4 ] = commandDir.getAbsolutePath();
newcmd[ 5 ] = "&&";
System.arraycopy( cmd, 0, newcmd, preCmdLength, cmd.length );
return exec( project, newcmd, env );
}
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/ExecuteStreamHandler.java
Index: ExecuteStreamHandler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.myrmidon.api.TaskException;
/**
* Used by <code>Execute</code> to handle input and output stream of
* subprocesses.
*
* @author [EMAIL PROTECTED]
*/
public interface ExecuteStreamHandler
{
/**
* Install a handler for the input stream of the subprocess.
*
* @param os output stream to write to the standard input stream of the
* subprocess
* @exception IOException Description of Exception
*/
void setProcessInputStream( OutputStream os )
throws IOException;
/**
* Install a handler for the error stream of the subprocess.
*
* @param is input stream to read from the error stream from the
subprocess
* @exception IOException Description of Exception
*/
void setProcessErrorStream( InputStream is )
throws IOException;
/**
* Install a handler for the output stream of the subprocess.
*
* @param is input stream to read from the error stream from the
subprocess
* @exception IOException Description of Exception
*/
void setProcessOutputStream( InputStream is )
throws TaskException, IOException;
/**
* Start handling of the streams.
*
* @exception IOException Description of Exception
*/
void start()
throws IOException;
/**
* Stop handling of the streams - will not be restarted.
*/
void stop()
throws TaskException;
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/ExecuteWatchdog.java
Index: ExecuteWatchdog.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import org.apache.myrmidon.api.TaskException;
/**
* Destroys a process running for too long. For example: <pre>
* ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
* Execute exec = new Execute(myloghandler, watchdog);
* exec.setCommandLine(mycmdline);
* int exitvalue = exec.execute();
* if (exitvalue != SUCCESS && watchdog.killedProcess()){
* // it was killed on purpose by the watchdog
* }
* </pre>
*
* @author [EMAIL PROTECTED]
* @author <a href="mailto:[EMAIL PROTECTED]">Stephane Bailliez</a>
* @see Execute
*/
public class ExecuteWatchdog implements Runnable
{
/**
* say whether or not the watchog is currently monitoring a process
*/
private boolean watch = false;
/**
* exception that might be thrown during the process execution
*/
private Exception caught = null;
/**
* say whether or not the process was killed due to running overtime
*/
private boolean killedProcess = false;
/**
* the process to execute and watch for duration
*/
private Process process;
/**
* timeout duration. Once the process running time exceeds this it should
be
* killed
*/
private int timeout;
/**
* Creates a new watchdog with a given timeout.
*
* @param timeout the timeout for the process in milliseconds. It must be
* greather than 0.
*/
public ExecuteWatchdog( int timeout )
{
if( timeout < 1 )
{
throw new IllegalArgumentException( "timeout lesser than 1." );
}
this.timeout = timeout;
}
/**
* Indicates whether or not the watchdog is still monitoring the process.
*
* @return <tt>true</tt> if the process is still running, otherwise <tt>
* false</tt> .
*/
public boolean isWatching()
{
return watch;
}
/**
* This method will rethrow the exception that was possibly caught during
* the run of the process. It will only remains valid once the process has
* been terminated either by 'error', timeout or manual intervention.
* Information will be discarded once a new process is ran.
*
* @throws TaskException a wrapped exception over the one that was
silently
* swallowed and stored during the process run.
*/
public void checkException()
throws TaskException
{
if( caught != null )
{
throw new TaskException( "Exception in ExecuteWatchdog.run: "
+ caught.getMessage(), caught );
}
}
/**
* Indicates whether the last process run was killed on timeout or not.
*
* @return <tt>true</tt> if the process was killed otherwise
<tt>false</tt>
* .
*/
public boolean killedProcess()
{
return killedProcess;
}
/**
* Watches the process and terminates it, if it runs for to long.
*/
public synchronized void run()
{
try
{
// This isn't a Task, don't have a Project object to log.
// project.log("ExecuteWatchdog: timeout = "+timeout+" msec",
Project.MSG_VERBOSE);
final long until = System.currentTimeMillis() + timeout;
long now;
while( watch && until > ( now = System.currentTimeMillis() ) )
{
try
{
wait( until - now );
}
catch( InterruptedException e )
{
}
}
// if we are here, either someone stopped the watchdog,
// we are on timeout and the process must be killed, or
// we are on timeout and the process has already stopped.
try
{
// We must check if the process was not stopped
// before being here
process.exitValue();
}
catch( IllegalThreadStateException e )
{
// the process is not terminated, if this is really
// a timeout and not a manual stop then kill it.
if( watch )
{
killedProcess = true;
process.destroy();
}
}
}
catch( Exception e )
{
caught = e;
}
finally
{
cleanUp();
}
}
/**
* Watches the given process and terminates it, if it runs for too long.
All
* information from the previous run are reset.
*
* @param process the process to monitor. It cannot be <tt>null</tt>
* @throws IllegalStateException thrown if a process is still being
* monitored.
*/
public synchronized void start( Process process )
{
if( process == null )
{
throw new NullPointerException( "process is null." );
}
if( this.process != null )
{
throw new IllegalStateException( "Already running." );
}
this.caught = null;
this.killedProcess = false;
this.watch = true;
this.process = process;
final Thread thread = new Thread( this, "WATCHDOG" );
thread.setDaemon( true );
thread.start();
}
/**
* Stops the watcher. It will notify all threads possibly waiting on this
* object.
*/
public synchronized void stop()
{
watch = false;
notifyAll();
}
/**
* reset the monitor flag and the process.
*/
protected void cleanUp()
{
watch = false;
process = null;
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/LogOutputStream.java
Index: LogOutputStream.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
/**
* Logs each line written to this stream to the log system of ant. Tries to be
* smart about line separators.<br>
* TODO: This class can be split to implement other line based processing of
* data written to the stream.
*
* @author [EMAIL PROTECTED]
*/
public class LogOutputStream extends OutputStream
{
private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private boolean skip = false;
private int level = Project.MSG_INFO;
private Task task;
/**
* Creates a new instance of this class.
*
* @param task the task for whom to log
* @param level loglevel used to log data written to this stream.
*/
public LogOutputStream( Task task, int level )
{
this.task = task;
this.level = level;
}
public int getMessageLevel()
{
return level;
}
/**
* Writes all remaining
*
* @exception IOException Description of Exception
*/
public void close()
throws IOException
{
if( buffer.size() > 0 )
processBuffer();
super.close();
}
/**
* Write the data to the buffer and flush the buffer, if a line separator
is
* detected.
*
* @param cc data to log (byte).
* @exception IOException Description of Exception
*/
public void write( int cc )
throws IOException
{
final byte c = (byte)cc;
if( ( c == '\n' ) || ( c == '\r' ) )
{
if( !skip )
processBuffer();
}
else
buffer.write( cc );
skip = ( c == '\r' );
}
/**
* Converts the buffer to a string and sends it to
<code>processLine</code>
*/
protected void processBuffer()
{
processLine( buffer.toString() );
buffer.reset();
}
/**
* Logs a line to the log system of ant.
*
* @param line the line to log.
*/
protected void processLine( String line )
{
processLine( line, level );
}
/**
* Logs a line to the log system of ant.
*
* @param line the line to log.
* @param level Description of Parameter
*/
protected void processLine( String line, int level )
{
task.log( line, level );
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/LogStreamHandler.java
Index: LogStreamHandler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.IOException;
import org.apache.myrmidon.api.TaskException;
import org.apache.tools.ant.Task;
/**
* Logs standard output and error of a subprocess to the log system of ant.
*
* @author [EMAIL PROTECTED]
*/
public class LogStreamHandler
extends PumpStreamHandler
{
/**
* Creates a new instance of this class.
*
* @param task the task for whom to log
* @param outlevel the loglevel used to log standard output
* @param errlevel the loglevel used to log standard error
*/
public LogStreamHandler( Task task, int outlevel, int errlevel )
{
super( new LogOutputStream( task, outlevel ),
new LogOutputStream( task, errlevel ) );
}
public void stop()
throws TaskException
{
super.stop();
try
{
getErr().close();
getOut().close();
}
catch( IOException e )
{
// plain impossible
throw new TaskException( "Error", e );
}
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/ProcessDestroyer.java
Index: ProcessDestroyer.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Vector;
/**
* Destroys all registered <code>Process</code>es when the VM exits.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Michael Newcomb</a>
*/
class ProcessDestroyer
extends Thread
{
private Vector processes = new Vector();
/**
* Constructs a <code>ProcessDestroyer</code> and registers it as a
shutdown
* hook.
*/
public ProcessDestroyer()
{
try
{
// check to see if the method exists (support pre-JDK 1.3 VMs)
//
Class[] paramTypes = {Thread.class};
Method addShutdownHook =
Runtime.class.getMethod( "addShutdownHook", paramTypes );
// add the hook
//
Object[] args = {this};
addShutdownHook.invoke( Runtime.getRuntime(), args );
}
catch( Exception e )
{
// it just won't be added as a shutdown hook... :(
}
}
/**
* Returns <code>true</code> if the specified <code>Process</code> was
* successfully added to the list of processes to destroy upon VM exit.
*
* @param process the process to add
* @return <code>true</code> if the specified <code>Process</code> was
* successfully added
*/
public boolean add( Process process )
{
processes.addElement( process );
return processes.contains( process );
}
/**
* Returns <code>true</code> if the specified <code>Process</code> was
* successfully removed from the list of processes to destroy upon VM
exit.
*
* @param process the process to remove
* @return <code>true</code> if the specified <code>Process</code> was
* successfully removed
*/
public boolean remove( Process process )
{
return processes.removeElement( process );
}
/**
* Invoked by the VM when it is exiting.
*/
public void run()
{
synchronized( processes )
{
Enumeration e = processes.elements();
while( e.hasMoreElements() )
{
( (Process)e.nextElement() ).destroy();
}
}
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/PumpStreamHandler.java
Index: PumpStreamHandler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.myrmidon.api.TaskException;
/**
* Copies standard output and error of subprocesses to standard output and
error
* of the parent process. TODO: standard input of the subprocess is not
* implemented.
*
* @author [EMAIL PROTECTED]
*/
public class PumpStreamHandler implements ExecuteStreamHandler
{
private Thread errorThread;
private Thread inputThread;
private OutputStream out, err;
public PumpStreamHandler( OutputStream out, OutputStream err )
{
this.out = out;
this.err = err;
}
public PumpStreamHandler( OutputStream outAndErr )
{
this( outAndErr, outAndErr );
}
public PumpStreamHandler()
{
this( System.out, System.err );
}
public void setProcessErrorStream( InputStream is )
{
createProcessErrorPump( is, err );
}
public void setProcessInputStream( OutputStream os )
{
}
public void setProcessOutputStream( InputStream is )
{
createProcessOutputPump( is, out );
}
public void start()
{
inputThread.start();
errorThread.start();
}
public void stop()
throws TaskException
{
try
{
inputThread.join();
}
catch( InterruptedException e )
{
}
try
{
errorThread.join();
}
catch( InterruptedException e )
{
}
try
{
err.flush();
}
catch( IOException e )
{
}
try
{
out.flush();
}
catch( IOException e )
{
}
}
protected OutputStream getErr()
{
return err;
}
protected OutputStream getOut()
{
return out;
}
protected void createProcessErrorPump( InputStream is, OutputStream os )
{
errorThread = createPump( is, os );
}
protected void createProcessOutputPump( InputStream is, OutputStream os )
{
inputThread = createPump( is, os );
}
/**
* Creates a stream pumper to copy the given input stream to the given
* output stream.
*
* @param is Description of Parameter
* @param os Description of Parameter
* @return Description of the Returned Value
*/
protected Thread createPump( InputStream is, OutputStream os )
{
final Thread result = new Thread( new StreamPumper( is, os ) );
result.setDaemon( true );
return result;
}
}
1.1
jakarta-ant/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/exec/StreamPumper.java
Index: StreamPumper.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.tools.ant.taskdefs.exec;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Copies all data from an input stream to an output stream.
*
* @author [EMAIL PROTECTED]
*/
public class StreamPumper implements Runnable
{
// TODO: make SIZE and SLEEP instance variables.
// TODO: add a status flag to note if an error occured in run.
private final static int SLEEP = 5;
private final static int SIZE = 128;
private InputStream is;
private OutputStream os;
/**
* Create a new stream pumper.
*
* @param is input stream to read data from
* @param os output stream to write data to.
*/
public StreamPumper( InputStream is, OutputStream os )
{
this.is = is;
this.os = os;
}
/**
* Copies data from the input stream to the output stream. Terminates as
* soon as the input stream is closed or an error occurs.
*/
public void run()
{
final byte[] buf = new byte[ SIZE ];
int length;
try
{
while( ( length = is.read( buf ) ) > 0 )
{
os.write( buf, 0, length );
try
{
Thread.sleep( SLEEP );
}
catch( InterruptedException e )
{
}
}
}
catch( IOException e )
{
}
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>