added path to redirect options

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2315 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-11-23 08:09:59 +00:00
parent 2cd5bfa7cd
commit 0b94f0ef12
14 changed files with 206 additions and 65 deletions

View File

@ -57,7 +57,7 @@ public class AWSRedirectionRetryHandler extends RedirectionRetryHandler {
&& (response.getStatusCode() == 301 || response.getStatusCode() == 307)) {
byte[] content = Utils.closeClientButKeepContentStream(response);
if (command.getRequest().getMethod() == HttpMethod.HEAD) {
command.setMethod(HttpMethod.GET);
command.redirectAsGet();
return true;
} else {
command.incrementRedirectCount();
@ -71,7 +71,7 @@ public class AWSRedirectionRetryHandler extends RedirectionRetryHandler {
// http://developer.amazonwebservices.com/connect/thread.jspa?messageID=72287&#72287
return backoffHandler.shouldRetryRequest(command, response);
} else {
command.setHostAndPort(host, command.getRequest().getEndpoint().getPort());
command.redirect(host, command.getRequest().getEndpoint().getPort());
}
return true;
} else {

View File

@ -68,7 +68,7 @@ public class S3UtilsTest {
@Override
protected void configure() {
bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool());
// bind(new TypeLiteral<ClearListStrategy<S3Object>>(){}).to
// bind(new TypeLiteral<ClearListStrategy<S3Object>>(){}).to
bindConstant().annotatedWith(
Jsr330.named(S3Constants.PROPERTY_AWS_ACCESSKEYID)).to("user");
bindConstant().annotatedWith(
@ -99,12 +99,10 @@ public class S3UtilsTest {
return false;
}
public HttpRequest setHostAndPort(String host, int port) {
return null;
public void redirect(String host, int port) {
}
public HttpRequest setMethod(String method) {
return null;
public void redirectAsGet() {
}
public Exception getException() {
@ -126,6 +124,10 @@ public class S3UtilsTest {
public void setException(Exception exception) {
}
@Override
public void redirectPath(String newPath) {
}
};
}

View File

@ -43,10 +43,15 @@ public interface HttpCommand extends EndpointCommand<URI, HttpRequest, HttpRespo
/**
* to allow redirects to work
*/
HttpRequest setHostAndPort(String host, int port);
void redirect(String host, int port);
/**
* to allow redirects to work on methods that were HEAD
*/
HttpRequest setMethod(String method);
void redirectAsGet();
/**
* change the path of the service
*/
void redirectPath(String newPath);
}

View File

@ -30,10 +30,12 @@ import java.util.concurrent.Future;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriBuilder;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
import com.google.inject.internal.Nullable;
@ -52,7 +54,7 @@ public class TransformingHttpCommandImpl<T> implements TransformingHttpCommand<T
private final Function<HttpResponse, T> transformer;
private final Function<Exception, T> exceptionTransformer;
private HttpRequest request;
private GeneratedHttpRequest<?> request;
private volatile int failureCount;
@Resource
@ -63,7 +65,7 @@ public class TransformingHttpCommandImpl<T> implements TransformingHttpCommand<T
@Inject
public TransformingHttpCommandImpl(TransformingHttpCommandExecutorService executorService,
HttpRequest request, Function<HttpResponse, T> transformer,
GeneratedHttpRequest<?> request, Function<HttpResponse, T> transformer,
@Nullable Function<Exception, T> exceptionTransformer) {
this.request = request;
this.executorService = executorService;
@ -91,13 +93,12 @@ public class TransformingHttpCommandImpl<T> implements TransformingHttpCommand<T
* <p />
* This also removes the Host header in order to avoid ssl problems.
*/
public HttpRequest setHostAndPort(String host, int port) {
public void redirect(String host, int port) {
UriBuilder builder = UriBuilder.fromUri(request.getEndpoint());
builder.host(host);
builder.port(port);
request.setEndpoint(builder.build());
request.getHeaders().replaceValues(HttpHeaders.HOST, Collections.singletonList(host));
return request;
}
/**
@ -105,9 +106,8 @@ public class TransformingHttpCommandImpl<T> implements TransformingHttpCommand<T
*
* @param method
*/
public HttpRequest setMethod(String method) {
request.setMethod(method);
return request;
public void redirectAsGet() {
request.setMethod(HttpMethod.GET);
}
public void setException(Exception exception) {
@ -145,4 +145,9 @@ public class TransformingHttpCommandImpl<T> implements TransformingHttpCommand<T
return request;
}
@Override
public void redirectPath(String newPath) {
request.replacePath(newPath);
}
}

View File

@ -70,9 +70,13 @@ public class RedirectionRetryHandler implements HttpRetryHandler {
URI redirectionUrl = UriBuilder.fromUri(hostHeader).build();
if (redirectionUrl.getHost().equals(command.getRequest().getEndpoint().getHost())
&& redirectionUrl.getPort() == command.getRequest().getEndpoint().getPort()) {
return backoffHandler.shouldRetryRequest(command, response);
if (!redirectionUrl.getPath().equals(command.getRequest().getEndpoint().getPath())) {
command.redirectPath(redirectionUrl.getPath());
} else {
command.setHostAndPort(redirectionUrl.getHost(), redirectionUrl.getPort());
return backoffHandler.shouldRetryRequest(command, response);
}
} else {
command.redirect(redirectionUrl.getHost(), redirectionUrl.getPort());
}
return true;
} else {

View File

@ -26,13 +26,13 @@ package org.jclouds.rest.config;
import javax.inject.Inject;
import javax.ws.rs.ext.RuntimeDelegate;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.TransformingHttpCommand;
import org.jclouds.http.TransformingHttpCommandExecutorService;
import org.jclouds.http.TransformingHttpCommandImpl;
import org.jclouds.http.functions.config.ParserModule;
import org.jclouds.rest.internal.AsyncRestClientProxy;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.rest.internal.RuntimeDelegateImpl;
import com.google.common.base.Function;
@ -57,7 +57,7 @@ public class RestModule extends AbstractModule {
private TransformingHttpCommandExecutorService executorService;
@SuppressWarnings("unchecked")
public TransformingHttpCommand<?> create(HttpRequest request,
public TransformingHttpCommand<?> create(GeneratedHttpRequest<?> request,
Function<HttpResponse, ?> transformer, Function<Exception, ?> exceptionTransformer) {
return new TransformingHttpCommandImpl(executorService, request, transformer,
exceptionTransformer);

View File

@ -0,0 +1,16 @@
package org.jclouds.rest.domain;
import org.jclouds.rest.internal.NamedResourceImpl;
import com.google.inject.ImplementedBy;
/**
* Location of a Rest resource
*
* @author Adrian Cole
*
*/
@ImplementedBy(NamedResourceImpl.class)
public interface NamedResource extends NamedLink, Comparable<NamedResource> {
String getId();
}

View File

@ -41,7 +41,6 @@ import javax.inject.Singleton;
import org.jclouds.concurrent.FutureExceptionParser;
import org.jclouds.http.HttpConstants;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.TransformingHttpCommand;
import org.jclouds.logging.Logger;
@ -169,7 +168,7 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
}
public static interface Factory {
public TransformingHttpCommand<?> create(HttpRequest request,
public TransformingHttpCommand<?> create(GeneratedHttpRequest<?> request,
Function<HttpResponse, ?> transformer,
@Nullable Function<Exception, ?> exceptionTransformer);
}

View File

@ -0,0 +1,60 @@
package org.jclouds.rest.internal;
import java.net.URI;
import org.jclouds.rest.domain.NamedResource;
import org.jclouds.rest.domain.internal.NamedLinkImpl;
/**
* Location of a Rest resource
*
* @author Adrian Cole
*
*/
public class NamedResourceImpl extends NamedLinkImpl implements NamedResource {
private final String id;
public NamedResourceImpl(String id, String name, String type, URI location) {
super(name, type, location);
this.id = id;
}
public String getId() {
return id;
}
public int compareTo(NamedResource that) {
return (this == that) ? 0 : this.id.compareTo(that.getId());
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
NamedResourceImpl other = (NamedResourceImpl) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
return "NamedResourceImpl [id=" + id + ", name=" + getName() + ", location="
+ getLocation() + ", type=" + getType() + "]";
}
}

View File

@ -28,8 +28,10 @@ import java.util.Map;
import org.jclouds.rest.domain.Link;
import org.jclouds.rest.domain.NamedLink;
import org.jclouds.rest.domain.NamedResource;
import org.jclouds.rest.domain.internal.LinkImpl;
import org.jclouds.rest.domain.internal.NamedLinkImpl;
import org.jclouds.rest.internal.NamedResourceImpl;
import org.xml.sax.Attributes;
/**
@ -52,4 +54,15 @@ public class Utils {
.getValue(attributes.getIndex("type")), URI.create(attributes.getValue(attributes
.getIndex("href"))));
}
public static NamedResource newNamedResource(Attributes attributes) {
String uri = attributes.getValue(attributes.getIndex("href"));
String id = uri.substring(uri.lastIndexOf('/') + 1);
return new NamedResourceImpl(id, attributes.getValue(attributes.getIndex("name")), attributes
.getValue(attributes.getIndex("type")), URI.create(uri));
}
public static void putNamedResource(Map<String, NamedResource> map, Attributes attributes) {
map.put(attributes.getValue(attributes.getIndex("name")), newNamedResource(attributes));
}
}

View File

@ -452,12 +452,10 @@ public class StubAsyncBlobStore implements AsyncBlobStore {
return false;
}
public HttpRequest setHostAndPort(String host, int port) {
return null;
public void redirect(String host, int port) {
}
public HttpRequest setMethod(String method) {
return null;
public void redirectAsGet() {
}
public Exception getException() {
@ -479,6 +477,10 @@ public class StubAsyncBlobStore implements AsyncBlobStore {
public void setException(Exception exception) {
}
@Override
public void redirectPath(String newPath) {
}
}, response));
}

View File

@ -66,22 +66,42 @@ import com.google.inject.TypeLiteral;
public abstract class BaseJettyTest {
public static final class IntegrationContextBuilder extends
RestContextBuilder<IntegrationTestAsyncClient, IntegrationTestClient> {
private final int testPort;
public IntegrationContextBuilder(Properties properties, int testPort) {
super(new TypeLiteral<IntegrationTestAsyncClient>() {
}, new TypeLiteral<IntegrationTestClient>() {
}, properties);
this.testPort = testPort;
}
@Override
protected void addContextModule(List<Module> modules) {
modules.add(new JettyContextModule(properties, testPort));
}
@Override
protected void addClientModule(List<Module> modules) {
modules.add(new RestIntegrationTestClientModule());
}
}
@ConfiguresRestClient
@RequiresHttp
private final class RestIntegrationTestClientModule extends AbstractModule {
public static class RestIntegrationTestClientModule extends AbstractModule {
@Override
protected void configure() {
}
@SuppressWarnings("unused")
@Provides
@Singleton
public IntegrationTestAsyncClient provideAsyncClient(RestClientFactory factory) {
return factory.create(IntegrationTestAsyncClient.class);
}
@SuppressWarnings("unused")
@Provides
@Singleton
public IntegrationTestClient provideClient(IntegrationTestAsyncClient client)
@ -90,7 +110,7 @@ public abstract class BaseJettyTest {
}
}
private final class JettyContextModule extends AbstractModule {
public static class JettyContextModule extends AbstractModule {
private final Properties properties;
private final int testPort;
@ -106,7 +126,7 @@ public abstract class BaseJettyTest {
URI.create("http://localhost:" + testPort));
}
@SuppressWarnings( { "unchecked", "unused" })
@SuppressWarnings( { "unchecked" })
@Provides
@Singleton
RestContext<IntegrationTestAsyncClient, IntegrationTestClient> provideContext(Closer closer,
@ -214,27 +234,18 @@ public abstract class BaseJettyTest {
final Properties properties = new Properties();
addConnectionProperties(properties);
context = new RestContextBuilder<IntegrationTestAsyncClient, IntegrationTestClient>(
new TypeLiteral<IntegrationTestAsyncClient>() {
}, new TypeLiteral<IntegrationTestClient>() {
}, properties) {
@Override
protected void addContextModule(List<Module> modules) {
modules.add(new JettyContextModule(properties, testPort));
}
@Override
protected void addClientModule(List<Module> modules) {
modules.add(new RestIntegrationTestClientModule());
}
}.withModules(createConnectionModule()).buildContext();
context = newBuilder(testPort, properties, createConnectionModule()).buildContext();
client = context.getApi();
assert client != null;
assert client.newStringBuffer() != null;
}
public static RestContextBuilder<IntegrationTestAsyncClient, IntegrationTestClient> newBuilder(
final int testPort, final Properties properties, Module connectionModule) {
return new IntegrationContextBuilder(properties, testPort).withModules(connectionModule);
}
@AfterTest
public void tearDownJetty() throws Exception {
context.close();

View File

@ -28,24 +28,30 @@ import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jclouds.http.BaseJettyTest;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.IntegrationTestAsyncClient;
import org.jclouds.http.TransformingHttpCommandExecutorServiceImpl;
import org.jclouds.http.TransformingHttpCommandImpl;
import org.jclouds.http.functions.ReturnStringIf200;
import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
import org.jclouds.http.internal.HttpWire;
import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
import org.jclouds.logging.Logger;
import org.jclouds.logging.Logger.LoggerFactory;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
@Test(groups = "unit", testName = "core.BackoffLimitedRetryHandler")
public class BackoffLimitedRetryHandlerTest {
@ -58,32 +64,32 @@ public class BackoffLimitedRetryHandlerTest {
long startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(1, "TEST FAILURE: 1");
long elapsedTime = (System.nanoTime() - startTime)/1000000;
long elapsedTime = (System.nanoTime() - startTime) / 1000000;
assertTrue(elapsedTime >= 50);
assertTrue(elapsedTime < 50 + acceptableDelay);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(2, "TEST FAILURE: 2");
elapsedTime = (System.nanoTime() - startTime)/1000000;
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assertTrue(elapsedTime >= 200);
assertTrue(elapsedTime < 200 + acceptableDelay);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(3, "TEST FAILURE: 3");
elapsedTime = (System.nanoTime() - startTime)/1000000;
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assertTrue(elapsedTime >= 450);
assertTrue(elapsedTime < 450 + acceptableDelay);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(4, "TEST FAILURE: 4");
elapsedTime = (System.nanoTime() - startTime)/1000000;
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assertTrue(elapsedTime >= 800);
assertTrue(elapsedTime < 800 + acceptableDelay);
startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(5, "TEST FAILURE: 5");
elapsedTime = (System.nanoTime() - startTime)/1000000;
assert (elapsedTime >= 1249) : elapsedTime ;
elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert (elapsedTime >= 1249) : elapsedTime;
assertTrue(elapsedTime < 1250 + acceptableDelay);
}
@ -93,8 +99,8 @@ public class BackoffLimitedRetryHandlerTest {
void setupExecutorService() throws Exception {
ExecutorService execService = Executors.newCachedThreadPool();
JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(
execService, new DelegatingRetryHandler(), new DelegatingErrorHandler(), new HttpWire(
Executors.newCachedThreadPool()));
execService, new DelegatingRetryHandler(), new DelegatingErrorHandler(),
new HttpWire(Executors.newCachedThreadPool()));
executorService = new TransformingHttpCommandExecutorServiceImpl(httpService, execService,
new LoggerFactory() {
@ -106,7 +112,8 @@ public class BackoffLimitedRetryHandlerTest {
}
@Test
void testClosesInputStream() throws InterruptedException, IOException {
void testClosesInputStream() throws InterruptedException, IOException, SecurityException,
NoSuchMethodException {
HttpCommand command = createCommand();
HttpResponse response = new HttpResponse();
@ -145,11 +152,26 @@ public class BackoffLimitedRetryHandlerTest {
assertEquals(response.getContent().read(), -1);
}
private final HttpRequest request = new HttpRequest("GET", URI.create("http://localhost"));
private final RestAnnotationProcessor<IntegrationTestAsyncClient> processor = BaseJettyTest
.newBuilder(8100, new Properties(), new AbstractModule() {
private HttpCommand createCommand() {
HttpCommand command = new TransformingHttpCommandImpl<String>(executorService, request,
new ReturnStringIf200(), new Function<Exception, String>() {
@Override
protected void configure() {
}
})
.buildInjector()
.getInstance(
Key
.get(new TypeLiteral<RestAnnotationProcessor<IntegrationTestAsyncClient>>() {
}));
private HttpCommand createCommand() throws SecurityException, NoSuchMethodException {
Method method = IntegrationTestAsyncClient.class.getMethod("download", String.class);
HttpCommand command = new TransformingHttpCommandImpl<String>(executorService, processor
.createRequest(method, "1"), new ReturnStringIf200(),
new Function<Exception, String>() {
public String apply(Exception from) {
return null;
}
@ -158,7 +180,8 @@ public class BackoffLimitedRetryHandlerTest {
}
@Test
void testIncrementsFailureCount() throws InterruptedException, IOException {
void testIncrementsFailureCount() throws InterruptedException, IOException, SecurityException,
NoSuchMethodException {
HttpCommand command = createCommand();
HttpResponse response = new HttpResponse();
@ -173,7 +196,8 @@ public class BackoffLimitedRetryHandlerTest {
}
@Test
void testDisallowsExcessiveRetries() throws InterruptedException, IOException {
void testDisallowsExcessiveRetries() throws InterruptedException, IOException,
SecurityException, NoSuchMethodException {
HttpCommand command = createCommand();
HttpResponse response = new HttpResponse();

View File

@ -158,7 +158,7 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
HttpRequest request = command.getRequest();
String hostHeader = request.getFirstHeaderOrNull(HttpHeaders.HOST);
if (hostHeader != null) {
command.setHostAndPort(hostHeader, request.getEndpoint().getPort());
command.redirect(hostHeader, request.getEndpoint().getPort());
}
}