Issue 79: improved session handling

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1825 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-21 16:50:58 +00:00
parent ea874483b0
commit c63e532271
6 changed files with 218 additions and 29 deletions

View File

@ -1,9 +1,33 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.ssh; package org.jclouds.ssh;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
public class ExecResponse { public class ExecResponse {
private final String error; private final String error;
private final String output; private final String output;
@ -20,4 +44,40 @@ public class ExecResponse {
return output; return output;
} }
@Override
public String toString() {
return "ExecResponse [error=" + error + ", output=" + output + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((error == null) ? 0 : error.hashCode());
result = prime * result + ((output == null) ? 0 : output.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ExecResponse other = (ExecResponse) obj;
if (error == null) {
if (other.error != null)
return false;
} else if (!error.equals(other.error))
return false;
if (output == null) {
if (other.output != null)
return false;
} else if (!output.equals(other.output))
return false;
return true;
}
} }

View File

@ -1,3 +1,26 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.ssh; package org.jclouds.ssh;
import java.io.InputStream; import java.io.InputStream;

View File

@ -1,3 +1,26 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.ssh; package org.jclouds.ssh;
/** /**

View File

@ -1,9 +1,33 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.ssh.jsch; package org.jclouds.ssh.jsch;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetAddress; import java.net.InetAddress;
@ -11,6 +35,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.apache.commons.io.input.ProxyInputStream;
import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.io.output.ByteArrayOutputStream;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.ssh.ExecResponse; import org.jclouds.ssh.ExecResponse;
@ -20,7 +45,6 @@ import org.jclouds.util.Utils;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSch;
@ -34,11 +58,27 @@ import com.jcraft.jsch.SftpException;
*/ */
public class JschSshConnection implements SshConnection { public class JschSshConnection implements SshConnection {
private final class CloseFtpChannelOnCloseInputStream extends ProxyInputStream {
private final ChannelSftp sftp;
private CloseFtpChannelOnCloseInputStream(InputStream proxy, ChannelSftp sftp) {
super(proxy);
this.sftp = sftp;
}
@Override
public void close() throws IOException {
super.close();
if (sftp != null)
sftp.disconnect();
}
}
private final InetAddress host; private final InetAddress host;
private final int port; private final int port;
private final String username; private final String username;
private final String password; private final String password;
private ChannelSftp sftp;
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private Session session; private Session session;
@ -54,10 +94,20 @@ public class JschSshConnection implements SshConnection {
} }
public InputStream get(String path) { public InputStream get(String path) {
checkConnected();
checkNotNull(path, "path"); checkNotNull(path, "path");
checkConnected();
logger.debug("%s@%s:%d: Opening sftp Channel.", username, host.getHostAddress(), port);
ChannelSftp sftp = null;
try { try {
return sftp.get(path); sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
} catch (JSchException e) {
throw new SshException(String.format("%s@%s:%d: Error connecting to sftp.", username, host
.getHostAddress(), port), e);
}
try {
return new CloseFtpChannelOnCloseInputStream(sftp.get(path), sftp);
} catch (SftpException e) { } catch (SftpException e) {
throw new SshException(String.format("%s@%s:%d: Error getting path: %s", username, host throw new SshException(String.format("%s@%s:%d: Error getting path: %s", username, host
.getHostAddress(), port, path), e); .getHostAddress(), port, path), e);
@ -65,17 +115,15 @@ public class JschSshConnection implements SshConnection {
} }
private void checkConnected() { private void checkConnected() {
checkState(sftp != null && sftp.isConnected(), String.format("%s@%s:%d: SFTP not connected!", checkState(session != null && session.isConnected(), String.format(
username, host.getHostAddress(), port)); "%s@%s:%d: SFTP not connected!", username, host.getHostAddress(), port));
} }
@PostConstruct @PostConstruct
public void connect() { public void connect() {
if (sftp != null && sftp.isConnected()) disconnect();
return;
JSch jsch = new JSch(); JSch jsch = new JSch();
session = null; session = null;
Channel channel = null;
try { try {
session = jsch.getSession(username, host.getHostAddress(), port); session = jsch.getSession(username, host.getHostAddress(), port);
} catch (JSchException e) { } catch (JSchException e) {
@ -94,24 +142,16 @@ public class JschSshConnection implements SshConnection {
host.getHostAddress(), port), e); host.getHostAddress(), port), e);
} }
logger.debug("%s@%s:%d: Session connected.", username, host.getHostAddress(), port); logger.debug("%s@%s:%d: Session connected.", username, host.getHostAddress(), port);
logger.debug("%s@%s:%d: Opening sftp Channel.", username, host.getHostAddress(), port);
try {
channel = session.openChannel("sftp");
channel.connect();
} catch (JSchException e) {
throw new SshException(String.format("%s@%s:%d: Error connecting to sftp.", username, host
.getHostAddress(), port), e);
}
sftp = (ChannelSftp) channel;
} }
@PreDestroy @PreDestroy
public void disconnect() { public void disconnect() {
if (sftp != null && sftp.isConnected()) if (session != null && session.isConnected())
sftp.quit(); session.disconnect();
} }
public ExecResponse exec(String command) { public ExecResponse exec(String command) {
checkConnected();
ChannelExec executor = null; ChannelExec executor = null;
try { try {
try { try {

View File

@ -1,3 +1,26 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.ssh.jsch; package org.jclouds.ssh.jsch;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
@ -12,7 +35,6 @@ import org.jclouds.ssh.ExecResponse;
import org.jclouds.ssh.SshConnection; import org.jclouds.ssh.SshConnection;
import org.jclouds.ssh.jsch.config.JschSshConnectionModule; import org.jclouds.ssh.jsch.config.JschSshConnectionModule;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
import org.testng.ITestContext;
import org.testng.annotations.BeforeGroups; import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -31,18 +53,15 @@ public class JschSshConnectionLiveTest {
protected static final String sshUser = System.getProperty("jclouds.test.ssh.username"); protected static final String sshUser = System.getProperty("jclouds.test.ssh.username");
protected static final String sshPass = System.getProperty("jclouds.test.ssh.password"); protected static final String sshPass = System.getProperty("jclouds.test.ssh.password");
protected SshConnection connection;
@BeforeGroups(groups = { "live" }) @BeforeGroups(groups = { "live" })
public void setupConnection(ITestContext context) throws NumberFormatException, public SshConnection setupConnection() throws NumberFormatException, UnknownHostException {
UnknownHostException {
int port = (sshPort != null) ? Integer.parseInt(sshPort) : 22; int port = (sshPort != null) ? Integer.parseInt(sshPort) : 22;
InetAddress host = (sshHost != null) ? InetAddress.getByName(sshHost) : InetAddress InetAddress host = (sshHost != null) ? InetAddress.getByName(sshHost) : InetAddress
.getLocalHost(); .getLocalHost();
if (sshUser == null || sshPass == null || sshUser.trim().equals("") if (sshUser == null || sshPass == null || sshUser.trim().equals("")
|| sshPass.trim().equals("")) { || sshPass.trim().equals("")) {
System.err.println("ssh credentials not present. Tests will be lame"); System.err.println("ssh credentials not present. Tests will be lame");
connection = new SshConnection() { return new SshConnection() {
public void connect() { public void connect() {
} }
@ -72,19 +91,20 @@ public class JschSshConnectionLiveTest {
} else { } else {
Injector i = Guice.createInjector(new JschSshConnectionModule()); Injector i = Guice.createInjector(new JschSshConnectionModule());
SshConnection.Factory factory = i.getInstance(SshConnection.Factory.class); SshConnection.Factory factory = i.getInstance(SshConnection.Factory.class);
connection = factory.create(host, port, sshUser, sshPass); SshConnection connection = factory.create(host, port, sshUser, sshPass);
connection.connect(); connection.connect();
return connection;
} }
} }
public void testGetEtcPassword() throws IOException { public void testGetEtcPassword() throws IOException {
InputStream input = connection.get("/etc/passwd"); InputStream input = setupConnection().get("/etc/passwd");
String contents = Utils.toStringAndClose(input); String contents = Utils.toStringAndClose(input);
assert contents.indexOf("root") >= 0 : "no root in " + contents; assert contents.indexOf("root") >= 0 : "no root in " + contents;
} }
public void testExecHostname() throws IOException { public void testExecHostname() throws IOException {
ExecResponse response = connection.exec("hostname"); ExecResponse response = setupConnection().exec("hostname");
assertEquals(response.getError(), ""); assertEquals(response.getError(), "");
assertEquals(response.getOutput().trim(), InetAddress.getLocalHost().getHostName()); assertEquals(response.getOutput().trim(), InetAddress.getLocalHost().getHostName());
} }

View File

@ -1,3 +1,26 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.ssh.jsch.config; package org.jclouds.ssh.jsch.config;
import java.net.InetAddress; import java.net.InetAddress;