package gobblin.writer.http;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.testng.annotations.Test;
import com.typesafe.config.Config;
import junit.framework.Assert;
import gobblin.configuration.State;
import gobblin.http.HttpClient;
import gobblin.http.ResponseHandler;
import gobblin.http.ResponseStatus;
import gobblin.http.StatusType;
import gobblin.writer.DataWriter;
import gobblin.writer.WriteCallback;
import gobblin.writer.WriteResponse;
@Test
public class AsyncHttpWriterTest {
/**
* Test successful writes of 4 records
*/
public void testSuccessfulWrites() {
MockHttpClient client = new MockHttpClient();
MockRequestBuilder requestBuilder = new MockRequestBuilder();
MockResponseHandler responseHandler = new MockResponseHandler();
MockAsyncHttpWriterBuilder builder = new MockAsyncHttpWriterBuilder(client, requestBuilder, responseHandler);
TestAsyncHttpWriter asyncHttpWriter = new TestAsyncHttpWriter(builder);
List<MockWriteCallback> callbacks = new ArrayList<>();
for (int i = 0; i < 4; i++) {
callbacks.add(new MockWriteCallback());
}
asyncHttpWriter.write(new Object(), callbacks.get(0));
asyncHttpWriter.write(new Object(), callbacks.get(1));
asyncHttpWriter.write(new Object(), callbacks.get(2));
try {
asyncHttpWriter.flush();
} catch (IOException e) {
Assert.fail("Flush failed");
}
asyncHttpWriter.write(new Object(), callbacks.get(3));
try {
asyncHttpWriter.close();
} catch (IOException e) {
Assert.fail("Close failed");
}
// Assert all successful callbacks are invoked
for (MockWriteCallback callback : callbacks) {
Assert.assertTrue(callback.isSuccess);
}
Assert.assertTrue(client.isCloseCalled);
}
/**
* Test failure triggered by client error. No retries
*/
public void testClientError() {
MockHttpClient client = new MockHttpClient();
MockRequestBuilder requestBuilder = new MockRequestBuilder();
MockResponseHandler responseHandler = new MockResponseHandler();
MockAsyncHttpWriterBuilder builder = new MockAsyncHttpWriterBuilder(client, requestBuilder, responseHandler);
TestAsyncHttpWriter asyncHttpWriter = new TestAsyncHttpWriter(builder);
responseHandler.type = StatusType.CLIENT_ERROR;
MockWriteCallback callback = new MockWriteCallback();
asyncHttpWriter.write(new Object(), callback);
boolean hasAnException = false;
try {
asyncHttpWriter.close();
} catch (Exception e) {
hasAnException = true;
}
Assert.assertTrue(hasAnException);
Assert.assertFalse(callback.isSuccess);
Assert.assertTrue(client.isCloseCalled);
// No retries are done
Assert.assertTrue(client.attempts == 1);
Assert.assertTrue(responseHandler.attempts == 1);
}
/**
* Test max attempts triggered by failing to send. Attempt 3 times
*/
public void testMaxAttempts() {
MockHttpClient client = new MockHttpClient();
MockRequestBuilder requestBuilder = new MockRequestBuilder();
MockResponseHandler responseHandler = new MockResponseHandler();
MockAsyncHttpWriterBuilder builder = new MockAsyncHttpWriterBuilder(client, requestBuilder, responseHandler);
TestAsyncHttpWriter asyncHttpWriter = new TestAsyncHttpWriter(builder);
client.shouldSendSucceed = false;
MockWriteCallback callback = new MockWriteCallback();
asyncHttpWriter.write(new Object(), callback);
boolean hasAnException = false;
try {
asyncHttpWriter.close();
} catch (Exception e) {
hasAnException = true;
}
Assert.assertTrue(hasAnException);
Assert.assertFalse(callback.isSuccess);
Assert.assertTrue(client.isCloseCalled);
Assert.assertTrue(client.attempts == AsyncHttpWriter.DEFAULT_MAX_ATTEMPTS);
Assert.assertTrue(responseHandler.attempts == 0);
}
/**
* Test server error. Attempt 3 times
*/
public void testServerError() {
MockHttpClient client = new MockHttpClient();
MockRequestBuilder requestBuilder = new MockRequestBuilder();
MockResponseHandler responseHandler = new MockResponseHandler();
MockAsyncHttpWriterBuilder builder = new MockAsyncHttpWriterBuilder(client, requestBuilder, responseHandler);
TestAsyncHttpWriter asyncHttpWriter = new TestAsyncHttpWriter(builder);
responseHandler.type = StatusType.SERVER_ERROR;
MockWriteCallback callback = new MockWriteCallback();
asyncHttpWriter.write(new Object(), callback);
boolean hasAnException = false;
try {
asyncHttpWriter.close();
} catch (Exception e) {
hasAnException = true;
}
Assert.assertTrue(hasAnException);
Assert.assertFalse(callback.isSuccess);
Assert.assertTrue(client.isCloseCalled);
Assert.assertTrue(client.attempts == AsyncHttpWriter.DEFAULT_MAX_ATTEMPTS);
Assert.assertTrue(responseHandler.attempts == AsyncHttpWriter.DEFAULT_MAX_ATTEMPTS);
}
class MockHttpClient implements HttpClient<HttpUriRequest, CloseableHttpResponse> {
boolean isCloseCalled = false;
int attempts = 0;
boolean shouldSendSucceed = true;
@Override
public CloseableHttpResponse sendRequest(HttpUriRequest request)
throws IOException {
attempts++;
if (shouldSendSucceed) {
// We won't consume the response anyway
return null;
}
throw new IOException("Send failed");
}
@Override
public void close()
throws IOException {
isCloseCalled = true;
}
}
class MockRequestBuilder implements AsyncWriteRequestBuilder<Object, HttpUriRequest> {
@Override
public AsyncWriteRequest<Object, HttpUriRequest> buildRequest(Queue<BufferedRecord<Object>> buffer) {
BufferedRecord<Object> item = buffer.poll();
AsyncWriteRequest<Object, HttpUriRequest> request = new AsyncWriteRequest<>();
request.markRecord(item, 1);
request.setRawRequest(null);
return request;
}
}
class MockResponseHandler implements ResponseHandler<CloseableHttpResponse> {
volatile StatusType type = StatusType.OK;
int attempts = 0;
@Override
public ResponseStatus handleResponse(CloseableHttpResponse response) {
attempts++;
switch (type) {
case OK:
return new ResponseStatus(StatusType.OK);
case CLIENT_ERROR:
return new ResponseStatus(StatusType.CLIENT_ERROR);
case SERVER_ERROR:
return new ResponseStatus(StatusType.SERVER_ERROR);
}
return null;
}
}
class MockWriteCallback implements WriteCallback<Object> {
boolean isSuccess = false;
@Override
public void onSuccess(WriteResponse<Object> writeResponse) {
isSuccess = true;
}
@Override
public void onFailure(Throwable throwable) {
isSuccess = false;
}
}
class MockAsyncHttpWriterBuilder extends AsyncHttpWriterBuilder<Object, HttpUriRequest, CloseableHttpResponse> {
MockAsyncHttpWriterBuilder(MockHttpClient client, MockRequestBuilder requestBuilder,
MockResponseHandler responseHandler) {
this.client = client;
this.asyncRequestBuilder = requestBuilder;
this.responseHandler = responseHandler;
this.state = new State();
this.queueCapacity = 2;
}
@Override
public DataWriter<Object> build()
throws IOException {
return null;
}
@Override
public AsyncHttpWriterBuilder<Object, HttpUriRequest, CloseableHttpResponse> fromConfig(Config config) {
return null;
}
}
class TestAsyncHttpWriter extends AsyncHttpWriter<Object, HttpUriRequest, CloseableHttpResponse> {
public TestAsyncHttpWriter(AsyncHttpWriterBuilder builder) {
super(builder);
}
}
}