mirror of https://github.com/apache/jclouds.git
reduced log clutter and hardened code
git-svn-id: http://jclouds.googlecode.com/svn/trunk@2621 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
7f5b800470
commit
b9de6558d5
|
@ -19,7 +19,7 @@
|
|||
====================================================================
|
||||
|
||||
-->
|
||||
<project name="javassh" default="javassh" basedir=".">
|
||||
<project name="sshjava" default="sshjava" basedir=".">
|
||||
<description>
|
||||
simple example build file
|
||||
</description>
|
||||
|
@ -67,17 +67,17 @@
|
|||
<delete dir="${dist}"/>
|
||||
</target>
|
||||
|
||||
<taskdef name="javassh" classname="org.jclouds.tools.ant.JavaOverSsh"/>
|
||||
<taskdef name="sshjava" classname="org.jclouds.tools.ant.taskdefs.sshjava.SSHJava" />
|
||||
|
||||
<target name="javassh" depends="compile" description="remote execute the java command">
|
||||
<target name="sshjava" depends="compile" description="remote execute the java command">
|
||||
<echo message="normal java task"/>
|
||||
<java classname="TestClass" classpath="${build}" dir="${user.dir}" >
|
||||
<arg line="${line}"/>
|
||||
</java>
|
||||
<echo message="java task over ssh"/>
|
||||
<javassh classname="TestClass" classpath="${build}" dir="${user.dir}" host="${host}" username="${username}" keyfile="${keyfile}" trust="true" remotedir="/tmp/test" >
|
||||
<sshjava classname="TestClass" classpath="${build}" dir="${user.dir}" host="${host}" username="${username}" keyfile="${keyfile}" trust="true" remotebase="/tmp/test" >
|
||||
<arg line="${line}"/>
|
||||
</javassh>
|
||||
</sshjava>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,394 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.tools.ant.taskdefs.optional.ssh;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.types.Resource;
|
||||
import org.apache.tools.ant.types.resources.FileResource;
|
||||
import org.apache.tools.ant.util.FileUtils;
|
||||
import org.apache.tools.ant.util.KeepAliveOutputStream;
|
||||
import org.apache.tools.ant.util.TeeOutputStream;
|
||||
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
/**
|
||||
* Executes a command on a remote machine via ssh.
|
||||
* @since Ant 1.6 (created February 2, 2003)
|
||||
*/
|
||||
public class SSHExec extends SSHBase {
|
||||
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
private static final int RETRY_INTERVAL = 500;
|
||||
|
||||
/** the command to execute via ssh */
|
||||
private String command = null;
|
||||
|
||||
/** units are milliseconds, default is 0=infinite */
|
||||
private long maxwait = 0;
|
||||
|
||||
/** for waiting for the command to finish */
|
||||
private Thread thread = null;
|
||||
|
||||
private String outputProperty = null;
|
||||
private File outputFile = null;
|
||||
private String errorProperty = null;
|
||||
private File errorFile = null;
|
||||
private String resultProperty;
|
||||
private boolean append = false;
|
||||
|
||||
private Resource commandResource = null;
|
||||
|
||||
private static final String TIMEOUT_MESSAGE =
|
||||
"Timeout period exceeded, connection dropped.";
|
||||
|
||||
/**
|
||||
* Constructor for SSHExecTask.
|
||||
*/
|
||||
public SSHExec() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command to execute on the remote host.
|
||||
*
|
||||
* @param command The new command value
|
||||
*/
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a commandResource from a file
|
||||
* @param f the value to use.
|
||||
* @since Ant 1.7.1
|
||||
*/
|
||||
public void setCommandResource(String f) {
|
||||
this.commandResource = new FileResource(new File(f));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(long timeout) {
|
||||
maxwait = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* If used, stores the output of the command to the given file.
|
||||
*
|
||||
* @param output The file to write to.
|
||||
*/
|
||||
public void setOutput(File output) {
|
||||
outputFile = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* If used, stores the error of the command to the given file.
|
||||
*
|
||||
* @param error The file to write to.
|
||||
*/
|
||||
public void setError(File error) {
|
||||
this.errorFile = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the output is 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) {
|
||||
outputProperty = property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property name whose value should be set to the error of
|
||||
* the process.
|
||||
*
|
||||
* @param property The name of a property in which the standard error of
|
||||
* the command should be stored.
|
||||
*/
|
||||
public void setErrorproperty(String property) {
|
||||
errorProperty = property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the property in which the return code of the
|
||||
* command should be stored. Only of interest if failonerror=false.
|
||||
*
|
||||
* @param resultProperty name of property.
|
||||
*
|
||||
* @since Ant 1.6
|
||||
*/
|
||||
public void setResultProperty(String resultProperty) {
|
||||
this.resultProperty = resultProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command on the remote host.
|
||||
*
|
||||
* @exception BuildException Most likely a network error or bad parameter.
|
||||
*/
|
||||
public void execute() throws BuildException {
|
||||
if (getHost() == null) {
|
||||
throw new BuildException("Host is required.");
|
||||
}
|
||||
if (getUserInfo().getName() == null) {
|
||||
throw new BuildException("Username is required.");
|
||||
}
|
||||
if (getUserInfo().getKeyfile() == null
|
||||
&& getUserInfo().getPassword() == null) {
|
||||
throw new BuildException("Password or Keyfile is required.");
|
||||
}
|
||||
if (command == null && commandResource == null) {
|
||||
throw new BuildException("Command or commandResource is required.");
|
||||
}
|
||||
|
||||
Session session = null;
|
||||
|
||||
try {
|
||||
session = openSession();
|
||||
/* called once */
|
||||
if (command != null) {
|
||||
log("cmd : " + command, Project.MSG_INFO);
|
||||
ExecResponse response = executeCommand(session, command);
|
||||
if (outputProperty != null) {
|
||||
getProject().setNewProperty(outputProperty, response.getOutput());
|
||||
}
|
||||
if (errorProperty != null) {
|
||||
getProject().setNewProperty(outputProperty, response.getError());
|
||||
}
|
||||
if (resultProperty != null) {
|
||||
getProject().setNewProperty(resultProperty, response.getCode()+"");
|
||||
}
|
||||
} else { // read command resource and execute for each command
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(commandResource.getInputStream()));
|
||||
String cmd;
|
||||
StringBuilder output = new StringBuilder();
|
||||
StringBuilder error = new StringBuilder();
|
||||
int lastCode = -1;
|
||||
while ((cmd = br.readLine()) != null) {
|
||||
log("cmd : " + cmd, Project.MSG_INFO);
|
||||
ExecResponse response = executeCommand(session, cmd);
|
||||
output.append(response.getOutput());
|
||||
error.append(response.getError());
|
||||
lastCode = response.getCode();
|
||||
}
|
||||
if (outputProperty != null) {
|
||||
getProject().setNewProperty(outputProperty, output.toString());
|
||||
}
|
||||
if (errorProperty != null) {
|
||||
getProject().setNewProperty(outputProperty, error.toString());
|
||||
}
|
||||
if (resultProperty != null) {
|
||||
getProject().setNewProperty(resultProperty, lastCode+"");
|
||||
}
|
||||
FileUtils.close(br);
|
||||
} catch (IOException e) {
|
||||
throw new BuildException(e);
|
||||
}
|
||||
}
|
||||
} catch (JSchException e) {
|
||||
throw new BuildException(e);
|
||||
} finally {
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
private static class ExecResponse {
|
||||
|
||||
private final String output;
|
||||
private final String error;
|
||||
private final int code;
|
||||
|
||||
public ExecResponse(String output, String error, int code) {
|
||||
this.output = output;
|
||||
this.error = error;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ExecResponse executeCommand(Session session, String cmd)
|
||||
throws BuildException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TeeOutputStream tee = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
|
||||
ByteArrayOutputStream err = new ByteArrayOutputStream();
|
||||
TeeOutputStream teeErr = new TeeOutputStream(err, new KeepAliveOutputStream(System.err));
|
||||
int ec = -1;
|
||||
|
||||
try {
|
||||
final ChannelExec channel;
|
||||
session.setTimeout((int) maxwait);
|
||||
/* execute the command */
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(cmd);
|
||||
channel.setOutputStream(tee);
|
||||
channel.setExtOutputStream(tee);
|
||||
channel.setErrStream(teeErr);
|
||||
channel.connect();
|
||||
// wait for it to finish
|
||||
thread =
|
||||
new Thread() {
|
||||
public void run() {
|
||||
while (!channel.isClosed()) {
|
||||
if (thread == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
sleep(RETRY_INTERVAL);
|
||||
} catch (Exception e) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
thread.start();
|
||||
thread.join(maxwait);
|
||||
|
||||
if (thread.isAlive()) {
|
||||
// ran out of time
|
||||
thread = null;
|
||||
if (getFailonerror()) {
|
||||
throw new BuildException(TIMEOUT_MESSAGE);
|
||||
} else {
|
||||
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
|
||||
}
|
||||
} else {
|
||||
//success
|
||||
if (outputFile != null) {
|
||||
writeToFile(out.toString(), append, outputFile);
|
||||
}
|
||||
if (errorFile != null) {
|
||||
writeToFile(err.toString(), append, errorFile);
|
||||
}
|
||||
// this is the wrong test if the remote OS is OpenVMS,
|
||||
// but there doesn't seem to be a way to detect it.
|
||||
ec = channel.getExitStatus();
|
||||
if (ec != 0) {
|
||||
String msg = "Remote command failed with exit status " + ec;
|
||||
if (getFailonerror()) {
|
||||
throw new BuildException(msg);
|
||||
} else {
|
||||
log(msg, Project.MSG_ERR);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (BuildException e) {
|
||||
throw e;
|
||||
} catch (JSchException e) {
|
||||
if (e.getMessage().indexOf("session is down") >= 0) {
|
||||
if (getFailonerror()) {
|
||||
throw new BuildException(TIMEOUT_MESSAGE, e);
|
||||
} else {
|
||||
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
|
||||
}
|
||||
} else {
|
||||
if (getFailonerror()) {
|
||||
throw new BuildException(e);
|
||||
} else {
|
||||
log("Caught exception: " + e.getMessage(),
|
||||
Project.MSG_ERR);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (getFailonerror()) {
|
||||
throw new BuildException(e);
|
||||
} else {
|
||||
log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
|
||||
}
|
||||
}
|
||||
return new ExecResponse(out.toString(), err.toString(), ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while (true) {
|
||||
bytesRead = in.read(buffer);
|
||||
if (bytesRead == -1) {
|
||||
break;
|
||||
}
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
out.flush();
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.
|
||||
* ====================================================================
|
||||
*/
|
||||
package org.jclouds.tools.ant.taskdefs.sshjava;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
|
||||
import org.apache.tools.ant.taskdefs.PumpStreamHandler;
|
||||
import org.apache.tools.ant.taskdefs.optional.ssh.SSHUserInfo;
|
||||
import org.apache.tools.ant.util.FileUtils;
|
||||
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
/**
|
||||
* Executes a command on a remote machine via ssh.
|
||||
*
|
||||
* <p/>
|
||||
* adapted from SSHBase and SSHExec ant tasks, and Execute from the ant 1.7.1 release.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class SSHExecute {
|
||||
|
||||
private static final int RETRY_INTERVAL = 500;
|
||||
|
||||
/** units are milliseconds, default is 0=infinite */
|
||||
private long maxwait = 0;
|
||||
|
||||
private ExecuteStreamHandler streamHandler;
|
||||
private String host;
|
||||
private SSHUserInfo userInfo;
|
||||
private int port = 22;
|
||||
private Project project;
|
||||
private String knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts";
|
||||
|
||||
/**
|
||||
* Creates a new execute object using <code>PumpStreamHandler</code> for stream handling.
|
||||
*/
|
||||
public SSHExecute() {
|
||||
this(new PumpStreamHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ssh object.
|
||||
*
|
||||
* @param streamHandler
|
||||
* the stream handler used to handle the input and output streams of the subprocess.
|
||||
*/
|
||||
public SSHExecute(ExecuteStreamHandler streamHandler) {
|
||||
setStreamHandler(streamHandler);
|
||||
userInfo = new SSHUserInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stream handler to use.
|
||||
*
|
||||
* @param streamHandler
|
||||
* ExecuteStreamHandler.
|
||||
*/
|
||||
public void setStreamHandler(ExecuteStreamHandler streamHandler) {
|
||||
this.streamHandler = streamHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting this to true trusts hosts whose identity is unknown.
|
||||
*
|
||||
* @param yesOrNo
|
||||
* if true trust the identity of unknown hosts.
|
||||
*/
|
||||
public void setTrust(boolean yesOrNo) {
|
||||
userInfo.setTrust(yesOrNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for logging
|
||||
*/
|
||||
public void setProject(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Username known to remote host.
|
||||
*
|
||||
* @param username
|
||||
* The new username value
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
userInfo.setName(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password for the user.
|
||||
*
|
||||
* @param password
|
||||
* The new password value
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
userInfo.setPassword(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keyfile for the user.
|
||||
*
|
||||
* @param keyfile
|
||||
* The new keyfile value
|
||||
*/
|
||||
public void setKeyfile(String keyfile) {
|
||||
userInfo.setKeyfile(keyfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the passphrase for the users key.
|
||||
*
|
||||
* @param passphrase
|
||||
* The new passphrase value
|
||||
*/
|
||||
public void setPassphrase(String passphrase) {
|
||||
userInfo.setPassphrase(passphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote host, either DNS name or IP.
|
||||
*
|
||||
* @param host
|
||||
* The new host value
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the port used to connect to the remote host.
|
||||
*
|
||||
* @param port
|
||||
* port number of remote host.
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(long timeout) {
|
||||
maxwait = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the file that has the identities of all known hosts. This is used by SSH
|
||||
* protocol to validate the identity of the host. The default is
|
||||
* <i>${user.home}/.ssh/known_hosts</i>.
|
||||
*
|
||||
* @param knownHosts
|
||||
* a path to the known hosts file.
|
||||
*/
|
||||
public void setKnownhosts(String knownHosts) {
|
||||
this.knownHosts = knownHosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command on the remote host.
|
||||
*
|
||||
* @param command
|
||||
* - what to execute on the remote host.
|
||||
*
|
||||
* @return return code of the process.
|
||||
* @throws BuildException
|
||||
* bad parameter.
|
||||
* @throws JSchException
|
||||
* if there's an underlying problem exposed in SSH
|
||||
* @throws IOException
|
||||
* if there's a problem attaching streams.
|
||||
* @throws TimeoutException
|
||||
* if we exceeded our timeout
|
||||
*/
|
||||
public int execute(String command) throws BuildException, JSchException, IOException,
|
||||
TimeoutException {
|
||||
if (command == null) {
|
||||
throw new BuildException("Command is required.");
|
||||
}
|
||||
if (host == null) {
|
||||
throw new BuildException("Host is required.");
|
||||
}
|
||||
if (userInfo.getName() == null) {
|
||||
throw new BuildException("Username is required.");
|
||||
}
|
||||
if (userInfo.getKeyfile() == null && userInfo.getPassword() == null) {
|
||||
throw new BuildException("Password or Keyfile is required.");
|
||||
}
|
||||
|
||||
Session session = null;
|
||||
try {
|
||||
session = openSession();
|
||||
return executeCommand(session, command);
|
||||
} finally {
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an ssh seession.
|
||||
*
|
||||
* @return the opened session
|
||||
* @throws JSchException
|
||||
* on error
|
||||
*/
|
||||
protected Session openSession() throws JSchException {
|
||||
JSch jsch = new JSch();
|
||||
if (null != userInfo.getKeyfile()) {
|
||||
jsch.addIdentity(userInfo.getKeyfile());
|
||||
}
|
||||
|
||||
if (!userInfo.getTrust() && knownHosts != null) {
|
||||
project.log("Using known hosts: " + knownHosts, Project.MSG_DEBUG);
|
||||
jsch.setKnownHosts(knownHosts);
|
||||
}
|
||||
|
||||
Session session = jsch.getSession(userInfo.getName(), host, port);
|
||||
session.setUserInfo(userInfo);
|
||||
project.log("Connecting to " + host + ":" + port, Project.MSG_VERBOSE);
|
||||
session.connect();
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* FIXME Comment this
|
||||
*
|
||||
* @param session
|
||||
* @param cmd
|
||||
* @return return code of the process.
|
||||
* @throws JSchException
|
||||
* if there's an underlying problem exposed in SSH
|
||||
* @throws IOException
|
||||
* if there's a problem attaching streams.
|
||||
* @throws TimeoutException
|
||||
* if we exceeded our timeout
|
||||
*/
|
||||
private int executeCommand(Session session, String cmd) throws JSchException, IOException,
|
||||
TimeoutException {
|
||||
final ChannelExec channel;
|
||||
session.setTimeout((int) maxwait);
|
||||
/* execute the command */
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(cmd);
|
||||
attachStreams(channel);
|
||||
project.log("executing command: " + cmd, Project.MSG_VERBOSE);
|
||||
channel.connect();
|
||||
try {
|
||||
waitFor(channel);
|
||||
} finally {
|
||||
streamHandler.stop();
|
||||
closeStreams(channel);
|
||||
}
|
||||
return channel.getExitStatus();
|
||||
}
|
||||
|
||||
private void attachStreams(final ChannelExec channel) throws IOException {
|
||||
streamHandler.setProcessInputStream(channel.getOutputStream());
|
||||
streamHandler.setProcessOutputStream(channel.getInputStream());
|
||||
streamHandler.setProcessErrorStream(channel.getErrStream());
|
||||
streamHandler.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the streams belonging to the given Process.
|
||||
*
|
||||
* @param process
|
||||
* the <code>Process</code>.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void closeStreams(ChannelExec process) throws IOException {
|
||||
FileUtils.close(process.getInputStream());
|
||||
FileUtils.close(process.getOutputStream());
|
||||
FileUtils.close(process.getErrStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void waitFor(final ChannelExec channel) throws TimeoutException {
|
||||
// wait for it to finish
|
||||
Thread thread = new Thread() {
|
||||
public void run() {
|
||||
while (!channel.isClosed()) {
|
||||
try {
|
||||
sleep(RETRY_INTERVAL);
|
||||
} catch (InterruptedException e) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
thread.start();
|
||||
try {
|
||||
thread.join(maxwait);
|
||||
} catch (InterruptedException e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (thread.isAlive()) {
|
||||
thread.destroy();
|
||||
throw new TimeoutException("command still running");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -25,22 +25,18 @@ import java.io.BufferedWriter;
|
|||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Location;
|
||||
import org.apache.tools.ant.MagicNames;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.PropertyHelper;
|
||||
import org.apache.tools.ant.Target;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.taskdefs.Java;
|
||||
import org.apache.tools.ant.taskdefs.optional.ssh.SSHExec;
|
||||
import org.apache.tools.ant.taskdefs.optional.ssh.SSHUserInfo;
|
||||
import org.apache.tools.ant.taskdefs.optional.ssh.Scp;
|
||||
import org.apache.tools.ant.types.CommandlineJava;
|
||||
|
@ -60,6 +56,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
|
||||
/**
|
||||
* Version of the Java task that executes over ssh.
|
||||
|
@ -67,11 +64,9 @@ import com.google.common.collect.Maps;
|
|||
* @author Adrian Cole
|
||||
*/
|
||||
public class SSHJava extends Java {
|
||||
private final SSHExec exec;
|
||||
private final SSHExecute exec;
|
||||
private final Scp scp;
|
||||
private final SSHUserInfo userInfo;
|
||||
|
||||
private String jvm = "/usr/bin/java";
|
||||
private File localDirectory;
|
||||
private File remotebase;
|
||||
private File remotedir;
|
||||
|
@ -91,7 +86,8 @@ public class SSHJava extends Java {
|
|||
public SSHJava() {
|
||||
super();
|
||||
setFork(true);
|
||||
exec = new SSHExec();
|
||||
exec = new SSHExecute();
|
||||
exec.setProject(getProject());
|
||||
scp = new Scp();
|
||||
userInfo = new SSHUserInfo();
|
||||
}
|
||||
|
@ -107,7 +103,6 @@ public class SSHJava extends Java {
|
|||
|
||||
@Override
|
||||
public int executeJava() throws BuildException {
|
||||
checkNotNull(jvm, "jvm must be set");
|
||||
checkNotNull(remotebase, "remotebase must be set");
|
||||
|
||||
if (localDirectory == null) {
|
||||
|
@ -124,13 +119,13 @@ public class SSHJava extends Java {
|
|||
|
||||
if (osFamily == OsFamily.UNIX) {
|
||||
log("removing old contents: " + remotedir.getAbsolutePath(), Project.MSG_VERBOSE);
|
||||
exec.setCommand(exec("rm -rf " + remotedir.getAbsolutePath()).render(osFamily));
|
||||
exec.execute();
|
||||
sshexec(exec("rm -rf " + remotedir.getAbsolutePath()).render(osFamily));
|
||||
} else {
|
||||
// TODO need recursive remove on windows
|
||||
}
|
||||
// must copy the files over first as we are changing the system properties based on this.
|
||||
String command = convertJavaToScriptNormalizingPaths(getCommandLine());
|
||||
String command = createInitScript(osFamily, id, remotedir.getAbsolutePath(), env,
|
||||
getCommandLine());
|
||||
|
||||
try {
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(new File(localDirectory, "init."
|
||||
|
@ -162,27 +157,43 @@ public class SSHJava extends Java {
|
|||
}
|
||||
|
||||
if (osFamily == OsFamily.UNIX) {
|
||||
exec.setCommand(exec("chmod 755 " + remotedir.getAbsolutePath() + "{fs}init.{sh}").render(
|
||||
osFamily));
|
||||
exec.execute();
|
||||
sshexec(exec("chmod 755 " + remotedir.getAbsolutePath() + "{fs}init.{sh}")
|
||||
.render(osFamily));
|
||||
}
|
||||
|
||||
Statement statement = new StatementList(exec("{cd} " + remotedir.getAbsolutePath()),
|
||||
exec(remotedir.getAbsolutePath() + "{fs}init.{sh} init"), exec(remotedir
|
||||
.getAbsolutePath()
|
||||
+ "{fs}init.{sh} run"));
|
||||
getProjectProperties().remove(id);
|
||||
exec.setResultProperty(id);
|
||||
exec.setFailonerror(false);
|
||||
exec.setCommand(statement.render(osFamily));
|
||||
exec.setError(errorFile);
|
||||
exec.setErrorproperty(errorProperty);
|
||||
exec.setOutput(outputFile);
|
||||
exec.setOutputproperty(outputProperty);
|
||||
exec.setAppend(append);
|
||||
try {
|
||||
return sshexecRedirectStreams(statement);
|
||||
} catch (IOException e) {
|
||||
throw new BuildException(e, getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
private int sshexec(String command) {
|
||||
try {
|
||||
return exec.execute(command);
|
||||
} catch (JSchException e) {
|
||||
throw new BuildException(e, getLocation());
|
||||
} catch (IOException e) {
|
||||
throw new BuildException(e, getLocation());
|
||||
} catch (TimeoutException e) {
|
||||
throw new BuildException(e, getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
private int sshexecRedirectStreams(Statement statement) throws IOException {
|
||||
exec.setStreamHandler(redirector.createHandler());
|
||||
log("starting java as:\n" + statement.render(osFamily), Project.MSG_VERBOSE);
|
||||
exec.execute();
|
||||
return Integer.parseInt(getProject().getProperty(id));
|
||||
int rc;
|
||||
try {
|
||||
rc = sshexec(statement.render(osFamily));
|
||||
} finally {
|
||||
redirector.complete();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
private void mkdirAndCopyTo(String destination, Iterable<FileSet> sets) {
|
||||
|
@ -190,19 +201,11 @@ public class SSHJava extends Java {
|
|||
log("no content: " + destination, Project.MSG_DEBUG);
|
||||
return;
|
||||
}
|
||||
exec.setCommand(exec("test -d " + destination).render(osFamily)); // TODO windows
|
||||
getProjectProperties().remove(id);
|
||||
exec.setResultProperty(id);
|
||||
exec.setFailonerror(false);
|
||||
exec.execute();
|
||||
if (getProject().getProperty(id).equals("0")) {
|
||||
if (sshexec(exec("test -d " + destination).render(osFamily)) == 0) {// TODO windows
|
||||
log("already created: " + destination, Project.MSG_VERBOSE);
|
||||
return;
|
||||
}
|
||||
getProjectProperties().remove(id);
|
||||
exec.setCommand(exec("{md} " + destination).render(osFamily));
|
||||
exec.setFailonerror(true);
|
||||
exec.execute();
|
||||
sshexec(exec("{md} " + destination).render(osFamily));
|
||||
scp.init();
|
||||
String scpDestination = getScpDir(destination);
|
||||
log("staging: " + scpDestination, Project.MSG_VERBOSE);
|
||||
|
@ -271,20 +274,21 @@ public class SSHJava extends Java {
|
|||
return in;
|
||||
}
|
||||
|
||||
String convertJavaToScriptNormalizingPaths(CommandlineJava commandLine) {
|
||||
String createInitScript(OsFamily osFamily, String id, String basedir, Environment env,
|
||||
CommandlineJava commandLine) {
|
||||
|
||||
Map<String, String> envVariables = Maps.newHashMap();
|
||||
String[] environment = env.getVariables();
|
||||
if (environment != null) {
|
||||
for (int i = 0; i < environment.length; i++) {
|
||||
log("Setting environment variable: " + environment[i], Project.MSG_VERBOSE);
|
||||
log("Setting environment variable: " + environment[i], Project.MSG_DEBUG);
|
||||
String[] keyValue = environment[i].split("=");
|
||||
envVariables.put(keyValue[0], keyValue[1]);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder commandBuilder = new StringBuilder(jvm);
|
||||
if (getCommandLine().getBootclasspath() != null) {
|
||||
StringBuilder commandBuilder = new StringBuilder(commandLine.getVmCommand().getExecutable());
|
||||
if (commandLine.getBootclasspath() != null) {
|
||||
commandBuilder.append(" -Xbootclasspath:bootclasspath");
|
||||
resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getBootclasspath(),
|
||||
"bootclasspath", commandBuilder);
|
||||
|
@ -314,8 +318,8 @@ public class SSHJava extends Java {
|
|||
Joiner.on(' ').join(commandLine.getJavaCommand().getArguments()));
|
||||
}
|
||||
|
||||
InitBuilder testInitBuilder = new InitBuilder(id, remotedir.getAbsolutePath(), remotedir
|
||||
.getAbsolutePath(), envVariables, commandBuilder.toString());
|
||||
InitBuilder testInitBuilder = new InitBuilder(id, basedir, basedir, envVariables,
|
||||
commandBuilder.toString());
|
||||
String script = testInitBuilder.build(osFamily);
|
||||
return reprefix(script);
|
||||
}
|
||||
|
@ -348,11 +352,6 @@ public class SSHJava extends Java {
|
|||
throw new IllegalArgumentException("this only operates when fork is set");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJvm(String jvm) {
|
||||
this.jvm = checkNotNull(jvm, "jvm");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote host, either DNS name or IP.
|
||||
*
|
||||
|
@ -364,15 +363,6 @@ public class SSHJava extends Java {
|
|||
scp.setHost(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host.
|
||||
*
|
||||
* @return the host
|
||||
*/
|
||||
public String getHost() {
|
||||
return exec.getHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Username known to remote host.
|
||||
*
|
||||
|
@ -459,27 +449,6 @@ public class SSHJava extends Java {
|
|||
scp.setPort(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port attribute.
|
||||
*
|
||||
* @return the port
|
||||
*/
|
||||
public int getPort() {
|
||||
return exec.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the task. This initializizs the known hosts and sets the default port.
|
||||
*
|
||||
* @throws BuildException
|
||||
* on error
|
||||
*/
|
||||
public void init() throws BuildException {
|
||||
super.init();
|
||||
exec.init();
|
||||
scp.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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".
|
||||
|
@ -491,43 +460,6 @@ public class SSHJava extends Java {
|
|||
exec.setTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the verbose flag.
|
||||
*
|
||||
* @param verbose
|
||||
* if true output more verbose logging
|
||||
* @since Ant 1.6.2
|
||||
*/
|
||||
public void setVerbose(boolean verbose) {
|
||||
exec.setVerbose(verbose);
|
||||
scp.setVerbose(verbose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(File error) {
|
||||
this.errorFile = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorProperty(String property) {
|
||||
errorProperty = property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutput(File out) {
|
||||
outputFile = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutputproperty(String outputProp) {
|
||||
outputProperty = outputProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAppend(boolean append) {
|
||||
this.append = append;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProject(Project project) {
|
||||
super.setProject(project);
|
||||
|
@ -538,45 +470,40 @@ public class SSHJava extends Java {
|
|||
@Override
|
||||
public void setOwningTarget(Target target) {
|
||||
super.setOwningTarget(target);
|
||||
exec.setOwningTarget(target);
|
||||
scp.setOwningTarget(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskName(String taskName) {
|
||||
super.setTaskName(taskName);
|
||||
exec.setTaskName(taskName);
|
||||
scp.setTaskName(taskName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) {
|
||||
super.setDescription(description);
|
||||
exec.setDescription(description);
|
||||
scp.setDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Location location) {
|
||||
super.setLocation(location);
|
||||
exec.setLocation(location);
|
||||
scp.setLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskType(String type) {
|
||||
super.setTaskType(type);
|
||||
exec.setTaskType(type);
|
||||
scp.setTaskType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SSHJava [append=" + append + ", env=" + env + ", errorFile=" + errorFile
|
||||
+ ", errorProperty=" + errorProperty + ", jvm=" + jvm + ", localDirectory="
|
||||
+ localDirectory + ", osFamily=" + osFamily + ", outputFile=" + outputFile
|
||||
+ ", outputProperty=" + outputProperty + ", remoteDirectory=" + remotebase
|
||||
+ ", userInfo=" + userInfo + "]";
|
||||
+ ", errorProperty=" + errorProperty + ", localDirectory=" + localDirectory
|
||||
+ ", osFamily=" + osFamily + ", outputFile=" + outputFile + ", outputProperty="
|
||||
+ outputProperty + ", remoteDirectory=" + remotebase + ", userInfo=" + userInfo
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -592,17 +519,4 @@ public class SSHJava extends Java {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Hashtable<String, String> getProjectProperties() {
|
||||
PropertyHelper helper = (PropertyHelper) getProject().getReference(
|
||||
MagicNames.REFID_PROPERTY_HELPER);
|
||||
Field field;
|
||||
try {
|
||||
field = PropertyHelper.class.getDeclaredField("properties");
|
||||
field.setAccessible(true);
|
||||
return (Hashtable<String, String>) field.get(helper);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,28 +47,9 @@ public class SSHJavaTest {
|
|||
.entrySet());
|
||||
|
||||
// TODO, this test will break in windows
|
||||
@Test(enabled = false)
|
||||
public void testFull() throws SecurityException, NoSuchMethodException {
|
||||
SSHJava task = makeSSHJava();
|
||||
String expected = String
|
||||
.format(
|
||||
"export %s=\"%s\"%ncd /tmp/foo\n%s -Xms16m -Xmx32m -cp classpath -Dfooble=baz -Dfoo=bar org.jclouds.tools.ant.TestClass %s hello world\n",
|
||||
LAST_ENV.getKey(), LAST_ENV.getValue(), System.getProperty("java.home")
|
||||
+ "/bin/java", LAST_ENV.getKey());
|
||||
assertEquals(task.convertJavaToScriptNormalizingPaths(task.getCommandLine()), expected);
|
||||
}
|
||||
|
||||
// TODO, this test will break in windows
|
||||
@Test(enabled = false)
|
||||
public void testFullShift() throws SecurityException, NoSuchMethodException {
|
||||
public void testShift() throws SecurityException, NoSuchMethodException {
|
||||
SSHJava task = makeSSHJava();
|
||||
task = directoryShift(task);
|
||||
String expected = String
|
||||
.format(
|
||||
"export %s=\"%s\"%ncd /tmp/foo\n%s -Xms16m -Xmx32m -cp classpath -Dfooble=baz -Dfoo=bar -Dsettingsfile=/tmp/foo/maven/conf/settings.xml -DappHome=/tmp/foo/maven org.jclouds.tools.ant.TestClass %s hello world\n",
|
||||
LAST_ENV.getKey(), LAST_ENV.getValue(), System.getProperty("java.home")
|
||||
+ "/bin/java", LAST_ENV.getKey());
|
||||
assertEquals(task.convertJavaToScriptNormalizingPaths(task.getCommandLine()), expected);
|
||||
assertEquals(task.shiftMap, ImmutableMap.<String, String> of(System.getProperty("user.home")
|
||||
+ "/apache-maven-2.2.1", "maven"));
|
||||
}
|
||||
|
@ -160,20 +141,10 @@ public class SSHJavaTest {
|
|||
}
|
||||
}
|
||||
|
||||
public void testSSHJavaPropertyOverride() {
|
||||
SSHJava task = new SSHJava();
|
||||
Project p = new Project();
|
||||
task.setProject(p);
|
||||
p.setProperty("foo", "bar");
|
||||
task.getProjectProperties().remove("foo");
|
||||
assertEquals(p.getProperty("foo"), null);
|
||||
}
|
||||
|
||||
private SSHJava makeSSHJava() {
|
||||
SSHJava task = new SSHJava();
|
||||
populateTask(task);
|
||||
task.setRemotebase(new File("/tmp/foo"));
|
||||
task.setVerbose(true);
|
||||
task.setTrust(true);
|
||||
return task;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue