Issue 79: first run of ssh support

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1649 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-18 18:45:19 +00:00
parent 2272333eaa
commit 3daa05652b
14 changed files with 526 additions and 14 deletions

View File

@ -24,6 +24,8 @@
package org.jclouds.http.binders; package org.jclouds.http.binders;
import java.util.Collections; import java.util.Collections;
import static com.google.common.base.Preconditions.checkState;
import java.util.Map; import java.util.Map;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
@ -38,22 +40,20 @@ import com.google.inject.Inject;
/** /**
* Binds the object to the request as a json object. * Binds the object to the request as a json object.
* *
* @author adriancole * @author Adrian Cole
* @since 4.0 * @since 4.0
*/ */
public class JsonBinder implements PostEntityBinder { public class JsonBinder implements PostEntityBinder {
protected final Gson gson;
@Inject @Inject
public JsonBinder(Gson gson) { protected Gson gson;
this.gson = gson;
}
public void addEntityToRequest(Map<String, String> postParams, HttpRequest request) { public void addEntityToRequest(Map<String, String> postParams, HttpRequest request) {
addEntityToRequest((Object) postParams, request); addEntityToRequest((Object) postParams, request);
} }
public void addEntityToRequest(Object toBind, HttpRequest request) { public void addEntityToRequest(Object toBind, HttpRequest request) {
checkState(gson != null, "Program error: gson should have been injected at this point");
String json = gson.toJson(toBind); String json = gson.toJson(toBind);
request.setEntity(json); request.setEntity(json);
request.getHeaders().replaceValues(HttpHeaders.CONTENT_LENGTH, request.getHeaders().replaceValues(HttpHeaders.CONTENT_LENGTH,

View File

@ -60,7 +60,13 @@ import com.google.inject.assistedinject.FactoryProvider;
public class ParserModule extends AbstractModule { public class ParserModule extends AbstractModule {
private final static TypeLiteral<ParseSax.Factory> parseSaxFactoryLiteral = new TypeLiteral<ParseSax.Factory>() { private final static TypeLiteral<ParseSax.Factory> parseSaxFactoryLiteral = new TypeLiteral<ParseSax.Factory>() {
}; };
protected void configure() {
bind(parseSaxFactoryLiteral).toProvider(
FactoryProvider.newFactory(parseSaxFactoryLiteral, new TypeLiteral<ParseSax<?>>() {
}));
}
static class DateTimeAdapter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> { static class DateTimeAdapter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> {
private final static DateService dateService = new DateService(); private final static DateService dateService = new DateService();
@ -122,9 +128,4 @@ public class ParserModule extends AbstractModule {
return gson.create(); return gson.create();
} }
protected void configure() {
bind(parseSaxFactoryLiteral).toProvider(
FactoryProvider.newFactory(parseSaxFactoryLiteral, new TypeLiteral<ParseSax<?>>() {
}));
}
} }

View File

@ -314,8 +314,11 @@ public class JaxrsAnnotationProcessor {
public PostEntityBinder getPostEntityBinderOrNull(Method method, Object[] args) { public PostEntityBinder getPostEntityBinderOrNull(Method method, Object[] args) {
for (Object arg : args) { for (Object arg : args) {
if (arg instanceof PostEntityBinder) if (arg instanceof PostEntityBinder) {
return (PostEntityBinder) arg; PostEntityBinder binder = (PostEntityBinder) arg;
injector.injectMembers(binder);
return binder;
}
} }
PostBinder annotation = method.getAnnotation(PostBinder.class); PostBinder annotation = method.getAnnotation(PostBinder.class);
if (annotation != null) { if (annotation != null) {

View File

@ -0,0 +1,42 @@
/**
*
* 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;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* designates the module configures an Ssh Connection.
*
* @author Adrian Cole
*
*/
@Retention(RUNTIME)
@Target(TYPE)
public @interface ConfiguresSshConnection {
}

View File

@ -0,0 +1,29 @@
package org.jclouds.ssh;
import java.io.InputStream;
import java.net.InetAddress;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import com.google.inject.assistedinject.Assisted;
/**
* @author Adrian Cole
*/
public interface SshConnection {
public interface Factory {
SshConnection create(InetAddress host, int port, @Assisted("username") String username,
@Assisted("password") String password);
}
InputStream get(String path);
@PostConstruct
void connect();
@PreDestroy
void disconnect();
}

View File

@ -0,0 +1,27 @@
package org.jclouds.ssh;
/**
* @author Adrian Cole
*/
public class SshException extends RuntimeException {
/** The serialVersionUID */
private static final long serialVersionUID = 7271048517353750433L;
public SshException() {
super();
}
public SshException(String arg0, Throwable arg1) {
super(arg0, arg1);
}
public SshException(String arg0) {
super(arg0);
}
public SshException(Throwable arg0) {
super(arg0);
}
}

View File

@ -40,7 +40,7 @@ import com.google.inject.Injector;
import com.google.inject.name.Names; import com.google.inject.name.Names;
/** /**
* // TODO: Adrian: Document this! * Tests the ability to configure a {@link GaeHttpCommandExecutorService}
* *
* @author Adrian Cole * @author Adrian Cole
*/ */

View File

@ -40,6 +40,7 @@
<module>gae</module> <module>gae</module>
<module>httpnio</module> <module>httpnio</module>
<module>log4j</module> <module>log4j</module>
<module>ssh</module>
</modules> </modules>
<dependencies> <dependencies>
<dependency> <dependency>

48
extensions/ssh/jsch/pom.xml Executable file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
====================================================================
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.html
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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>jclouds-ssh-project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-jsch</artifactId>
<name>jclouds jsch ssh client</name>
<packaging>jar</packaging>
<description>jclouds jsch ssh client</description>
<dependencies>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.41</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,109 @@
package org.jclouds.ssh.jsch;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.io.InputStream;
import java.net.InetAddress;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.jclouds.logging.Logger;
import org.jclouds.ssh.SshConnection;
import org.jclouds.ssh.SshException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
/**
*
* @author Adrian Cole
*/
public class JschSshConnection implements SshConnection {
private final InetAddress host;
private final int port;
private final String username;
private final String password;
private ChannelSftp sftp;
@Resource
protected Logger logger = Logger.NULL;
@Inject
public JschSshConnection(@Assisted InetAddress host, @Assisted int port,
@Assisted("username") String username, @Assisted("password") String password) {
this.host = checkNotNull(host, "host");
checkArgument(port > 0, "ssh port must be greater then zero" + port);
this.port = port;
this.username = checkNotNull(username, "username");
this.password = checkNotNull(password, "password");
}
public InputStream get(String path) {
checkConnected();
checkNotNull(path, "path");
try {
return sftp.get(path);
} catch (SftpException e) {
throw new SshException(String.format("%s@%s:%d: Error getting path: %s", username, host
.getHostAddress(), port, path), e);
}
}
private void checkConnected() {
checkState(sftp.isConnected(), String.format("%s@%s:%d: SFTP not connected!", username, host
.getHostAddress(), port));
}
@PostConstruct
public void connect() {
if (sftp != null && sftp.isConnected())
return;
JSch jsch = new JSch();
Session session = null;
Channel channel = null;
try {
session = jsch.getSession(username, host.getHostAddress(), port);
} catch (JSchException e) {
throw new SshException(String.format("%s@%s:%d: Error creating session.", username, host
.getHostAddress(), port), e);
}
logger.debug("%s@%s:%d: Session created.", username, host.getHostAddress(), port);
session.setPassword(password);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
try {
session.connect();
} catch (JSchException e) {
throw new SshException(String.format("%s@%s:%d: Error connecting to session.", username,
host.getHostAddress(), port), e);
}
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
public void disconnect() {
if (sftp != null && sftp.isConnected())
sftp.quit();
}
}

View File

@ -0,0 +1,44 @@
/**
*
* 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;
import org.jclouds.ssh.ConfiguresSshConnection;
import org.jclouds.ssh.SshConnection;
import org.jclouds.ssh.jsch.JschSshConnection;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryProvider;
/**
*
* @author Adrian Cole
*/
@ConfiguresSshConnection
public class JschSshConnectionModule extends AbstractModule {
protected void configure() {
bind(SshConnection.Factory.class).toProvider(
FactoryProvider.newFactory(SshConnection.Factory.class, JschSshConnection.class));
}
}

View File

@ -0,0 +1,72 @@
package org.jclouds.ssh.jsch;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.apache.commons.io.IOUtils;
import org.jclouds.ssh.SshConnection;
import org.jclouds.ssh.jsch.config.JschSshConnectionModule;
import org.jclouds.util.Utils;
import org.testng.ITestContext;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests the ability of a {@link JschSshConnection}
*
* @author Adrian Cole
*/
@Test(groups = "live", testName = "ssh.JschSshConnectionLiveTest")
public class JschSshConnectionLiveTest {
protected static final String sshHost = System.getProperty("jclouds.test.ssh.host");
protected static final String sshPort = System.getProperty("jclouds.test.ssh.port");
protected static final String sshUser = System.getProperty("jclouds.test.ssh.username");
protected static final String sshPass = System.getProperty("jclouds.test.ssh.password");
protected SshConnection connection;
@BeforeGroups(groups = { "live" })
public void setupConnection(ITestContext context) throws NumberFormatException,
UnknownHostException {
int port = (sshPort != null) ? Integer.parseInt(sshPort) : 22;
InetAddress host = (sshHost != null) ? InetAddress.getByName(sshHost) : InetAddress
.getLocalHost();
if (sshUser == null || sshPass == null || sshUser.trim().equals("")
|| sshPass.trim().equals("")) {
System.err.println("ssh credentials not present. Tests will be lame");
connection = new SshConnection() {
public void connect() {
}
public void disconnect() {
}
public InputStream get(String path) {
if (path.equals("/etc/passwd")) {
return IOUtils.toInputStream("root");
}
throw new RuntimeException("path " + path + " not stubbed");
}
};
} else {
Injector i = Guice.createInjector(new JschSshConnectionModule());
SshConnection.Factory factory = i.getInstance(SshConnection.Factory.class);
connection = factory.create(host, port, sshUser, sshPass);
connection.connect();
}
}
public void testGetEtcPassword() throws IOException {
InputStream input = connection.get("/etc/passwd");
String contents = Utils.toStringAndClose(input);
assert contents.indexOf("root") >= 0 : "no root in " + contents;
}
}

View File

@ -0,0 +1,30 @@
package org.jclouds.ssh.jsch.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.jclouds.ssh.SshConnection;
import org.jclouds.ssh.jsch.JschSshConnection;
import org.jclouds.ssh.jsch.config.JschSshConnectionModule;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests the ability to configure a {@link JschSshConnection}
*
* @author Adrian Cole
*/
@Test
public class JschSshConnectionModuleTest {
public void testConfigureBindsClient() throws UnknownHostException {
Injector i = Guice.createInjector(new JschSshConnectionModule());
SshConnection.Factory factory = i.getInstance(SshConnection.Factory.class);
SshConnection connection = factory.create(InetAddress.getLocalHost(), 22, "username",
"password");
assert connection instanceof JschSshConnection;
}
}

106
extensions/ssh/pom.xml Executable file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
====================================================================
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.html
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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>jclouds-extensions-project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-ssh-project</artifactId>
<packaging>pom</packaging>
<name>jclouds ssh project</name>
<modules>
<module>jsch</module>
</modules>
<properties>
<jclouds.test.ssh.host>localhost</jclouds.test.ssh.host>
<jclouds.test.ssh.port>22</jclouds.test.ssh.port>
<jclouds.test.ssh.username />
<jclouds.test.ssh.password />
</properties>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<!-- note that the groups/excluded groups don't work due to some problem
in surefire or testng. instead, we have to exclude via file path
<groups>live,integration</groups>
<excludedGroups>unit,performance</excludedGroups> -->
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/*IntegrationTest.java</include>
<include>**/*LiveTest.java</include>
</includes>
<systemProperties>
<property>
<name>file.encoding</name>
<value>UTF-8</value>
</property>
<property>
<name>jclouds.test.ssh.host</name>
<value>${jclouds.test.ssh.host}</value>
</property>
<property>
<name>jclouds.test.ssh.port</name>
<value>${jclouds.test.ssh.port}</value>
</property>
<property>
<name>jclouds.test.ssh.username</name>
<value>${jclouds.test.ssh.username}</value>
</property>
<property>
<name>jclouds.test.ssh.password</name>
<value>${jclouds.test.ssh.password}</value>
</property>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>