1515 */
1616package org .springframework .batch .core .configuration .xml ;
1717
18+ import java .util .List ;
19+
1820import org .springframework .batch .core .listener .StepListenerMetaData ;
19- import org .springframework .batch .core .step .item .ForceRollbackForWriteSkipException ;
2021import org .springframework .batch .repeat .policy .SimpleCompletionPolicy ;
2122import org .springframework .beans .MutablePropertyValues ;
2223import org .springframework .beans .factory .config .BeanDefinition ;
2324import org .springframework .beans .factory .config .BeanDefinitionHolder ;
2425import org .springframework .beans .factory .config .RuntimeBeanReference ;
25- import org .springframework .beans .factory .config .TypedStringValue ;
2626import org .springframework .beans .factory .parsing .CompositeComponentDefinition ;
2727import org .springframework .beans .factory .support .AbstractBeanDefinition ;
2828import org .springframework .beans .factory .support .BeanDefinitionBuilder ;
2929import org .springframework .beans .factory .support .GenericBeanDefinition ;
3030import org .springframework .beans .factory .support .ManagedList ;
3131import org .springframework .beans .factory .support .ManagedMap ;
3232import org .springframework .beans .factory .xml .ParserContext ;
33+ import org .springframework .util .CollectionUtils ;
3334import org .springframework .util .StringUtils ;
3435import org .springframework .util .xml .DomUtils ;
3536import org .w3c .dom .Element ;
3637
37- import java .util .List ;
38-
3938/**
4039 * Internal parser for the <chunk/> element inside a step.
4140 *
@@ -108,18 +107,18 @@ protected void parse(Element element, AbstractBeanDefinition bd, ParserContext p
108107 if (propertyValues .contains ("commitInterval" )) {
109108 parserContext .getReaderContext ().error (
110109 "The <" + element .getNodeName () + "/> element must contain either '" + COMMIT_INTERVAL_ATTR
111- + "' " + "or '" + CHUNK_COMPLETION_POLICY_ATTR + "', but not both." , element );
110+ + "' " + "or '" + CHUNK_COMPLETION_POLICY_ATTR + "', but not both." , element );
112111 }
113112 else {
114113 parserContext .getReaderContext ().error (
115114 "The <" + element .getNodeName () + "/> element must contain either '" + COMMIT_INTERVAL_ATTR
116- + "' " + "or '" + CHUNK_COMPLETION_POLICY_ATTR + "'." , element );
115+ + "' " + "or '" + CHUNK_COMPLETION_POLICY_ATTR + "'." , element );
117116
118117 }
119118 }
120119
121120 String skipLimit = element .getAttribute ("skip-limit" );
122- ManagedMap skippableExceptions = handleExceptionElement (element , parserContext , "skippable-exception-classes" );
121+ ManagedMap skippableExceptions = new ExceptionElementParser (). parse (element , parserContext , "skippable-exception-classes" );
123122 if (StringUtils .hasText (skipLimit )) {
124123 if (skippableExceptions == null ) {
125124 skippableExceptions = new ManagedMap ();
@@ -128,6 +127,12 @@ protected void parse(Element element, AbstractBeanDefinition bd, ParserContext p
128127 propertyValues .addPropertyValue ("skipLimit" , skipLimit );
129128 }
130129 if (skippableExceptions != null ) {
130+ List <Element > exceptionClassElements = DomUtils .getChildElementsByTagName (element , "skippable-exception-classes" );
131+
132+ if (!CollectionUtils .isEmpty (exceptionClassElements )) {
133+ skippableExceptions .setMergeEnabled (exceptionClassElements .get (0 ).hasAttribute (MERGE_ATTR )
134+ && Boolean .valueOf (exceptionClassElements .get (0 ).getAttribute (MERGE_ATTR )));
135+ }
131136 // Even if there is no retryLimit, we can still accept exception
132137 // classes for an abstract parent bean definition
133138 propertyValues .addPropertyValue ("skippableExceptionClasses" , skippableExceptions );
@@ -137,7 +142,7 @@ protected void parse(Element element, AbstractBeanDefinition bd, ParserContext p
137142 underspecified );
138143
139144 String retryLimit = element .getAttribute ("retry-limit" );
140- ManagedMap retryableExceptions = handleExceptionElement (element , parserContext , "retryable-exception-classes" );
145+ ManagedMap retryableExceptions = new ExceptionElementParser (). parse (element , parserContext , "retryable-exception-classes" );
141146 if (StringUtils .hasText (retryLimit )) {
142147 if (retryableExceptions == null ) {
143148 retryableExceptions = new ManagedMap ();
@@ -146,6 +151,12 @@ protected void parse(Element element, AbstractBeanDefinition bd, ParserContext p
146151 propertyValues .addPropertyValue ("retryLimit" , retryLimit );
147152 }
148153 if (retryableExceptions != null ) {
154+ List <Element > exceptionClassElements = DomUtils .getChildElementsByTagName (element , "retryable-exception-classes" );
155+
156+ if (!CollectionUtils .isEmpty (exceptionClassElements )) {
157+ retryableExceptions .setMergeEnabled (exceptionClassElements .get (0 ).hasAttribute (MERGE_ATTR )
158+ && Boolean .valueOf (exceptionClassElements .get (0 ).getAttribute (MERGE_ATTR )));
159+ }
149160 // Even if there is no retryLimit, we can still accept exception
150161 // classes for an abstract parent bean definition
151162 propertyValues .addPropertyValue ("retryableExceptionClasses" , retryableExceptions );
@@ -189,7 +200,7 @@ private void handleItemHandler(AbstractBeanDefinition enclosing, String handlerN
189200 if (StringUtils .hasText (refName )) {
190201 parserContext .getReaderContext ().error (
191202 "The <" + element .getNodeName () + "/> element may not have both a '" + handlerName
192- + "' attribute and a <" + handlerName + "/> element." , element );
203+ + "' attribute and a <" + handlerName + "/> element." , element );
193204 }
194205 handleItemHandlerElement (enclosing , propertyName , adapterClassName , propertyValues , children .get (0 ), parserContext );
195206 }
@@ -204,7 +215,7 @@ else if (StringUtils.hasText(refName)) {
204215 else if (required && !underspecified ) {
205216 parserContext .getReaderContext ().error (
206217 "The <" + element .getNodeName () + "/> element has neither a '" + handlerName
207- + "' attribute nor a <" + handlerName + "/> element." , element );
218+ + "' attribute nor a <" + handlerName + "/> element." , element );
208219 }
209220 }
210221
@@ -220,7 +231,7 @@ private void handleItemHandlerElement(AbstractBeanDefinition enclosing, String p
220231 if (beanElements .size () + refElements .size () != 1 ) {
221232 parserContext .getReaderContext ().error (
222233 "The <" + element .getNodeName () + "/> must have exactly one of either a <" + BEAN_ELE
223- + "/> element or a <" + REF_ELE + "/> element." , element );
234+ + "/> element or a <" + REF_ELE + "/> element." , element );
224235 }
225236 else if (beanElements .size () == 1 ) {
226237 Element beanElement = beanElements .get (0 );
@@ -313,35 +324,35 @@ private void handleStreamsElement(Element element, MutablePropertyValues propert
313324 propertyValues .addPropertyValue ("streams" , streamBeans );
314325 }
315326 }
316-
317- @ SuppressWarnings ("unchecked" )
318- private ManagedMap handleExceptionElement (Element element , ParserContext parserContext , String exceptionListName ) {
319- List <Element > children = DomUtils .getChildElementsByTagName (element , exceptionListName );
320- if (children .size () == 1 ) {
321- ManagedMap map = new ManagedMap ();
322- Element exceptionClassesElement = children .get (0 );
323- map .setMergeEnabled (exceptionClassesElement .hasAttribute (MERGE_ATTR )
324- && Boolean .valueOf (exceptionClassesElement .getAttribute (MERGE_ATTR )));
325- addExceptionClasses ("include" , true , exceptionClassesElement , map , parserContext );
326- addExceptionClasses ("exclude" , false , exceptionClassesElement , map , parserContext );
327- map .put (ForceRollbackForWriteSkipException .class , true );
328- return map ;
329- }
330- else if (children .size () > 1 ) {
331- parserContext .getReaderContext ().error (
332- "The <" + exceptionListName + "/> element may not appear more than once in a single <"
333- + element .getNodeName () + "/>." , element );
334- }
335- return null ;
336- }
337-
338- @ SuppressWarnings ("unchecked" )
339- private void addExceptionClasses (String elementName , boolean include , Element exceptionClassesElement ,
340- ManagedMap map , ParserContext parserContext ) {
341- for (Element child : (List <Element >) DomUtils .getChildElementsByTagName (exceptionClassesElement , elementName )) {
342- String className = child .getAttribute ("class" );
343- map .put (new TypedStringValue (className , Class .class ), include );
344- }
345- }
346-
327+ //
328+ // @SuppressWarnings("unchecked")
329+ // private ManagedMap handleExceptionElement(Element element, ParserContext parserContext, String exceptionListName) {
330+ // List<Element> children = DomUtils.getChildElementsByTagName(element, exceptionListName);
331+ // if (children.size() == 1) {
332+ // ManagedMap map = new ManagedMap();
333+ // Element exceptionClassesElement = children.get(0);
334+ // map.setMergeEnabled(exceptionClassesElement.hasAttribute(MERGE_ATTR)
335+ // && Boolean.valueOf(exceptionClassesElement.getAttribute(MERGE_ATTR)));
336+ // addExceptionClasses("include", true, exceptionClassesElement, map, parserContext);
337+ // addExceptionClasses("exclude", false, exceptionClassesElement, map, parserContext);
338+ // map.put(ForceRollbackForWriteSkipException.class, true);
339+ // return map;
340+ // }
341+ // else if (children.size() > 1) {
342+ // parserContext.getReaderContext().error(
343+ // "The <" + exceptionListName + "/> element may not appear more than once in a single <"
344+ // + element.getNodeName() + "/>.", element);
345+ // }
346+ // return null;
347+ // }
348+ //
349+ // @SuppressWarnings("unchecked")
350+ // private void addExceptionClasses(String elementName, boolean include, Element exceptionClassesElement,
351+ // ManagedMap map, ParserContext parserContext) {
352+ // for (Element child : (List<Element>) DomUtils.getChildElementsByTagName(exceptionClassesElement, elementName)) {
353+ // String className = child.getAttribute("class");
354+ // map.put(new TypedStringValue(className, Class.class), include);
355+ // }
356+ // }
357+ //
347358}
0 commit comments