Factored Jetty testing code into a separate class, and added a derived test for Backoff Retry implementation. This test needs some work to obtain the system's actual retry limit, rather than assuming a constant limit of 5

git-svn-id: http://jclouds.googlecode.com/svn/trunk@854 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
jamurty 2009-05-25 18:33:27 +00:00
parent 1f64fb1db3
commit 2c4b66a960
3 changed files with 316 additions and 117 deletions

View File

@ -0,0 +1,160 @@
/**
*
* Copyright (C) 2009 James Murty <jamurty@gmail.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 org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jclouds.http.annotation.RetryHandler;
import org.jclouds.http.commands.GetString;
import org.jclouds.http.config.JavaUrlHttpFutureCommandClientModule;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.mortbay.jetty.Request;
import org.testng.annotations.Test;
import com.google.inject.Module;
/**
* Tests the retry behavior of the default {@link RetryHandler} implementation
* {@link BackoffLimitedRetryHandler} to ensure that retries up to the default
* limit succeed.
*
* TODO: Should either explicitly set retry limit or get it from Guice, rather than assuming it's 5.
*
* @author James Murty
*/
@Test(sequential = true)
public class BackoffLimitedRetryJavaIntegrationTest extends BaseJettyTest {
private int beginToFailOnRequestNumber = 0;
private int endFailuresOnRequestNumber = 0;
private int requestCount = 0;
@Override
protected void addConnectionProperties(Properties props) {
}
@Override
protected Module createClientModule() {
return new JavaUrlHttpFutureCommandClientModule();
}
@Override
protected boolean failOnRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException
{
requestCount++;
boolean shouldFail =
requestCount >= beginToFailOnRequestNumber
&& requestCount <= endFailuresOnRequestNumber;
if (shouldFail) {
response.sendError(500);
((Request) request).setHandled(true);
return true;
} else {
return false;
}
}
protected String submitGetRequest() throws InterruptedException, ExecutionException {
GetString get = factory.createGetString("/");
assertNotNull(get);
client.submit(get);
return get.get();
}
@Test
void testNoRetriesSuccessful() throws InterruptedException, ExecutionException {
beginToFailOnRequestNumber = 1;
endFailuresOnRequestNumber = 1;
requestCount = 0;
assertEquals(submitGetRequest().trim(), XML);
}
@Test
void testSingleRetrySuccessful() throws InterruptedException, ExecutionException {
beginToFailOnRequestNumber = 0;
endFailuresOnRequestNumber = 1;
requestCount = 0;
assertEquals(submitGetRequest().trim(), XML);
}
@Test
void testMaximumRetriesSuccessful() throws InterruptedException, ExecutionException {
beginToFailOnRequestNumber = 0;
endFailuresOnRequestNumber = 5;
requestCount = 0;
assertEquals(submitGetRequest().trim(), XML);
}
@Test
void testMaximumRetriesExceeded() throws InterruptedException {
beginToFailOnRequestNumber = 0;
endFailuresOnRequestNumber = 6;
requestCount = 0;
try {
submitGetRequest();
fail("Request should not succeed within " + endFailuresOnRequestNumber + " requests");
} catch (ExecutionException e) {
assertEquals(e.getCause().getClass(), HttpResponseException.class);
HttpResponseException responseException = (HttpResponseException) e.getCause();
assertEquals(responseException.getResponse().getStatusCode(), 500);
}
}
@Test
void testInterleavedSuccessesAndFailures() throws InterruptedException, ExecutionException {
beginToFailOnRequestNumber = 3;
endFailuresOnRequestNumber = 3 + 5; // Force third request to fail completely
requestCount = 0;
assertEquals(submitGetRequest().trim(), XML);
assertEquals(submitGetRequest().trim(), XML);
try {
submitGetRequest();
fail("Third request should not succeed by attempt number " + requestCount);
} catch (ExecutionException e) {
assertEquals(e.getCause().getClass(), HttpResponseException.class);
HttpResponseException responseException = (HttpResponseException) e.getCause();
assertEquals(responseException.getResponse().getStatusCode(), 500);
}
assertEquals(submitGetRequest().trim(), XML);
}
}

View File

@ -23,140 +23,24 @@
*/
package org.jclouds.http;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
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;
import javax.servlet.http.HttpServletResponse;
import org.jclouds.http.commands.CommandFactory;
import org.jclouds.http.commands.GetAndParseSax;
import org.jclouds.http.commands.GetString;
import org.jclouds.http.commands.Head;
import org.jclouds.http.commands.callables.xml.ParseSax;
import org.jclouds.http.commands.config.HttpCommandsModule;
import org.jclouds.lifecycle.Closer;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
/**
* // TODO: Adrian: Document this!
*
* @author Adrian Cole
*/
@Test(threadPoolSize = 10)
public abstract class BaseHttpFutureCommandClientTest {
protected static final String XML = "<foo><bar>whoppers</bar></foo>";
protected Server server = null;
protected CommandFactory factory;
protected HttpFutureCommandClient client;
protected Injector injector;
private Closer closer;
@BeforeTest
@Parameters( { "test-jetty-port" })
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 {
if (request.getHeader("test") != null) {
response.setContentType("text/plain");
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);
server.setHandler(handler);
server.start();
final Properties properties = new Properties();
properties.put(HttpConstants.PROPERTY_HTTP_ADDRESS, "localhost");
properties.put(HttpConstants.PROPERTY_HTTP_PORT, testPort + "");
properties.put(HttpConstants.PROPERTY_HTTP_SECURE, "false");
addConnectionProperties(properties);
final List<HttpRequestFilter> filters = new ArrayList<HttpRequestFilter>(
1);
filters.add(new HttpRequestFilter() {
public void filter(HttpRequest request) throws HttpException {
if (request.getHeaders().containsKey("filterme")) {
request.getHeaders().put("test", "test");
}
}
});
injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), properties);
}
}, new JDKLoggingModule(), new HttpCommandsModule(),
createClientModule(), new AbstractModule() {
@Override
protected void configure() {
bind(new TypeLiteral<List<HttpRequestFilter>>() {
}).toInstance(filters);
}
});
factory = injector.getInstance(Key.get(CommandFactory.class));
client = injector.getInstance(HttpFutureCommandClient.class);
closer = injector.getInstance(Closer.class);
assert client != null;
}
protected abstract void addConnectionProperties(Properties props);
protected abstract Module createClientModule();
@AfterTest
public void tearDownJetty() throws Exception {
closer.close();
server.stop();
}
public abstract class BaseHttpFutureCommandClientTest extends BaseJettyTest {
@Test(invocationCount = 50, timeOut = 3000)
public void testRequestFilter() throws MalformedURLException,

View File

@ -0,0 +1,155 @@
/**
*
* 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
*
* 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 java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jclouds.http.commands.CommandFactory;
import org.jclouds.http.commands.config.HttpCommandsModule;
import org.jclouds.lifecycle.Closer;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
public abstract class BaseJettyTest {
protected static final String XML = "<foo><bar>whoppers</bar></foo>";
protected Server server = null;
protected CommandFactory factory;
protected HttpFutureCommandClient client;
protected Injector injector;
private Closer closer;
private AtomicInteger cycle = new AtomicInteger(0);
@BeforeTest
@Parameters( { "test-jetty-port" })
public void setUpJetty(@Optional("8123") final int testPort)
throws Exception {
Handler handler = new AbstractHandler() {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, int dispatch)
throws IOException, ServletException {
if (request.getHeader("test") != null) {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("test");
} else {
if (failOnRequest(request, response))
return;
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(XML);
}
((Request) request).setHandled(true);
}
};
server = new Server(testPort);
server.setHandler(handler);
server.start();
final Properties properties = new Properties();
properties.put(HttpConstants.PROPERTY_HTTP_ADDRESS, "localhost");
properties.put(HttpConstants.PROPERTY_HTTP_PORT, testPort + "");
properties.put(HttpConstants.PROPERTY_HTTP_SECURE, "false");
addConnectionProperties(properties);
final List<HttpRequestFilter> filters = new ArrayList<HttpRequestFilter>(
1);
filters.add(new HttpRequestFilter() {
public void filter(HttpRequest request) throws HttpException {
if (request.getHeaders().containsKey("filterme")) {
request.getHeaders().put("test", "test");
}
}
});
injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Names.bindProperties(binder(), properties);
}
}, new JDKLoggingModule(), new HttpCommandsModule(),
createClientModule(), new AbstractModule() {
@Override
protected void configure() {
bind(new TypeLiteral<List<HttpRequestFilter>>() {
}).toInstance(filters);
}
});
factory = injector.getInstance(Key.get(CommandFactory.class));
client = injector.getInstance(HttpFutureCommandClient.class);
closer = injector.getInstance(Closer.class);
assert client != null;
}
@AfterTest
public void tearDownJetty() throws Exception {
closer.close();
server.stop();
}
protected abstract void addConnectionProperties(Properties props);
protected abstract Module createClientModule();
/**
* Fails every 10 requests.
*
* @param request
* @param response
* @return
* @throws IOException
*/
protected boolean failOnRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException {
if (cycle.incrementAndGet() % 10 == 0) {
response.sendError(500);
((Request) request).setHandled(true);
return true;
}
return false;
}
}