1 | /* |
2 | * Copyright 2006-2008 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 | */ |
16 | package org.springframework.batch.core.configuration.xml; |
17 | |
18 | import java.util.Arrays; |
19 | import java.util.List; |
20 | |
21 | import org.springframework.beans.BeanMetadataElement; |
22 | import org.springframework.beans.factory.config.BeanDefinition; |
23 | import org.springframework.beans.factory.config.BeanDefinitionHolder; |
24 | import org.springframework.beans.factory.config.RuntimeBeanReference; |
25 | import org.springframework.beans.factory.parsing.CompositeComponentDefinition; |
26 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
27 | import org.springframework.beans.factory.support.ManagedList; |
28 | import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; |
29 | import org.springframework.beans.factory.xml.ParserContext; |
30 | import org.springframework.util.StringUtils; |
31 | import org.springframework.util.xml.DomUtils; |
32 | import org.w3c.dom.Element; |
33 | |
34 | /** |
35 | * Parser for the lt;job/gt; element in the Batch namespace. Sets up and returns |
36 | * a bean definition for a {@link org.springframework.batch.core.Job}. |
37 | * |
38 | * @author Dave Syer |
39 | * |
40 | */ |
41 | public class JobParser extends AbstractSingleBeanDefinitionParser { |
42 | |
43 | private static final String MERGE_ATTR = "merge"; |
44 | |
45 | private static final String REF_ATTR = "ref"; |
46 | |
47 | private static final String BEAN_ELE = "bean"; |
48 | |
49 | private static final String REF_ELE = "ref"; |
50 | |
51 | private static final JobExecutionListenerParser jobListenerParser = new JobExecutionListenerParser(); |
52 | |
53 | @Override |
54 | protected Class<JobParserJobFactoryBean> getBeanClass(Element element) { |
55 | return JobParserJobFactoryBean.class; |
56 | } |
57 | |
58 | /** |
59 | * Create a bean definition for a |
60 | * {@link org.springframework.batch.core.job.flow.FlowJob}. Nested step |
61 | * elements are delegated to an {@link InlineStepParser}. |
62 | * |
63 | * @see AbstractSingleBeanDefinitionParser#doParse(Element, ParserContext, |
64 | * BeanDefinitionBuilder) |
65 | */ |
66 | @SuppressWarnings("unchecked") |
67 | @Override |
68 | protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { |
69 | |
70 | if (!CoreNamespaceUtils.namespaceMatchesVersion(element)) { |
71 | parserContext.getReaderContext().error( |
72 | "You cannot use spring-batch-2.0.xsd with Spring Batch 2.1. Please upgrade your schema declarations " |
73 | + "(or use the spring-batch.xsd alias if you are feeling lucky).", element); |
74 | return; |
75 | } |
76 | |
77 | CoreNamespaceUtils.autoregisterBeansForNamespace(parserContext, parserContext.extractSource(element)); |
78 | |
79 | String jobName = element.getAttribute("id"); |
80 | builder.addConstructorArgValue(jobName); |
81 | |
82 | boolean isAbstract = CoreNamespaceUtils.isAbstract(element); |
83 | builder.setAbstract(isAbstract); |
84 | |
85 | String parentRef = element.getAttribute("parent"); |
86 | if (StringUtils.hasText(parentRef)) { |
87 | builder.setParentName(parentRef); |
88 | } |
89 | |
90 | String repositoryAttribute = element.getAttribute("job-repository"); |
91 | if (StringUtils.hasText(repositoryAttribute)) { |
92 | builder.addPropertyReference("jobRepository", repositoryAttribute); |
93 | } |
94 | |
95 | Element validator = DomUtils.getChildElementByTagName(element, "validator"); |
96 | if (validator != null) { |
97 | builder.addPropertyValue("jobParametersValidator", parseBeanElement(validator, parserContext)); |
98 | } |
99 | |
100 | String restartableAttribute = element.getAttribute("restartable"); |
101 | if (StringUtils.hasText(restartableAttribute)) { |
102 | builder.addPropertyValue("restartable", restartableAttribute); |
103 | } |
104 | |
105 | String incrementer = (element.getAttribute("incrementer")); |
106 | if (StringUtils.hasText(incrementer)) { |
107 | builder.addPropertyReference("jobParametersIncrementer", incrementer); |
108 | } |
109 | |
110 | if (isAbstract) { |
111 | for (String tagName : Arrays.asList("step", "decision", "split")) { |
112 | if (!DomUtils.getChildElementsByTagName(element, tagName).isEmpty()) { |
113 | parserContext.getReaderContext().error( |
114 | "The <" + tagName + "/> element may not appear on a <job/> with abstract=\"true\" [" |
115 | + jobName + "]", element); |
116 | } |
117 | } |
118 | } |
119 | else { |
120 | InlineFlowParser flowParser = new InlineFlowParser(jobName, jobName); |
121 | BeanDefinition flowDef = flowParser.parse(element, parserContext); |
122 | builder.addPropertyValue("flow", flowDef); |
123 | } |
124 | |
125 | Element description = DomUtils.getChildElementByTagName(element, "description"); |
126 | if (description != null) { |
127 | builder.getBeanDefinition().setDescription(description.getTextContent()); |
128 | } |
129 | |
130 | List<Element> listenersElements = DomUtils.getChildElementsByTagName(element, "listeners"); |
131 | if (listenersElements.size() == 1) { |
132 | Element listenersElement = listenersElements.get(0); |
133 | CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(listenersElement.getTagName(), |
134 | parserContext.extractSource(element)); |
135 | parserContext.pushContainingComponent(compositeDef); |
136 | ManagedList listeners = new ManagedList(); |
137 | listeners.setMergeEnabled(listenersElement.hasAttribute(MERGE_ATTR) |
138 | && Boolean.valueOf(listenersElement.getAttribute(MERGE_ATTR))); |
139 | List<Element> listenerElements = DomUtils.getChildElementsByTagName(listenersElement, "listener"); |
140 | for (Element listenerElement : listenerElements) { |
141 | listeners.add(jobListenerParser.parse(listenerElement, parserContext)); |
142 | } |
143 | builder.addPropertyValue("jobExecutionListeners", listeners); |
144 | parserContext.popAndRegisterContainingComponent(); |
145 | } |
146 | else if (listenersElements.size() > 1) { |
147 | parserContext.getReaderContext().error( |
148 | "The '<listeners/>' element may not appear more than once in a single <job/>.", element); |
149 | } |
150 | |
151 | } |
152 | |
153 | public BeanMetadataElement parseBeanElement(Element element, ParserContext parserContext) { |
154 | String refAttribute = element.getAttribute(REF_ATTR); |
155 | Element beanElement = DomUtils.getChildElementByTagName(element, BEAN_ELE); |
156 | Element refElement = DomUtils.getChildElementByTagName(element, REF_ELE); |
157 | |
158 | if (StringUtils.hasText(refAttribute)) { |
159 | return new RuntimeBeanReference(refAttribute); |
160 | } |
161 | else if (beanElement != null) { |
162 | BeanDefinitionHolder beanDefinitionHolder = parserContext.getDelegate().parseBeanDefinitionElement( |
163 | beanElement); |
164 | parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDefinitionHolder); |
165 | return beanDefinitionHolder; |
166 | } |
167 | else if (refElement != null) { |
168 | return (BeanMetadataElement) parserContext.getDelegate().parsePropertySubElement(refElement, null); |
169 | } |
170 | |
171 | parserContext.getReaderContext().error( |
172 | "One of ref attribute or a nested bean definition or ref element must be specified", element); |
173 | return null; |
174 | } |
175 | |
176 | } |