mirror of https://github.com/apache/jclouds.git
Issue 69: removed unusable or redundant code wrt http error handling
git-svn-id: http://jclouds.googlecode.com/svn/trunk@1455 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
7e181c92b0
commit
39e96d041f
|
@ -33,15 +33,10 @@ import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
|
||||||
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.http.HttpConstants;
|
import org.jclouds.http.HttpConstants;
|
||||||
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
|
||||||
import org.jclouds.http.HttpRetryHandler;
|
import org.jclouds.http.HttpRetryHandler;
|
||||||
import org.jclouds.http.annotation.ClientErrorHandler;
|
|
||||||
import org.jclouds.http.annotation.RedirectHandler;
|
|
||||||
import org.jclouds.http.annotation.RetryHandler;
|
|
||||||
import org.jclouds.http.annotation.ServerErrorHandler;
|
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
|
@ -73,22 +68,16 @@ public class LiveS3ConnectionModule extends AbstractModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bindResponseHandlers();
|
|
||||||
|
|
||||||
bind(S3Connection.class).to(LiveS3Connection.class).in(Scopes.SINGLETON);
|
bind(S3Connection.class).to(LiveS3Connection.class).in(Scopes.SINGLETON);
|
||||||
bind(HttpRetryHandler.class).annotatedWith(RetryHandler.class).to(
|
bind(HttpRetryHandler.class).to(BackoffLimitedRetryHandler.class).in(Scopes.SINGLETON);
|
||||||
BackoffLimitedRetryHandler.class).in(Scopes.SINGLETON);
|
bindErrorHandler();
|
||||||
requestInjection(this);
|
requestInjection(this);
|
||||||
logger.info("S3 Context = %1$s://%2$s:%3$s", (isSecure ? "https" : "http"), address, port);
|
logger.info("S3 Context = %1$s://%2$s:%3$s", (isSecure ? "https" : "http"), address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void bindResponseHandlers() {
|
protected void bindErrorHandler() {
|
||||||
bind(HttpResponseHandler.class).annotatedWith(RedirectHandler.class).to(
|
bind(HttpErrorHandler.class).to(ParseAWSErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
||||||
CloseContentAndSetExceptionHandler.class).in(Scopes.SINGLETON);
|
|
||||||
bind(HttpResponseHandler.class).annotatedWith(ClientErrorHandler.class).to(
|
|
||||||
ParseAWSErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
|
||||||
bind(HttpResponseHandler.class).annotatedWith(ServerErrorHandler.class).to(
|
|
||||||
ParseAWSErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -35,7 +35,7 @@ import org.jclouds.aws.s3.reference.S3Headers;
|
||||||
import org.jclouds.aws.s3.xml.S3ParserFactory;
|
import org.jclouds.aws.s3.xml.S3ParserFactory;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
@ -47,7 +47,7 @@ import com.google.inject.Inject;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ParseAWSErrorFromXmlContent implements HttpResponseHandler {
|
public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
|
||||||
@Resource
|
@Resource
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,10 @@ import static org.testng.Assert.assertEquals;
|
||||||
import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent;
|
import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent;
|
||||||
import org.jclouds.aws.s3.reference.S3Constants;
|
import org.jclouds.aws.s3.reference.S3Constants;
|
||||||
import org.jclouds.aws.s3.xml.config.S3ParserModule;
|
import org.jclouds.aws.s3.xml.config.S3ParserModule;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpRetryHandler;
|
import org.jclouds.http.HttpRetryHandler;
|
||||||
import org.jclouds.http.annotation.ClientErrorHandler;
|
|
||||||
import org.jclouds.http.annotation.RedirectHandler;
|
|
||||||
import org.jclouds.http.annotation.RetryHandler;
|
|
||||||
import org.jclouds.http.annotation.ServerErrorHandler;
|
|
||||||
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
|
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
|
@ -74,22 +69,10 @@ public class S3ContextModuleTest {
|
||||||
}, new JavaUrlHttpFutureCommandClientModule());
|
}, new JavaUrlHttpFutureCommandClientModule());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ClientErrorHandlerTest {
|
|
||||||
@Inject
|
|
||||||
@ClientErrorHandler
|
|
||||||
HttpResponseHandler errorHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testClientErrorHandler() {
|
|
||||||
ClientErrorHandlerTest error = createInjector().getInstance(ClientErrorHandlerTest.class);
|
|
||||||
assertEquals(error.errorHandler.getClass(), ParseAWSErrorFromXmlContent.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ServerErrorHandlerTest {
|
private static class ServerErrorHandlerTest {
|
||||||
@Inject
|
@Inject
|
||||||
@ServerErrorHandler
|
HttpErrorHandler errorHandler;
|
||||||
HttpResponseHandler errorHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -98,21 +81,9 @@ public class S3ContextModuleTest {
|
||||||
assertEquals(error.errorHandler.getClass(), ParseAWSErrorFromXmlContent.class);
|
assertEquals(error.errorHandler.getClass(), ParseAWSErrorFromXmlContent.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RedirectHandlerTest {
|
|
||||||
@Inject
|
|
||||||
@RedirectHandler
|
|
||||||
HttpResponseHandler errorHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testRedirectHandler() {
|
|
||||||
RedirectHandlerTest error = createInjector().getInstance(RedirectHandlerTest.class);
|
|
||||||
assertEquals(error.errorHandler.getClass(), CloseContentAndSetExceptionHandler.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class RetryHandlerTest {
|
private static class RetryHandlerTest {
|
||||||
@Inject
|
@Inject
|
||||||
@RetryHandler
|
|
||||||
HttpRetryHandler retryHandler;
|
HttpRetryHandler retryHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,7 @@ 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.config.S3ConnectionModule;
|
||||||
import org.jclouds.aws.s3.suncloud.handlers.ParseSunCloudS3ErrorFromXmlContent;
|
import org.jclouds.aws.s3.suncloud.handlers.ParseSunCloudS3ErrorFromXmlContent;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.annotation.ClientErrorHandler;
|
|
||||||
import org.jclouds.http.annotation.RedirectHandler;
|
|
||||||
import org.jclouds.http.annotation.ServerErrorHandler;
|
|
||||||
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
|
||||||
|
|
||||||
import com.google.inject.Scopes;
|
import com.google.inject.Scopes;
|
||||||
|
|
||||||
|
@ -42,13 +38,9 @@ import com.google.inject.Scopes;
|
||||||
@S3ConnectionModule
|
@S3ConnectionModule
|
||||||
public class SunCloudS3ConnectionModule extends LiveS3ConnectionModule {
|
public class SunCloudS3ConnectionModule extends LiveS3ConnectionModule {
|
||||||
|
|
||||||
protected void bindResponseHandlers() {
|
protected void bindErrorHandler() {
|
||||||
bind(HttpResponseHandler.class).annotatedWith(RedirectHandler.class).to(
|
bind(HttpErrorHandler.class).to(ParseSunCloudS3ErrorFromXmlContent.class)
|
||||||
CloseContentAndSetExceptionHandler.class).in(Scopes.SINGLETON);
|
.in(Scopes.SINGLETON);
|
||||||
bind(HttpResponseHandler.class).annotatedWith(ClientErrorHandler.class).to(
|
|
||||||
ParseSunCloudS3ErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
|
||||||
bind(HttpResponseHandler.class).annotatedWith(ServerErrorHandler.class).to(
|
|
||||||
ParseSunCloudS3ErrorFromXmlContent.class).in(Scopes.SINGLETON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@ import org.jclouds.aws.s3.suncloud.domain.SunCloudS3Error;
|
||||||
import org.jclouds.aws.s3.xml.S3ParserFactory;
|
import org.jclouds.aws.s3.xml.S3ParserFactory;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ import com.google.inject.Inject;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ParseSunCloudS3ErrorFromXmlContent implements HttpResponseHandler {
|
public class ParseSunCloudS3ErrorFromXmlContent implements HttpErrorHandler {
|
||||||
@Resource
|
@Resource
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ package org.jclouds.http;
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public interface HttpResponseHandler {
|
public interface HttpErrorHandler {
|
||||||
public static final HttpResponseHandler NOOP = new HttpResponseHandler() {
|
public static final HttpErrorHandler NOOP = new HttpErrorHandler() {
|
||||||
public void handle(HttpFutureCommand<?> command, HttpResponse response) {
|
public void handle(HttpFutureCommand<?> command, HttpResponse response) {
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -32,7 +32,7 @@ package org.jclouds.http;
|
||||||
*/
|
*/
|
||||||
public interface HttpRetryHandler {
|
public interface HttpRetryHandler {
|
||||||
public static final HttpRetryHandler ALWAYS_RETRY = new HttpRetryHandler() {
|
public static final HttpRetryHandler ALWAYS_RETRY = new HttpRetryHandler() {
|
||||||
public boolean retryRequest(HttpFutureCommand<?> command, HttpResponse response)
|
public boolean shouldRetryRequest(HttpFutureCommand<?> command, HttpResponse response)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,7 @@ public interface HttpRetryHandler {
|
||||||
* Return true if the command should be retried. This method should only be
|
* Return true if the command should be retried. This method should only be
|
||||||
* invoked when the response has failed with a HTTP 5xx error indicating a
|
* invoked when the response has failed with a HTTP 5xx error indicating a
|
||||||
* server-side error.
|
* server-side error.
|
||||||
*
|
|
||||||
* @param command
|
|
||||||
* @param response
|
|
||||||
* @return
|
|
||||||
* @throws InterruptedException
|
|
||||||
*/
|
*/
|
||||||
boolean retryRequest(HttpFutureCommand<?> command, HttpResponse response)
|
boolean shouldRetryRequest(HttpFutureCommand<?> command, HttpResponse response)
|
||||||
throws InterruptedException;
|
throws InterruptedException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.annotation;
|
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implies that the object can address {@link org.jclouds.http.HttpResponse}s
|
|
||||||
* that contain status code 4xx.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@BindingAnnotation
|
|
||||||
@Target( { FIELD, PARAMETER, METHOD })
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface ClientErrorHandler {
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.annotation;
|
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implies that the object can address {@link org.jclouds.http.HttpResponse}s that contain status
|
|
||||||
* code 3xx.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@BindingAnnotation
|
|
||||||
@Target( { FIELD, PARAMETER, METHOD })
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface RedirectHandler {
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.annotation;
|
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implies that the object can address {@link org.jclouds.http.HttpRetryHandler}s.
|
|
||||||
*
|
|
||||||
* @author James Murty
|
|
||||||
*/
|
|
||||||
@BindingAnnotation
|
|
||||||
@Target( { FIELD, PARAMETER, METHOD })
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface RetryHandler {
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.annotation;
|
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implies that the object can address {@link org.jclouds.http.HttpResponse}s that contain status
|
|
||||||
* code 5xx.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@BindingAnnotation
|
|
||||||
@Target( { FIELD, PARAMETER, METHOD })
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface ServerErrorHandler {
|
|
||||||
}
|
|
|
@ -67,7 +67,7 @@ public class BackoffLimitedRetryHandler implements HttpRetryHandler {
|
||||||
this.retryCountLimit = retryCountLimit;
|
this.retryCountLimit = retryCountLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean retryRequest(HttpFutureCommand<?> command, HttpResponse response)
|
public boolean shouldRetryRequest(HttpFutureCommand<?> command, HttpResponse response)
|
||||||
throws InterruptedException
|
throws InterruptedException
|
||||||
{
|
{
|
||||||
IOUtils.closeQuietly(response.getContent());
|
IOUtils.closeQuietly(response.getContent());
|
||||||
|
|
|
@ -28,14 +28,14 @@ import java.io.IOException;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class CloseContentAndSetExceptionHandler implements HttpResponseHandler {
|
public class CloseContentAndSetExceptionHandler implements HttpErrorHandler {
|
||||||
|
|
||||||
public void handle(HttpFutureCommand<?> command, HttpResponse response) {
|
public void handle(HttpFutureCommand<?> command, HttpResponse response) {
|
||||||
String content;
|
String content;
|
||||||
|
|
|
@ -23,64 +23,75 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http.internal;
|
package org.jclouds.http.internal;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpFutureCommandClient;
|
import org.jclouds.http.HttpFutureCommandClient;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
|
||||||
import org.jclouds.http.HttpRetryHandler;
|
import org.jclouds.http.HttpRetryHandler;
|
||||||
import org.jclouds.http.annotation.ClientErrorHandler;
|
|
||||||
import org.jclouds.http.annotation.RedirectHandler;
|
|
||||||
import org.jclouds.http.annotation.RetryHandler;
|
|
||||||
import org.jclouds.http.annotation.ServerErrorHandler;
|
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
public abstract class BaseHttpFutureCommandClient implements HttpFutureCommandClient {
|
public abstract class BaseHttpFutureCommandClient<Q> implements HttpFutureCommandClient {
|
||||||
|
|
||||||
protected final URI target;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
protected List<HttpRequestFilter> requestFilters = Collections.emptyList();
|
protected List<HttpRequestFilter> requestFilters = Collections.emptyList();
|
||||||
@RedirectHandler
|
|
||||||
@Inject(optional = true)
|
|
||||||
protected HttpResponseHandler redirectHandler = new CloseContentAndSetExceptionHandler();
|
|
||||||
@ClientErrorHandler
|
|
||||||
@Inject(optional = true)
|
|
||||||
protected HttpResponseHandler clientErrorHandler = new CloseContentAndSetExceptionHandler();
|
|
||||||
@ServerErrorHandler
|
|
||||||
@Inject(optional = true)
|
|
||||||
protected HttpResponseHandler serverErrorHandler = new CloseContentAndSetExceptionHandler();
|
|
||||||
|
|
||||||
@RetryHandler
|
@Inject(optional = true)
|
||||||
|
protected HttpErrorHandler httpErrorHandler = new CloseContentAndSetExceptionHandler();
|
||||||
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
protected HttpRetryHandler httpRetryHandler = new BackoffLimitedRetryHandler(5);
|
protected HttpRetryHandler httpRetryHandler = new BackoffLimitedRetryHandler(5);
|
||||||
|
|
||||||
@Inject
|
public void submit(HttpFutureCommand<?> command) {
|
||||||
public BaseHttpFutureCommandClient(URI target) {
|
HttpRequest request = command.getRequest();
|
||||||
this.target = target;
|
|
||||||
|
Q nativeRequest = null;
|
||||||
|
try {
|
||||||
|
for (HttpRequestFilter filter : requestFilters) {
|
||||||
|
filter.filter(request);
|
||||||
|
}
|
||||||
|
HttpResponse response = null;
|
||||||
|
for (;;) {
|
||||||
|
logger.trace("%1$s - converting request %2$s", request.getEndPoint(), request);
|
||||||
|
nativeRequest = convert(request);
|
||||||
|
response = invoke(nativeRequest);
|
||||||
|
int statusCode = response.getStatusCode();
|
||||||
|
if (statusCode >= 500 && httpRetryHandler.shouldRetryRequest(command, response))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
handleResponse(command, response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
command.setException(e);
|
||||||
|
} finally {
|
||||||
|
cleanup(nativeRequest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract Q convert(HttpRequest request) throws IOException;
|
||||||
|
|
||||||
|
protected abstract HttpResponse invoke(Q nativeRequest) throws IOException;
|
||||||
|
|
||||||
|
protected abstract void cleanup(Q nativeResponse);
|
||||||
|
|
||||||
protected void handleResponse(HttpFutureCommand<?> command, HttpResponse response) {
|
protected void handleResponse(HttpFutureCommand<?> command, HttpResponse response) {
|
||||||
int code = response.getStatusCode();
|
int code = response.getStatusCode();
|
||||||
if (code >= 500) {
|
if (code >= 300) {
|
||||||
serverErrorHandler.handle(command, response);
|
httpErrorHandler.handle(command, response);
|
||||||
} else if (code >= 400 && code < 500) {
|
|
||||||
clientErrorHandler.handle(command, response);
|
|
||||||
} else if (code >= 300 && code < 400) {
|
|
||||||
redirectHandler.handle(command, response);
|
|
||||||
} else {
|
} else {
|
||||||
command.getResponseFuture().setResponse(response);
|
command.getResponseFuture().setResponse(response);
|
||||||
command.getResponseFuture().run();
|
command.getResponseFuture().run();
|
||||||
|
|
|
@ -30,70 +30,36 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.jclouds.http.HttpConstants;
|
import org.jclouds.http.HttpConstants;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
|
||||||
import org.jclouds.http.HttpFutureCommandClient;
|
import org.jclouds.http.HttpFutureCommandClient;
|
||||||
import org.jclouds.http.HttpMethod;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic implementation of a {@link HttpFutureCommandClient}.
|
* Basic implementation of a {@link HttpFutureCommandClient}.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class JavaUrlHttpFutureCommandClient extends BaseHttpFutureCommandClient {
|
public class JavaUrlHttpFutureCommandClient extends BaseHttpFutureCommandClient<HttpURLConnection> {
|
||||||
|
|
||||||
@Inject
|
@Override
|
||||||
public JavaUrlHttpFutureCommandClient(URI target) throws MalformedURLException {
|
protected HttpResponse invoke(HttpURLConnection connection) throws IOException {
|
||||||
super(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void submit(HttpFutureCommand<?> command) {
|
|
||||||
HttpRequest request = command.getRequest();
|
|
||||||
HttpURLConnection connection = null;
|
|
||||||
try {
|
|
||||||
HttpResponse response = null;
|
|
||||||
for (;;) {
|
|
||||||
for (HttpRequestFilter filter : requestFilters) {
|
|
||||||
filter.filter(request);
|
|
||||||
}
|
|
||||||
logger.trace("%1$s - converting request %2$s", target, request);
|
|
||||||
connection = openJavaConnection(request);
|
|
||||||
logger.trace("%1$s - submitting request %2$s", target, connection);
|
|
||||||
response = getResponse(connection);
|
|
||||||
logger.trace("%1$s - received response %2$s", target, response);
|
|
||||||
if (response.getStatusCode() >= 500 && httpRetryHandler.retryRequest(command, response))
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
handleResponse(command, response);
|
|
||||||
} catch (Exception e) {
|
|
||||||
command.setException(e);
|
|
||||||
} finally {
|
|
||||||
// DO NOT disconnect, as it will also close the unconsumed
|
|
||||||
// outputStream from above.
|
|
||||||
if (request.getMethod().equals(HttpMethod.HEAD))
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HttpResponse getResponse(HttpURLConnection connection) throws IOException {
|
|
||||||
HttpResponse response = new HttpResponse();
|
HttpResponse response = new HttpResponse();
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
logger.trace("%1$s - submitting request %2$s, headers: %3$s", connection.getURL()
|
||||||
|
.getHost(), connection.getURL(), connection.getRequestProperties());
|
||||||
InputStream in;
|
InputStream in;
|
||||||
try {
|
try {
|
||||||
in = connection.getInputStream();
|
in = connection.getInputStream();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
in = connection.getErrorStream();
|
in = connection.getErrorStream();
|
||||||
}
|
}
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
logger.info("%1$s - received response code %2$s, headers: %3$s", connection.getURL()
|
||||||
|
.getHost(), connection.getResponseCode(), connection.getHeaderFields());
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
response.setContent(in);
|
response.setContent(in);
|
||||||
}
|
}
|
||||||
|
@ -106,8 +72,9 @@ public class JavaUrlHttpFutureCommandClient extends BaseHttpFutureCommandClient
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpURLConnection openJavaConnection(HttpRequest request) throws IOException {
|
@Override
|
||||||
URL url = new URL(target.toURL(), request.getUri());
|
protected HttpURLConnection convert(HttpRequest request) throws IOException {
|
||||||
|
URL url = new URL(request.getEndPoint().toURL(), request.getUri());
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
connection.setAllowUserInteraction(false);
|
connection.setAllowUserInteraction(false);
|
||||||
|
@ -143,4 +110,14 @@ public class JavaUrlHttpFutureCommandClient extends BaseHttpFutureCommandClient
|
||||||
}
|
}
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only disconnect if there is no content, as disconnecting will throw away unconsumed content.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void cleanup(HttpURLConnection connection) {
|
||||||
|
if (connection.getContentLength() == 0)
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ import java.util.concurrent.ExecutionException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.jclouds.http.annotation.RetryHandler;
|
|
||||||
import org.jclouds.http.commands.GetString;
|
import org.jclouds.http.commands.GetString;
|
||||||
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
|
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.jclouds.http.commands.CommandFactory;
|
||||||
import org.jclouds.http.commands.config.HttpCommandsModule;
|
import org.jclouds.http.commands.config.HttpCommandsModule;
|
||||||
import org.jclouds.lifecycle.Closer;
|
import org.jclouds.lifecycle.Closer;
|
||||||
import org.jclouds.logging.jdk.config.JDKLoggingModule;
|
import org.jclouds.logging.jdk.config.JDKLoggingModule;
|
||||||
|
import org.jclouds.util.Utils;
|
||||||
import org.mortbay.jetty.Handler;
|
import org.mortbay.jetty.Handler;
|
||||||
import org.mortbay.jetty.Request;
|
import org.mortbay.jetty.Request;
|
||||||
import org.mortbay.jetty.Server;
|
import org.mortbay.jetty.Server;
|
||||||
|
@ -98,9 +99,17 @@ public abstract class BaseJettyTest {
|
||||||
Handler server2Handler = new AbstractHandler() {
|
Handler server2Handler = new AbstractHandler() {
|
||||||
public void handle(String target, HttpServletRequest request,
|
public void handle(String target, HttpServletRequest request,
|
||||||
HttpServletResponse response, int dispatch) throws IOException, ServletException {
|
HttpServletResponse response, int dispatch) throws IOException, ServletException {
|
||||||
response.setContentType("text/xml");
|
if (request.getMethod().equals("PUT")) {
|
||||||
response.setStatus(HttpServletResponse.SC_OK);
|
if (request.getContentLength() > 0) {
|
||||||
response.getWriter().println(XML2);
|
Utils.toStringAndClose(request.getInputStream());
|
||||||
|
} else {
|
||||||
|
response.sendError(500, "no content");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response.setContentType("text/xml");
|
||||||
|
response.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
response.getWriter().println(XML2);
|
||||||
|
}
|
||||||
((Request) request).setHandled(true);
|
((Request) request).setHandled(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,18 +31,17 @@ import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // TODO: Adrian: Document this!
|
* // TODO: Adrian: Document this!
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public class JavaUrlHttpFutureCommandFutureCommandClientTest extends BaseHttpFutureCommandClientTest {
|
public class JavaUrlHttpFutureCommandClientTest extends BaseHttpFutureCommandClientTest {
|
||||||
|
|
||||||
protected Module createClientModule() {
|
|
||||||
return new JavaUrlHttpFutureCommandClientModule();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected Module createClientModule() {
|
||||||
|
return new JavaUrlHttpFutureCommandClientModule();
|
||||||
|
}
|
||||||
|
|
||||||
protected void addConnectionProperties(Properties props) {
|
protected void addConnectionProperties(Properties props) {
|
||||||
//NONE
|
// NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -111,7 +111,7 @@ public class BackoffLimitedRetryHandlerTest {
|
||||||
assertEquals(response.getContent().available(), 1);
|
assertEquals(response.getContent().available(), 1);
|
||||||
assertEquals(response.getContent().read(), 1);
|
assertEquals(response.getContent().read(), 1);
|
||||||
|
|
||||||
handler.retryRequest(command, response);
|
handler.shouldRetryRequest(command, response);
|
||||||
|
|
||||||
assertEquals(response.getContent().available(), 0);
|
assertEquals(response.getContent().available(), 0);
|
||||||
assertEquals(response.getContent().read(), -1);
|
assertEquals(response.getContent().read(), -1);
|
||||||
|
@ -123,13 +123,13 @@ public class BackoffLimitedRetryHandlerTest {
|
||||||
"uri", new ReturnStringIf200());
|
"uri", new ReturnStringIf200());
|
||||||
HttpResponse response = new HttpResponse();
|
HttpResponse response = new HttpResponse();
|
||||||
|
|
||||||
handler.retryRequest(command, response);
|
handler.shouldRetryRequest(command, response);
|
||||||
assertEquals(command.getFailureCount(), 1);
|
assertEquals(command.getFailureCount(), 1);
|
||||||
|
|
||||||
handler.retryRequest(command, response);
|
handler.shouldRetryRequest(command, response);
|
||||||
assertEquals(command.getFailureCount(), 2);
|
assertEquals(command.getFailureCount(), 2);
|
||||||
|
|
||||||
handler.retryRequest(command, response);
|
handler.shouldRetryRequest(command, response);
|
||||||
assertEquals(command.getFailureCount(), 3);
|
assertEquals(command.getFailureCount(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,17 +139,17 @@ public class BackoffLimitedRetryHandlerTest {
|
||||||
"uri", new ReturnStringIf200());
|
"uri", new ReturnStringIf200());
|
||||||
HttpResponse response = new HttpResponse();
|
HttpResponse response = new HttpResponse();
|
||||||
|
|
||||||
assertEquals(handler.retryRequest(command, response), true); // Failure 1
|
assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 1
|
||||||
|
|
||||||
assertEquals(handler.retryRequest(command, response), true); // Failure 2
|
assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 2
|
||||||
|
|
||||||
assertEquals(handler.retryRequest(command, response), true); // Failure 3
|
assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 3
|
||||||
|
|
||||||
assertEquals(handler.retryRequest(command, response), true); // Failure 4
|
assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 4
|
||||||
|
|
||||||
assertEquals(handler.retryRequest(command, response), true); // Failure 5
|
assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 5
|
||||||
|
|
||||||
assertEquals(handler.retryRequest(command, response), false); // Failure 6
|
assertEquals(handler.shouldRetryRequest(command, response), false); // Failure 6
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -37,10 +37,9 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.jclouds.http.HttpConstants;
|
import org.jclouds.http.HttpConstants;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
|
||||||
import org.jclouds.http.HttpFutureCommandClient;
|
import org.jclouds.http.HttpFutureCommandClient;
|
||||||
|
import org.jclouds.http.HttpHeaders;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.internal.BaseHttpFutureCommandClient;
|
import org.jclouds.http.internal.BaseHttpFutureCommandClient;
|
||||||
|
|
||||||
|
@ -58,61 +57,12 @@ import com.google.inject.Inject;
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
|
public class URLFetchServiceClient extends BaseHttpFutureCommandClient<HTTPRequest> {
|
||||||
private final URLFetchService urlFetchService;
|
private final URLFetchService urlFetchService;
|
||||||
private final int port;
|
|
||||||
private final boolean isSecure;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public URLFetchServiceClient(URI target, URLFetchService urlFetchService)
|
public URLFetchServiceClient(URLFetchService urlFetchService) throws MalformedURLException {
|
||||||
throws MalformedURLException {
|
|
||||||
super(target);
|
|
||||||
this.urlFetchService = urlFetchService;
|
this.urlFetchService = urlFetchService;
|
||||||
this.port = target.getPort();
|
|
||||||
this.isSecure = target.getScheme().equals("https");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void submit(HttpFutureCommand<?> command) {
|
|
||||||
HttpRequest request = command.getRequest();
|
|
||||||
|
|
||||||
HTTPResponse gaeResponse = null;
|
|
||||||
try {
|
|
||||||
for (HttpRequestFilter filter : requestFilters) {
|
|
||||||
filter.filter(request);
|
|
||||||
}
|
|
||||||
HttpResponse response = null;
|
|
||||||
for (;;) {
|
|
||||||
logger.trace("%1$s - converting request %2$s", target, request);
|
|
||||||
HTTPRequest gaeRequest = convert(request);
|
|
||||||
if (logger.isTraceEnabled())
|
|
||||||
logger.trace("%1$s - submitting request %2$s, headers: %3$s", target, gaeRequest
|
|
||||||
.getURL(), headersAsString(gaeRequest.getHeaders()));
|
|
||||||
gaeResponse = this.urlFetchService.fetch(gaeRequest);
|
|
||||||
if (logger.isTraceEnabled())
|
|
||||||
logger.info("%1$s - received response code %2$s, headers: %3$s", target, gaeResponse
|
|
||||||
.getResponseCode(), headersAsString(gaeResponse.getHeaders()));
|
|
||||||
response = convert(gaeResponse);
|
|
||||||
int statusCode = response.getStatusCode();
|
|
||||||
if (statusCode >= 500 && httpRetryHandler.retryRequest(command, response))
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
handleResponse(command, response);
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (gaeResponse != null && gaeResponse.getContent() != null) {
|
|
||||||
logger.error(e, "error encountered during the execution: %1$s%n%2$s", gaeResponse,
|
|
||||||
new String(gaeResponse.getContent()));
|
|
||||||
}
|
|
||||||
command.setException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String headersAsString(List<HTTPHeader> headers) {
|
|
||||||
StringBuilder builder = new StringBuilder("");
|
|
||||||
for (HTTPHeader header : headers)
|
|
||||||
builder.append("[").append(header.getName()).append("=").append(header.getValue()).append(
|
|
||||||
"],");
|
|
||||||
return builder.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,7 +92,7 @@ public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
HttpResponse convert(HTTPResponse gaeResponse) {
|
protected HttpResponse convert(HTTPResponse gaeResponse) {
|
||||||
HttpResponse response = new HttpResponse();
|
HttpResponse response = new HttpResponse();
|
||||||
response.setStatusCode(gaeResponse.getResponseCode());
|
response.setStatusCode(gaeResponse.getResponseCode());
|
||||||
for (HTTPHeader header : gaeResponse.getHeaders()) {
|
for (HTTPHeader header : gaeResponse.getHeaders()) {
|
||||||
|
@ -155,17 +105,11 @@ public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
HTTPRequest convert(HttpRequest request) throws IOException {
|
protected HTTPRequest convert(HttpRequest request) throws IOException {
|
||||||
String hostHeader = request.getFirstHeaderOrNull(HttpConstants.HOST);
|
|
||||||
URL url;
|
convertHostHeaderToEndPoint(request);
|
||||||
// As host headers are not supported in GAE/J v1.2.1, we'll change the
|
|
||||||
// hostname of the destination to the same value as the host header
|
URL url = new URL(request.getEndPoint().toURL(), request.getUri());
|
||||||
if (hostHeader != null) {
|
|
||||||
url = new URL(new URL(isSecure ? "https" : "http", hostHeader, port, "/"), request
|
|
||||||
.getUri());
|
|
||||||
} else {
|
|
||||||
url = new URL(target.toURL(), request.getUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
FetchOptions options = disallowTruncate();
|
FetchOptions options = disallowTruncate();
|
||||||
followRedirectsUnlessRequestContainsPayload(request, options);
|
followRedirectsUnlessRequestContainsPayload(request, options);
|
||||||
|
@ -174,11 +118,8 @@ public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
|
||||||
.toString()), options);
|
.toString()), options);
|
||||||
|
|
||||||
for (String header : request.getHeaders().keySet()) {
|
for (String header : request.getHeaders().keySet()) {
|
||||||
// GAE/J v1.2.1 re-writes the host header, so we'll skip it.
|
for (String value : request.getHeaders().get(header)) {
|
||||||
if (!header.equals(HttpConstants.HOST)) {
|
gaeRequest.addHeader(new HTTPHeader(header, value));
|
||||||
for (String value : request.getHeaders().get(header)) {
|
|
||||||
gaeRequest.addHeader(new HTTPHeader(header, value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +132,24 @@ public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
|
||||||
return gaeRequest;
|
return gaeRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As host headers are not supported in GAE/J v1.2.1, we'll change the hostname of the
|
||||||
|
* destination to the same value as the host header
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void convertHostHeaderToEndPoint(HttpRequest request) {
|
||||||
|
|
||||||
|
String hostHeader = request.getFirstHeaderOrNull(HttpConstants.HOST);
|
||||||
|
|
||||||
|
if (hostHeader != null) {
|
||||||
|
request.setEndPoint(URI.create(String.format("%1$s://%2$s:%3$d", request.getEndPoint()
|
||||||
|
.getScheme(), hostHeader, request.getEndPoint().getPort())));
|
||||||
|
request.getHeaders().removeAll(HttpHeaders.HOST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void followRedirectsUnlessRequestContainsPayload(HttpRequest request,
|
private void followRedirectsUnlessRequestContainsPayload(HttpRequest request,
|
||||||
FetchOptions options) {
|
FetchOptions options) {
|
||||||
if (request.getPayload() != null)
|
if (request.getPayload() != null)
|
||||||
|
@ -198,4 +157,32 @@ public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
|
||||||
else
|
else
|
||||||
options.followRedirects();
|
options.followRedirects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nothing to clean up.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void cleanup(HTTPRequest nativeRequest) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpResponse invoke(HTTPRequest request) throws IOException {
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
logger.trace("%1$s - submitting request %2$s, headers: %3$s", request.getURL().getHost(),
|
||||||
|
request.getURL(), headersAsString(request.getHeaders()));
|
||||||
|
HTTPResponse response = urlFetchService.fetch(request);
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
logger.info("%1$s - received response code %2$s, headers: %3$s", request.getURL()
|
||||||
|
.getHost(), response.getResponseCode(), headersAsString(response.getHeaders()));
|
||||||
|
return convert(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
String headersAsString(List<HTTPHeader> headers) {
|
||||||
|
StringBuilder builder = new StringBuilder("");
|
||||||
|
for (HTTPHeader header : headers)
|
||||||
|
builder.append("[").append(header.getName()).append("=").append(header.getValue()).append(
|
||||||
|
"],");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,16 @@ public class URLFetchServiceClientTest {
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
void setupClient() throws MalformedURLException {
|
void setupClient() throws MalformedURLException {
|
||||||
endPoint = URI.create("http://localhost:80");
|
endPoint = URI.create("http://localhost:80");
|
||||||
client = new URLFetchServiceClient(endPoint, createNiceMock(URLFetchService.class));
|
client = new URLFetchServiceClient(createNiceMock(URLFetchService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConvertHostHeaderToEndPoint() {
|
||||||
|
HttpRequest request = new HttpRequest(endPoint, HttpMethod.GET, "foo");
|
||||||
|
request.getHeaders().put(HttpHeaders.HOST, "weird");
|
||||||
|
client.convertHostHeaderToEndPoint(request);
|
||||||
|
assertEquals(request.getEndPoint().getHost(), "weird");
|
||||||
|
assert request.getFirstHeaderOrNull(HttpHeaders.HOST) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -35,14 +35,10 @@ import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.nio.entity.ConsumingNHttpEntity;
|
import org.apache.http.nio.entity.ConsumingNHttpEntity;
|
||||||
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
|
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponseHandler;
|
|
||||||
import org.jclouds.http.HttpRetryHandler;
|
import org.jclouds.http.HttpRetryHandler;
|
||||||
import org.jclouds.http.annotation.ClientErrorHandler;
|
|
||||||
import org.jclouds.http.annotation.RedirectHandler;
|
|
||||||
import org.jclouds.http.annotation.RetryHandler;
|
|
||||||
import org.jclouds.http.annotation.ServerErrorHandler;
|
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
||||||
import org.jclouds.http.httpnio.util.HttpNioUtils;
|
import org.jclouds.http.httpnio.util.HttpNioUtils;
|
||||||
|
@ -52,131 +48,114 @@ import com.google.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // TODO: Adrian: Document this!
|
* // TODO: Adrian: Document this!
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class HttpNioFutureCommandExecutionHandler implements
|
public class HttpNioFutureCommandExecutionHandler implements NHttpRequestExecutionHandler {
|
||||||
NHttpRequestExecutionHandler {
|
private final ExecutorService executor;
|
||||||
private final ExecutorService executor;
|
@Resource
|
||||||
@Resource
|
protected Logger logger = Logger.NULL;
|
||||||
protected Logger logger = Logger.NULL;
|
private final ConsumingNHttpEntityFactory entityFactory;
|
||||||
private final ConsumingNHttpEntityFactory entityFactory;
|
private final BlockingQueue<HttpFutureCommand<?>> commandQueue;
|
||||||
private final BlockingQueue<HttpFutureCommand<?>> commandQueue;
|
|
||||||
|
|
||||||
@RedirectHandler
|
@Inject(optional = true)
|
||||||
@Inject(optional = true)
|
private HttpErrorHandler serverErrorHandler = new CloseContentAndSetExceptionHandler();
|
||||||
private HttpResponseHandler redirectHandler = new CloseContentAndSetExceptionHandler();
|
|
||||||
|
|
||||||
@ClientErrorHandler
|
@Inject(optional = true)
|
||||||
@Inject(optional = true)
|
protected HttpRetryHandler httpRetryHandler = new BackoffLimitedRetryHandler(5);
|
||||||
private HttpResponseHandler clientErrorHandler = new CloseContentAndSetExceptionHandler();
|
|
||||||
|
|
||||||
@ServerErrorHandler
|
public interface ConsumingNHttpEntityFactory {
|
||||||
@Inject(optional = true)
|
public ConsumingNHttpEntity create(HttpEntity httpEntity);
|
||||||
private HttpResponseHandler serverErrorHandler = new CloseContentAndSetExceptionHandler();
|
}
|
||||||
|
|
||||||
@RetryHandler
|
@Inject
|
||||||
@Inject(optional = true)
|
public HttpNioFutureCommandExecutionHandler(ConsumingNHttpEntityFactory entityFactory,
|
||||||
protected HttpRetryHandler httpRetryHandler = new BackoffLimitedRetryHandler(5);
|
ExecutorService executor, BlockingQueue<HttpFutureCommand<?>> commandQueue) {
|
||||||
|
this.executor = executor;
|
||||||
|
this.entityFactory = entityFactory;
|
||||||
|
this.commandQueue = commandQueue;
|
||||||
|
}
|
||||||
|
|
||||||
public interface ConsumingNHttpEntityFactory {
|
public void initalizeContext(HttpContext context, Object attachment) {
|
||||||
public ConsumingNHttpEntity create(HttpEntity httpEntity);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
public HttpEntityEnclosingRequest submitRequest(HttpContext context) {
|
||||||
public HttpNioFutureCommandExecutionHandler(
|
HttpFutureCommand<?> command = (HttpFutureCommand<?>) context.removeAttribute("command");
|
||||||
ConsumingNHttpEntityFactory entityFactory,
|
if (command != null) {
|
||||||
ExecutorService executor,
|
HttpRequest object = command.getRequest();
|
||||||
BlockingQueue<HttpFutureCommand<?>> commandQueue) {
|
return HttpNioUtils.convertToApacheRequest(object);
|
||||||
this.executor = executor;
|
}
|
||||||
this.entityFactory = entityFactory;
|
return null;
|
||||||
this.commandQueue = commandQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initalizeContext(HttpContext context, Object attachment) {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public HttpEntityEnclosingRequest submitRequest(HttpContext context) {
|
public ConsumingNHttpEntity responseEntity(HttpResponse response, HttpContext context)
|
||||||
HttpFutureCommand<?> command = (HttpFutureCommand<?>) context
|
|
||||||
.removeAttribute("command");
|
|
||||||
if (command != null) {
|
|
||||||
HttpRequest object = command.getRequest();
|
|
||||||
return HttpNioUtils.convertToApacheRequest(object);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConsumingNHttpEntity responseEntity(HttpResponse response,
|
|
||||||
HttpContext context) throws IOException {
|
|
||||||
return entityFactory.create(response.getEntity());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleResponse(HttpResponse apacheResponse, HttpContext context)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
HttpNioFutureCommandConnectionHandle handle = (HttpNioFutureCommandConnectionHandle) context
|
return entityFactory.create(response.getEntity());
|
||||||
.removeAttribute("command-handle");
|
}
|
||||||
if (handle != null) {
|
|
||||||
try {
|
|
||||||
HttpFutureCommand<?> command = handle.getCommand();
|
|
||||||
org.jclouds.http.HttpResponse response = HttpNioUtils
|
|
||||||
.convertToJavaCloudsResponse(apacheResponse);
|
|
||||||
|
|
||||||
int code = response.getStatusCode();
|
public void handleResponse(HttpResponse apacheResponse, HttpContext context) throws IOException {
|
||||||
if (code >= 500) {
|
HttpNioFutureCommandConnectionHandle handle = (HttpNioFutureCommandConnectionHandle) context
|
||||||
boolean retryRequest = false;
|
.removeAttribute("command-handle");
|
||||||
try {
|
if (handle != null) {
|
||||||
retryRequest = httpRetryHandler.retryRequest(command, response);
|
try {
|
||||||
} catch (InterruptedException ie) {
|
HttpFutureCommand<?> command = handle.getCommand();
|
||||||
// TODO: Add interrupt exception to command and abort?
|
org.jclouds.http.HttpResponse response = HttpNioUtils
|
||||||
}
|
.convertToJavaCloudsResponse(apacheResponse);
|
||||||
if (retryRequest) {
|
|
||||||
commandQueue.add(command);
|
int code = response.getStatusCode();
|
||||||
} else {
|
if (code >= 500) {
|
||||||
this.serverErrorHandler.handle(command, response);
|
boolean retryRequest = false;
|
||||||
}
|
try {
|
||||||
} else if (code >= 400 && code < 500) {
|
retryRequest = httpRetryHandler.shouldRetryRequest(command, response);
|
||||||
this.clientErrorHandler.handle(command, response);
|
} catch (InterruptedException ie) {
|
||||||
} else if (code >= 300 && code < 400) {
|
// TODO: Add interrupt exception to command and abort?
|
||||||
this.redirectHandler.handle(command, response);
|
}
|
||||||
} else {
|
if (retryRequest) {
|
||||||
processResponse(response, command);
|
commandQueue.add(command);
|
||||||
}
|
} else {
|
||||||
} finally {
|
serverErrorHandler.handle(command, response);
|
||||||
releaseConnectionToPool(handle);
|
}
|
||||||
|
} else if (code >= 400 && code < 500) {
|
||||||
|
serverErrorHandler.handle(command, response);
|
||||||
|
} else if (code >= 300 && code < 400) {
|
||||||
|
serverErrorHandler.handle(command, response);
|
||||||
|
} else {
|
||||||
|
processResponse(response, command);
|
||||||
}
|
}
|
||||||
} else {
|
} finally {
|
||||||
throw new IllegalStateException(String.format(
|
releaseConnectionToPool(handle);
|
||||||
"No command-handle associated with command %1$s", context));
|
}
|
||||||
}
|
} else {
|
||||||
}
|
throw new IllegalStateException(String.format(
|
||||||
|
"No command-handle associated with command %1$s", context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void releaseConnectionToPool(
|
protected void releaseConnectionToPool(HttpNioFutureCommandConnectionHandle handle) {
|
||||||
HttpNioFutureCommandConnectionHandle handle) {
|
try {
|
||||||
try {
|
handle.release();
|
||||||
handle.release();
|
} catch (InterruptedException e) {
|
||||||
} catch (InterruptedException e) {
|
logger.error(e, "Interrupted releasing handle %1$s", handle);
|
||||||
logger.error(e, "Interrupted releasing handle %1$s", handle);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected void processResponse(org.jclouds.http.HttpResponse response,
|
protected void processResponse(org.jclouds.http.HttpResponse response,
|
||||||
HttpFutureCommand<?> command) throws IOException {
|
HttpFutureCommand<?> command) throws IOException {
|
||||||
command.getResponseFuture().setResponse(response);
|
command.getResponseFuture().setResponse(response);
|
||||||
logger.trace("submitting response task %1$s", command
|
logger.trace("submitting response task %1$s", command.getResponseFuture());
|
||||||
.getResponseFuture());
|
executor.submit(command.getResponseFuture());
|
||||||
executor.submit(command.getResponseFuture());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void finalizeContext(HttpContext context) {
|
public void finalizeContext(HttpContext context) {
|
||||||
HttpNioFutureCommandConnectionHandle handle = (HttpNioFutureCommandConnectionHandle) context
|
HttpNioFutureCommandConnectionHandle handle = (HttpNioFutureCommandConnectionHandle) context
|
||||||
.removeAttribute("command-handle");
|
.removeAttribute("command-handle");
|
||||||
if (handle != null) {
|
if (handle != null) {
|
||||||
try {
|
try {
|
||||||
handle.cancel();
|
handle.cancel();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e, "Error cancelling handle %1$s", handle);
|
logger.error(e, "Error cancelling handle %1$s", handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue