EMMA Coverage Report (generated Thu May 22 12:08:10 CDT 2014)
[all classes][org.springframework.batch.core.configuration.xml]

COVERAGE SUMMARY FOR SOURCE FILE [ChunkElementParser.java]

nameclass, %method, %block, %line, %
ChunkElementParser.java100% (1/1)100% (11/11)76%  (599/789)93%  (130/140)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ChunkElementParser100% (1/1)100% (11/11)76%  (599/789)93%  (130/140)
handleItemHandler (AbstractBeanDefinition, String, String, String, boolean, E... 100% (1/1)42%  (47/113)77%  (10/13)
handleItemHandlerElement (AbstractBeanDefinition, String, String, MutableProp... 100% (1/1)55%  (48/87)79%  (11/14)
handleExceptionElement (Element, ParserContext, String): ManagedMap 100% (1/1)76%  (60/79)92%  (11/12)
handleStreamsElement (Element, MutablePropertyValues, ParserContext): void 100% (1/1)81%  (62/77)93%  (13/14)
parse (Element, AbstractBeanDefinition, ParserContext, boolean): void 100% (1/1)82%  (237/288)96%  (53/55)
<static initializer> 100% (1/1)100% (6/6)100% (1/1)
ChunkElementParser (): void 100% (1/1)100% (3/3)100% (1/1)
addExceptionClasses (String, boolean, Element, ManagedMap, ParserContext): void 100% (1/1)100% (28/28)100% (5/5)
handleAdapterMethodAttribute (String, String, MutablePropertyValues, Element)... 100% (1/1)100% (33/33)100% (9/9)
handleRetryListenerElements (ParserContext, Element, ManagedList, BeanDefinit... 100% (1/1)100% (25/25)100% (6/6)
handleRetryListenersElement (Element, MutablePropertyValues, ParserContext, B... 100% (1/1)100% (50/50)100% (10/10)

1/*
2 * Copyright 2006-2009 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.springframework.batch.core.configuration.xml;
17 
18import org.springframework.batch.core.listener.StepListenerMetaData;
19import org.springframework.batch.core.step.item.ForceRollbackForWriteSkipException;
20import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
21import org.springframework.beans.MutablePropertyValues;
22import org.springframework.beans.factory.config.BeanDefinition;
23import org.springframework.beans.factory.config.BeanDefinitionHolder;
24import org.springframework.beans.factory.config.RuntimeBeanReference;
25import org.springframework.beans.factory.config.TypedStringValue;
26import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
27import org.springframework.beans.factory.support.AbstractBeanDefinition;
28import org.springframework.beans.factory.support.BeanDefinitionBuilder;
29import org.springframework.beans.factory.support.GenericBeanDefinition;
30import org.springframework.beans.factory.support.ManagedList;
31import org.springframework.beans.factory.support.ManagedMap;
32import org.springframework.beans.factory.xml.ParserContext;
33import org.springframework.util.StringUtils;
34import org.springframework.util.xml.DomUtils;
35import org.w3c.dom.Element;
36 
37import java.util.List;
38 
39/**
40 * Internal parser for the &lt;chunk/&gt; element inside a step.
41 * 
42 * @author Thomas Risberg
43 * @since 2.0
44 */
45public class ChunkElementParser {
46 
47        private static final String REF_ATTR = "ref";
48 
49        private static final String MERGE_ATTR = "merge";
50 
51        private static final String COMMIT_INTERVAL_ATTR = "commit-interval";
52 
53        private static final String CHUNK_COMPLETION_POLICY_ATTR = "chunk-completion-policy";
54 
55        private static final String BEAN_ELE = "bean";
56 
57        private static final String REF_ELE = "ref";
58 
59        private static final String ITEM_READER_ADAPTER_CLASS = "org.springframework.batch.item.adapter.ItemReaderAdapter";
60 
61        private static final String ITEM_PROCESSOR_ADAPTER_CLASS = "org.springframework.batch.item.adapter.ItemProcessorAdapter";
62 
63        private static final String ITEM_WRITER_ADAPTER_CLASS = "org.springframework.batch.item.adapter.ItemWriterAdapter";
64 
65        private static final StepListenerParser stepListenerParser = new StepListenerParser(
66                        StepListenerMetaData.itemListenerMetaData());
67 
68        /**
69         * @param element
70         * @param parserContext
71         */
72        protected void parse(Element element, AbstractBeanDefinition bd, ParserContext parserContext, boolean underspecified) {
73 
74                MutablePropertyValues propertyValues = bd.getPropertyValues();
75 
76                propertyValues.addPropertyValue("hasChunkElement", Boolean.TRUE);
77 
78                handleItemHandler(bd, "reader", "itemReader", ITEM_READER_ADAPTER_CLASS, true, element, parserContext,
79                                propertyValues, underspecified);
80                handleItemHandler(bd, "processor", "itemProcessor", ITEM_PROCESSOR_ADAPTER_CLASS, false, element, parserContext,
81                                propertyValues, underspecified);
82                handleItemHandler(bd, "writer", "itemWriter", ITEM_WRITER_ADAPTER_CLASS, true, element, parserContext,
83                                propertyValues, underspecified);
84 
85                String commitInterval = element.getAttribute(COMMIT_INTERVAL_ATTR);
86                if (StringUtils.hasText(commitInterval)) {
87                        if (commitInterval.startsWith("#")) {
88                                // It's a late binding expression, so we need step scope...
89                                BeanDefinitionBuilder completionPolicy = BeanDefinitionBuilder
90                                                .genericBeanDefinition(SimpleCompletionPolicy.class);
91                                completionPolicy.addConstructorArgValue(commitInterval);
92                                completionPolicy.setScope("step");
93                                propertyValues.addPropertyValue("chunkCompletionPolicy", completionPolicy.getBeanDefinition());
94                        }
95                        else {
96                                propertyValues.addPropertyValue("commitInterval", commitInterval);
97                        }
98                }
99 
100                String completionPolicyRef = element.getAttribute(CHUNK_COMPLETION_POLICY_ATTR);
101                if (StringUtils.hasText(completionPolicyRef)) {
102                        RuntimeBeanReference completionPolicy = new RuntimeBeanReference(completionPolicyRef);
103                        propertyValues.addPropertyValue("chunkCompletionPolicy", completionPolicy);
104                }
105 
106                if (!underspecified
107                                && propertyValues.contains("commitInterval") == propertyValues.contains("chunkCompletionPolicy")) {
108                        if (propertyValues.contains("commitInterval")) {
109                                parserContext.getReaderContext().error(
110                                                "The <" + element.getNodeName() + "/> element must contain either '" + COMMIT_INTERVAL_ATTR
111                                                                + "' " + "or '" + CHUNK_COMPLETION_POLICY_ATTR + "', but not both.", element);
112                        }
113                        else {
114                                parserContext.getReaderContext().error(
115                                                "The <" + element.getNodeName() + "/> element must contain either '" + COMMIT_INTERVAL_ATTR
116                                                                + "' " + "or '" + CHUNK_COMPLETION_POLICY_ATTR + "'.", element);
117 
118                        }
119                }
120 
121                String skipLimit = element.getAttribute("skip-limit");
122                ManagedMap skippableExceptions = handleExceptionElement(element, parserContext, "skippable-exception-classes");
123                if (StringUtils.hasText(skipLimit)) {
124                        if (skippableExceptions == null) {
125                                skippableExceptions = new ManagedMap();
126                                skippableExceptions.setMergeEnabled(true);
127                        }
128                        propertyValues.addPropertyValue("skipLimit", skipLimit);
129                }
130                if (skippableExceptions != null) {
131                        // Even if there is no retryLimit, we can still accept exception
132                        // classes for an abstract parent bean definition
133                        propertyValues.addPropertyValue("skippableExceptionClasses", skippableExceptions);
134                }
135 
136                handleItemHandler(bd, "skip-policy", "skipPolicy", null, false, element, parserContext, propertyValues,
137                                underspecified);
138 
139                String retryLimit = element.getAttribute("retry-limit");
140                ManagedMap retryableExceptions = handleExceptionElement(element, parserContext, "retryable-exception-classes");
141                if (StringUtils.hasText(retryLimit)) {
142                        if (retryableExceptions == null) {
143                                retryableExceptions = new ManagedMap();
144                                retryableExceptions.setMergeEnabled(true);
145                        }
146                        propertyValues.addPropertyValue("retryLimit", retryLimit);
147                }
148                if (retryableExceptions != null) {
149                        // Even if there is no retryLimit, we can still accept exception
150                        // classes for an abstract parent bean definition
151                        propertyValues.addPropertyValue("retryableExceptionClasses", retryableExceptions);
152                }
153 
154                handleItemHandler(bd, "retry-policy", "retryPolicy", null, false, element, parserContext, propertyValues,
155                                underspecified);
156 
157                String cacheCapacity = element.getAttribute("cache-capacity");
158                if (StringUtils.hasText(cacheCapacity)) {
159                        propertyValues.addPropertyValue("cacheCapacity", cacheCapacity);
160                }
161 
162                String isReaderTransactionalQueue = element.getAttribute("reader-transactional-queue");
163                if (StringUtils.hasText(isReaderTransactionalQueue)) {
164                        propertyValues.addPropertyValue("isReaderTransactionalQueue", isReaderTransactionalQueue);
165                }
166 
167                String isProcessorTransactional = element.getAttribute("processor-transactional");
168                if (StringUtils.hasText(isProcessorTransactional)) {
169                        propertyValues.addPropertyValue("processorTransactional", isProcessorTransactional);
170                }
171 
172                handleRetryListenersElement(element, propertyValues, parserContext, bd);
173 
174                handleStreamsElement(element, propertyValues, parserContext);
175 
176                stepListenerParser.handleListenersElement(element, bd, parserContext);
177 
178        }
179 
180        /**
181         * Handle the ItemReader, ItemProcessor, and ItemWriter attributes/elements.
182         */
183        private void handleItemHandler(AbstractBeanDefinition enclosing, String handlerName, String propertyName, String adapterClassName, boolean required,
184                        Element element, ParserContext parserContext, MutablePropertyValues propertyValues, boolean underspecified) {
185                String refName = element.getAttribute(handlerName);
186                @SuppressWarnings("unchecked")
187                List<Element> children = DomUtils.getChildElementsByTagName(element, handlerName);
188                if (children.size() == 1) {
189                        if (StringUtils.hasText(refName)) {
190                                parserContext.getReaderContext().error(
191                                                "The <" + element.getNodeName() + "/> element may not have both a '" + handlerName
192                                                                + "' attribute and a <" + handlerName + "/> element.", element);
193                        }
194                        handleItemHandlerElement(enclosing, propertyName, adapterClassName, propertyValues, children.get(0), parserContext);
195                }
196                else if (children.size() > 1) {
197                        parserContext.getReaderContext().error(
198                                        "The <" + handlerName + "/> element may not appear more than once in a single <"
199                                                        + element.getNodeName() + "/>.", element);
200                }
201                else if (StringUtils.hasText(refName)) {
202                        propertyValues.addPropertyValue(propertyName, new RuntimeBeanReference(refName));
203                }
204                else if (required && !underspecified) {
205                        parserContext.getReaderContext().error(
206                                        "The <" + element.getNodeName() + "/> element has neither a '" + handlerName
207                                                        + "' attribute nor a <" + handlerName + "/> element.", element);
208                }
209        }
210 
211        /**
212         * Handle the &lt;reader/&gt;, &lt;processor/&gt;, or &lt;writer/&gt; that
213         * is defined within the item handler.
214         */
215        @SuppressWarnings("unchecked")
216        private void handleItemHandlerElement(AbstractBeanDefinition enclosing, String propertyName, String adapterClassName,
217                        MutablePropertyValues propertyValues, Element element, ParserContext parserContext) {
218                List<Element> beanElements = DomUtils.getChildElementsByTagName(element, BEAN_ELE);
219                List<Element> refElements = DomUtils.getChildElementsByTagName(element, REF_ELE);
220                if (beanElements.size() + refElements.size() != 1) {
221                        parserContext.getReaderContext().error(
222                                        "The <" + element.getNodeName() + "/> must have exactly one of either a <" + BEAN_ELE
223                                                        + "/> element or a <" + REF_ELE + "/> element.", element);
224                }
225                else if (beanElements.size() == 1) {
226                        Element beanElement = beanElements.get(0);
227                        BeanDefinitionHolder beanDefinitionHolder = parserContext.getDelegate().parseBeanDefinitionElement(
228                                        beanElement, enclosing);
229                        parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDefinitionHolder);
230 
231                        propertyValues.addPropertyValue(propertyName, beanDefinitionHolder);
232                }
233                else if (refElements.size() == 1) {
234                        propertyValues.addPropertyValue(propertyName,
235                                        parserContext.getDelegate().parsePropertySubElement(refElements.get(0), null));
236                }
237 
238                handleAdapterMethodAttribute(propertyName, adapterClassName, propertyValues, element);
239        }
240 
241        /**
242         * Handle the adapter-method attribute by using an
243         * AbstractMethodInvokingDelegator
244         */
245        private void handleAdapterMethodAttribute(String propertyName, String adapterClassName,
246                        MutablePropertyValues stepPvs, Element element) {
247                String adapterMethodName = element.getAttribute("adapter-method");
248                if (StringUtils.hasText(adapterMethodName)) {
249                        //
250                        // Create an adapter
251                        //
252                        AbstractBeanDefinition adapterDef = new GenericBeanDefinition();
253                        adapterDef.setBeanClassName(adapterClassName);
254                        MutablePropertyValues adapterPvs = adapterDef.getPropertyValues();
255                        adapterPvs.addPropertyValue("targetMethod", adapterMethodName);
256                        // Inject the bean into the adapter
257                        adapterPvs.addPropertyValue("targetObject", stepPvs.getPropertyValue(propertyName).getValue());
258 
259                        //
260                        // Inject the adapter into the step
261                        //
262                        stepPvs.addPropertyValue(propertyName, adapterDef);
263                }
264        }
265 
266        private void handleRetryListenersElement(Element element, MutablePropertyValues propertyValues,
267                        ParserContext parserContext, BeanDefinition enclosing) {
268                Element listenersElement = DomUtils.getChildElementByTagName(element, "retry-listeners");
269                if (listenersElement != null) {
270                        CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(listenersElement.getTagName(),
271                                        parserContext.extractSource(element));
272                        parserContext.pushContainingComponent(compositeDef);
273                        ManagedList retryListenerBeans = new ManagedList();
274                        retryListenerBeans.setMergeEnabled(listenersElement.hasAttribute(MERGE_ATTR)
275                                        && Boolean.valueOf(listenersElement.getAttribute(MERGE_ATTR)));
276                        handleRetryListenerElements(parserContext, listenersElement, retryListenerBeans, enclosing);
277                        propertyValues.addPropertyValue("retryListeners", retryListenerBeans);
278                        parserContext.popAndRegisterContainingComponent();
279                }
280        }
281 
282        @SuppressWarnings("unchecked")
283        private void handleRetryListenerElements(ParserContext parserContext, Element element, ManagedList beans,
284                        BeanDefinition enclosing) {
285                List<Element> listenerElements = DomUtils.getChildElementsByTagName(element, "listener");
286                if (listenerElements != null) {
287                        for (Element listenerElement : listenerElements) {
288                                beans.add(AbstractListenerParser.parseListenerElement(listenerElement, parserContext, enclosing));
289                        }
290                }
291        }
292 
293        @SuppressWarnings("unchecked")
294        private void handleStreamsElement(Element element, MutablePropertyValues propertyValues, ParserContext parserContext) {
295                Element streamsElement = DomUtils.getChildElementByTagName(element, "streams");
296                if (streamsElement != null) {
297                        ManagedList streamBeans = new ManagedList();
298                        streamBeans.setMergeEnabled(streamsElement.hasAttribute(MERGE_ATTR)
299                                        && Boolean.valueOf(streamsElement.getAttribute(MERGE_ATTR)));
300                        List<Element> streamElements = DomUtils.getChildElementsByTagName(streamsElement, "stream");
301                        if (streamElements != null) {
302                                for (Element streamElement : streamElements) {
303                                        String streamRef = streamElement.getAttribute(REF_ATTR);
304                                        if (StringUtils.hasText(streamRef)) {
305                                                streamBeans.add(new RuntimeBeanReference(streamRef));
306                                        }
307                                        else {
308                                                parserContext.getReaderContext().error(
309                                                                REF_ATTR + " not specified for <" + streamElement.getTagName() + "> element", element);
310                                        }
311                                }
312                        }
313                        propertyValues.addPropertyValue("streams", streamBeans);
314                }
315        }
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 
347}

[all classes][org.springframework.batch.core.configuration.xml]
EMMA 2.0.5312 (C) Vladimir Roubtsov