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}