Issue 73: created module for object storage

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1604 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-05 21:24:22 +00:00
parent e5bc2dd5e7
commit f86e06f3d8
38 changed files with 1091 additions and 598 deletions

View File

@ -23,11 +23,13 @@
*/ */
package org.jclouds.aws.s3; package org.jclouds.aws.s3;
import org.jclouds.cloud.CloudContext;
/** /**
* Represents an authenticated context to S3. * Represents an authenticated context to S3.
* *
* <h2>Note</h2> Please issue {@link #close()} when you are finished with this * <h2>Note</h2> Please issue {@link #close()} when you are finished with this context in order to
* context in order to release resources. * release resources.
* *
* *
* @see S3Connection * @see S3Connection
@ -36,33 +38,20 @@ package org.jclouds.aws.s3;
* @author Adrian Cole * @author Adrian Cole
* *
*/ */
public interface S3Context { public interface S3Context extends CloudContext<S3Connection> {
/** /**
* low-level api to S3. Threadsafe implementations will return a singleton. * Creates a <code>Map<String,InputStream></code> view of the specified bucket.
* *
* @return a connection to S3 * @param bucket
*/ */
S3Connection getConnection(); S3InputStreamMap createInputStreamMap(String bucket);
/** /**
* Creates a <code>Map<String,InputStream></code> view of the specified * Creates a <code>Map<String,S3Object></code> view of the specified bucket.
* bucket. *
* * @param bucket
* @param bucket */
*/ S3ObjectMap createS3ObjectMap(String bucket);
S3InputStreamMap createInputStreamMap(String bucket);
/**
* Creates a <code>Map<String,S3Object></code> view of the specified bucket.
*
* @param bucket
*/
S3ObjectMap createS3ObjectMap(String bucket);
/**
* Closes all connections to S3.
*/
void close();
} }

View File

@ -0,0 +1,113 @@
/**
*
* 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.aws.s3;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTION_REUSE;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_SESSION_FAILURES;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_REQUEST_INVOKER_THREADS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_REDIRECTS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_RETRIES;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_SECURE;
import static org.jclouds.http.HttpConstants.PROPERTY_SAX_DEBUG;
import java.util.List;
import java.util.Properties;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.config.S3ContextModule;
import org.jclouds.aws.s3.xml.config.S3ParserModule;
import org.jclouds.cloud.CloudContextBuilder;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
* Creates {@link S3Context} or {@link Injector} instances based on the most commonly requested
* arguments.
* <p/>
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
* <p/>
* <p/>
* If no <code>Module</code>s are specified, the default {@link JDKLoggingModule logging} and
* {@link JavaUrlHttpFutureCommandClientModule http transports} will be installed.
*
* @author Adrian Cole, Andrew Newdigate
* @see S3Context
*/
public class S3ContextBuilder extends CloudContextBuilder<S3Connection, S3Context> {
public S3ContextBuilder(Properties props) {
super(props);
}
public static S3ContextBuilder newBuilder(String id, String secret) {
Properties properties = new Properties();
properties.setProperty(PROPERTY_HTTP_ADDRESS, "s3.amazonaws.com");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_SECURE, "true");
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, "5");
properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, "5");
properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, "75");
properties.setProperty(PROPERTY_POOL_MAX_SESSION_FAILURES, "2");
properties.setProperty(PROPERTY_POOL_REQUEST_INVOKER_THREADS, "1");
properties.setProperty(PROPERTY_POOL_IO_WORKER_THREADS, "2");
properties.setProperty(PROPERTY_POOL_MAX_CONNECTIONS, "12");
S3ContextBuilder builder = new S3ContextBuilder(properties);
builder.authenticate(id, secret);
return builder;
}
public void authenticate(String id, String secret) {
properties.setProperty(PROPERTY_AWS_ACCESSKEYID, checkNotNull(id, "awsAccessKeyId"));
properties.setProperty(PROPERTY_AWS_SECRETACCESSKEY, checkNotNull(secret,
"awsSecretAccessKey"));
}
public S3Context buildContext() {
return buildInjector().getInstance(S3Context.class);
}
protected void addParserModule(List<Module> modules) {
modules.add(new S3ParserModule());
}
protected void addContextModule(List<Module> modules) {
modules.add(new S3ContextModule());
}
protected void addConnectionModule(List<Module> modules) {
modules.add(new LiveS3ConnectionModule());
}
}

View File

@ -23,47 +23,13 @@
*/ */
package org.jclouds.aws.s3; package org.jclouds.aws.s3;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTION_REUSE;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_SESSION_FAILURES;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_REQUEST_INVOKER_THREADS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_REDIRECTS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_RETRIES;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_PORT;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_SECURE;
import static org.jclouds.http.HttpConstants.PROPERTY_SAX_DEBUG;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.config.S3ConnectionModule;
import org.jclouds.aws.s3.config.S3ContextModule;
import org.jclouds.aws.s3.xml.config.S3ParserModule;
import org.jclouds.http.config.HttpFutureCommandClientModule;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule; import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import org.jclouds.logging.config.LoggingModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule; import org.jclouds.logging.jdk.config.JDKLoggingModule;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.name.Names;
/** /**
* Creates {@link S3Context} or {@link Injector} instances based on the most commonly requested * Creates {@link S3Context} instances based on the most commonly requested
* arguments. * arguments.
* <p/> * <p/>
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided. * Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
@ -77,245 +43,10 @@ import com.google.inject.name.Names;
*/ */
public class S3ContextFactory { public class S3ContextFactory {
private static final String DEFAULT_SECURE_HTTP_PORT = "443";
private static final String DEFAULT_NON_SECURE_HTTP_PORT = "80";
private final Properties properties;
private final List<Module> modules = new ArrayList<Module>(3);
private S3ContextFactory(Properties properties) {
this.properties = properties;
}
public static S3ContextFactory createContext(String awsAccessKeyId, String awsSecretAccessKey) {
Properties properties = new Properties();
properties.setProperty(PROPERTY_AWS_ACCESSKEYID, checkNotNull(awsAccessKeyId,
"awsAccessKeyId"));
properties.setProperty(PROPERTY_AWS_SECRETACCESSKEY, checkNotNull(awsSecretAccessKey,
"awsSecretAccessKey"));
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_ADDRESS, "s3.amazonaws.com");
properties.setProperty(PROPERTY_HTTP_SECURE, "true");
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, "5");
properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, "5");
properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, "75");
properties.setProperty(PROPERTY_POOL_MAX_SESSION_FAILURES, "2");
properties.setProperty(PROPERTY_POOL_REQUEST_INVOKER_THREADS, "1");
properties.setProperty(PROPERTY_POOL_IO_WORKER_THREADS, "2");
properties.setProperty(PROPERTY_POOL_MAX_CONNECTIONS, "12");
return new S3ContextFactory(properties);
}
public S3Context build() {
return createInjector().getInstance(S3Context.class);
}
public static Injector createInjector(String awsAccessKeyId, String awsSecretAccessKey,
Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules)
.createInjector();
}
public static S3Context createS3Context(String awsAccessKeyId, String awsSecretAccessKey, public static S3Context createS3Context(String awsAccessKeyId, String awsSecretAccessKey,
Module... modules) { Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).build(); return S3ContextBuilder.newBuilder(awsAccessKeyId, awsSecretAccessKey).withModules(modules)
.buildContext();
} }
public static Injector createInjector(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).withHttpSecure(
isSecure).createInjector();
}
public static S3Context createS3Context(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).withHttpSecure(
isSecure).build();
}
public static Injector createInjector(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).withHttpSecure(
isSecure).withHttpAddress(server).createInjector();
}
public static S3Context createS3Context(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).withHttpSecure(
isSecure).withHttpAddress(server).build();
}
public static S3Context createS3Context(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, int port, Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).withHttpSecure(
isSecure).withHttpAddress(server).withHttpPort(port).build();
}
public static Injector createInjector(String awsAccessKeyId, String awsSecretAccessKey,
boolean isSecure, String server, int port, Module... modules) {
return createContext(awsAccessKeyId, awsSecretAccessKey).withModules(modules).withHttpSecure(
isSecure).withHttpAddress(server).withHttpPort(port).createInjector();
}
public S3ContextFactory withHttpAddress(String httpAddress) {
properties.setProperty(PROPERTY_HTTP_ADDRESS, httpAddress);
return this;
}
public S3ContextFactory withSaxDebug() {
properties.setProperty(PROPERTY_SAX_DEBUG, "true");
return this;
}
public S3ContextFactory withHttpMaxRetries(int httpMaxRetries) {
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, Integer.toString(httpMaxRetries));
return this;
}
public S3ContextFactory withHttpMaxRedirects(int httpMaxRedirects) {
properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, Integer.toString(httpMaxRedirects));
return this;
}
public S3ContextFactory withHttpPort(int httpPort) {
properties.setProperty(PROPERTY_HTTP_PORT, Integer.toString(httpPort));
return this;
}
public S3ContextFactory withHttpSecure(boolean httpSecure) {
properties.setProperty(PROPERTY_HTTP_SECURE, Boolean.toString(httpSecure));
return this;
}
public S3ContextFactory withPoolMaxConnectionReuse(int poolMaxConnectionReuse) {
properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, Integer
.toString(poolMaxConnectionReuse));
return this;
}
public S3ContextFactory withPoolMaxSessionFailures(int poolMaxSessionFailures) {
properties.setProperty(PROPERTY_POOL_MAX_SESSION_FAILURES, Integer
.toString(poolMaxSessionFailures));
return this;
}
public S3ContextFactory withPoolRequestInvokerThreads(int poolRequestInvokerThreads) {
properties.setProperty(PROPERTY_POOL_REQUEST_INVOKER_THREADS, Integer
.toString(poolRequestInvokerThreads));
return this;
}
public S3ContextFactory withPoolIoWorkerThreads(int poolIoWorkerThreads) {
properties
.setProperty(PROPERTY_POOL_IO_WORKER_THREADS, Integer.toString(poolIoWorkerThreads));
return this;
}
public S3ContextFactory withPoolMaxConnections(int poolMaxConnections) {
properties.setProperty(PROPERTY_POOL_MAX_CONNECTIONS, Integer.toString(poolMaxConnections));
return this;
}
public S3ContextFactory withModule(Module module) {
modules.add(module);
return this;
}
public S3ContextFactory withModules(Module... modules) {
this.modules.addAll(Arrays.asList(modules));
return this;
}
private Injector createInjector() {
useDefaultPortIfNotPresent(properties);
addLoggingModuleIfNotPresent(modules);
addS3ParserModuleIfNotPresent(modules);
addS3ConnectionModuleIfNotPresent(modules);
addHttpModuleIfNeededAndNotPresent(modules);
modules.add(new AbstractModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), checkNotNull(properties, "properties"));
}
});
modules.add(new S3ContextModule());
return Guice.createInjector(modules);
}
private void useDefaultPortIfNotPresent(Properties properties) {
/* Use 80 or 443 as the default port if one hasn't been set? */
if (!properties.containsKey(PROPERTY_HTTP_PORT)) {
if (Boolean.parseBoolean(properties.getProperty(PROPERTY_HTTP_SECURE))) {
properties.setProperty(PROPERTY_HTTP_PORT, DEFAULT_SECURE_HTTP_PORT);
} else {
properties.setProperty(PROPERTY_HTTP_PORT, DEFAULT_NON_SECURE_HTTP_PORT);
}
}
}
@VisibleForTesting
static void addS3ParserModuleIfNotPresent(List<Module> modules) {
if (!Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input instanceof S3ParserModule;
}
}))
modules.add(new S3ParserModule());
}
@VisibleForTesting
static void addHttpModuleIfNeededAndNotPresent(final List<Module> modules) {
if (Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input instanceof LiveS3ConnectionModule;
}
}) && (!Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(HttpFutureCommandClientModule.class);
}
})))
modules.add(new JavaUrlHttpFutureCommandClientModule());
}
@VisibleForTesting
static void addS3ConnectionModuleIfNotPresent(final List<Module> modules) {
if (!Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(S3ConnectionModule.class);
}
})) {
modules.add(0, new LiveS3ConnectionModule());
}
}
@VisibleForTesting
static void addLoggingModuleIfNotPresent(final List<Module> modules) {
if (!Iterables.any(modules, Predicates.instanceOf(LoggingModule.class)))
modules.add(new JDKLoggingModule());
}
@VisibleForTesting
Properties getProperties() {
return properties;
}
} }

View File

@ -36,10 +36,12 @@ import org.jclouds.aws.s3.handlers.AWSClientErrorRetryHandler;
import org.jclouds.aws.s3.handlers.AWSRedirectionRetryHandler; import org.jclouds.aws.s3.handlers.AWSRedirectionRetryHandler;
import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent; import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent;
import org.jclouds.aws.s3.internal.LiveS3Connection; import org.jclouds.aws.s3.internal.LiveS3Connection;
import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.HttpConstants; import org.jclouds.http.HttpConstants;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError; import org.jclouds.http.annotation.ServerError;
@ -57,7 +59,8 @@ import com.google.inject.name.Named;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@S3ConnectionModule @ConfiguresCloudConnection
@RequiresHttp
public class LiveS3ConnectionModule extends AbstractModule { public class LiveS3ConnectionModule extends AbstractModule {
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;

View File

@ -68,14 +68,14 @@ public abstract class BaseS3Map<V> implements S3Map<String, V> {
* maximum duration of an S3 Request * maximum duration of an S3 Request
*/ */
@Inject(optional = true) @Inject(optional = true)
@Named(S3Constants.PROPERTY_S3_MAP_TIMEOUT) @Named(S3Constants.PROPERTY_OBJECTMAP_TIMEOUT)
protected long requestTimeoutMilliseconds = 10000; protected long requestTimeoutMilliseconds = 10000;
/** /**
* time to pause before retrying a transient failure * time to pause before retrying a transient failure
*/ */
@Inject(optional = true) @Inject(optional = true)
@Named(S3Constants.PROPERTY_S3_MAP_RETRY) @Named(S3Constants.PROPERTY_OBJECTMAP_RETRY)
protected long requestRetryMilliseconds = 10; protected long requestRetryMilliseconds = 10;
@Inject @Inject

View File

@ -24,22 +24,14 @@
package org.jclouds.aws.s3.reference; package org.jclouds.aws.s3.reference;
import org.jclouds.aws.reference.AWSConstants; import org.jclouds.aws.reference.AWSConstants;
import org.jclouds.objectstore.reference.ObjectStoreConstants;
/** /**
* Configuration properties and constants used in S3 connections. * Configuration properties and constants used in S3 connections.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public interface S3Constants extends AWSConstants, S3Headers { public interface S3Constants extends AWSConstants, S3Headers, ObjectStoreConstants {
/**
* longest time a single Map operation can take before throwing an exception.
*/
public static final String PROPERTY_S3_MAP_TIMEOUT = "jclouds.s3.map.timeout";
/**
* time to pause before retrying a transient failure
*/
public static final String PROPERTY_S3_MAP_RETRY = "jclouds.s3.map.retry";
/** /**
* S3 service's XML Namespace, as used in XML request and response documents. * S3 service's XML Namespace, as used in XML request and response documents.

View File

@ -35,6 +35,7 @@ import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler; import org.jclouds.aws.s3.xml.ListBucketHandler;
import org.jclouds.aws.s3.xml.S3ParserFactory; import org.jclouds.aws.s3.xml.S3ParserFactory;
import org.jclouds.aws.xml.ErrorHandler; import org.jclouds.aws.xml.ErrorHandler;
import org.jclouds.command.ConfiguresResponseParser;
import org.jclouds.http.commands.callables.xml.ParseSax; import org.jclouds.http.commands.callables.xml.ParseSax;
import org.jclouds.http.commands.callables.xml.config.SaxModule; import org.jclouds.http.commands.callables.xml.config.SaxModule;
@ -47,6 +48,7 @@ import com.google.inject.assistedinject.FactoryProvider;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@ConfiguresResponseParser
public class S3ParserModule extends AbstractModule { public class S3ParserModule extends AbstractModule {
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>> errorTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>>() { protected final TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>> errorTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>>() {
}; };

View File

@ -0,0 +1,94 @@
/**
*
* 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.aws.s3;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.testng.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.config.S3ContextModule;
import org.jclouds.aws.s3.internal.GuiceS3Context;
import org.jclouds.aws.s3.xml.config.S3ParserModule;
import org.jclouds.cloud.CloudContext;
import org.testng.annotations.Test;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
* Tests behavior of modules configured in S3ContextBuilder
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "s3.S3ContextBuilderTest")
public class S3ContextBuilderTest {
public void testNewBuilder() {
S3ContextBuilder builder = S3ContextBuilder.newBuilder("id", "secret");
assertEquals(builder.getProperties().getProperty(PROPERTY_HTTP_ADDRESS), "s3.amazonaws.com");
assertEquals(builder.getProperties().getProperty(PROPERTY_AWS_ACCESSKEYID), "id");
assertEquals(builder.getProperties().getProperty(PROPERTY_AWS_SECRETACCESSKEY), "secret");
}
public void testBuildContext() {
CloudContext<S3Connection> context = S3ContextBuilder.newBuilder("id", "secret")
.buildContext();
assertEquals(context.getClass(), GuiceS3Context.class);
}
public void testBuildInjector() {
Injector i = S3ContextBuilder.newBuilder("id", "secret").buildInjector();
assert i.getInstance(S3Context.class) != null;
}
protected void testAddParserModule() {
List<Module> modules = new ArrayList<Module>();
S3ContextBuilder builder = S3ContextBuilder.newBuilder("id", "secret");
builder.addParserModule(modules);
assertEquals(modules.size(), 1);
assertEquals(modules.get(0).getClass(), S3ParserModule.class);
}
protected void testAddContextModule() {
List<Module> modules = new ArrayList<Module>();
S3ContextBuilder builder = S3ContextBuilder.newBuilder("id", "secret");
builder.addContextModule(modules);
assertEquals(modules.size(), 1);
assertEquals(modules.get(0).getClass(), S3ContextModule.class);
}
protected void addConnectionModule() {
List<Module> modules = new ArrayList<Module>();
S3ContextBuilder builder = S3ContextBuilder.newBuilder("id", "secret");
builder.addConnectionModule(modules);
assertEquals(modules.size(), 1);
assertEquals(modules.get(0).getClass(), LiveS3ConnectionModule.class);
}
}

View File

@ -174,14 +174,13 @@ public class S3IntegrationTest {
} }
protected void createStubS3Context() { protected void createStubS3Context() {
context = S3ContextFactory.createContext("stub", "stub").withHttpAddress("stub").withModule( context = S3ContextFactory.createS3Context("stub", "stub", new StubS3ConnectionModule());
new StubS3ConnectionModule()).build();
SANITY_CHECK_RETURNED_BUCKET_NAME = true; SANITY_CHECK_RETURNED_BUCKET_NAME = true;
} }
protected void createLiveS3Context(String AWSAccessKeyId, String AWSSecretAccessKey) { protected void createLiveS3Context(String AWSAccessKeyId, String AWSSecretAccessKey) {
context = buildS3ContextFactory(AWSAccessKeyId, AWSSecretAccessKey).withModule( context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey,
createHttpModule()).withModule(new Log4JLoggingModule()).build(); createHttpModule(), new Log4JLoggingModule());
} }
public String getBucketName() throws InterruptedException, ExecutionException, TimeoutException { public String getBucketName() throws InterruptedException, ExecutionException, TimeoutException {
@ -261,9 +260,9 @@ public class S3IntegrationTest {
} }
} }
protected S3ContextFactory buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) { protected S3ContextBuilder buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) {
return S3ContextFactory.createContext(AWSAccessKeyId, AWSSecretAccessKey).withSaxDebug() return (S3ContextBuilder) S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey)
.withHttpSecure(false).withHttpPort(80); .withSaxDebug().withHttpSecure(false).withHttpPort(80);
} }
protected Module createHttpModule() { protected Module createHttpModule() {

View File

@ -27,17 +27,15 @@ import org.testng.annotations.Test;
/** /**
* This performs the same test as {@link S3ConnectionLiveTest}, except using SSL. * This performs the same test as {@link S3ConnectionLiveTest}, except using SSL.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = {"live"}, testName = "s3.SecureS3ConnectionLiveTest") @Test(groups = { "live" }, testName = "s3.SecureS3ConnectionLiveTest")
public class SecureS3ConnectionLiveTest extends S3ConnectionLiveTest { public class SecureS3ConnectionLiveTest extends S3ConnectionLiveTest {
@Override @Override
protected S3ContextFactory buildS3ContextFactory(String AWSAccessKeyId, protected S3ContextBuilder buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) {
String AWSSecretAccessKey) { return (S3ContextBuilder) S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey)
return S3ContextFactory.createContext(AWSAccessKeyId, AWSSecretAccessKey) .withHttpSecure(true).withHttpPort(443);
.withHttpSecure(true) }
.withHttpPort(443);
}
} }

View File

@ -26,21 +26,16 @@ package org.jclouds.aws.s3.commands.callables;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock; import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay; import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.reset;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Object.Metadata; import org.jclouds.aws.s3.domain.S3Object.Metadata;
import org.jclouds.aws.util.DateService;
import org.jclouds.http.HttpException; import org.jclouds.http.HttpException;
import org.jclouds.http.HttpHeaders; import org.jclouds.http.HttpHeaders;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@ -91,22 +86,5 @@ public class ParseObjectFromHeadersAndHttpContentTest {
assertEquals(object.getContentRange(), "0-10485759/20232760"); assertEquals(object.getContentRange(), "0-10485759/20232760");
} }
private void buildDefaultResponseMock(HttpResponse response) {
reset(response);
// Headers required for the parse classes to work, but we don't care about this data.
String data = "test data";
expect(response.getStatusCode()).andReturn(200).atLeastOnce();
Multimap<String, String> emptyHeaders = HashMultimap.create();
expect(response.getHeaders()).andReturn(emptyHeaders).atLeastOnce();
expect(response.getContent()).andReturn(IOUtils.toInputStream(data)).atLeastOnce();
expect(response.getFirstHeaderOrNull(HttpHeaders.LAST_MODIFIED))
.andReturn(new DateService().rfc822DateFormat()).atLeastOnce();
expect(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE))
.andReturn("text/plain").atLeastOnce();
expect(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH))
.andReturn("" + data.length()).atLeastOnce();
}
} }

View File

@ -27,6 +27,7 @@ import java.net.URI;
import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.internal.StubS3Connection; import org.jclouds.aws.s3.internal.StubS3Connection;
import org.jclouds.cloud.ConfiguresCloudConnection;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@ -35,7 +36,7 @@ import com.google.inject.AbstractModule;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@S3ConnectionModule @ConfiguresCloudConnection
public class StubS3ConnectionModule extends AbstractModule { public class StubS3ConnectionModule extends AbstractModule {
protected void configure() { protected void configure() {
bind(S3Connection.class).to(StubS3Connection.class); bind(S3Connection.class).to(StubS3Connection.class);

View File

@ -33,7 +33,7 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.aws.s3.commands.options.CopyObjectOptions; import org.jclouds.aws.s3.commands.options.CopyObjectOptions;
import org.jclouds.aws.s3.commands.options.GetObjectOptions; import org.jclouds.aws.s3.commands.options.GetObjectOptions;
import org.jclouds.aws.s3.commands.options.ListBucketOptions; import org.jclouds.aws.s3.commands.options.ListBucketOptions;
@ -79,8 +79,8 @@ public class JCloudsS3Service extends S3Service {
protected JCloudsS3Service(AWSCredentials awsCredentials, Module... modules) protected JCloudsS3Service(AWSCredentials awsCredentials, Module... modules)
throws S3ServiceException { throws S3ServiceException {
super(awsCredentials); super(awsCredentials);
context = S3ContextFactory.createS3Context(awsCredentials.getAccessKey(), awsCredentials context = S3ContextBuilder.newBuilder(awsCredentials.getAccessKey(),
.getSecretKey(), modules); awsCredentials.getSecretKey()).withModules(modules).buildContext();
connection = context.getConnection(); connection = context.getConnection();
} }
@ -98,17 +98,14 @@ public class JCloudsS3Service extends S3Service {
protected Map copyObjectImpl(String sourceBucketName, String sourceObjectKey, protected Map copyObjectImpl(String sourceBucketName, String sourceObjectKey,
String destinationBucketName, String destinationObjectKey, AccessControlList acl, String destinationBucketName, String destinationObjectKey, AccessControlList acl,
Map destinationMetadata, Calendar ifModifiedSince, Calendar ifUnmodifiedSince, Map destinationMetadata, Calendar ifModifiedSince, Calendar ifUnmodifiedSince,
String[] ifMatchTags, String[] ifNoneMatchTags) throws S3ServiceException String[] ifMatchTags, String[] ifNoneMatchTags) throws S3ServiceException {
{
try { try {
CopyObjectOptions options = Util.convertCopyObjectOptions(acl, CopyObjectOptions options = Util.convertCopyObjectOptions(acl, destinationMetadata,
destinationMetadata, ifModifiedSince, ifUnmodifiedSince, ifModifiedSince, ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags);
ifMatchTags, ifNoneMatchTags); org.jclouds.aws.s3.domain.S3Object.Metadata jcObjectMetadata = connection.copyObject(
org.jclouds.aws.s3.domain.S3Object.Metadata jcObjectMetadata = sourceBucketName, sourceObjectKey, destinationBucketName, destinationObjectKey,
connection.copyObject(sourceBucketName, sourceObjectKey, options).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
destinationBucketName, destinationObjectKey, options)
.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
Map map = new HashMap(); Map map = new HashMap();
// Result fields returned when copy is successful. // Result fields returned when copy is successful.
map.put("Last-Modified", jcObjectMetadata.getLastModified().toDate()); map.put("Last-Modified", jcObjectMetadata.getLastModified().toDate());
@ -129,8 +126,8 @@ public class JCloudsS3Service extends S3Service {
throw new UnsupportedOperationException("Bucket ACL is not yet supported"); throw new UnsupportedOperationException("Bucket ACL is not yet supported");
try { try {
if (connection.putBucketIfNotExists(bucketName) if (connection.putBucketIfNotExists(bucketName).get(requestTimeoutMilliseconds,
.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)) { TimeUnit.MILLISECONDS)) {
// Bucket created. // Bucket created.
} }
} catch (Exception e) { } catch (Exception e) {
@ -176,9 +173,8 @@ public class JCloudsS3Service extends S3Service {
@Override @Override
protected AccessControlList getBucketAclImpl(String bucketName) throws S3ServiceException { protected AccessControlList getBucketAclImpl(String bucketName) throws S3ServiceException {
try { try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getBucketACL(bucketName)
connection.getBucketACL(bucketName) .get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
.get(requestTimeoutMilliseconds,TimeUnit.MILLISECONDS);
return Util.convertAccessControlList(jcACL); return Util.convertAccessControlList(jcACL);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -203,9 +199,8 @@ public class JCloudsS3Service extends S3Service {
protected AccessControlList getObjectAclImpl(String bucketName, String objectKey) protected AccessControlList getObjectAclImpl(String bucketName, String objectKey)
throws S3ServiceException { throws S3ServiceException {
try { try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getObjectACL(bucketName,
connection.getObjectACL(bucketName, objectKey) objectKey).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
.get(requestTimeoutMilliseconds,TimeUnit.MILLISECONDS);
return Util.convertAccessControlList(jcACL); return Util.convertAccessControlList(jcACL);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -240,8 +235,8 @@ public class JCloudsS3Service extends S3Service {
Calendar ifUnmodifiedSince, String[] ifMatchTags, String[] ifNoneMatchTags, Calendar ifUnmodifiedSince, String[] ifMatchTags, String[] ifNoneMatchTags,
Long byteRangeStart, Long byteRangeEnd) throws S3ServiceException { Long byteRangeStart, Long byteRangeEnd) throws S3ServiceException {
try { try {
GetObjectOptions options = Util.convertGetObjectOptions(ifModifiedSince, GetObjectOptions options = Util.convertGetObjectOptions(ifModifiedSince,
ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags); ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags);
return Util.convertObject(connection.getObject(bucketName, objectKey, options).get()); return Util.convertObject(connection.getObject(bucketName, objectKey, options).get());
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -278,16 +273,16 @@ public class JCloudsS3Service extends S3Service {
protected S3ObjectsChunk listObjectsChunkedImpl(String bucketName, String prefix, protected S3ObjectsChunk listObjectsChunkedImpl(String bucketName, String prefix,
String delimiter, long maxListingLength, String priorLastKey, boolean completeListing) String delimiter, long maxListingLength, String priorLastKey, boolean completeListing)
throws S3ServiceException { throws S3ServiceException {
try { try {
List<S3Object> jsObjects = new ArrayList<S3Object>(); List<S3Object> jsObjects = new ArrayList<S3Object>();
List<String> commonPrefixes = new ArrayList<String>(); List<String> commonPrefixes = new ArrayList<String>();
org.jclouds.aws.s3.domain.S3Bucket jcBucket = null; org.jclouds.aws.s3.domain.S3Bucket jcBucket = null;
do { do {
ListBucketOptions options = Util.convertListObjectOptions(prefix, priorLastKey, ListBucketOptions options = Util.convertListObjectOptions(prefix, priorLastKey,
delimiter, maxListingLength); delimiter, maxListingLength);
jcBucket = connection.listBucket(bucketName, options) jcBucket = connection.listBucket(bucketName, options).get(requestTimeoutMilliseconds,
.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket.getContents()))); jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket.getContents())));
commonPrefixes.addAll(jcBucket.getCommonPrefixes()); commonPrefixes.addAll(jcBucket.getCommonPrefixes());
@ -296,30 +291,28 @@ public class JCloudsS3Service extends S3Service {
} else { } else {
priorLastKey = null; priorLastKey = null;
} }
} while (completeListing && jcBucket.isTruncated()); // Build entire listing if requested } while (completeListing && jcBucket.isTruncated()); // Build entire listing if requested
return new S3ObjectsChunk( return new S3ObjectsChunk(prefix, // Return the supplied prefix, not the one in the S3
prefix, // Return the supplied prefix, not the one in the S3 response. // response.
jcBucket.getDelimiter(), jcBucket.getDelimiter(), (S3Object[]) jsObjects.toArray(new S3Object[jsObjects
(S3Object[]) jsObjects.toArray(new S3Object[jsObjects.size()]), .size()]), (String[]) commonPrefixes.toArray(new String[commonPrefixes
(String[]) commonPrefixes.toArray(new String[commonPrefixes.size()]), .size()]), priorLastKey);
priorLastKey);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException("error listing objects in bucket " + bucketName, e); throw new S3ServiceException("error listing objects in bucket " + bucketName, e);
} }
} }
@Override @Override
protected S3Object[] listObjectsImpl(String bucketName, String prefix, String delimiter, protected S3Object[] listObjectsImpl(String bucketName, String prefix, String delimiter,
long maxListingLength) throws S3ServiceException long maxListingLength) throws S3ServiceException {
{
try { try {
return listObjectsChunked(bucketName, prefix, delimiter, maxListingLength, null, true) return listObjectsChunked(bucketName, prefix, delimiter, maxListingLength, null, true)
.getObjects(); .getObjects();
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException("error listing objects in bucket " + bucketName, e); throw new S3ServiceException("error listing objects in bucket " + bucketName, e);
} }
} }
@ -328,8 +321,8 @@ public class JCloudsS3Service extends S3Service {
throws S3ServiceException { throws S3ServiceException {
try { try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL); org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL);
connection.putBucketACL(bucketName, jcACL) connection.putBucketACL(bucketName, jcACL).get(requestTimeoutMilliseconds,
.get(requestTimeoutMilliseconds,TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException("error putting bucket's ACL: " + bucketName, e); throw new S3ServiceException("error putting bucket's ACL: " + bucketName, e);
@ -341,8 +334,8 @@ public class JCloudsS3Service extends S3Service {
throws S3ServiceException { throws S3ServiceException {
try { try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL); org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL);
connection.putObjectACL(bucketName, objectKey, jcACL) connection.putObjectACL(bucketName, objectKey, jcACL).get(requestTimeoutMilliseconds,
.get(requestTimeoutMilliseconds,TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException("error putting object's ACL", e); throw new S3ServiceException("error putting object's ACL", e);
@ -351,16 +344,16 @@ public class JCloudsS3Service extends S3Service {
@Override @Override
protected S3Object putObjectImpl(String bucketName, S3Object jsObject) throws S3ServiceException { protected S3Object putObjectImpl(String bucketName, S3Object jsObject) throws S3ServiceException {
try { try {
PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl()); PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl());
org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject); org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject);
byte md5[] = connection.putObject(bucketName, jcObject, options) byte md5[] = connection.putObject(bucketName, jcObject, options).get(
.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
jsObject.setMd5Hash(md5); jsObject.setMd5Hash(md5);
return jsObject; return jsObject;
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException("error putting object", e); throw new S3ServiceException("error putting object", e);
} }
} }

View File

@ -61,7 +61,6 @@ import org.jets3t.service.multithread.CreateObjectsEvent;
import org.jets3t.service.multithread.S3ServiceEventAdaptor; import org.jets3t.service.multithread.S3ServiceEventAdaptor;
import org.jets3t.service.multithread.S3ServiceEventListener; import org.jets3t.service.multithread.S3ServiceEventListener;
import org.jets3t.service.multithread.S3ServiceMulti; import org.jets3t.service.multithread.S3ServiceMulti;
import org.jets3t.service.multithread.S3ServiceSimpleMulti;
import org.jets3t.service.security.AWSCredentials; import org.jets3t.service.security.AWSCredentials;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -24,8 +24,8 @@
package org.jclouds.aws.s3.suncloud.config; package org.jclouds.aws.s3.suncloud.config;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule; import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.config.S3ConnectionModule;
import org.jclouds.aws.s3.suncloud.handlers.ParseSunCloudS3ErrorFromXmlContent; import org.jclouds.aws.s3.suncloud.handlers.ParseSunCloudS3ErrorFromXmlContent;
import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.ServerError; import org.jclouds.http.annotation.ServerError;
@ -37,7 +37,7 @@ import com.google.inject.Scopes;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@S3ConnectionModule @ConfiguresCloudConnection
public class SunCloudS3ConnectionModule extends LiveS3ConnectionModule { public class SunCloudS3ConnectionModule extends LiveS3ConnectionModule {
protected void bindErrorHandlers() { protected void bindErrorHandlers() {

View File

@ -24,7 +24,7 @@
package org.jclouds.aws.s3.suncloud; package org.jclouds.aws.s3.suncloud;
import org.jclouds.aws.s3.S3ConnectionLiveTest; import org.jclouds.aws.s3.S3ConnectionLiveTest;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.aws.s3.suncloud.config.SunCloudS3ConnectionModule; import org.jclouds.aws.s3.suncloud.config.SunCloudS3ConnectionModule;
import org.jclouds.aws.s3.suncloud.xml.config.SunCloudS3ParserModule; import org.jclouds.aws.s3.suncloud.xml.config.SunCloudS3ParserModule;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -38,11 +38,11 @@ import org.testng.annotations.Test;
public class SunCloudS3ConnectionLiveTest extends S3ConnectionLiveTest { public class SunCloudS3ConnectionLiveTest extends S3ConnectionLiveTest {
@Override @Override
protected S3ContextFactory buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) { protected S3ContextBuilder buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) {
return S3ContextFactory.createContext(AWSAccessKeyId, AWSSecretAccessKey).withModules( return (S3ContextBuilder) S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey)
new SunCloudS3ConnectionModule(), new SunCloudS3ParserModule()).withHttpAddress( .withModules(new SunCloudS3ConnectionModule(), new SunCloudS3ParserModule())
"object.storage.network.com").withHttpSecure(false).withHttpPort(80); .withHttpAddress("object.storage.network.com").withHttpSecure(false)
.withHttpPort(80);
} }
} }

View File

@ -28,22 +28,11 @@ import org.testng.annotations.Test;
import com.google.inject.Module; import com.google.inject.Module;
@Test(sequential = true, testName = "s3.JCloudsNioPerformanceLiveTest", groups = {"live"}) @Test(sequential = true, testName = "s3.JCloudsNioPerformanceLiveTest", groups = { "live" })
public class JCloudsNioPerformanceLiveTest extends BaseJCloudsPerformance { public class JCloudsNioPerformanceLiveTest extends BaseJCloudsPerformance {
@Override @Override
protected S3ContextFactory buildS3ContextFactory(String AWSAccessKeyId, protected Module createHttpModule() {
String AWSSecretAccessKey) { return new HttpNioConnectionPoolClientModule();
return super.buildS3ContextFactory(AWSAccessKeyId, AWSSecretAccessKey) }
.withPoolMaxConnectionReuse(75)
.withPoolMaxSessionFailures(2)
.withPoolRequestInvokerThreads(1)
.withPoolIoWorkerThreads(2)
.withPoolMaxConnections(12);
}
@Override
protected Module createHttpModule() {
return new HttpNioConnectionPoolClientModule();
}
} }

View File

@ -43,6 +43,11 @@
<module>samples</module> <module>samples</module>
</modules> </modules>
<dependencies> <dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-objectstore-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>jclouds-aws-core</artifactId> <artifactId>jclouds-aws-core</artifactId>

View File

@ -32,7 +32,6 @@ import javax.annotation.Resource;
import org.jclouds.aws.s3.CreateListOwnedBuckets; import org.jclouds.aws.s3.CreateListOwnedBuckets;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.domain.S3Bucket; import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
@ -69,8 +68,7 @@ public class MainApp {
String bucketName = args[2]; String bucketName = args[2];
// Init // Init
context = S3ContextFactory.createS3Context(accesskeyid, secretkey, context = S3ContextFactory.createS3Context(accesskeyid, secretkey);
new LiveS3ConnectionModule());
listMyOwnBuckets = new CreateListOwnedBuckets(context); listMyOwnBuckets = new CreateListOwnedBuckets(context);
try { try {

View File

@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.CreateListOwnedBuckets; import org.jclouds.aws.s3.CreateListOwnedBuckets;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.config.StubS3ConnectionModule; import org.jclouds.aws.s3.config.StubS3ConnectionModule;
import org.jclouds.aws.s3.reference.S3Constants; import org.jclouds.aws.s3.reference.S3Constants;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
@ -61,11 +60,10 @@ public class CreateListOwnedBucketsIntegrationTest {
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey; AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
if ((AWSAccessKeyId != null) && (AWSSecretAccessKey != null)) if ((AWSAccessKeyId != null) && (AWSSecretAccessKey != null))
context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey, context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey);
new LiveS3ConnectionModule());
else else
context = S3ContextFactory.createContext("stub", "stub").withHttpAddress("stub") context = S3ContextFactory.createS3Context("stub", "stub", new StubS3ConnectionModule());
.withModule(new StubS3ConnectionModule()).build();
} }
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })

View File

@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.CreateListOwnedBuckets; import org.jclouds.aws.s3.CreateListOwnedBuckets;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule;
import org.jclouds.aws.s3.reference.S3Constants; import org.jclouds.aws.s3.reference.S3Constants;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
@ -59,8 +58,7 @@ public class CreateListOwnedBucketsLiveTest {
AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId : sysAWSAccessKeyId; AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId : sysAWSAccessKeyId;
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey; AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey, context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey);
new LiveS3ConnectionModule());
} }

View File

@ -29,7 +29,7 @@ import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule; import com.google.inject.servlet.ServletModule;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.aws.s3.reference.S3Constants; import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.gae.config.URLFetchServiceClientModule; import org.jclouds.gae.config.URLFetchServiceClientModule;
import org.jclouds.samples.googleappengine.GetAllBucketsController; import org.jclouds.samples.googleappengine.GetAllBucketsController;
@ -41,56 +41,52 @@ import java.util.Properties;
/** /**
* Setup Logging and create Injector for use in testing S3. * Setup Logging and create Injector for use in testing S3.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class GuiceServletConfig extends GuiceServletContextListener { public class GuiceServletConfig extends GuiceServletContextListener {
@Inject @Inject
S3Context context; S3Context context;
String accessKeyId; String accessKeyId;
String secretAccessKey; String secretAccessKey;
@Override @Override
public void contextInitialized(ServletContextEvent servletContextEvent) { public void contextInitialized(ServletContextEvent servletContextEvent) {
Properties props = loadJCloudsProperties(servletContextEvent); Properties props = loadJCloudsProperties(servletContextEvent);
this.accessKeyId = props this.accessKeyId = props.getProperty(S3Constants.PROPERTY_AWS_ACCESSKEYID);
.getProperty(S3Constants.PROPERTY_AWS_ACCESSKEYID); this.secretAccessKey = props.getProperty(S3Constants.PROPERTY_AWS_SECRETACCESSKEY);
this.secretAccessKey = props super.contextInitialized(servletContextEvent);
.getProperty(S3Constants.PROPERTY_AWS_SECRETACCESSKEY); }
super.contextInitialized(servletContextEvent);
}
private Properties loadJCloudsProperties( private Properties loadJCloudsProperties(ServletContextEvent servletContextEvent) {
ServletContextEvent servletContextEvent) { InputStream input = servletContextEvent.getServletContext().getResourceAsStream(
InputStream input = servletContextEvent.getServletContext() "/WEB-INF/jclouds.properties");
.getResourceAsStream("/WEB-INF/jclouds.properties"); Properties props = new Properties();
Properties props = new Properties(); try {
try { props.load(input);
props.load(input); } catch (IOException e) {
} catch (IOException e) { throw new RuntimeException(e);
throw new RuntimeException(e); } finally {
} finally { IOUtils.closeQuietly(input);
IOUtils.closeQuietly(input); }
} return props;
return props; }
}
@Override @Override
protected Injector getInjector() { protected Injector getInjector() {
return S3ContextFactory.createInjector(accessKeyId, secretAccessKey, return S3ContextBuilder.newBuilder(accessKeyId, secretAccessKey).withHttpSecure(false)
false, new URLFetchServiceClientModule(), .withModules(new URLFetchServiceClientModule(), new ServletModule() {
new ServletModule() { @Override
@Override protected void configureServlets() {
protected void configureServlets() { serve("*.s3").with(GetAllBucketsController.class);
serve("*.s3").with(GetAllBucketsController.class); requestInjection(this);
requestInjection(this); }
} }).buildInjector();
}); }
}
@Override @Override
public void contextDestroyed(ServletContextEvent servletContextEvent) { public void contextDestroyed(ServletContextEvent servletContextEvent) {
context.close(); context.close();
super.contextDestroyed(servletContextEvent); super.contextDestroyed(servletContextEvent);
} }
} }

View File

@ -0,0 +1,50 @@
/**
*
* 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.cloud;
/**
* Represents an authenticated context to the cloud.
*
* <h2>Note</h2> Please issue {@link #close()} when you are finished with this context in order to
* release resources.
*
*
* @author Adrian Cole
*
*/
public interface CloudContext<C> {
/**
* low-level api to the cloud. Threadsafe implementations will return a singleton.
*
* @return a connection to the cloud
*/
C getConnection();
/**
* Closes all connections to Cloud Files.
*/
void close();
}

View File

@ -0,0 +1,249 @@
/**
*
* 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.cloud;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTION_REUSE;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_MAX_SESSION_FAILURES;
import static org.jclouds.command.pool.PoolConstants.PROPERTY_POOL_REQUEST_INVOKER_THREADS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_REDIRECTS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_RETRIES;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_PORT;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_SECURE;
import static org.jclouds.http.HttpConstants.PROPERTY_SAX_DEBUG;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.jclouds.command.ConfiguresResponseParser;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.config.ConfiguresHttpFutureCommandClient;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import org.jclouds.logging.config.LoggingModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.name.Names;
/**
* Creates {@link CloudContext} or {@link Injector} instances based on the most commonly requested
* arguments.
* <p/>
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
* <p/>
* <p/>
* If no <code>Module</code>s are specified, the default {@link JDKLoggingModule logging} and
* {@link JavaUrlHttpFutureCommandClientModule http transports} will be installed.
*
* @author Adrian Cole, Andrew Newdigate
* @see CloudContext
*/
public abstract class CloudContextBuilder<C, X extends CloudContext<C>> {
private static final String DEFAULT_SECURE_HTTP_PORT = "443";
private static final String DEFAULT_NON_SECURE_HTTP_PORT = "80";
protected final Properties properties;
private final List<Module> modules = new ArrayList<Module>(3);
protected CloudContextBuilder(Properties properties) {
this.properties = properties;
}
public CloudContextBuilder<C, X> withHttpAddress(String httpAddress) {
properties.setProperty(PROPERTY_HTTP_ADDRESS, httpAddress);
return this;
}
public CloudContextBuilder<C, X> withSaxDebug() {
properties.setProperty(PROPERTY_SAX_DEBUG, "true");
return this;
}
public CloudContextBuilder<C, X> withHttpMaxRetries(int httpMaxRetries) {
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, Integer.toString(httpMaxRetries));
return this;
}
public CloudContextBuilder<C, X> withHttpMaxRedirects(int httpMaxRedirects) {
properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, Integer.toString(httpMaxRedirects));
return this;
}
public CloudContextBuilder<C, X> withHttpPort(int httpPort) {
properties.setProperty(PROPERTY_HTTP_PORT, Integer.toString(httpPort));
return this;
}
public CloudContextBuilder<C, X> withHttpSecure(boolean httpSecure) {
properties.setProperty(PROPERTY_HTTP_SECURE, Boolean.toString(httpSecure));
return this;
}
public CloudContextBuilder<C, X> withPoolMaxConnectionReuse(int poolMaxConnectionReuse) {
properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, Integer
.toString(poolMaxConnectionReuse));
return this;
}
public CloudContextBuilder<C, X> withPoolMaxSessionFailures(int poolMaxSessionFailures) {
properties.setProperty(PROPERTY_POOL_MAX_SESSION_FAILURES, Integer
.toString(poolMaxSessionFailures));
return this;
}
public CloudContextBuilder<C, X> withPoolRequestInvokerThreads(int poolRequestInvokerThreads) {
properties.setProperty(PROPERTY_POOL_REQUEST_INVOKER_THREADS, Integer
.toString(poolRequestInvokerThreads));
return this;
}
public CloudContextBuilder<C, X> withPoolIoWorkerThreads(int poolIoWorkerThreads) {
properties
.setProperty(PROPERTY_POOL_IO_WORKER_THREADS, Integer.toString(poolIoWorkerThreads));
return this;
}
public CloudContextBuilder<C, X> withPoolMaxConnections(int poolMaxConnections) {
properties.setProperty(PROPERTY_POOL_MAX_CONNECTIONS, Integer.toString(poolMaxConnections));
return this;
}
public CloudContextBuilder<C, X> withModule(Module module) {
modules.add(module);
return this;
}
public CloudContextBuilder<C, X> withModules(Module... modules) {
this.modules.addAll(Arrays.asList(modules));
return this;
}
public Injector buildInjector() {
useDefaultPortIfNotPresent(properties);
addLoggingModuleIfNotPresent(modules);
addParserModuleIfNotPresent(modules);
addConnectionModuleIfNotPresent(modules);
addHttpModuleIfNeededAndNotPresent(modules);
modules.add(new AbstractModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), checkNotNull(properties, "properties"));
}
});
addContextModule(modules);
return Guice.createInjector(modules);
}
private void useDefaultPortIfNotPresent(Properties properties) {
/* Use 80 or 443 as the default port if one hasn't been set? */
if (!properties.containsKey(PROPERTY_HTTP_PORT)) {
if (Boolean.parseBoolean(properties.getProperty(PROPERTY_HTTP_SECURE))) {
properties.setProperty(PROPERTY_HTTP_PORT, DEFAULT_SECURE_HTTP_PORT);
} else {
properties.setProperty(PROPERTY_HTTP_PORT, DEFAULT_NON_SECURE_HTTP_PORT);
}
}
}
protected void addLoggingModuleIfNotPresent(final List<Module> modules) {
if (!Iterables.any(modules, Predicates.instanceOf(LoggingModule.class)))
modules.add(new JDKLoggingModule());
}
protected void addHttpModuleIfNeededAndNotPresent(final List<Module> modules) {
if (Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(RequiresHttp.class);
}
}) && (!Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(ConfiguresHttpFutureCommandClient.class);
}
})))
modules.add(new JavaUrlHttpFutureCommandClientModule());
}
protected void addConnectionModuleIfNotPresent(final List<Module> modules) {
if (!Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(ConfiguresCloudConnection.class);
}
})) {
addConnectionModule(modules);
}
}
protected void addParserModuleIfNotPresent(List<Module> modules) {
if (!Iterables.any(modules, new Predicate<Module>() {
public boolean apply(Module input) {
return input.getClass().isAnnotationPresent(ConfiguresResponseParser.class);
}
}))
addParserModule(modules);
}
@VisibleForTesting
public Properties getProperties() {
return properties;
}
public abstract X buildContext();
public abstract void authenticate(String id, String secret);
protected abstract void addParserModule(List<Module> modules);
protected abstract void addContextModule(List<Module> modules);
protected abstract void addConnectionModule(List<Module> modules);
}

View File

@ -21,7 +21,7 @@
* under the License. * under the License.
* ==================================================================== * ====================================================================
*/ */
package org.jclouds.aws.s3.config; package org.jclouds.cloud;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
@ -30,13 +30,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* designates the the module configures a {@link org.jclouds.aws.s3.S3Connection} * designates the module configures a Connection to a cloud.
* *
* @author Adrian Cole * @author Adrian Cole
* *
*/ */
@Retention(RUNTIME) @Retention(RUNTIME)
@Target(TYPE) @Target(TYPE)
public @interface S3ConnectionModule { public @interface ConfiguresCloudConnection {
} }

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.command;
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 a Connection to a cloud.
*
* @author Adrian Cole
*
*/
@Retention(RUNTIME)
@Target(TYPE)
public @interface ConfiguresResponseParser {
}

View File

@ -127,16 +127,16 @@ public class FutureCommand<E, Q extends Request<E>, R, T> implements Future<T> {
} }
public interface ResponseRunnableFuture<R, T> extends Response<R>, Runnable, Future<T> { public interface ResponseRunnableFuture<R, T> extends Response<R>, Runnable, Future<T> {
public void setException(Throwable throwable); void setException(Throwable throwable);
} }
public interface ResponseCallable<R, T> extends Response<R>, Callable<T> { public interface ResponseCallable<R, T> extends Response<R>, Callable<T> {
} }
public interface Response<R> { public interface Response<R> {
public R getResponse(); R getResponse();
public void setResponse(R response); void setResponse(R response);
} }
} }

View File

@ -38,33 +38,30 @@ import com.google.inject.name.Named;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public abstract class FutureCommandConnectionPoolClientModule<C> extends public abstract class FutureCommandConnectionPoolClientModule<C> extends AbstractModule {
AbstractModule {
protected void configure() {
install(new LifeCycleModule());
bind(AtomicInteger.class).toInstance(new AtomicInteger());// max errors
}
@Provides protected void configure() {
// @Singleton per uri... install(new LifeCycleModule());
public abstract BlockingQueue<C> provideAvailablePool( bind(AtomicInteger.class).toInstance(new AtomicInteger());// max errors
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS) int max) }
throws Exception;
/** @Provides
* controls production and destruction of real connections. // @Singleton per uri...
* <p/> public abstract BlockingQueue<C> provideAvailablePool(
* aquire before a new connection is created release after an error has @Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS) int max) throws Exception;
* occurred
* /**
* @param max * controls production and destruction of real connections.
* @throws Exception * <p/>
*/ * aquire before a new connection is created release after an error has occurred
@Provides *
// @Singleton per uri... * @param max
public Semaphore provideTotalConnectionSemaphore( * @throws Exception
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS) int max) */
throws Exception { @Provides
return new Semaphore(max, true); // @Singleton per uri...
} public Semaphore provideTotalConnectionSemaphore(
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS) int max) throws Exception {
return new Semaphore(max, true);
}
} }

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.http;
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 cloud has an HTTP API
*
* @author Adrian Cole
*
*/
@Retention(RUNTIME)
@Target(TYPE)
public @interface RequiresHttp {
}

View File

@ -30,39 +30,29 @@ import org.jclouds.http.HttpException;
import org.jclouds.http.HttpFutureCommand; import org.jclouds.http.HttpFutureCommand;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
import com.google.inject.Inject;
/** /**
* // TODO: Adrian: Document this! * // TODO: Adrian: Document this!
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class ReturnStringIf200 extends public class ReturnStringIf200 extends HttpFutureCommand.ResponseCallable<String> {
HttpFutureCommand.ResponseCallable<String> {
@Inject public String call() throws HttpException {
public ReturnStringIf200() { checkCode();
super(); if (getResponse().getStatusCode() == 200) {
} InputStream entity = getResponse().getContent();
if (entity == null)
public String call() throws HttpException { throw new HttpException("no content");
checkCode(); String toReturn = null;
if (getResponse().getStatusCode() == 200) { try {
InputStream entity = getResponse().getContent(); toReturn = Utils.toStringAndClose(entity);
if (entity == null) } catch (IOException e) {
throw new HttpException("no content"); throw new HttpException(String.format("Couldn't receive response %1$s, entity: %2$s ",
String toReturn = null; getResponse(), toReturn), e);
try { }
toReturn = Utils.toStringAndClose(entity); return toReturn;
} catch (IOException e) { } else {
throw new HttpException(String.format( throw new HttpException(String.format("Unhandled status code - %1$s", getResponse()));
"Couldn't receive response %1$s, entity: %2$s ", }
getResponse(), toReturn), e); }
}
return toReturn;
} else {
throw new HttpException(String.format(
"Unhandled status code - %1$s", getResponse()));
}
}
} }

View File

@ -27,24 +27,19 @@ import org.apache.commons.io.IOUtils;
import org.jclouds.http.HttpException; import org.jclouds.http.HttpException;
import org.jclouds.http.HttpFutureCommand; import org.jclouds.http.HttpFutureCommand;
import com.google.inject.Inject;
/** /**
* Simply returns true when the http response code is in the range 200-299. * Simply returns true when the http response code is in the range 200-299.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class ReturnTrueIf2xx extends public class ReturnTrueIf2xx extends HttpFutureCommand.ResponseCallable<Boolean> {
HttpFutureCommand.ResponseCallable<Boolean> {
@Inject public Boolean call() throws HttpException {
public ReturnTrueIf2xx() { IOUtils.closeQuietly(getResponse().getContent());
super(); int code = getResponse().getStatusCode();
} if (code >= 300 || code < 200) {
throw new IllegalStateException("incorrect code for this operation: " + getResponse());
public Boolean call() throws HttpException { }
checkCode(); return true;
IOUtils.closeQuietly(getResponse().getContent()); }
return true;
}
} }

View File

@ -39,6 +39,6 @@ import org.jclouds.http.HttpFutureCommandClient;
*/ */
@Retention(RUNTIME) @Retention(RUNTIME)
@Target(TYPE) @Target(TYPE)
public @interface HttpFutureCommandClientModule { public @interface ConfiguresHttpFutureCommandClient {
} }

View File

@ -33,7 +33,7 @@ import com.google.inject.AbstractModule;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@HttpFutureCommandClientModule @ConfiguresHttpFutureCommandClient
public class JavaUrlHttpFutureCommandClientModule extends AbstractModule { public class JavaUrlHttpFutureCommandClientModule extends AbstractModule {
@Override @Override

View File

@ -21,15 +21,16 @@
* under the License. * under the License.
* ==================================================================== * ====================================================================
*/ */
package org.jclouds.aws.s3; package org.jclouds.cloud;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Properties;
import org.jclouds.aws.s3.config.LiveS3ConnectionModule; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.config.HttpFutureCommandClientModule; import org.jclouds.http.config.ConfiguresHttpFutureCommandClient;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule; import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import org.jclouds.logging.config.LoggingModule; import org.jclouds.logging.config.LoggingModule;
import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.logging.config.NullLoggingModule;
@ -37,17 +38,18 @@ import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Module; import com.google.inject.Module;
/** /**
* Tests behavior of modules configured in S3ContextFactory * Tests behavior of modules configured in CloudContextBuilder
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit", testName = "s3.S3ContextFactoryTest") @Test(groups = "unit", testName = "s3.CloudContextBuilderTest")
public class S3ContextFactoryTest { public class CloudContextBuilderTest {
@HttpFutureCommandClientModule @ConfiguresHttpFutureCommandClient
static class HttpModule extends AbstractModule { static class HttpModule extends AbstractModule {
@Override @Override
@ -56,12 +58,65 @@ public class S3ContextFactoryTest {
} }
} }
class TestCloudContext implements CloudContext<String> {
public void close() {
}
public String getConnection() {
return "";
}
}
class TestCloudContextBuilder extends CloudContextBuilder<String, TestCloudContext> {
protected TestCloudContextBuilder(Properties properties) {
super(properties);
}
@Override
public TestCloudContext buildContext() {
return new TestCloudContext();
}
@Override
protected void addConnectionModule(List<Module> modules) {
modules.add(new Module() {
public void configure(Binder arg0) {
}
});
}
@Override
protected void addContextModule(List<Module> modules) {
modules.add(new Module() {
public void configure(Binder arg0) {
}
});
}
@Override
protected void addParserModule(List<Module> modules) {
modules.add(new Module() {
public void configure(Binder arg0) {
}
});
}
@Override
public void authenticate(String id, String secret) {
}
}
@Test @Test
public void testAddHttpModuleIfNotPresent() { public void testAddHttpModuleIfNotPresent() {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();
HttpModule module = new HttpModule(); HttpModule module = new HttpModule();
modules.add(module); modules.add(module);
S3ContextFactory.addHttpModuleIfNeededAndNotPresent(modules); new TestCloudContextBuilder(new Properties()).addHttpModuleIfNeededAndNotPresent(modules);
assertEquals(modules.size(), 1); assertEquals(modules.size(), 1);
assertEquals(modules.remove(0), module); assertEquals(modules.remove(0), module);
} }
@ -71,7 +126,7 @@ public class S3ContextFactoryTest {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();
LoggingModule module = new NullLoggingModule(); LoggingModule module = new NullLoggingModule();
modules.add(module); modules.add(module);
S3ContextFactory.addLoggingModuleIfNotPresent(modules); new TestCloudContextBuilder(new Properties()).addLoggingModuleIfNotPresent(modules);
assertEquals(modules.size(), 1); assertEquals(modules.size(), 1);
assertEquals(modules.remove(0), module); assertEquals(modules.remove(0), module);
} }
@ -83,47 +138,58 @@ public class S3ContextFactoryTest {
modules.add(loggingModule); modules.add(loggingModule);
HttpModule httpModule = new HttpModule(); HttpModule httpModule = new HttpModule();
modules.add(httpModule); modules.add(httpModule);
S3ContextFactory.addHttpModuleIfNeededAndNotPresent(modules); TestCloudContextBuilder builder = new TestCloudContextBuilder(new Properties());
S3ContextFactory.addLoggingModuleIfNotPresent(modules); builder.addHttpModuleIfNeededAndNotPresent(modules);
builder.addLoggingModuleIfNotPresent(modules);
assertEquals(modules.size(), 2); assertEquals(modules.size(), 2);
assertEquals(modules.remove(0), loggingModule); assertEquals(modules.remove(0), loggingModule);
assertEquals(modules.remove(0), httpModule); assertEquals(modules.remove(0), httpModule);
} }
@Test @Test
public void testAddBothWhenNotLive() { public void testAddBothWhenDoesntRequireHttp() {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();
S3ContextFactory.addHttpModuleIfNeededAndNotPresent(modules); TestCloudContextBuilder builder = new TestCloudContextBuilder(new Properties());
S3ContextFactory.addLoggingModuleIfNotPresent(modules); builder.addHttpModuleIfNeededAndNotPresent(modules);
builder.addLoggingModuleIfNotPresent(modules);
assertEquals(modules.size(), 1); assertEquals(modules.size(), 1);
assert modules.remove(0) instanceof JDKLoggingModule; assert modules.remove(0) instanceof JDKLoggingModule;
} }
@RequiresHttp
class RequiresHttpModule implements Module {
public void configure(Binder arg0) {
}
}
@Test @Test
public void testAddBothWhenLive() { public void testAddBothWhenLive() {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();
modules.add(new LiveS3ConnectionModule()); modules.add(new RequiresHttpModule());
S3ContextFactory.addHttpModuleIfNeededAndNotPresent(modules); TestCloudContextBuilder builder = new TestCloudContextBuilder(new Properties());
S3ContextFactory.addLoggingModuleIfNotPresent(modules); builder.addHttpModuleIfNeededAndNotPresent(modules);
builder.addLoggingModuleIfNotPresent(modules);
assertEquals(modules.size(), 3); assertEquals(modules.size(), 3);
assert modules.remove(0) instanceof LiveS3ConnectionModule; assert modules.remove(0) instanceof RequiresHttpModule;
assert modules.remove(0) instanceof JavaUrlHttpFutureCommandClientModule; assert modules.remove(0) instanceof JavaUrlHttpFutureCommandClientModule;
assert modules.remove(0) instanceof JDKLoggingModule; assert modules.remove(0) instanceof JDKLoggingModule;
} }
public void testBuilder() { public void testBuilder() {
String awsAccessKeyId = "awsAccessKeyId"; String id = "awsAccessKeyId";
String awsSecretAccessKey = "awsSecretAccessKey"; String secret = "awsSecretAccessKey";
String httpAddress = "httpAddress"; String httpAddress = "httpAddress";
int httpMaxRetries = 9875; int httpMaxRetries = 9875;
int httpPort = 3827; int httpPort = 3827;
boolean httpSecure = false; boolean httpSecure = false;
int poolIoWorkerThreads= 2727; int poolIoWorkerThreads = 2727;
int poolMaxConnectionReuse = 3932; int poolMaxConnectionReuse = 3932;
int poolMaxConnections = 3382; int poolMaxConnections = 3382;
int poolMaxSessionFailures = 857; int poolMaxSessionFailures = 857;
int poolRequestInvokerThreads = 8362; int poolRequestInvokerThreads = 8362;
AbstractModule module1 = new AbstractModule() { AbstractModule module1 = new AbstractModule() {
@Override @Override
@ -131,23 +197,23 @@ public class S3ContextFactoryTest {
} }
}; };
AbstractModule module2 = new AbstractModule() { AbstractModule module2 = new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
} }
}; };
TestCloudContextBuilder builder = new TestCloudContextBuilder(new Properties());
S3ContextFactory factory = S3ContextFactory.createContext(awsAccessKeyId, awsSecretAccessKey); builder.authenticate(id, secret);
factory.withHttpAddress(httpAddress); builder.withHttpAddress(httpAddress);
factory.withHttpMaxRetries(httpMaxRetries); builder.withHttpMaxRetries(httpMaxRetries);
factory.withHttpPort(httpPort); builder.withHttpPort(httpPort);
factory.withHttpSecure(httpSecure); builder.withHttpSecure(httpSecure);
factory.withModule(module1); builder.withModule(module1);
factory.withModules(module2); builder.withModules(module2);
factory.withPoolIoWorkerThreads(poolIoWorkerThreads); builder.withPoolIoWorkerThreads(poolIoWorkerThreads);
factory.withPoolMaxConnectionReuse(poolMaxConnectionReuse); builder.withPoolMaxConnectionReuse(poolMaxConnectionReuse);
factory.withPoolMaxConnections(poolMaxConnections); builder.withPoolMaxConnections(poolMaxConnections);
factory.withPoolMaxSessionFailures(poolMaxSessionFailures); builder.withPoolMaxSessionFailures(poolMaxSessionFailures);
factory.withPoolRequestInvokerThreads(poolRequestInvokerThreads); builder.withPoolRequestInvokerThreads(poolRequestInvokerThreads);
} }
} }

48
objectstore/core/pom.xml Normal 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>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-objectstore-project</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-objectstore-core</artifactId>
<name>jclouds objectstore Components Core</name>
<packaging>jar</packaging>
<description>jclouds Core components to access objectstores</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/objectstore/core</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/objectstore/core</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/objectstore/core</url>
</scm>
</project>

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.objectstore.reference;
/**
* Configuration properties and constants used in ObjectStore connections.
*
* @author Adrian Cole
*/
public interface ObjectStoreConstants {
/**
* longest time a single Map operation can take before throwing an exception.
*/
public static final String PROPERTY_OBJECTMAP_TIMEOUT = "jclouds.objectmap.timeout";
/**
* time to pause before retrying a transient failure
*/
public static final String PROPERTY_OBJECTMAP_RETRY = "jclouds.objectmap.retry";
}

View File

@ -0,0 +1,28 @@
/**
*
* 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.
* ====================================================================
*/
/**
* This package contains properties and reference data used in S3.
* @author Adrian Cole
*/
package org.jclouds.objectstore.reference;

68
objectstore/pom.xml Normal file
View File

@ -0,0 +1,68 @@
<?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-project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-objectstore-project</artifactId>
<packaging>pom</packaging>
<name>jclouds objectstore project</name>
<modules>
<module>core</module>
</modules>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-log4j</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>