View Javadoc

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 }