1 package org.springframework.batch.core.configuration.xml;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import org.springframework.batch.core.listener.AbstractListenerFactoryBean;
7 import org.springframework.batch.core.listener.ListenerMetaData;
8 import org.springframework.beans.BeanMetadataElement;
9 import org.springframework.beans.factory.config.BeanDefinition;
10 import org.springframework.beans.factory.config.BeanDefinitionHolder;
11 import org.springframework.beans.factory.config.RuntimeBeanReference;
12 import org.springframework.beans.factory.support.AbstractBeanDefinition;
13 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
14 import org.springframework.beans.factory.support.ManagedMap;
15 import org.springframework.beans.factory.xml.ParserContext;
16 import org.springframework.util.StringUtils;
17 import org.springframework.util.xml.DomUtils;
18 import org.w3c.dom.Element;
19
20
21
22
23
24
25
26 public abstract class AbstractListenerParser {
27
28 private static final String ID_ATTR = "id";
29
30 private static final String REF_ATTR = "ref";
31
32 private static final String BEAN_ELE = "bean";
33
34 private static final String REF_ELE = "ref";
35
36 public AbstractBeanDefinition parse(Element element, ParserContext parserContext) {
37 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(getBeanClass());
38 doParse(element, parserContext, builder);
39 return builder.getBeanDefinition();
40 }
41
42 @SuppressWarnings("unchecked")
43 public void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
44 builder.addPropertyValue("delegate", parseListenerElement(element, parserContext, builder.getRawBeanDefinition()));
45
46 ManagedMap metaDataMap = new ManagedMap();
47 for (String metaDataPropertyName : getMethodNameAttributes()) {
48 String listenerMethod = element.getAttribute(metaDataPropertyName);
49 if (StringUtils.hasText(listenerMethod)) {
50 metaDataMap.put(metaDataPropertyName, listenerMethod);
51 }
52 }
53 builder.addPropertyValue("metaDataMap", metaDataMap);
54 }
55
56 @SuppressWarnings("unchecked")
57 public static BeanMetadataElement parseListenerElement(Element element, ParserContext parserContext, BeanDefinition enclosing) {
58 String listenerRef = element.getAttribute(REF_ATTR);
59 List<Element> beanElements = DomUtils.getChildElementsByTagName(element, BEAN_ELE);
60 List<Element> refElements = DomUtils.getChildElementsByTagName(element, REF_ELE);
61
62 verifyListenerAttributesAndSubelements(listenerRef, beanElements, refElements, element, parserContext);
63
64 if (StringUtils.hasText(listenerRef)) {
65 return new RuntimeBeanReference(listenerRef);
66 }
67 else if (beanElements.size() == 1) {
68 Element beanElement = beanElements.get(0);
69 BeanDefinitionHolder beanDefinitionHolder = parserContext.getDelegate().parseBeanDefinitionElement(
70 beanElement, enclosing);
71 parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDefinitionHolder);
72 return beanDefinitionHolder;
73 }
74 else {
75 return (BeanMetadataElement) parserContext.getDelegate().parsePropertySubElement(refElements.get(0), null);
76 }
77 }
78
79 private static void verifyListenerAttributesAndSubelements(String listenerRef, List<Element> beanElements,
80 List<Element> refElements, Element element, ParserContext parserContext) {
81 int total = (StringUtils.hasText(listenerRef) ? 1 : 0) + beanElements.size() + refElements.size();
82 if (total != 1) {
83 StringBuilder found = new StringBuilder();
84 if (total == 0) {
85 found.append("None");
86 }
87 else {
88 if (StringUtils.hasText(listenerRef)) {
89 found.append("'" + REF_ATTR + "' attribute, ");
90 }
91 if (beanElements.size() == 1) {
92 found.append("<" + BEAN_ELE + "/> element, ");
93 }
94 else if (beanElements.size() > 1) {
95 found.append(beanElements.size() + " <" + BEAN_ELE + "/> elements, ");
96 }
97 if (refElements.size() == 1) {
98 found.append("<" + REF_ELE + "/> element, ");
99 }
100 else if (refElements.size() > 1) {
101 found.append(refElements.size() + " <" + REF_ELE + "/> elements, ");
102 }
103 found.delete(found.length() - 2, found.length());
104 }
105
106 String id = element.getAttribute(ID_ATTR);
107 parserContext.getReaderContext().error(
108 "The <" + element.getTagName() + (StringUtils.hasText(id) ? " id=\"" + id + "\"" : "")
109 + "/> element must have exactly one of: '" + REF_ATTR + "' attribute, <" + BEAN_ELE
110 + "/> attribute, or <" + REF_ELE + "/> element. Found: " + found + ".", element);
111 }
112 }
113
114 private List<String> getMethodNameAttributes() {
115 List<String> methodNameAttributes = new ArrayList<String>();
116 for (ListenerMetaData metaData : getMetaDataValues()) {
117 methodNameAttributes.add(metaData.getPropertyName());
118 }
119 return methodNameAttributes;
120 }
121
122 protected abstract Class<? extends AbstractListenerFactoryBean> getBeanClass();
123
124 protected abstract ListenerMetaData[] getMetaDataValues();
125
126 }