View Javadoc

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 }