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.Collection; |
19 | import java.util.List; |
20 | |
21 | import org.springframework.beans.MutablePropertyValues; |
22 | import org.springframework.beans.factory.config.BeanDefinition; |
23 | import org.springframework.beans.factory.config.RuntimeBeanReference; |
24 | import org.springframework.beans.factory.support.AbstractBeanDefinition; |
25 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
26 | import org.springframework.beans.factory.support.GenericBeanDefinition; |
27 | import org.springframework.beans.factory.support.ManagedList; |
28 | import org.springframework.beans.factory.xml.ParserContext; |
29 | import org.springframework.core.task.TaskExecutor; |
30 | import org.springframework.util.StringUtils; |
31 | import org.springframework.util.xml.DomUtils; |
32 | import org.w3c.dom.Element; |
33 | |
34 | /** |
35 | * Internal parser for the <split/> elements inside a job. A split element |
36 | * optionally references a bean definition for a {@link TaskExecutor} and goes |
37 | * on to list a set of transitions to other states with <next on="pattern" |
38 | * to="stepName"/>. Used by the {@link JobParser}. |
39 | * |
40 | * @see JobParser |
41 | * |
42 | * @author Dave Syer |
43 | * |
44 | */ |
45 | public class SplitParser { |
46 | |
47 | /** |
48 | * |
49 | */ |
50 | private static final String PARENT_ATTR = "parent"; |
51 | |
52 | private final String jobFactoryRef; |
53 | |
54 | /** |
55 | * Construct a {@link InlineFlowParser} using the provided job repository |
56 | * ref. |
57 | * |
58 | * @param jobFactoryRef the reference to the {@link JobParserJobFactoryBean} |
59 | * from the enclosing tag |
60 | */ |
61 | public SplitParser(String jobFactoryRef) { |
62 | this.jobFactoryRef = jobFactoryRef; |
63 | } |
64 | |
65 | /** |
66 | * Parse the split and turn it into a list of transitions. |
67 | * |
68 | * @param element the <split/gt; element to parse |
69 | * @param parserContext the parser context for the bean factory |
70 | * @return a collection of bean definitions for |
71 | * {@link org.springframework.batch.core.job.flow.support.StateTransition} |
72 | * instances objects |
73 | */ |
74 | public Collection<BeanDefinition> parse(Element element, ParserContext parserContext) { |
75 | |
76 | String idAttribute = element.getAttribute("id"); |
77 | |
78 | BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder |
79 | .genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.SplitState"); |
80 | |
81 | String taskExecutorBeanId = element.getAttribute("task-executor"); |
82 | if (StringUtils.hasText(taskExecutorBeanId)) { |
83 | RuntimeBeanReference taskExecutorRef = new RuntimeBeanReference(taskExecutorBeanId); |
84 | stateBuilder.addPropertyValue("taskExecutor", taskExecutorRef); |
85 | } |
86 | |
87 | @SuppressWarnings("unchecked") |
88 | List<Element> flowElements = DomUtils.getChildElementsByTagName(element, "flow"); |
89 | |
90 | if (flowElements.size() < 2) { |
91 | parserContext.getReaderContext().error("A <split/> must contain at least two 'flow' elements.", element); |
92 | } |
93 | |
94 | @SuppressWarnings("unchecked") |
95 | Collection<Object> flows = new ManagedList(); |
96 | int i = 0; |
97 | String prefix = idAttribute; |
98 | for (Element nextElement : flowElements) { |
99 | String ref = nextElement.getAttribute(PARENT_ATTR); |
100 | if (StringUtils.hasText(ref)) { |
101 | if (nextElement.getElementsByTagName("*").getLength() > 0) { |
102 | parserContext.getReaderContext().error( |
103 | "A <flow/> in a <split/> must have ref= or nested <flow/>, but not both.", nextElement); |
104 | } |
105 | AbstractBeanDefinition flowDefinition = new GenericBeanDefinition(); |
106 | flowDefinition.setParentName(ref); |
107 | MutablePropertyValues propertyValues = flowDefinition.getPropertyValues(); |
108 | propertyValues.addPropertyValue("name", prefix + "." + i); |
109 | flows.add(flowDefinition); |
110 | } |
111 | else { |
112 | InlineFlowParser flowParser = new InlineFlowParser(prefix + "." + i, jobFactoryRef); |
113 | flows.add(flowParser.parse(nextElement, parserContext)); |
114 | } |
115 | i++; |
116 | } |
117 | |
118 | stateBuilder.addConstructorArgValue(flows); |
119 | stateBuilder.addConstructorArgValue(prefix); |
120 | |
121 | return InlineFlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element); |
122 | |
123 | } |
124 | |
125 | } |