/* * Copyright 2006-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.batch.core.configuration.xml; import java.util.Arrays; import java.util.List; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; /** * Parser for the <job/> element in the Batch namespace. Sets up and returns * a bean definition for a {@link org.springframework.batch.core.Job}. * * @author Dave Syer * */ public class JobParser extends AbstractSingleBeanDefinitionParser { private static final String MERGE_ATTR = "merge"; private static final String REF_ATTR = "ref"; private static final String BEAN_ELE = "bean"; private static final String REF_ELE = "ref"; private static final JobExecutionListenerParser jobListenerParser = new JobExecutionListenerParser(); @Override protected Class<JobParserJobFactoryBean> getBeanClass(Element element) { return JobParserJobFactoryBean.class; } /** * Create a bean definition for a * {@link org.springframework.batch.core.job.flow.FlowJob}. Nested step * elements are delegated to an {@link InlineStepParser}. * * @see AbstractSingleBeanDefinitionParser#doParse(Element, ParserContext, * BeanDefinitionBuilder) */ @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { if (!CoreNamespaceUtils.namespaceMatchesVersion(element)) { parserContext.getReaderContext().error( "You are using a version of the spring-batch XSD that is not compatible with Spring Batch 3.0." + " Please upgrade your schema declarations (or use the spring-batch.xsd alias if you are " + "feeling lucky).", element); return; } CoreNamespaceUtils.autoregisterBeansForNamespace(parserContext, parserContext.extractSource(element)); String jobName = element.getAttribute("id"); builder.addConstructorArgValue(jobName); boolean isAbstract = CoreNamespaceUtils.isAbstract(element); builder.setAbstract(isAbstract); String parentRef = element.getAttribute("parent"); if (StringUtils.hasText(parentRef)) { builder.setParentName(parentRef); } String repositoryAttribute = element.getAttribute("job-repository"); if (StringUtils.hasText(repositoryAttribute)) { builder.addPropertyReference("jobRepository", repositoryAttribute); } Element validator = DomUtils.getChildElementByTagName(element, "validator"); if (validator != null) { builder.addPropertyValue("jobParametersValidator", parseBeanElement(validator, parserContext)); } String restartableAttribute = element.getAttribute("restartable"); if (StringUtils.hasText(restartableAttribute)) { builder.addPropertyValue("restartable", restartableAttribute); } String incrementer = (element.getAttribute("incrementer")); if (StringUtils.hasText(incrementer)) { builder.addPropertyReference("jobParametersIncrementer", incrementer); } if (isAbstract) { for (String tagName : Arrays.asList("step", "decision", "split")) { if (!DomUtils.getChildElementsByTagName(element, tagName).isEmpty()) { parserContext.getReaderContext().error( "The <" + tagName + "/> element may not appear on a <job/> with abstract=\"true\" [" + jobName + "]", element); } } } else { InlineFlowParser flowParser = new InlineFlowParser(jobName, jobName); BeanDefinition flowDef = flowParser.parse(element, parserContext); builder.addPropertyValue("flow", flowDef); } Element description = DomUtils.getChildElementByTagName(element, "description"); if (description != null) { builder.getBeanDefinition().setDescription(description.getTextContent()); } List<Element> listenersElements = DomUtils.getChildElementsByTagName(element, "listeners"); if (listenersElements.size() == 1) { Element listenersElement = listenersElements.get(0); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(listenersElement.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); ManagedList<BeanDefinition> listeners = new ManagedList<BeanDefinition>(); listeners.setMergeEnabled(listenersElement.hasAttribute(MERGE_ATTR) && Boolean.valueOf(listenersElement.getAttribute(MERGE_ATTR))); List<Element> listenerElements = DomUtils.getChildElementsByTagName(listenersElement, "listener"); for (Element listenerElement : listenerElements) { listeners.add(jobListenerParser.parse(listenerElement, parserContext)); } builder.addPropertyValue("jobExecutionListeners", listeners); parserContext.popAndRegisterContainingComponent(); } else if (listenersElements.size() > 1) { parserContext.getReaderContext().error( "The '<listeners/>' element may not appear more than once in a single <job/>.", element); } } public BeanMetadataElement parseBeanElement(Element element, ParserContext parserContext) { String refAttribute = element.getAttribute(REF_ATTR); Element beanElement = DomUtils.getChildElementByTagName(element, BEAN_ELE); Element refElement = DomUtils.getChildElementByTagName(element, REF_ELE); if (StringUtils.hasText(refAttribute)) { return new RuntimeBeanReference(refAttribute); } else if (beanElement != null) { BeanDefinitionHolder beanDefinitionHolder = parserContext.getDelegate().parseBeanDefinitionElement( beanElement); parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDefinitionHolder); return beanDefinitionHolder; } else if (refElement != null) { return (BeanMetadataElement) parserContext.getDelegate().parsePropertySubElement(refElement, null); } parserContext.getReaderContext().error( "One of ref attribute or a nested bean definition or ref element must be specified", element); return null; } }