001/* 002 * Copyright 2006-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.job.flow; 017 018import java.util.Collection; 019import java.util.Map; 020import java.util.concurrent.ConcurrentHashMap; 021 022import org.springframework.batch.core.Job; 023import org.springframework.batch.core.JobExecution; 024import org.springframework.batch.core.JobExecutionException; 025import org.springframework.batch.core.Step; 026import org.springframework.batch.core.job.AbstractJob; 027import org.springframework.batch.core.job.SimpleStepHandler; 028import org.springframework.batch.core.step.StepHolder; 029import org.springframework.batch.core.step.StepLocator; 030 031/** 032 * Implementation of the {@link Job} interface that allows for complex flows of 033 * steps, rather than requiring sequential execution. In general, this job 034 * implementation was designed to be used behind a parser, allowing for a 035 * namespace to abstract away details. 036 * 037 * @author Dave Syer 038 * @since 2.0 039 */ 040public class FlowJob extends AbstractJob { 041 042 protected Flow flow; 043 044 private Map<String, Step> stepMap = new ConcurrentHashMap<String, Step>(); 045 046 private volatile boolean initialized = false; 047 048 /** 049 * Create a {@link FlowJob} with null name and no flow (invalid state). 050 */ 051 public FlowJob() { 052 super(); 053 } 054 055 /** 056 * Create a {@link FlowJob} with provided name and no flow (invalid state). 057 * 058 * @param name the name to be associated with the FlowJob. 059 */ 060 public FlowJob(String name) { 061 super(name); 062 } 063 064 /** 065 * Public setter for the flow. 066 * 067 * @param flow the flow to set 068 */ 069 public void setFlow(Flow flow) { 070 this.flow = flow; 071 } 072 073 /** 074 * {@inheritDoc} 075 */ 076 @Override 077 public Step getStep(String stepName) { 078 if (!initialized) { 079 init(); 080 } 081 return stepMap.get(stepName); 082 } 083 084 /** 085 * Initialize the step names 086 */ 087 private void init() { 088 findSteps(flow, stepMap); 089 } 090 091 /** 092 * @param flow 093 * @param map 094 */ 095 private void findSteps(Flow flow, Map<String, Step> map) { 096 097 for (State state : flow.getStates()) { 098 if (state instanceof StepLocator) { 099 StepLocator locator = (StepLocator) state; 100 for (String name : locator.getStepNames()) { 101 map.put(name, locator.getStep(name)); 102 } 103 } else if (state instanceof StepHolder) { 104 Step step = ((StepHolder) state).getStep(); 105 String name = step.getName(); 106 stepMap.put(name, step); 107 } 108 else if (state instanceof FlowHolder) { 109 for (Flow subflow : ((FlowHolder) state).getFlows()) { 110 findSteps(subflow, map); 111 } 112 } 113 } 114 115 } 116 117 /** 118 * {@inheritDoc} 119 */ 120 @Override 121 public Collection<String> getStepNames() { 122 if (!initialized) { 123 init(); 124 } 125 return stepMap.keySet(); 126 } 127 128 /** 129 * @see AbstractJob#doExecute(JobExecution) 130 */ 131 @Override 132 protected void doExecute(final JobExecution execution) throws JobExecutionException { 133 try { 134 JobFlowExecutor executor = new JobFlowExecutor(getJobRepository(), 135 new SimpleStepHandler(getJobRepository()), execution); 136 executor.updateJobExecutionStatus(flow.start(executor).getStatus()); 137 } 138 catch (FlowExecutionException e) { 139 if (e.getCause() instanceof JobExecutionException) { 140 throw (JobExecutionException) e.getCause(); 141 } 142 throw new JobExecutionException("Flow execution ended unexpectedly", e); 143 } 144 } 145 146}