001/* 002 * Copyright 2006-2008 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.springframework.batch.core.configuration.xml; 017 018import java.util.Collection; 019import java.util.List; 020 021import org.springframework.beans.MutablePropertyValues; 022import org.springframework.beans.factory.config.BeanDefinition; 023import org.springframework.beans.factory.config.RuntimeBeanReference; 024import org.springframework.beans.factory.support.AbstractBeanDefinition; 025import org.springframework.beans.factory.support.BeanDefinitionBuilder; 026import org.springframework.beans.factory.support.GenericBeanDefinition; 027import org.springframework.beans.factory.support.ManagedList; 028import org.springframework.beans.factory.xml.ParserContext; 029import org.springframework.core.task.TaskExecutor; 030import org.springframework.util.StringUtils; 031import org.springframework.util.xml.DomUtils; 032import org.w3c.dom.Element; 033 034/** 035 * Internal parser for the <split/> elements inside a job. A split element 036 * optionally references a bean definition for a {@link TaskExecutor} and goes 037 * on to list a set of transitions to other states with <next on="pattern" 038 * to="stepName"/>. Used by the {@link JobParser}. 039 * 040 * @see JobParser 041 * 042 * @author Dave Syer 043 * 044 */ 045public class SplitParser { 046 047 private static final String PARENT_ATTR = "parent"; 048 049 private final String jobFactoryRef; 050 051 /** 052 * Construct a {@link InlineFlowParser} using the provided job repository 053 * ref. 054 * 055 * @param jobFactoryRef the reference to the {@link JobParserJobFactoryBean} 056 * from the enclosing tag 057 */ 058 public SplitParser(String jobFactoryRef) { 059 this.jobFactoryRef = jobFactoryRef; 060 } 061 062 /** 063 * Parse the split and turn it into a list of transitions. 064 * 065 * @param element the <split/gt; element to parse 066 * @param parserContext the parser context for the bean factory 067 * @return a collection of bean definitions for 068 * {@link org.springframework.batch.core.job.flow.support.StateTransition} 069 * instances objects 070 */ 071 public Collection<BeanDefinition> parse(Element element, ParserContext parserContext) { 072 073 String idAttribute = element.getAttribute("id"); 074 075 BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder 076 .genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.SplitState"); 077 078 String taskExecutorBeanId = element.getAttribute("task-executor"); 079 if (StringUtils.hasText(taskExecutorBeanId)) { 080 RuntimeBeanReference taskExecutorRef = new RuntimeBeanReference(taskExecutorBeanId); 081 stateBuilder.addPropertyValue("taskExecutor", taskExecutorRef); 082 } 083 084 List<Element> flowElements = DomUtils.getChildElementsByTagName(element, "flow"); 085 086 if (flowElements.size() < 2) { 087 parserContext.getReaderContext().error("A <split/> must contain at least two 'flow' elements.", element); 088 } 089 090 Collection<BeanDefinition> flows = new ManagedList<BeanDefinition>(); 091 int i = 0; 092 String prefix = idAttribute; 093 for (Element nextElement : flowElements) { 094 String ref = nextElement.getAttribute(PARENT_ATTR); 095 if (StringUtils.hasText(ref)) { 096 if (nextElement.getElementsByTagName("*").getLength() > 0) { 097 parserContext.getReaderContext().error( 098 "A <flow/> in a <split/> must have ref= or nested <flow/>, but not both.", nextElement); 099 } 100 AbstractBeanDefinition flowDefinition = new GenericBeanDefinition(); 101 flowDefinition.setParentName(ref); 102 MutablePropertyValues propertyValues = flowDefinition.getPropertyValues(); 103 propertyValues.addPropertyValue("name", prefix + "." + i); 104 flows.add(flowDefinition); 105 } 106 else { 107 InlineFlowParser flowParser = new InlineFlowParser(prefix + "." + i, jobFactoryRef); 108 flows.add(flowParser.parse(nextElement, parserContext)); 109 } 110 i++; 111 } 112 113 stateBuilder.addConstructorArgValue(flows); 114 stateBuilder.addConstructorArgValue(prefix); 115 116 return InlineFlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element); 117 118 } 119 120}