001/* 002 * Copyright 2012-2014 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.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.List; 023 024import org.springframework.batch.core.job.flow.Flow; 025import org.springframework.batch.core.job.flow.FlowExecutionStatus; 026import org.springframework.batch.core.job.flow.FlowExecutor; 027import org.springframework.batch.core.job.flow.FlowHolder; 028import org.springframework.batch.core.job.flow.State; 029import org.springframework.batch.core.job.flow.support.SimpleFlow; 030import org.springframework.batch.core.job.flow.support.StateTransition; 031import org.springframework.batch.core.job.flow.support.state.AbstractState; 032import org.springframework.batch.core.job.flow.support.state.StepState; 033import org.springframework.beans.factory.FactoryBean; 034import org.springframework.beans.factory.InitializingBean; 035import org.springframework.util.Assert; 036 037/** 038 * Convenience factory for SimpleFlow instances for use in XML namespace. It 039 * replaces the states in the input with proxies that have a unique name formed 040 * from the flow name and the original state name (unless the name is already in 041 * that form, in which case it is not modified). 042 * 043 * @author Dave Syer 044 * @author Michael Minella 045 */ 046public class SimpleFlowFactoryBean implements FactoryBean<SimpleFlow>, InitializingBean { 047 048 private String name; 049 050 private List<StateTransition> stateTransitions; 051 052 private String prefix; 053 054 private Comparator<StateTransition> stateTransitionComparator; 055 056 private Class<SimpleFlow> flowType; 057 058 /** 059 * @param stateTransitionComparator {@link Comparator} implementation that addresses 060 * the ordering of state evaluation 061 */ 062 public void setStateTransitionComparator(Comparator<StateTransition> stateTransitionComparator) { 063 this.stateTransitionComparator = stateTransitionComparator; 064 } 065 066 /** 067 * @param flowType Used to inject the type of flow (regular Spring Batch or JSR-352) 068 */ 069 public void setFlowType(Class<SimpleFlow> flowType) { 070 this.flowType = flowType; 071 } 072 073 /** 074 * The name of the flow that is created by this factory. 075 * 076 * @param name the value of the name 077 */ 078 public void setName(String name) { 079 this.name = name; 080 this.prefix = name + "."; 081 } 082 083 /** 084 * The raw state transitions for the flow. They will be transformed into 085 * proxies that have the same behavior but unique names prefixed with the 086 * flow name. 087 * 088 * @param stateTransitions the list of transitions 089 */ 090 public void setStateTransitions(List<StateTransition> stateTransitions) { 091 this.stateTransitions = stateTransitions; 092 } 093 094 /** 095 * Check mandatory properties (name). 096 * 097 * @throws Exception thrown if error occurs. 098 */ 099 @Override 100 public void afterPropertiesSet() throws Exception { 101 Assert.hasText(name, "The flow must have a name"); 102 103 if(flowType == null) { 104 flowType = SimpleFlow.class; 105 } 106 } 107 108 /* (non-Javadoc) 109 * @see org.springframework.beans.factory.FactoryBean#getObject() 110 */ 111 @Override 112 public SimpleFlow getObject() throws Exception { 113 SimpleFlow flow = flowType.getConstructor(String.class).newInstance(name); 114 115 flow.setStateTransitionComparator(stateTransitionComparator); 116 117 List<StateTransition> updatedTransitions = new ArrayList<StateTransition>(); 118 for (StateTransition stateTransition : stateTransitions) { 119 State state = getProxyState(stateTransition.getState()); 120 updatedTransitions.add(StateTransition.switchOriginAndDestination(stateTransition, state, 121 getNext(stateTransition.getNext()))); 122 } 123 124 flow.setStateTransitions(updatedTransitions); 125 flow.afterPropertiesSet(); 126 return flow; 127 128 } 129 130 private String getNext(String next) { 131 if (next == null) { 132 return null; 133 } 134 return (next.startsWith(this.prefix) ? "" : this.prefix) + next; 135 } 136 137 /** 138 * Convenience method to get a state that proxies the input but with a 139 * different name, appropriate to this flow. If the state is a StepState 140 * then the step name is also changed. 141 * 142 * @param state 143 * @return 144 */ 145 private State getProxyState(State state) { 146 String oldName = state.getName(); 147 if (oldName.startsWith(prefix)) { 148 return state; 149 } 150 String stateName = prefix + oldName; 151 if (state instanceof StepState) { 152 return createNewStepState(state, oldName, stateName); 153 } 154 return new DelegateState(stateName, state); 155 } 156 157 /** 158 * Provides an extension point to provide alternative {@link StepState} 159 * implementations within a {@link SimpleFlow} 160 * 161 * @param state The state that will be used to create the StepState 162 * @param oldName The name to be replaced 163 * @param stateName The name for the new State 164 * @return a state for the requested data 165 */ 166 protected State createNewStepState(State state, String oldName, 167 String stateName) { 168 return new StepState(stateName, ((StepState) state).getStep(oldName)); 169 } 170 171 @Override 172 public Class<?> getObjectType() { 173 return SimpleFlow.class; 174 } 175 176 @Override 177 public boolean isSingleton() { 178 return true; 179 } 180 181 /** 182 * A State that proxies a delegate and changes its name but leaves its 183 * behavior unchanged. 184 * 185 * @author Dave Syer 186 * 187 */ 188 public static class DelegateState extends AbstractState implements FlowHolder { 189 private final State state; 190 191 private DelegateState(String name, State state) { 192 super(name); 193 this.state = state; 194 } 195 196 public State getState() { 197 return this.state; 198 } 199 200 @Override 201 public boolean isEndState() { 202 return state.isEndState(); 203 } 204 205 @Override 206 public FlowExecutionStatus handle(FlowExecutor executor) throws Exception { 207 return state.handle(executor); 208 } 209 210 @Override 211 public Collection<Flow> getFlows() { 212 return (state instanceof FlowHolder) ? ((FlowHolder)state).getFlows() : Collections.<Flow>emptyList(); 213 } 214 215 } 216 217}