View Javadoc

1   /*
2    * Copyright 2012-2013 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.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.springframework.batch.core.job.flow.Flow;
24  import org.springframework.batch.core.job.flow.FlowExecutionStatus;
25  import org.springframework.batch.core.job.flow.FlowExecutor;
26  import org.springframework.batch.core.job.flow.FlowHolder;
27  import org.springframework.batch.core.job.flow.State;
28  import org.springframework.batch.core.job.flow.support.SimpleFlow;
29  import org.springframework.batch.core.job.flow.support.StateTransition;
30  import org.springframework.batch.core.job.flow.support.state.AbstractState;
31  import org.springframework.batch.core.job.flow.support.state.StepState;
32  import org.springframework.beans.factory.FactoryBean;
33  import org.springframework.beans.factory.InitializingBean;
34  import org.springframework.util.Assert;
35  
36  /**
37   * Convenience factory for SimpleFlow instances for use in XML namespace. It
38   * replaces the states in the input with proxies that have a unique name formed
39   * from the flow name and the original state name (unless the name is already in
40   * that form, in which case it is not modified).
41   *
42   * @author Dave Syer
43   *
44   */
45  @SuppressWarnings("rawtypes")
46  public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
47  
48  	private String name;
49  
50  	private List<StateTransition> stateTransitions;
51  
52  	private String prefix;
53  
54  	/**
55  	 * The name of the flow that is created by this factory.
56  	 *
57  	 * @param name the value of the name
58  	 */
59  	public void setName(String name) {
60  		this.name = name;
61  		this.prefix = name + ".";
62  	}
63  
64  	/**
65  	 * The raw state transitions for the flow. They will be transformed into
66  	 * proxies that have the same behavior but unique names prefixed with the
67  	 * flow name.
68  	 *
69  	 * @param stateTransitions the list of transitions
70  	 */
71  	public void setStateTransitions(List<StateTransition> stateTransitions) {
72  		this.stateTransitions = stateTransitions;
73  	}
74  
75  	/**
76  	 * Check mandatory properties (name).
77  	 *
78  	 * @throws Exception
79  	 */
80  	@Override
81  	public void afterPropertiesSet() throws Exception {
82  		Assert.hasText(name, "The flow must have a name");
83  	}
84  
85  	@Override
86  	public Object getObject() throws Exception {
87  
88  		SimpleFlow flow = new SimpleFlow(name);
89  
90  		List<StateTransition> updatedTransitions = new ArrayList<StateTransition>();
91  		for (StateTransition stateTransition : stateTransitions) {
92  			State state = getProxyState(stateTransition.getState());
93  			updatedTransitions.add(StateTransition.switchOriginAndDestination(stateTransition, state,
94  					getNext(stateTransition.getNext())));
95  		}
96  
97  		flow.setStateTransitions(updatedTransitions);
98  		flow.afterPropertiesSet();
99  		return flow;
100 
101 	}
102 
103 	private String getNext(String next) {
104 		if (next == null) {
105 			return null;
106 		}
107 		return (next.startsWith(this.prefix) ? "" : this.prefix) + next;
108 	}
109 
110 	/**
111 	 * Convenience method to get a state that proxies the input but with a
112 	 * different name, appropriate to this flow. If the state is a StepState
113 	 * then the step name is also changed.
114 	 *
115 	 * @param state
116 	 * @return
117 	 */
118 	private State getProxyState(State state) {
119 		String oldName = state.getName();
120 		if (oldName.startsWith(prefix)) {
121 			return state;
122 		}
123 		String stateName = prefix + oldName;
124 		if (state instanceof StepState) {
125 			return new StepState(stateName, ((StepState) state).getStep());
126 		}
127 		return new DelegateState(stateName, state);
128 	}
129 
130 	@Override
131 	public Class<?> getObjectType() {
132 		return SimpleFlow.class;
133 	}
134 
135 	@Override
136 	public boolean isSingleton() {
137 		return true;
138 	}
139 
140 	/**
141 	 * A State that proxies a delegate and changes its name but leaves its
142 	 * behavior unchanged.
143 	 *
144 	 * @author Dave Syer
145 	 *
146 	 */
147 	private static class DelegateState extends AbstractState implements FlowHolder {
148 		private final State state;
149 
150 		private DelegateState(String name, State state) {
151 			super(name);
152 			this.state = state;
153 		}
154 
155 		@Override
156 		public boolean isEndState() {
157 			return state.isEndState();
158 		}
159 
160 		@Override
161 		public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
162 			return state.handle(executor);
163 		}
164 
165 		@Override
166 		public Collection<Flow> getFlows() {
167 			return (state instanceof FlowHolder) ? ((FlowHolder)state).getFlows() : Collections.<Flow>emptyList();
168 		}
169 
170 	}
171 
172 }