diff --git a/core/src/main/java/org/jclouds/command/FutureCommand.java b/core/src/main/java/org/jclouds/command/FutureCommand.java index 6e3f03053f..ead4b712fe 100644 --- a/core/src/main/java/org/jclouds/command/FutureCommand.java +++ b/core/src/main/java/org/jclouds/command/FutureCommand.java @@ -100,7 +100,7 @@ public class FutureCommand implements Future { @Override public String toString() { - return getClass().getSimpleName()+"{" + "tCallable=" + callable + return getClass().getSimpleName() + "{" + "tCallable=" + callable + '}'; } @@ -138,4 +138,5 @@ public class FutureCommand implements Future { public void setResponse(R response); } + } diff --git a/core/src/main/java/org/jclouds/command/pool/FutureCommandConnectionPool.java b/core/src/main/java/org/jclouds/command/pool/FutureCommandConnectionPool.java index 23c857fa32..38a3c28941 100644 --- a/core/src/main/java/org/jclouds/command/pool/FutureCommandConnectionPool.java +++ b/core/src/main/java/org/jclouds/command/pool/FutureCommandConnectionPool.java @@ -126,14 +126,20 @@ public abstract class FutureCommandConnectionPool handle = getHandleFromConnection(connection); if (handle != null && handle.getCommand() != null) { diff --git a/core/src/main/java/org/jclouds/http/HttpFutureCommand.java b/core/src/main/java/org/jclouds/http/HttpFutureCommand.java index a7b8a53b5d..a20c0649ba 100644 --- a/core/src/main/java/org/jclouds/http/HttpFutureCommand.java +++ b/core/src/main/java/org/jclouds/http/HttpFutureCommand.java @@ -37,6 +37,7 @@ import org.jclouds.logging.Logger; */ public class HttpFutureCommand extends FutureCommand { + public HttpFutureCommand(String method, String uri, ResponseCallable responseCallable) { super(new HttpRequest(checkNotNull(method, "method"), checkNotNull(uri, @@ -73,4 +74,5 @@ public class HttpFutureCommand extends this.response = response; } } + } diff --git a/core/src/main/java/org/jclouds/http/HttpRequest.java b/core/src/main/java/org/jclouds/http/HttpRequest.java index c5acdec794..31ccb9089d 100644 --- a/core/src/main/java/org/jclouds/http/HttpRequest.java +++ b/core/src/main/java/org/jclouds/http/HttpRequest.java @@ -25,8 +25,13 @@ package org.jclouds.http; import static com.google.common.base.Preconditions.checkNotNull; +import java.io.InputStream; import java.util.Collection; +import javax.annotation.Resource; + +import org.jclouds.logging.Logger; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; @@ -44,6 +49,9 @@ public class HttpRequest { String contentType; long contentLength = -1; + @Resource + protected Logger logger = Logger.NULL; + public HttpRequest(String method, String uri) { this.method = checkNotNull(method, "method"); this.uri = checkNotNull(uri, "uri"); @@ -87,6 +95,15 @@ public class HttpRequest { this.headers = headers; } + public boolean isReplayable() { + Object content = getContent(); + if (content != null && content instanceof InputStream) { + logger.warn("%1s: InputStreams are not replayable", toString()); + return false; + } + return true; + } + public Object getContent() { return content; } @@ -110,7 +127,7 @@ public class HttpRequest { public void setContentLength(long contentLength) { this.contentLength = contentLength; } - + public String getFirstHeaderOrNull(String string) { Collection values = headers.get(string); return (values != null && values.size() >= 1) ? values.iterator() diff --git a/core/src/main/java/org/jclouds/http/JavaUrlHttpFutureCommandClient.java b/core/src/main/java/org/jclouds/http/JavaUrlHttpFutureCommandClient.java index 4ba19f9420..269e365ef1 100644 --- a/core/src/main/java/org/jclouds/http/JavaUrlHttpFutureCommandClient.java +++ b/core/src/main/java/org/jclouds/http/JavaUrlHttpFutureCommandClient.java @@ -70,26 +70,47 @@ public class JavaUrlHttpFutureCommandClient implements HttpFutureCommandClient { this.target = target; } - public void submit(HttpFutureCommand operation) { - HttpRequest request = (HttpRequest) operation.getRequest(); + public void submit(HttpFutureCommand command) { + HttpRequest request = (HttpRequest) command.getRequest(); HttpURLConnection connection = null; try { for (HttpRequestFilter filter : getRequestFilters()) { filter.filter(request); } - logger.trace("%1s - converting request %2s", target, request); - connection = openJavaConnection(request); - logger.trace("%1s - submitting request %2s", target, connection); - HttpResponse response = getResponse(connection); - logger.trace("%1s - received response %2s", target, response); - - operation.getResponseFuture().setResponse(response); - operation.getResponseFuture().run(); + HttpResponse response = null; + for (;;) { + try { + logger.trace("%1s - converting request %2s", target, + request); + connection = openJavaConnection(request); + logger.trace("%1s - submitting request %2s", target, + connection); + response = getResponse(connection); + logger.trace("%1s - received response %2s", target, + response); + if (request.isReplayable() + && response.getStatusCode() >= 500) { + logger.info("resubmitting command: %1s", command); + continue; + } + break; + } catch (IOException e) { + if (request.isReplayable() + && e.getMessage().indexOf( + "Server returned HTTP response code: 5") >= 0) { + logger.info("resubmitting command: %1s", command); + continue; + } + throw e; + } + } + command.getResponseFuture().setResponse(response); + command.getResponseFuture().run(); } catch (FileNotFoundException e) { HttpResponse response = new HttpResponse(); response.setStatusCode(404); - operation.getResponseFuture().setResponse(response); - operation.getResponseFuture().run(); + command.getResponseFuture().setResponse(response); + command.getResponseFuture().run(); } catch (Exception e) { if (connection != null) { StringBuilder errors = new StringBuilder(); @@ -108,7 +129,7 @@ public class JavaUrlHttpFutureCommandClient implements HttpFutureCommandClient { } catch (IOException e2) { } } - operation.setException(e); + command.setException(e); } finally { // DO NOT disconnect, as it will also close the unconsumed // outputStream from above. diff --git a/core/src/test/java/org/jclouds/http/BaseHttpFutureCommandClientTest.java b/core/src/test/java/org/jclouds/http/BaseHttpFutureCommandClientTest.java index 60855349b2..8df93aa13f 100644 --- a/core/src/test/java/org/jclouds/http/BaseHttpFutureCommandClientTest.java +++ b/core/src/test/java/org/jclouds/http/BaseHttpFutureCommandClientTest.java @@ -31,6 +31,7 @@ import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -81,6 +82,8 @@ public abstract class BaseHttpFutureCommandClientTest { public void setUpJetty(@Optional("8123") final int testPort) throws Exception { Handler handler = new AbstractHandler() { + private AtomicInteger cycle = new AtomicInteger(0); + public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { @@ -89,12 +92,24 @@ public abstract class BaseHttpFutureCommandClientTest { response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println("test"); } else { + if (failEveryTenRequests(request, response)) + return; response.setContentType("text/xml"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println(XML); } ((Request) request).setHandled(true); } + + private boolean failEveryTenRequests(HttpServletRequest request, + HttpServletResponse response) throws IOException { + if (cycle.incrementAndGet() % 10 == 0) { + response.sendError(500); + ((Request) request).setHandled(true); + return true; + } + return false; + } }; server = new Server(testPort);