001/* 002 * Copyright 2013-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.jsr.job.flow; 017 018import org.springframework.batch.core.ExitStatus; 019import org.springframework.batch.core.JobExecution; 020import org.springframework.batch.core.JobExecutionException; 021import org.springframework.batch.core.JobInterruptedException; 022import org.springframework.batch.core.Step; 023import org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean.DelegateState; 024import org.springframework.batch.core.explore.JobExplorer; 025import org.springframework.batch.core.job.AbstractJob; 026import org.springframework.batch.core.job.flow.Flow; 027import org.springframework.batch.core.job.flow.FlowExecutionException; 028import org.springframework.batch.core.job.flow.FlowJob; 029import org.springframework.batch.core.job.flow.JobFlowExecutor; 030import org.springframework.batch.core.job.flow.State; 031import org.springframework.batch.core.job.flow.support.state.FlowState; 032import org.springframework.batch.core.jsr.job.JsrStepHandler; 033import org.springframework.batch.core.jsr.job.flow.support.JsrFlow; 034import org.springframework.batch.core.jsr.job.flow.support.state.JsrStepState; 035import org.springframework.batch.core.jsr.step.DecisionStep; 036import org.springframework.batch.core.launch.NoSuchJobException; 037import org.springframework.batch.core.launch.support.ExitCodeMapper; 038 039/** 040 * JSR-352 specific extension of the {@link FlowJob}. 041 * 042 * @author Michael Minella 043 * @since 3.0 044 */ 045public class JsrFlowJob extends FlowJob { 046 047 private JobExplorer jobExplorer; 048 049 /** 050 * No arg constructor (invalid state) 051 */ 052 public JsrFlowJob() { 053 super(); 054 } 055 056 /** 057 * Main constructor 058 * 059 * @param name of the flow 060 */ 061 public JsrFlowJob(String name) { 062 super(name); 063 } 064 065 public void setJobExplorer(JobExplorer jobExplorer) { 066 this.jobExplorer = jobExplorer; 067 } 068 069 /** 070 * @see AbstractJob#doExecute(JobExecution) 071 */ 072 @Override 073 protected void doExecute(final JobExecution execution) throws JobExecutionException { 074 try { 075 JobFlowExecutor executor = new JsrFlowExecutor(getJobRepository(), 076 new JsrStepHandler(getJobRepository(), jobExplorer), execution); 077 078 State startState = ((JsrFlow)flow).getStartState(); 079 080 validateFirstStep(startState); 081 082 executor.updateJobExecutionStatus(flow.start(executor).getStatus()); 083 } 084 catch (FlowExecutionException e) { 085 if (e.getCause() instanceof JobExecutionException) { 086 throw (JobExecutionException) e.getCause(); 087 } 088 throw new JobExecutionException("Flow execution ended unexpectedly", e); 089 } 090 } 091 092 private void validateFirstStep(State startState) 093 throws JobExecutionException { 094 while(true) { 095 if(startState instanceof DelegateState) { 096 startState = ((DelegateState) startState).getState(); 097 } else if(startState instanceof JsrStepState) { 098 String stepName = startState.getName().substring(startState.getName().indexOf(".") + 1, startState.getName().length()); 099 Step step = ((JsrStepState) startState).getStep(stepName); 100 if(step instanceof DecisionStep) { 101 throw new JobExecutionException("Decision step is an invalid first step"); 102 } else { 103 break; 104 } 105 } else if(startState instanceof FlowState){ 106 Flow firstFlow = ((FlowState) startState).getFlows().iterator().next(); 107 startState = firstFlow.getStates().iterator().next(); 108 } else { 109 break; 110 } 111 } 112 } 113 114 /** 115 * Default mapping from throwable to {@link ExitStatus}. 116 * 117 * @param ex the cause of the failure 118 * @return an {@link ExitStatus} 119 */ 120 @Override 121 protected ExitStatus getDefaultExitStatusForFailure(Throwable ex, JobExecution execution) { 122 if(!ExitStatus.isNonDefaultExitStatus(execution.getExitStatus())) { 123 return execution.getExitStatus(); 124 } else { 125 ExitStatus exitStatus; 126 if (ex instanceof JobInterruptedException 127 || ex.getCause() instanceof JobInterruptedException) { 128 exitStatus = ExitStatus.STOPPED 129 .addExitDescription(JobInterruptedException.class.getName()); 130 } else if (ex instanceof NoSuchJobException 131 || ex.getCause() instanceof NoSuchJobException) { 132 exitStatus = new ExitStatus(ExitCodeMapper.NO_SUCH_JOB, ex 133 .getClass().getName()); 134 } else { 135 exitStatus = ExitStatus.FAILED.addExitDescription(ex); 136 } 137 138 return exitStatus; 139 } 140 } 141}