Skip to content

Commit 5274b55

Browse files
author
trisberg
committed
BATCH-63: added <retry-listeners>
1 parent 2c0f0ca commit 5274b55

File tree

5 files changed

+93
-37
lines changed

5 files changed

+93
-37
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ protected RootBeanDefinition parseProcessTask(Element element, ParserContext par
266266

267267
handleListenersElement(element, bd, parserContext);
268268

269+
handleRetryListenersElement(element, bd, parserContext);
270+
269271
handleStreamsElement(element, bd, parserContext);
270272

271273
bd.setRole(BeanDefinition.ROLE_SUPPORT);
@@ -299,47 +301,68 @@ private void handleListenersElement(Element element, RootBeanDefinition bd, Pars
299301
DomUtils.getChildElementByTagName(element, "listeners");
300302
if (listenersElement != null) {
301303
List<BeanReference> listenerBeans = new ArrayList<BeanReference>();
302-
List<Element> listenerElements =
303-
DomUtils.getChildElementsByTagName(listenersElement, "listener");
304-
if (listenerElements != null) {
305-
for (Element listenerElement : listenerElements) {
306-
String id = listenerElement.getAttribute("id");
307-
String listenerRef = listenerElement.getAttribute("ref");
308-
String className = listenerElement.getAttribute("class");
309-
if ((StringUtils.hasText(id) || StringUtils.hasText(className))
310-
&& StringUtils.hasText(listenerRef)) {
311-
NamedNodeMap attributeNodes = listenerElement.getAttributes();
312-
StringBuilder attributes = new StringBuilder();
313-
for (int i = 0; i < attributeNodes.getLength(); i++) {
314-
if (i > 0) {
315-
attributes.append(" ");
316-
}
317-
attributes.append(attributeNodes.item(i));
318-
}
319-
throw new BeanCreationException("Both 'id' or 'ref' plus 'class' specified; use 'class' with an optional 'id' or just 'ref' for <" +
320-
listenerElement.getTagName() + "> element with attributes: " + attributes);
321-
}
322-
if (StringUtils.hasText(listenerRef)) {
323-
BeanReference bean = new RuntimeBeanReference(listenerRef);
324-
listenerBeans.add(bean);
325-
}
326-
else if (StringUtils.hasText(className)) {
327-
RootBeanDefinition beanDef = new RootBeanDefinition(className, null, null);
328-
if (!StringUtils.hasText(id)) {
329-
id = parserContext.getReaderContext().generateBeanName(beanDef);
304+
handleListenerElements(parserContext, listenersElement,
305+
listenerBeans);
306+
ManagedList arguments = new ManagedList();
307+
arguments.addAll(listenerBeans);
308+
bd.getPropertyValues().addPropertyValue("listeners", arguments);
309+
}
310+
}
311+
312+
@SuppressWarnings("unchecked")
313+
private void handleRetryListenersElement(Element element, RootBeanDefinition bd, ParserContext parserContext) {
314+
Element retryListenersElement =
315+
DomUtils.getChildElementByTagName(element, "retry-listeners");
316+
if (retryListenersElement != null) {
317+
List<BeanReference> retryListenerBeans = new ArrayList<BeanReference>();
318+
handleListenerElements(parserContext, retryListenersElement,
319+
retryListenerBeans);
320+
ManagedList arguments = new ManagedList();
321+
arguments.addAll(retryListenerBeans);
322+
bd.getPropertyValues().addPropertyValue("retryListeners", arguments);
323+
}
324+
}
325+
326+
@SuppressWarnings("unchecked")
327+
private void handleListenerElements(ParserContext parserContext,
328+
Element element, List<BeanReference> beans) {
329+
List<Element> listenerElements =
330+
DomUtils.getChildElementsByTagName(element, "listener");
331+
if (listenerElements != null) {
332+
for (Element listenerElement : listenerElements) {
333+
String id = listenerElement.getAttribute("id");
334+
String listenerRef = listenerElement.getAttribute("ref");
335+
String className = listenerElement.getAttribute("class");
336+
if ((StringUtils.hasText(id) || StringUtils.hasText(className))
337+
&& StringUtils.hasText(listenerRef)) {
338+
NamedNodeMap attributeNodes = listenerElement.getAttributes();
339+
StringBuilder attributes = new StringBuilder();
340+
for (int i = 0; i < attributeNodes.getLength(); i++) {
341+
if (i > 0) {
342+
attributes.append(" ");
330343
}
331-
parserContext.getRegistry().registerBeanDefinition(id, beanDef);
332-
BeanReference bean = new RuntimeBeanReference(id);
333-
listenerBeans.add(bean);
344+
attributes.append(attributeNodes.item(i));
334345
}
335-
else {
336-
throw new BeanCreationException("Neither 'ref' or 'class' specified for <" + listenerElement.getTagName() + "> element");
346+
throw new BeanCreationException("Both 'id' or 'ref' plus 'class' specified; use 'class' with an optional 'id' or just 'ref' for <" +
347+
listenerElement.getTagName() + "> element with attributes: " + attributes);
348+
}
349+
if (StringUtils.hasText(listenerRef)) {
350+
BeanReference bean = new RuntimeBeanReference(listenerRef);
351+
beans.add(bean);
352+
}
353+
else if (StringUtils.hasText(className)) {
354+
RootBeanDefinition beanDef = new RootBeanDefinition(className, null, null);
355+
if (!StringUtils.hasText(id)) {
356+
id = parserContext.getReaderContext().generateBeanName(beanDef);
337357
}
358+
parserContext.getRegistry().registerBeanDefinition(id, beanDef);
359+
BeanReference bean = new RuntimeBeanReference(id);
360+
beans.add(bean);
361+
}
362+
else {
363+
throw new BeanCreationException("Neither 'ref' or 'class' specified for <" + listenerElement.getTagName() + "> element");
338364
}
339365
}
340-
ManagedList arguments = new ManagedList();
341-
arguments.addAll(listenerBeans);
342-
bd.getPropertyValues().addPropertyValue("listeners", arguments);
343366
}
344367
}
345368

spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@
251251
</xsd:sequence>
252252
</xsd:complexType>
253253
</xsd:element>
254-
<xsd:element name="retryListeners" minOccurs="0" maxOccurs="1">
254+
<xsd:element name="retry-listeners" minOccurs="0" maxOccurs="1">
255255
<xsd:annotation>
256256
<xsd:documentation><![CDATA[
257257
List of all listeners for the step definition

spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepWithFaultTolerantProcessTaskJobParserTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
3232
import org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean;
3333
import org.springframework.batch.item.ItemStream;
34+
import org.springframework.batch.retry.RetryListener;
3435
import org.springframework.beans.factory.annotation.Autowired;
3536
import org.springframework.beans.factory.annotation.Qualifier;
3637
import org.springframework.test.context.ContextConfiguration;
@@ -59,6 +60,9 @@ public class StepWithFaultTolerantProcessTaskJobParserTests {
5960
@Qualifier("listener")
6061
private TestListener listener;
6162

63+
@Autowired
64+
private TestRetryListener retryListener;
65+
6266
@Autowired
6367
private TestProcessor processor;
6468

@@ -85,6 +89,8 @@ public void testStepWithTask() throws Exception {
8589
assertEquals("wrong retry-limit:", 3, rl);
8690
Object listeners = ReflectionTestUtils.getField(factory, "listeners");
8791
assertEquals("wrong number of listeners:", 2, ((StepListener[])listeners).length);
92+
Object retryListeners = ReflectionTestUtils.getField(factory, "retryListeners");
93+
assertEquals("wrong number of retry-listeners:", 1, ((RetryListener[])retryListeners).length);
8894
Object streams = ReflectionTestUtils.getField(factory, "streams");
8995
assertEquals("wrong number of streams:", 1, ((ItemStream[])streams).length);
9096
JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters());
@@ -96,5 +102,6 @@ public void testStepWithTask() throws Exception {
96102
assertTrue(processor.isExecuted());
97103
assertTrue(writer.isExecuted());
98104
assertTrue(listener.isExecuted());
105+
assertTrue(retryListener.isExecuted());
99106
}
100107
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.springframework.batch.core.configuration.xml;
2+
3+
import org.springframework.batch.retry.RetryCallback;
4+
import org.springframework.batch.retry.RetryContext;
5+
import org.springframework.batch.retry.RetryListener;
6+
7+
public class TestRetryListener extends AbstractTestComponent implements RetryListener {
8+
9+
public <T> void close(RetryContext context, RetryCallback<T> callback,
10+
Throwable throwable) {
11+
}
12+
13+
public <T> void onError(RetryContext context, RetryCallback<T> callback,
14+
Throwable throwable) {
15+
}
16+
17+
public <T> boolean open(RetryContext context, RetryCallback<T> callback) {
18+
System.out.println("RETRY RETRY RETRY RETRY");
19+
executed = true;
20+
return true;
21+
}
22+
23+
}

spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithFaultTolerantProcessTaskJobParserTests-context.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
<listener class="org.springframework.batch.core.configuration.xml.TestListener"/>
1515
<listener ref="listener"/>
1616
</listeners>
17+
<retry-listeners>
18+
<listener class="org.springframework.batch.core.configuration.xml.TestRetryListener"/>
19+
</retry-listeners>
1720
<streams>
1821
<stream ref="reader"/>
1922
</streams>

0 commit comments

Comments
 (0)