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 | */ |
16 | package org.springframework.batch.core.configuration.xml; |
17 | |
18 | import java.util.Map; |
19 | |
20 | import org.springframework.beans.PropertyValue; |
21 | import org.springframework.beans.factory.config.BeanDefinition; |
22 | import org.springframework.beans.factory.config.TypedStringValue; |
23 | import org.springframework.beans.factory.support.AbstractBeanDefinition; |
24 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
25 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
26 | import org.springframework.beans.factory.support.ManagedMap; |
27 | import org.springframework.beans.factory.xml.ParserContext; |
28 | import org.springframework.util.StringUtils; |
29 | import org.w3c.dom.Element; |
30 | |
31 | /** |
32 | * Utility methods used in parsing of the batch core namespace |
33 | * |
34 | * @author Thomas Risberg |
35 | */ |
36 | public class CoreNamespaceUtils { |
37 | |
38 | private static final String STEP_SCOPE_PROCESSOR_BEAN_NAME = "org.springframework.batch.core.scope.internalStepScope"; |
39 | |
40 | private static final String STEP_SCOPE_PROCESSOR_CLASS_NAME = "org.springframework.batch.core.scope.StepScope"; |
41 | |
42 | private static final String CUSTOM_EDITOR_CONFIGURER_CLASS_NAME = "org.springframework.beans.factory.config.CustomEditorConfigurer"; |
43 | |
44 | private static final String RANGE_ARRAY_CLASS_NAME = "org.springframework.batch.item.file.transform.Range[]"; |
45 | |
46 | private static final String RANGE_ARRAY_EDITOR_CLASS_NAME = "org.springframework.batch.item.file.transform.RangeArrayPropertyEditor"; |
47 | |
48 | private static final String CORE_NAMESPACE_POST_PROCESSOR_CLASS_NAME = "org.springframework.batch.core.configuration.xml.CoreNamespacePostProcessor"; |
49 | |
50 | protected static void autoregisterBeansForNamespace(ParserContext parserContext, Object source) { |
51 | checkForStepScope(parserContext, source); |
52 | addRangePropertyEditor(parserContext); |
53 | addCoreNamespacePostProcessor(parserContext); |
54 | } |
55 | |
56 | private static void checkForStepScope(ParserContext parserContext, Object source) { |
57 | boolean foundStepScope = false; |
58 | String[] beanNames = parserContext.getRegistry().getBeanDefinitionNames(); |
59 | for (String beanName : beanNames) { |
60 | BeanDefinition bd = parserContext.getRegistry().getBeanDefinition(beanName); |
61 | if (STEP_SCOPE_PROCESSOR_CLASS_NAME.equals(bd.getBeanClassName())) { |
62 | foundStepScope = true; |
63 | break; |
64 | } |
65 | } |
66 | if (!foundStepScope) { |
67 | BeanDefinitionBuilder stepScopeBuilder = BeanDefinitionBuilder |
68 | .genericBeanDefinition(STEP_SCOPE_PROCESSOR_CLASS_NAME); |
69 | AbstractBeanDefinition abd = stepScopeBuilder.getBeanDefinition(); |
70 | abd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
71 | abd.setSource(source); |
72 | parserContext.getRegistry().registerBeanDefinition(STEP_SCOPE_PROCESSOR_BEAN_NAME, abd); |
73 | } |
74 | } |
75 | |
76 | /** |
77 | * Register a RangePropertyEditor if one does not already exist. |
78 | * |
79 | * @param parserContext |
80 | */ |
81 | @SuppressWarnings("unchecked") |
82 | private static void addRangePropertyEditor(ParserContext parserContext) { |
83 | BeanDefinitionRegistry registry = parserContext.getRegistry(); |
84 | if (!rangeArrayEditorAlreadyDefined(registry)) { |
85 | AbstractBeanDefinition customEditorConfigurer = BeanDefinitionBuilder.genericBeanDefinition( |
86 | CUSTOM_EDITOR_CONFIGURER_CLASS_NAME).getBeanDefinition(); |
87 | customEditorConfigurer.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
88 | ManagedMap editors = new ManagedMap(); |
89 | editors.put(RANGE_ARRAY_CLASS_NAME, RANGE_ARRAY_EDITOR_CLASS_NAME); |
90 | customEditorConfigurer.getPropertyValues().addPropertyValue("customEditors", editors); |
91 | registry.registerBeanDefinition(CUSTOM_EDITOR_CONFIGURER_CLASS_NAME, customEditorConfigurer); |
92 | } |
93 | } |
94 | |
95 | private static boolean rangeArrayEditorAlreadyDefined(BeanDefinitionRegistry registry) { |
96 | for (String beanName : registry.getBeanDefinitionNames()) { |
97 | BeanDefinition bd = registry.getBeanDefinition(beanName); |
98 | if (CUSTOM_EDITOR_CONFIGURER_CLASS_NAME.equals(bd.getBeanClassName())) { |
99 | PropertyValue pv = bd.getPropertyValues().getPropertyValue("customEditors"); |
100 | if (pv != null) { |
101 | for (Map.Entry<?, ?> entry : ((Map<?, ?>) pv.getValue()).entrySet()) { |
102 | if (entry.getKey() instanceof TypedStringValue) { |
103 | if (RANGE_ARRAY_CLASS_NAME.equals(((TypedStringValue) entry.getKey()).getValue())) { |
104 | return true; |
105 | } |
106 | } |
107 | else if (entry.getKey() instanceof String) { |
108 | if (RANGE_ARRAY_CLASS_NAME.equals(entry.getKey())) { |
109 | return true; |
110 | } |
111 | } |
112 | } |
113 | } |
114 | } |
115 | } |
116 | return false; |
117 | } |
118 | |
119 | /** |
120 | * @param parserContext |
121 | */ |
122 | private static void addCoreNamespacePostProcessor(ParserContext parserContext) { |
123 | BeanDefinitionRegistry registry = parserContext.getRegistry(); |
124 | if (!coreNamespaceBeanPostProcessorAlreadyDefined(registry)) { |
125 | AbstractBeanDefinition postProcessorBeanDef = BeanDefinitionBuilder.genericBeanDefinition( |
126 | CORE_NAMESPACE_POST_PROCESSOR_CLASS_NAME).getBeanDefinition(); |
127 | postProcessorBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
128 | registry.registerBeanDefinition(CORE_NAMESPACE_POST_PROCESSOR_CLASS_NAME, postProcessorBeanDef); |
129 | } |
130 | } |
131 | |
132 | private static boolean coreNamespaceBeanPostProcessorAlreadyDefined(BeanDefinitionRegistry registry) { |
133 | for (String beanName : registry.getBeanDefinitionNames()) { |
134 | BeanDefinition bd = registry.getBeanDefinition(beanName); |
135 | if (CORE_NAMESPACE_POST_PROCESSOR_CLASS_NAME.equals(bd.getBeanClassName())) { |
136 | return true; |
137 | } |
138 | } |
139 | return false; |
140 | } |
141 | |
142 | /** |
143 | * Should this element be treated as incomplete? If it has a parent or is |
144 | * abstract, then it may not have all properties. |
145 | * |
146 | * @param element |
147 | * @return TRUE if the element is abstract or has a parent |
148 | */ |
149 | public static boolean isUnderspecified(Element element) { |
150 | return isAbstract(element) || StringUtils.hasText(element.getAttribute("parent")); |
151 | } |
152 | |
153 | /** |
154 | * @param element |
155 | * @return TRUE if the element is abstract |
156 | */ |
157 | public static boolean isAbstract(Element element) { |
158 | String abstractAttr = element.getAttribute("abstract"); |
159 | return StringUtils.hasText(abstractAttr) && Boolean.valueOf(abstractAttr); |
160 | } |
161 | |
162 | /** |
163 | * Check that the schema location declared in the source file being parsed |
164 | * matches the Spring Batch version. (The old 2.0 schema is not 100% |
165 | * compatible with the new parser, so it is an error to explicitly define |
166 | * 2.0. It might be an error to declare spring-batch.xsd as an alias, but |
167 | * you are only going to find that out when one of the sub parses breaks.) |
168 | * |
169 | * @param element the element that is to be parsed next |
170 | * @return true if we find a schema declaration that matches |
171 | */ |
172 | public static boolean namespaceMatchesVersion(Element element) { |
173 | return matchesVersionInternal(element) |
174 | && matchesVersionInternal(element.getOwnerDocument().getDocumentElement()); |
175 | } |
176 | |
177 | private static boolean matchesVersionInternal(Element element) { |
178 | String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"); |
179 | return schemaLocation.matches("(?m).*spring-batch-2.2.xsd.*") |
180 | || schemaLocation.matches("(?m).*spring-batch.xsd.*") |
181 | || !schemaLocation.matches("(?m).*spring-batch.*"); |
182 | } |
183 | |
184 | } |