OPEN - issue BATCH-569: Add RetryOperationsInterceptor with stateful retry
Refactored ItemWriterRetry* to be less dependent on Item* interfaces. Added StatefulRetryOperationsInterceptor.
This commit is contained in:
@@ -21,8 +21,8 @@ import junit.framework.TestCase;
|
||||
public class ItemRecoveryHandlerTests extends TestCase {
|
||||
|
||||
ItemRecoverer recoverer = new ItemRecoverer() {
|
||||
public boolean recover(Object data, Throwable cause) {
|
||||
return false;
|
||||
public Object recover(Object data, Throwable cause) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -171,12 +171,12 @@ public class JmsItemReaderTests extends TestCase {
|
||||
messageControl.replay();
|
||||
|
||||
itemProvider.setItemType(Message.class);
|
||||
assertEquals(true, itemProvider.hasFailed(message));
|
||||
assertEquals(false, itemProvider.isNew(message));
|
||||
messageControl.verify();
|
||||
}
|
||||
|
||||
public void testIsNewForNonMessage() throws Exception {
|
||||
itemProvider.setItemType(String.class);
|
||||
assertEquals(true, itemProvider.hasFailed("foo"));
|
||||
assertEquals(false, itemProvider.isNew("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,19 +22,19 @@ import org.springframework.batch.item.ItemRecoverer;
|
||||
public class StubItemKeyGeneratorRecoverer implements ItemRecoverer, ItemKeyGenerator {
|
||||
|
||||
/**
|
||||
* Do nothing. Subclassses should override to implement recovery behaviour.
|
||||
* Do nothing and return null. Subclassses should override to implement
|
||||
* recovery behaviour.
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemRecoverer#recover(java.lang.Object,
|
||||
* Throwable)
|
||||
*
|
||||
* @return false if nothing can be done (the default), or true if the item
|
||||
* can now safely be ignored or committed.
|
||||
* @return null.
|
||||
*/
|
||||
public boolean recover(Object item, Throwable cause) {
|
||||
return false;
|
||||
public Object recover(Object item, Throwable cause) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Return the item (assume it is its own key).
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemKeyGenerator#getKey(java.lang.Object)
|
||||
|
||||
@@ -21,16 +21,16 @@ import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.item.AbstractItemWriter;
|
||||
import org.springframework.batch.retry.StubItemKeyGeneratorRecoverer;
|
||||
import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.RetryException;
|
||||
import org.springframework.batch.retry.StubItemKeyGeneratorRecoverer;
|
||||
import org.springframework.batch.retry.TerminatedRetryException;
|
||||
import org.springframework.batch.retry.context.RetryContextSupport;
|
||||
import org.springframework.batch.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
|
||||
public class ItemWriterRetryCallbackTests extends TestCase {
|
||||
public class RecoveryRetryCallbackTests extends TestCase {
|
||||
|
||||
List calls = new ArrayList();
|
||||
|
||||
@@ -40,32 +40,28 @@ public class ItemWriterRetryCallbackTests extends TestCase {
|
||||
|
||||
StubItemKeyGeneratorRecoverer recoverer;
|
||||
|
||||
ItemWriterRetryCallback callback;
|
||||
|
||||
private AbstractItemWriter writer;
|
||||
RecoveryRetryCallback callback;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
template = new RetryTemplate();
|
||||
recoverer = new StubItemKeyGeneratorRecoverer() {
|
||||
public boolean recover(Object data, Throwable cause) {
|
||||
public Object recover(Object data, Throwable cause) {
|
||||
count++;
|
||||
calls.add(data);
|
||||
return true;
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object getKey(Object item) {
|
||||
return "key" + (count++);
|
||||
}
|
||||
};
|
||||
writer = new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
callback = new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
if (data.equals("bar")) {
|
||||
throw new IllegalStateException("Bar detected");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
callback = new ItemWriterRetryCallback("foo", writer);
|
||||
});
|
||||
}
|
||||
|
||||
public void testDoWithRetrySuccessfulFirstTime() throws Exception {
|
||||
@@ -76,12 +72,17 @@ public class ItemWriterRetryCallbackTests extends TestCase {
|
||||
public void testContextInitializedWithItemAndCanRetry() throws Exception {
|
||||
// We can use the policy to intercept the context and do something with
|
||||
// the item...
|
||||
callback = new ItemWriterRetryCallback("bar", writer);
|
||||
callback = new RecoveryRetryCallback("bar", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
throw new IllegalStateException("Detected bar");
|
||||
}
|
||||
});
|
||||
assertEquals(0, calls.size());
|
||||
template.setRetryPolicy(new NeverRetryPolicy() {
|
||||
public boolean canRetry(RetryContext context) {
|
||||
// ...register the failed item
|
||||
calls.add("item(" + count + ")=" + callback.getItem());
|
||||
calls.add("item(" + count + ")=" + callback.getKey());
|
||||
// Do not call the base class method - the attempt counts as
|
||||
// successful now
|
||||
if (count < 2) // only retry once
|
||||
@@ -107,12 +108,17 @@ public class ItemWriterRetryCallbackTests extends TestCase {
|
||||
public void testContextInitializedWithItemAndRegisterThrowable() throws Exception {
|
||||
// We can use the policy to intercept the context and do something with
|
||||
// the item...
|
||||
callback = new ItemWriterRetryCallback("bar", writer);
|
||||
callback = new RecoveryRetryCallback("bar", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
throw new IllegalStateException("Detected bar");
|
||||
}
|
||||
});
|
||||
assertEquals(0, calls.size());
|
||||
template.setRetryPolicy(new NeverRetryPolicy() {
|
||||
public void registerThrowable(RetryContext context, Throwable throwable) throws TerminatedRetryException {
|
||||
// ...register the failed item
|
||||
calls.add("item=" + callback.getItem());
|
||||
calls.add("item=" + callback.getKey());
|
||||
// Call the base class method so that the next attempt is a
|
||||
// failure.
|
||||
super.registerThrowable(context, throwable);
|
||||
@@ -143,8 +149,8 @@ public class ItemWriterRetryCallbackTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testGetKey() throws Exception {
|
||||
callback.setKeyGenerator(recoverer);
|
||||
assertEquals("key0", callback.getKeyGenerator().getKey("foo"));
|
||||
callback = new RecoveryRetryCallback("foo", null, "key0");
|
||||
assertEquals("key0", callback.getKey());
|
||||
}
|
||||
|
||||
public void testRecoverWithoutSession() throws Exception {
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.batch.retry.interceptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.batch.item.ItemRecoverer;
|
||||
import org.springframework.batch.retry.policy.AlwaysRetryPolicy;
|
||||
import org.springframework.batch.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StatefulRetryOperationsInterceptorTests extends TestCase {
|
||||
|
||||
private StatefulRetryOperationsInterceptor interceptor;
|
||||
|
||||
private Service service;
|
||||
|
||||
private Transformer transformer;
|
||||
|
||||
private static int count;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
interceptor = new StatefulRetryOperationsInterceptor();
|
||||
service = (Service) ProxyFactory.getProxy(Service.class, new SingletonTargetSource(new ServiceImpl()));
|
||||
transformer = (Transformer) ProxyFactory.getProxy(Transformer.class, new SingletonTargetSource(new TransformerImpl()));
|
||||
count = 0;
|
||||
}
|
||||
|
||||
public void testDefaultInterceptorSunnyDay() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
public void testDefaultTransformerInterceptorSunnyDay() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
public void testDefaultInterceptorAlwaysRetry() throws Exception {
|
||||
interceptor.setRetryPolicy(new AlwaysRetryPolicy());
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
public void testInterceptorChainWithRetry() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
final List list = new ArrayList();
|
||||
((Advised) service).addAdvice(new MethodInterceptor() {
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
list.add("chain");
|
||||
return invocation.proceed();
|
||||
}
|
||||
});
|
||||
interceptor.setRetryPolicy(new SimpleRetryPolicy(2));
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
service.service("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
public void testTransformerWithSuccessfulRetry() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
interceptor.setRetryPolicy(new SimpleRetryPolicy(2));
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
Collection result = transformer.transform("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
public void testRetryExceptionAfterTooManyAttempts() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
interceptor.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
interceptor.setRecoverer(new ItemRecoverer() {
|
||||
public Object recover(Object data, Throwable cause) {
|
||||
count++;
|
||||
return data;
|
||||
}
|
||||
});
|
||||
service.service("foo");
|
||||
assertEquals(2, count);
|
||||
}
|
||||
|
||||
public void testTransformerRecoveryAfterTooManyAttempts() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
interceptor.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message, message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
interceptor.setRecoverer(new ItemRecoverer() {
|
||||
public Object recover(Object data, Throwable cause) {
|
||||
count++;
|
||||
return Collections.singleton(data);
|
||||
}
|
||||
});
|
||||
Collection result = transformer.transform("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
public static interface Service {
|
||||
void service(String in) throws Exception;
|
||||
}
|
||||
|
||||
public static class ServiceImpl implements Service {
|
||||
|
||||
public void service(String in) throws Exception {
|
||||
count++;
|
||||
if (count < 2) {
|
||||
throw new Exception("Not enough calls: " + count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface Transformer {
|
||||
Collection transform(String in) throws Exception;
|
||||
}
|
||||
|
||||
public static class TransformerImpl implements Transformer {
|
||||
|
||||
public Collection transform(String in) throws Exception {
|
||||
count++;
|
||||
if (count < 2) {
|
||||
throw new Exception("Not enough calls: " + count);
|
||||
}
|
||||
return Collections.singleton(in + ":" + count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -17,56 +17,40 @@
|
||||
package org.springframework.batch.retry.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.item.AbstractItemWriter;
|
||||
import org.springframework.batch.item.FailedItemIdentifier;
|
||||
import org.springframework.batch.item.ItemKeyGenerator;
|
||||
import org.springframework.batch.item.support.ListItemReader;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.context.RepeatContextSupport;
|
||||
import org.springframework.batch.repeat.support.RepeatSynchronizationManager;
|
||||
import org.springframework.batch.retry.RecoveryCallback;
|
||||
import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.RetryException;
|
||||
import org.springframework.batch.retry.StubItemKeyGeneratorRecoverer;
|
||||
import org.springframework.batch.retry.callback.ItemWriterRetryCallback;
|
||||
import org.springframework.batch.retry.context.RetryContextSupport;
|
||||
import org.springframework.batch.retry.callback.RecoveryRetryCallback;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
|
||||
public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
public class RecoveryRetryPolicyTests extends TestCase {
|
||||
|
||||
private ItemWriterRetryPolicy policy = new ItemWriterRetryPolicy();
|
||||
|
||||
private StubItemKeyGeneratorRecoverer recoverer;
|
||||
private RecoveryCallbackRetryPolicy policy = new RecoveryCallbackRetryPolicy();
|
||||
|
||||
private int count = 0;
|
||||
|
||||
private List list = new ArrayList();
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// The list simulates a failed delivery, redelivery of the same message,
|
||||
// then a new message...
|
||||
recoverer = new StubItemKeyGeneratorRecoverer() {
|
||||
public boolean recover(Object data, Throwable cause) {
|
||||
public void testOpenSunnyDay() throws Exception {
|
||||
|
||||
final StringHolder item = new StringHolder("foo");
|
||||
RetryCallback writer = new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return true;
|
||||
list.add(item.string);
|
||||
return item;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void testOpenSunnyDay() throws Exception {
|
||||
RetryContext context = policy.open(new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
count++;
|
||||
list.add(data);
|
||||
}
|
||||
}), null);
|
||||
RetryContext context = policy.open(new RecoveryRetryCallback("foo", writer), null);
|
||||
assertNotNull(context);
|
||||
// we haven't called the processor yet...
|
||||
assertEquals(0, count);
|
||||
@@ -89,9 +73,10 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
public void testCanRetry() {
|
||||
policy.setDelegate(new AlwaysRetryPolicy());
|
||||
|
||||
RetryContext context = policy.open(new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RetryContext context = policy.open(new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
return null;
|
||||
}
|
||||
}), null);
|
||||
assertNotNull(context);
|
||||
@@ -102,10 +87,10 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
|
||||
public void testRegisterThrowable() {
|
||||
policy.setDelegate(new NeverRetryPolicy());
|
||||
RetryContext context = policy.open(new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RetryContext context = policy.open(new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return null;
|
||||
}
|
||||
}), null);
|
||||
assertNotNull(context);
|
||||
@@ -115,10 +100,10 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
|
||||
public void testClose() throws Exception {
|
||||
policy.setDelegate(new NeverRetryPolicy());
|
||||
RetryContext context = policy.open(new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RetryContext context = policy.open(new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return null;
|
||||
}
|
||||
}), null);
|
||||
assertNotNull(context);
|
||||
@@ -131,10 +116,10 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testOpenTwice() throws Exception {
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RecoveryRetryCallback callback = new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
policy.setDelegate(new SimpleRetryPolicy(2));
|
||||
@@ -156,13 +141,21 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testRecover() throws Exception {
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
final String input = "foo";
|
||||
RecoveryRetryCallback callback = new RecoveryRetryCallback(input, new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
callback.setRecoveryCallback(new RecoveryCallback() {
|
||||
public Object recover(Throwable cause) {
|
||||
count++;
|
||||
list.add(input);
|
||||
return input;
|
||||
}
|
||||
});
|
||||
callback.setRecoverer(recoverer);
|
||||
RetryContext context = policy.open(callback, null);
|
||||
assertNotNull(context);
|
||||
assertTrue(policy.canRetry(context));
|
||||
@@ -172,7 +165,7 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
context = policy.open(callback, null);
|
||||
// On the second retry, the recovery path is taken...
|
||||
Object result = policy.handleRetryExhausted(context);
|
||||
assertNotNull(result); // default result is null
|
||||
assertEquals("foo", result); // the recoverer returns the item
|
||||
assertEquals(1, count);
|
||||
assertFalse(policy.canRetry(context));
|
||||
assertEquals("foo", list.get(0));
|
||||
@@ -186,25 +179,22 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
RepeatSynchronizationManager.clear();
|
||||
}
|
||||
|
||||
public void testFailedItemIdentifier() throws Exception {
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
MockFailedItemProvider provider = new MockFailedItemProvider(Collections.EMPTY_LIST);
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback("foo", null);
|
||||
callback.setFailedItemIdentifier(provider);
|
||||
policy.open(callback, null);
|
||||
assertEquals(1, provider.hasFailedCount);
|
||||
}
|
||||
|
||||
public void testRecoverWithTemplate() throws Exception {
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
final String input = "foo";
|
||||
RecoveryRetryCallback callback = new RecoveryRetryCallback(input, new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
throw new RuntimeException("Barf!");
|
||||
}
|
||||
});
|
||||
callback.setRecoverer(recoverer);
|
||||
callback.setRecoveryCallback(new RecoveryCallback() {
|
||||
public Object recover(Throwable cause) {
|
||||
count++;
|
||||
list.add(input);
|
||||
return input;
|
||||
}
|
||||
});
|
||||
RetryTemplate template = new RetryTemplate();
|
||||
template.setRetryPolicy(policy);
|
||||
Object result = null;
|
||||
@@ -217,16 +207,16 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
// On the second retry, the recovery path is taken...
|
||||
result = template.execute(callback);
|
||||
assertNotNull(result); // default result is last item processed
|
||||
assertEquals(input, result); // default result is the item
|
||||
assertEquals(1, count);
|
||||
assertEquals("foo", list.get(0));
|
||||
assertEquals(input, list.get(0));
|
||||
}
|
||||
|
||||
public void testExhaustedClearsHistoryAfterLastAttempt() throws Exception {
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RecoveryRetryCallback callback = new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
@@ -241,7 +231,7 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
assertFalse(policy.canRetry(context));
|
||||
policy.close(context);
|
||||
Object result = policy.handleRetryExhausted(context);
|
||||
assertEquals("foo", result); // default result is last item
|
||||
assertNull(result); // default result is null
|
||||
|
||||
context = policy.open(callback, null);
|
||||
// True after exhausted - the history is reset...
|
||||
@@ -249,12 +239,12 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testRetryCount() throws Exception {
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
RetryContext context = policy.open(new ItemWriterRetryCallback("foo", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RetryContext context = policy.open(new RecoveryRetryCallback("foo", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return null;
|
||||
}
|
||||
}), null);
|
||||
assertNotNull(context);
|
||||
@@ -266,14 +256,14 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testRetryCountPreservedBetweenRetries() throws Exception {
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback("bar", new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
RecoveryRetryCallback callback = new RecoveryRetryCallback("bar", new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
RetryContext context = policy.open(callback, null);
|
||||
assertNotNull(context);
|
||||
@@ -285,29 +275,23 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
assertEquals(2, context.getRetryCount());
|
||||
}
|
||||
|
||||
public void testSetCacheAndHasFailed() throws Exception {
|
||||
MapRetryContextCache cache = new MapRetryContextCache();
|
||||
policy.setRetryContextCache(cache);
|
||||
cache.put("foo", new RetryContextSupport(null));
|
||||
assertTrue(policy.hasFailed(null, "foo"));
|
||||
}
|
||||
|
||||
public void testKeyGeneratorNotConsistentAfterFailure() throws Throwable {
|
||||
|
||||
AbstractItemWriter writer = new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(3));
|
||||
final StringHolder item = new StringHolder("bar");
|
||||
|
||||
RetryCallback writer = new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
// This simulates what happens if someone uses a primary key
|
||||
// for hasCode and equals and then relies on default key
|
||||
// generator
|
||||
((StringHolder) data).string = ((StringHolder) data).string + (count++);
|
||||
((StringHolder) item).string = ((StringHolder) item).string + (count++);
|
||||
throw new RuntimeException("Barf!");
|
||||
}
|
||||
};
|
||||
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(3));
|
||||
StringHolder item = new StringHolder("bar");
|
||||
ItemWriterRetryCallback callback = new ItemWriterRetryCallback(item, writer);
|
||||
RecoveryRetryCallback callback = new RecoveryRetryCallback(item, writer);
|
||||
RetryContext context = policy.open(callback, null);
|
||||
assertNotNull(context);
|
||||
try {
|
||||
@@ -330,20 +314,24 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testCacheCapacity() throws Exception {
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
policy.setRetryContextCache(new MapRetryContextCache(1));
|
||||
AbstractItemWriter writer = new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
final StringHolder item = new StringHolder("foo");
|
||||
|
||||
RetryCallback writer = new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
list.add(item.string);
|
||||
return item;
|
||||
}
|
||||
};
|
||||
RetryContext context;
|
||||
context = policy.open(new ItemWriterRetryCallback("foo", writer), null);
|
||||
context = policy.open(new RecoveryRetryCallback(item, writer), null);
|
||||
policy.registerThrowable(context, null);
|
||||
assertEquals(0, context.getRetryCount());
|
||||
context = policy.open(new ItemWriterRetryCallback("bar", writer), null);
|
||||
item.string = "bar";
|
||||
context = policy.open(new RecoveryRetryCallback(item, writer), null);
|
||||
try {
|
||||
policy.registerThrowable(context, new RuntimeException("foo"));
|
||||
fail("Expected RetryException");
|
||||
@@ -355,48 +343,32 @@ public class ItemWriterRetryPolicyTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testCacheCapacityNotReachedIfRecovered() throws Exception {
|
||||
policy = new ItemWriterRetryPolicy();
|
||||
policy = new RecoveryCallbackRetryPolicy();
|
||||
policy.setDelegate(new SimpleRetryPolicy(1));
|
||||
policy.setRetryContextCache(new MapRetryContextCache(2));
|
||||
AbstractItemWriter writer = new AbstractItemWriter() {
|
||||
public void write(Object data) {
|
||||
final StringHolder item = new StringHolder("foo");
|
||||
|
||||
RetryCallback writer = new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext context) throws Throwable {
|
||||
count++;
|
||||
list.add(data);
|
||||
list.add(item.string);
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
RetryContext context;
|
||||
context = policy.open(new ItemWriterRetryCallback("foo", writer), null);
|
||||
context = policy.open(new RecoveryRetryCallback(item, writer), null);
|
||||
policy.registerThrowable(context, null);
|
||||
assertEquals(0, context.getRetryCount());
|
||||
policy.registerThrowable(context, new RuntimeException("foo"));
|
||||
context = policy.open(new ItemWriterRetryCallback("bar", writer), null);
|
||||
context = policy.open(new RecoveryRetryCallback("bar", writer), null);
|
||||
policy.registerThrowable(context, null);
|
||||
policy.handleRetryExhausted(context);
|
||||
context = policy.open(new ItemWriterRetryCallback("spam", writer), null);
|
||||
context = policy.open(new RecoveryRetryCallback("spam", writer), null);
|
||||
policy.registerThrowable(context, null);
|
||||
assertEquals(0, context.getRetryCount());
|
||||
}
|
||||
|
||||
private static class MockFailedItemProvider extends ListItemReader implements ItemKeyGenerator,
|
||||
FailedItemIdentifier {
|
||||
|
||||
private int hasFailedCount = 0;
|
||||
|
||||
public MockFailedItemProvider(List list) {
|
||||
super(list);
|
||||
}
|
||||
|
||||
public boolean hasFailed(Object item) {
|
||||
hasFailedCount++;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object getKey(Object item) {
|
||||
throw new UnsupportedOperationException("Should not call this method");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class StringHolder {
|
||||
|
||||
private String string;
|
||||
Reference in New Issue
Block a user