You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by bo...@apache.org on 2003/04/01 15:01:10 UTC
cvs commit: ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh SSHExec.java
bodewig 2003/04/01 05:01:09
Modified: docs/manual/OptionalTasks scp.html sshexec.html
src/main/org/apache/tools/ant/taskdefs/optional/ssh
SSHExec.java
Log:
Make <sshexec> wait for the remote command to finish.
Requires jsch-0.1.3 to work.
Submitted by: Dale Anson <danson at germane dash software dot com>
Revision Changes Path
1.6 +1 -1 ant/docs/manual/OptionalTasks/scp.html
Index: scp.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/OptionalTasks/scp.html,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- scp.html 14 Mar 2003 15:10:26 -0000 1.5
+++ scp.html 1 Apr 2003 13:01:09 -0000 1.6
@@ -19,7 +19,7 @@
<p><b>Note:</b> This task depends on external libraries not included
in the Ant distribution. See <a
href="../install.html#librarydependencies">Library Dependencies</a>
-for more information. This task has been tested with jsch-0.1.2.</p>
+for more information. This task has been tested with jsch-0.1.2 and jsch-0.1.3.</p>
<h3>Parameters</h3>
<table border="1" cellpadding="2" cellspacing="0">
1.3 +23 -1 ant/docs/manual/OptionalTasks/sshexec.html
Index: sshexec.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/OptionalTasks/sshexec.html,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- sshexec.html 14 Mar 2003 15:10:27 -0000 1.2
+++ sshexec.html 1 Apr 2003 13:01:09 -0000 1.3
@@ -18,7 +18,7 @@
<p><b>Note:</b> This task depends on external libraries not included
in the Ant distribution. See <a
href="../install.html#librarydependencies">Library Dependencies</a>
-for more information. This task has been tested with jsch-0.1.2.</p>
+for more information. This task has been tested with jsch-0.1.3.</p>
<h3>Parameters</h3>
<table border="1" cellpadding="2" cellspacing="0">
@@ -79,6 +79,28 @@
<td valign="top">passphrase</td>
<td valign="top">Passphrase for your private key.</td>
<td valign="top" align="center">No, defaults to an empty string.</td>
+ </tr>
+ <tr>
+ <td valign="top">output</td>
+ <td valign="top">Name of a file to which to write the output.</td>
+ <td align="center" valign="top">No</td>
+ </tr>
+ <tr>
+ <td valign="top">append</td>
+ <td valign="top">Whether output file should be appended to or overwritten. Defaults to false, meaning overwrite any existing file.</td>
+ <td align="center" valign="top">No</td>
+ </tr>
+ <tr>
+ <td valign="top">outputproperty</td>
+ <td valign="top">The name of a property in which the output of the
+ command should be stored.</td>
+ <td align="center" valign="top">No</td>
+ </tr>
+ <tr>
+ <td valign="top">timeout</td>
+ <td valign="top">Stop the command if it doesn't finish within the
+ specified time (given in seconds). Defaults to 0 which means "wait forever".</td>
+ <td align="center" valign="top">No</td>
</tr>
</table>
1.2 +195 -25 ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
Index: SSHExec.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SSHExec.java 11 Mar 2003 13:15:43 -0000 1.1
+++ SSHExec.java 1 Apr 2003 13:01:09 -0000 1.2
@@ -56,14 +56,13 @@
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.TaskContainer;
-import org.apache.tools.ant.taskdefs.LogOutputStream;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.util.Vector;
-import java.util.Enumeration;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringReader;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
@@ -71,14 +70,22 @@
/**
* Executes a command on a remote machine via ssh.
+ *
* @author Robert Anderson, riznob@hotmail.com
+ * @author Dale Anson, danson@germane-software.com
+ * @version $Revision$
* @created February 2, 2003
- * @since Ant 1.6
+ * @since Ant 1.6
*/
public class SSHExec extends SSHBase {
- private String command = null;
- private int maxwait = 30000;
+ private String command = null; // the command to execute via ssh
+ private int maxwait = 0; // units are milliseconds, default is 0=infinite
+ private Thread thread = null; // for waiting for the command to finish
+
+ private String output_property = null; // like <exec>
+ private File output_file = null; // like <exec>
+ private boolean append = false; // like <exec>
/**
* Constructor for SSHExecTask.
@@ -97,44 +104,112 @@
}
/**
- * The connection will be dropped after maxwait seconds. This is
- * sometimes useful when a connection may be flaky. Default is to
- * wait forever.
+ * The connection can be dropped after a specified number of
+ * milliseconds. This is sometimes useful when a connection may be
+ * flaky. Default is 0, which means "wait forever".
+ *
+ * @param timeout The new timeout value in seconds
+ */
+ public void setTimeout(int timeout) {
+ maxwait = timeout * 1000;
+ }
+
+ /**
+ * If used, stores the output of the command to the given file.
*
* @param maxwait The new maxwait value
*/
- public void setMaxwait(int maxwait) {
- this.maxwait = maxwait;
+ public void setOutput(File output) {
+ output_file = output;
+ }
+
+ /**
+ * Should the output be appended to the file given in
+ * <code>setOutput</code> ? Default is false, that is, overwrite
+ * the file.
+ *
+ * @param append True to append to an existing file, false to overwrite.
+ */
+ public void setAppend(boolean append) {
+ this.append = append;
}
+ /**
+ * If set, the output of the command will be stored in the given property.
+ *
+ * @param property The name of the property in which the command output
+ * will be stored.
+ */
+ public void setOutputproperty(String property) {
+ output_property = property;
+ }
/**
* Execute the command on the remote host.
- * @exception BuildException Most likely a network error or bad
- * parameter.
+ *
+ * @exception BuildException Most likely a network error or bad parameter.
*/
public void execute() throws BuildException {
if (getHost() == null) {
- throw new BuildException("Host is null.");
+ throw new BuildException("Host is required.");
}
if (getUserInfo().getName() == null) {
- throw new BuildException("Username is null.");
+ throw new BuildException("Username is required.");
}
if (getUserInfo().getKeyfile() == null
&& getUserInfo().getPassword() == null) {
- throw new BuildException("Password and Keyfile are null.");
+ throw new BuildException("Password or Keyfile is required.");
}
if (command == null) {
- throw new BuildException("Command is null.");
+ throw new BuildException("Command is required.");
}
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Tee tee = new Tee(out, System.out);
+
try {
+ // execute the command
Session session = openSession();
- ChannelExec channel=(ChannelExec) session.openChannel("exec");
+ session.setTimeout(maxwait);
+ final ChannelExec channel=(ChannelExec) session.openChannel("exec");
channel.setCommand(command);
- channel.setInputStream(System.in);
- channel.setOutputStream(System.out);
+ channel.setOutputStream(tee);
channel.connect();
+
+ // wait for it to finish
+ thread =
+ new Thread() {
+ public void run() {
+ while (!channel.isEOF()) {
+ if (thread == null) {
+ return;
+ }
+ try {
+ sleep(500);
+ } catch (Exception e) {
+ // ignored
+ }
+ }
+ }
+ };
+
+ thread.start();
+ thread.join(maxwait);
+
+ if (thread.isAlive()) {
+ // ran out of time
+ thread = null;
+ log("Timeout period exceeded, connection dropped.");
+ } else {
+ // completed successfully
+ if (output_property != null) {
+ getProject().setProperty(output_property, out.toString());
+ }
+ if (output_file != null) {
+ writeToFile(out.toString(), append, output_file);
+ }
+ }
+
} catch(Exception e){
if (getFailonerror()) {
throw new BuildException(e);
@@ -143,5 +218,100 @@
}
}
}
+
+
+ /**
+ * Writes a string to a file. If destination file exists, it may be
+ * overwritten depending on the "append" value.
+ *
+ * @param from string to write
+ * @param to file to write to
+ * @param append if true, append to existing file, else overwrite
+ * @exception Exception most likely an IOException
+ */
+ private void writeToFile(String from, boolean append, File to)
+ throws IOException {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(to.getAbsolutePath(), append);
+ StringReader in = new StringReader(from);
+ char[] buffer = new char[8192];
+ int bytes_read;
+ while (true) {
+ bytes_read = in.read(buffer);
+ if (bytes_read == -1) {
+ break;
+ }
+ out.write(buffer, 0, bytes_read);
+ }
+ out.flush();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+ /**
+ * Similar to standard unix "tee" utility, sends output to two streams.
+ *
+ * @author Dale Anson, danson@germane-software.com
+ * @version $Revision$
+ */
+ public class Tee extends OutputStream {
+
+ private OutputStream left = null;
+ private OutputStream right = null;
+
+ /**
+ * Constructor for Tee, sends output to both of the given
+ * streams, which are referred to as the "teed" streams.
+ *
+ * @param left one stream to write to
+ * @param right the other stream to write to
+ */
+ public Tee(OutputStream left, OutputStream right) {
+ if (left == null || right == null) {
+ throw new IllegalArgumentException("Both streams are required.");
+ }
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * Writes the specified byte to both of the teed streams. Per java api,
+ * the general contract for write is that one byte is written to the
+ * output stream. The byte to be written is the eight low-order bits of
+ * the argument b. The 24 high-order bits of b are ignored.
+ *
+ * @param b
+ * @exception IOException If an IO error occurs
+ */
+ public void write( int b ) throws IOException {
+ left.write( b );
+ right.write( b );
+ }
+
+ /**
+ * Closes both of the teed streams.
+ *
+ * @exception IOException If an IO error occurs
+ */
+ public void close() throws IOException {
+ left.close();
+ right.close();
+ }
+
+ /**
+ * Flushes both of the teed streams.
+ *
+ * @exception IOException If an IO error occurs
+ */
+ public void flush() throws IOException {
+ left.flush();
+ right.flush();
+ }
+ }
+
}