revamped task so that it has no external deps. Added patched SSHExec task

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2581 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2010-01-03 07:00:06 +00:00
parent 7d70031a29
commit 53c6a2376a
6 changed files with 674 additions and 340 deletions

View File

@ -33,11 +33,6 @@
<jclouds.test.listener></jclouds.test.listener>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.shrinkwrap</groupId>
<artifactId>shrinkwrap-impl-base</artifactId>
<version>1.0.0-alpha-3</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-scriptbuilder</artifactId>

View File

@ -22,4 +22,4 @@
- ex. curl http://jclouds.rimuhosting.com/maven2/snapshots/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-20091215.023231-1-jar-with-dependencies.jar >jclouds-antcontrib-all.jar
2. invoke ant, adding the library above, and passing the properties 'host' 'username' 'keyfile' which corresponds to your remote credentials
- ex. ant -lib ~/.m2/repository/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-SNAPSHOT-jar-with-dependencies.jar -Dhost=67.202.42.237 -Dusername=root -Dkeyfile=$HOME/.ssh/id_dsa
- ex. ant -lib jclouds-antcontrib-all.jar -Dhost=67.202.42.237 -Dusername=root -Dkeyfile=$HOME/.ssh/id_dsa
- ex. ant -lib ~/.m2/repository/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-SNAPSHOT-jar-with-dependencies.jar -Dhost=localhost -Dusername=$USER -Dkeyfile=$HOME/.ssh/id_dsa

View File

@ -0,0 +1,394 @@
/*
* 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 &quot;wait forever&quot;.
*
* @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();
}
}
}
}

View File

@ -21,324 +21,201 @@ package org.jclouds.tools.ant;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.scriptbuilder.domain.Statements.exec;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.security.SecureRandom;
import java.util.List;
import java.util.zip.ZipFile;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Project;
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;
import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.util.KeepAliveOutputStream;
import org.apache.tools.ant.util.TeeOutputStream;
import org.jboss.shrinkwrap.api.Archives;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.importer.ExplodedImporter;
import org.jboss.shrinkwrap.api.importer.ZipImporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Closeables;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* Ported from Jsch SSHExec task.
* Version of the Java task that executes over ssh.
*
* @author Adrian Cole
*/
public class JavaOverSsh extends Java {
/** Default listen port for SSH daemon */
private static final int SSH_PORT = 22;
private final SSHExec exec;
private final Scp scp;
private final SSHUserInfo userInfo;
private String jvm = "/usr/bin/java";
private File localDirectory;
private File remoteDirectory;
private Environment env = new Environment();
private String host;
private String knownHosts;
private int port = SSH_PORT;
private SSHUserInfo userInfo = new SSHUserInfo();
private OsFamily osFamily = OsFamily.UNIX;
/** units are milliseconds, default is 0=infinite */
private long maxwait = 0;
/** for waiting for the command to finish */
private Thread watchDog = null;
private File outputFile;
private String outputProperty;
private String resultProperty;
private File errorFile;
private String errorProperty;
private File outputFile;
private String outputProperty;
private boolean append;
private static final String TIMEOUT_MESSAGE = "Timeout period exceeded, connection dropped.";
public JavaOverSsh() {
super();
setFork(true);
exec = new SSHExec();
scp = new Scp();
userInfo = new SSHUserInfo();
}
public JavaOverSsh(Task owner) {
super(owner);
setFork(true);
this();
bindToOwner(owner);
}
@Override
public int executeJava() throws BuildException {
String command = convertJavaToScript(getCommandLine());
InputStream classpathJar = (getCommandLine().getClasspath() != null) ? makeClasspathJarOrNull(getCommandLine()
.getClasspath().list())
: null;
InputStream bootClasspathJar = (getCommandLine().getBootclasspath() != null) ? makeClasspathJarOrNull(getCommandLine()
.getBootclasspath().list())
: null;
InputStream currentDirectoryZip = Archives.create("cwd.zip", ZipExporter.class).as(
ExplodedImporter.class).importDirectory(localDirectory).as(ZipExporter.class)
.exportZip();
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.");
}
Session session = null;
try {
// execute the command
session = openSession();
session.setTimeout((int) maxwait);
ChannelSftp sftp = null;
sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
sftp.put(currentDirectoryZip, remoteDirectory + "/cwd.zip");
Closeables.closeQuietly(currentDirectoryZip);
if (classpathJar != null || bootClasspathJar != null) {
if (classpathJar != null) {
sftp.put(classpathJar, remoteDirectory + "/classpath.jar");
Closeables.closeQuietly(classpathJar);
}
if (bootClasspathJar != null) {
sftp.put(classpathJar, remoteDirectory + "/boot-classpath.jar");
Closeables.closeQuietly(classpathJar);
}
}
final ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
ByteArrayOutputStream out = new ByteArrayOutputStream();
TeeOutputStream teeOut = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
ByteArrayOutputStream err = new ByteArrayOutputStream();
TeeOutputStream teeErr = new TeeOutputStream(err, new KeepAliveOutputStream(System.err));
channel.setOutputStream(teeOut);
channel.setExtOutputStream(teeOut);
channel.setErrStream(teeErr);
channel.connect();
// wait for it to finish
watchDog = new Thread() {
public void run() {
while (!channel.isEOF()) {
if (watchDog == null) {
return;
}
try {
sleep(500);
} catch (Exception e) {
// ignored
}
}
}
};
watchDog.start();
watchDog.join(maxwait);
if (watchDog.isAlive()) {
// ran out of time
throw new BuildException(TIMEOUT_MESSAGE);
} else {
// completed successfully
return writeOutputAndReturnExitStatus(channel, out, err);
}
} catch (BuildException e) {
throw e;
} catch (JSchException e) {
if (e.getMessage().indexOf("session is down") >= 0) {
throw new BuildException(TIMEOUT_MESSAGE, e);
} else {
throw new BuildException(e);
}
} catch (Exception e) {
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
InputStream makeClasspathJarOrNull(String... paths) {
if (paths != null && paths.length > 0) {
JavaArchive classpathArchive = Archives.create("classpath.jar", JavaArchive.class);
for (String path : paths) {
File file = new File(path);
if (file.exists()) {
if (file.isFile()) {
try {
classpathArchive.as(ZipImporter.class).importZip(
new ZipFile(file.getAbsolutePath()));
} catch (IOException e) {
throw new BuildException(e);
}
} else {
classpathArchive.as(ExplodedImporter.class).importDirectory(file);
}
}
}
return classpathArchive.as(ZipExporter.class).exportZip();
}
return null;
}
private int writeOutputAndReturnExitStatus(final ChannelExec channel, ByteArrayOutputStream out,
ByteArrayOutputStream err) throws IOException {
if (outputProperty != null) {
getProject().setProperty(outputProperty, out.toString());
}
if (outputFile != null) {
writeToFile(err.toString(), append, outputFile);
}
if (errorProperty != null) {
getProject().setProperty(errorProperty, err.toString());
}
if (errorFile != null) {
writeToFile(out.toString(), append, errorFile);
}
if (resultProperty != null) {
getProject().setProperty(resultProperty, channel.getExitStatus() + "");
}
return channel.getExitStatus();
}
/**
* 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 bytesRead;
while (true) {
bytesRead = in.read(buffer);
if (bytesRead == -1) {
break;
}
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
if (out != null) {
out.close();
}
}
}
String convertJavaToScript(CommandlineJava commandLine) {
checkNotNull(jvm, "jvm must be set");
checkNotNull(localDirectory, "dir must be set");
checkNotNull(remoteDirectory, "remotedir must be set");
// must copy the files over first as we are changing the system properties based on this.
if (localDirectory != null) {
FileSet cwd = new FileSet();
cwd.setDir(localDirectory);
mkdirAndCopyTo(remoteDirectory.getAbsolutePath(), ImmutableList.of(cwd));
}
if (getCommandLine().getClasspath() != null) {
copyPathTo(getCommandLine().getClasspath(), remoteDirectory.getAbsolutePath()
+ "/classpath");
}
if (getCommandLine().getBootclasspath() != null) {
copyPathTo(getCommandLine().getBootclasspath(), remoteDirectory.getAbsolutePath()
+ "/bootclasspath");
}
String command = convertJavaToScriptNormalizingPaths(getCommandLine());
String random = new SecureRandom().nextInt() + "";
exec.setResultProperty(random);
exec.setFailonerror(false);
exec.setCommand(command);
exec.setError(errorFile);
exec.setErrorproperty(errorProperty);
exec.setOutput(outputFile);
exec.setOutputproperty(outputProperty);
exec.setAppend(append);
exec.execute();
return Integer.parseInt(getProject().getProperty(random));
}
private void mkdirAndCopyTo(String destination, Iterable<FileSet> sets) {
scp.init();
exec.setCommand(exec("{md} " + destination).render(osFamily));
exec.execute();
String scpDestination = getScpDir(destination);
System.out.println("Sending to: " + scpDestination);
for (FileSet set : sets)
scp.addFileset(set);
scp.setTodir(scpDestination);
scp.execute();
}
private String getScpDir(String path) {
return String.format("%s:%s@%s:%s", userInfo.getName(),
userInfo.getKeyfile() == null ? userInfo.getPassword() : userInfo.getPassphrase(),
scp.getHost(), path);
}
void resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(Path path, String prefix,
StringBuilder destination) {
if (path == null)
return;
String[] paths = path.list();
if (paths != null && paths.length > 0) {
for (int i = 0; i < paths.length; i++) {
File file = new File(paths[i]);
if (file.exists()) {
// directories are flattened under the prefix anyway, so there's no need to add them
// to the path
if (file.isFile())
destination.append("{ps}").append(file.getName());
} else {
// if the file doesn't exist, it is probably a "forward reference" to something that
// is already on the remote machine
destination.append("{ps}").append(file.getAbsolutePath());
}
}
}
}
void copyPathTo(Path path, String destination) {
List<FileSet> filesets = Lists.newArrayList();
if (path.list() != null && path.list().length > 0) {
for (String filepath : path.list()) {
File file = new File(filepath);
if (file.exists()) {
FileSet fileset = new FileSet();
if (file.isFile()) {
fileset.setFile(file);
} else {
fileset.setDir(file);
}
filesets.add(fileset);
}
}
}
mkdirAndCopyTo(destination, filesets);
}
String convertJavaToScriptNormalizingPaths(CommandlineJava commandLine) {
List<Statement> statements = Lists.newArrayList();
String[] environment = env.getVariables();
if (environment != null) {
for (int i = 0; i < environment.length; i++) {
log("Setting environment variable: " + environment[i], Project.MSG_VERBOSE);
statements.add(exec("{export} " + environment[i]));
String[] keyValue = environment[i].split("=");
statements.add(exec(String.format("{export} %s={vq}%s{vq}", keyValue[0], keyValue[1])));
}
}
statements.add(exec("{cd} " + remoteDirectory));
statements.add(exec("jar -xf cwd.zip"));
StringBuilder commandBuilder = new StringBuilder(jvm);
if (commandLine.getBootclasspath() != null
&& commandLine.getBootclasspath().list().length > 0) {
commandBuilder.append(" -Xbootclasspath:boot-classpath.jar");
if (getCommandLine().getBootclasspath() != null) {
commandBuilder.append(" -Xbootclasspath:bootclasspath");
resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getBootclasspath(),
"bootclasspath", commandBuilder);
}
if (commandLine.getVmCommand().getArguments() != null
&& commandLine.getVmCommand().getArguments().length > 0) {
commandBuilder.append(" ");
String[] variables = commandLine.getVmCommand().getArguments();
for (int i = 0; i < variables.length; i++) {
commandBuilder.append(variables[i]);
if (i + 1 < variables.length)
commandBuilder.append(" ");
}
}
if (commandLine.getClasspath() != null && commandLine.getClasspath().list().length > 0) {
commandBuilder.append(" -cp classpath.jar");
commandBuilder.append(" ").append(
Joiner.on(' ').join(commandLine.getVmCommand().getArguments()));
}
commandBuilder.append(" -cp classpath");
resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getClasspath(),
"classpath", commandBuilder);
if (commandLine.getSystemProperties() != null
&& commandLine.getSystemProperties().getVariables() != null
&& commandLine.getSystemProperties().getVariables().length > 0) {
commandBuilder.append(" ");
String[] variables = commandLine.getSystemProperties().getVariables();
for (int i = 0; i < variables.length; i++) {
commandBuilder.append(variables[i]);
if (i + 1 < variables.length)
commandBuilder.append(" ");
}
commandBuilder.append(" ").append(
Joiner.on(' ').join(commandLine.getSystemProperties().getVariables()));
}
commandBuilder.append(" ").append(commandLine.getClassname());
if (commandLine.getJavaCommand().getArguments() != null
&& commandLine.getJavaCommand().getArguments().length > 0) {
commandBuilder.append(" ");
String[] variables = commandLine.getJavaCommand().getArguments();
for (int i = 0; i < variables.length; i++) {
commandBuilder.append(variables[i]);
if (i + 1 < variables.length)
commandBuilder.append(" ");
}
commandBuilder.append(" ").append(
Joiner.on(' ').join(commandLine.getJavaCommand().getArguments()));
}
statements.add(exec(commandBuilder.toString()));
@ -351,6 +228,10 @@ public class JavaOverSsh extends Java {
env.addVariable(var);
}
/**
* Note that if the {@code dir} property is set, this will be copied recursively to the remote
* host.
*/
@Override
public void setDir(File localDir) {
this.localDirectory = checkNotNull(localDir, "dir");
@ -378,7 +259,8 @@ public class JavaOverSsh extends Java {
* The new host value
*/
public void setHost(String host) {
this.host = host;
exec.setHost(host);
scp.setHost(host);
}
/**
@ -387,7 +269,7 @@ public class JavaOverSsh extends Java {
* @return the host
*/
public String getHost() {
return host;
return exec.getHost();
}
/**
@ -397,6 +279,8 @@ public class JavaOverSsh extends Java {
* The new username value
*/
public void setUsername(String username) {
exec.setUsername(username);
scp.setUsername(username);
userInfo.setName(username);
}
@ -407,6 +291,8 @@ public class JavaOverSsh extends Java {
* The new password value
*/
public void setPassword(String password) {
exec.setPassword(password);
scp.setPassword(password);
userInfo.setPassword(password);
}
@ -417,7 +303,11 @@ public class JavaOverSsh extends Java {
* The new keyfile value
*/
public void setKeyfile(String keyfile) {
exec.setKeyfile(keyfile);
scp.setKeyfile(keyfile);
userInfo.setKeyfile(keyfile);
if (userInfo.getPassphrase() == null)
userInfo.setPassphrase("");
}
/**
@ -427,6 +317,8 @@ public class JavaOverSsh extends Java {
* The new passphrase value
*/
public void setPassphrase(String passphrase) {
exec.setPassphrase(passphrase);
scp.setPassphrase(passphrase);
userInfo.setPassphrase(passphrase);
}
@ -439,7 +331,8 @@ public class JavaOverSsh extends Java {
* a path to the known hosts file.
*/
public void setKnownhosts(String knownHosts) {
this.knownHosts = knownHosts;
exec.setKnownhosts(knownHosts);
scp.setKnownhosts(knownHosts);
}
/**
@ -449,6 +342,8 @@ public class JavaOverSsh extends Java {
* if true trust the identity of unknown hosts.
*/
public void setTrust(boolean yesOrNo) {
exec.setTrust(yesOrNo);
scp.setTrust(yesOrNo);
userInfo.setTrust(yesOrNo);
}
@ -459,7 +354,8 @@ public class JavaOverSsh extends Java {
* port number of remote host.
*/
public void setPort(int port) {
this.port = port;
exec.setPort(port);
scp.setPort(port);
}
/**
@ -468,7 +364,7 @@ public class JavaOverSsh extends Java {
* @return the port
*/
public int getPort() {
return port;
return exec.getPort();
}
/**
@ -479,42 +375,8 @@ public class JavaOverSsh extends Java {
*/
public void init() throws BuildException {
super.init();
this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts";
this.port = SSH_PORT;
}
/**
* 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) {
log("Using known hosts: " + knownHosts, Project.MSG_DEBUG);
jsch.setKnownHosts(knownHosts);
}
Session session = jsch.getSession(userInfo.getName(), host, port);
session.setUserInfo(userInfo);
log("Connecting to " + host + ":" + port);
session.connect();
return session;
}
/**
* Get the user information.
*
* @return the user information
*/
protected SSHUserInfo getUserInfo() {
return userInfo;
exec.init();
scp.init();
}
/**
@ -525,36 +387,85 @@ public class JavaOverSsh extends Java {
* The new timeout value in seconds
*/
public void setTimeout(long timeout) {
maxwait = timeout;
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 out) {
this.errorFile = out;
public void setError(File error) {
this.errorFile = error;
}
@Override
public void setErrorProperty(String errorProp) {
this.errorProperty = errorProp;
public void setErrorProperty(String property) {
errorProperty = property;
}
@Override
public void setOutput(File out) {
this.outputFile = out;
outputFile = out;
}
@Override
public void setOutputproperty(String outputProp) {
this.outputProperty = outputProp;
}
@Override
public void setResultProperty(String resultProperty) {
this.resultProperty = resultProperty;
outputProperty = outputProp;
}
@Override
public void setAppend(boolean append) {
this.append = append;
}
@Override
public void setProject(Project project) {
super.setProject(project);
exec.setProject(project);
scp.setProject(project);
}
@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);
}
}

View File

@ -24,33 +24,39 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map.Entry;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Environment.Variable;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "jclouds.JavaOverSshTest")
public class JavaOverSshTest {
public static final Entry<String, String> LAST_ENV = Iterables.getLast(System.getenv()
.entrySet());
// TODO, this test will break in windows
public void testFull() throws SecurityException, NoSuchMethodException {
JavaOverSsh task = createTask();
assertEquals(
String
JavaOverSsh task = makeJavaOverSsh();
String expected = String
.format(
"cd /tmp/foo\njar -xf cwd.zip\n%s -Xms256 -cp classpath.jar -Dfooble=baz -Dfoo=bar org.jclouds.tools.ant.TestClass hello world\n",
System.getProperty("java.home") + "/bin/java", System
.getProperty("user.dir")), task.convertJavaToScript(task
.getCommandLine()));
"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);
}
private JavaOverSsh createTask() {
private Java populateTask(Java task) {
Project p = new Project();
JavaOverSsh task = new JavaOverSsh();
task.setProject(p);
task.setClassname(TestClass.class.getName());
task.createClasspath().add(new Path(p, "target/test-classes"));
@ -62,20 +68,43 @@ public class JavaOverSshTest {
Variable prop2 = new Environment.Variable();
prop2.setKey("foo");
prop2.setValue("bar");
task.addSysproperty(prop2);
task.createJvmarg().setValue("-Xms256");
task.createJvmarg().setValue("-Xms16m");
task.createJvmarg().setValue("-Xmx32m");
Variable env = new Environment.Variable();
env.setKey(LAST_ENV.getKey());
env.setValue(LAST_ENV.getValue());
task.addEnv(env);
task.createArg().setValue(env.getKey());
task.createArg().setValue("hello");
task.createArg().setValue("world");
task.setDir(new File(System.getProperty("user.dir")));
task.setRemotedir(new File("/tmp/foo"));
task.setFork(true);
task.setJvm(System.getProperty("java.home") + "/bin/java");
task.setOutputproperty("out");
task.setErrorProperty("err");
task.setResultProperty("result");
return task;
}
@Test(enabled = false, groups = { "live" })
public void testSsh() throws NumberFormatException, FileNotFoundException, IOException {
Java java = makeJava();
java.execute();
JavaOverSsh javaOverSsh = makeJavaOverSsh();
addDestinationTo(javaOverSsh);
javaOverSsh.execute();
assertEquals(javaOverSsh.getProject().getProperty("out"), javaOverSsh.getProject()
.getProperty("out"));
assertEquals(javaOverSsh.getProject().getProperty("err"), javaOverSsh.getProject()
.getProperty("err"));
assertEquals(javaOverSsh.getProject().getProperty("result"), javaOverSsh.getProject()
.getProperty("result"));
}
private void addDestinationTo(JavaOverSsh javaOverSsh) throws UnknownHostException {
String sshHost = System.getProperty("jclouds.test.ssh.host");
String sshPort = System.getProperty("jclouds.test.ssh.port");
String sshUser = System.getProperty("jclouds.test.ssh.username");
@ -85,23 +114,26 @@ public class JavaOverSshTest {
int port = (sshPort != null) ? Integer.parseInt(sshPort) : 22;
InetAddress host = (sshHost != null) ? InetAddress.getByName(sshHost) : InetAddress
.getLocalHost();
JavaOverSsh task = createTask();
task.setHost(host.getHostAddress());
task.setPort(port);
task.setTrust(true);
task.setUsername(sshUser);
javaOverSsh.setHost(host.getHostAddress());
javaOverSsh.setPort(port);
javaOverSsh.setUsername(sshUser);
if (sshKeyFile != null && !sshKeyFile.trim().equals("")) {
task.setKeyfile(sshKeyFile);
javaOverSsh.setKeyfile(sshKeyFile);
} else {
task.setPassword(sshPass);
javaOverSsh.setPassword(sshPass);
}
task.setOutputproperty("out");
task.setErrorProperty("err");
task.setResultProperty("result");
task.execute();
assertEquals(task.getProject().getProperty("out"), "[hello, world]\n");
assertEquals(task.getProject().getProperty("err"), "err\n");
assertEquals(task.getProject().getProperty("result"), "3");
}
private JavaOverSsh makeJavaOverSsh() {
JavaOverSsh task = new JavaOverSsh();
populateTask(task);
task.setRemotedir(new File("/tmp/foo"));
task.setVerbose(true);
task.setTrust(true);
return task;
}
private Java makeJava() {
return populateTask(new Java());
}
}

View File

@ -5,6 +5,8 @@ import java.util.Arrays;
public class TestClass {
public static void main(String... args) {
System.out.println("env:");
System.out.println(System.getenv(args[0]));
File cwd = new File(System.getProperty("user.dir"));
System.out.println("children:");
for (File child : cwd.listFiles())