View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.sys.context;
20  
21  
22  import java.util.Arrays;
23  
24  
25  /**
26   * BatchStepTrigger writes .run files containing a job name and step name for BatchContainerStep to read. 
27   * It loops and sleeps until either a .success or an .error file is written for the Step. Once a result file is found tt logs the results and exits. 
28   * 
29   * BatchStepTrigger also checks for BatchContainerStep's .runlock file. If it doesn't find one (indicating the batch container is not running) it exits.
30   * BatchStepTrigger adds a ConsoleAppender to its Logger if one hasn't been configured.
31   * 
32   * Note that this class runs without starting the SpringContext. KFS Services and Beans are not available for use.
33   *
34   */
35  public class BatchStepTrigger {
36      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BatchStepTrigger.class);
37      
38      private static BatchStepTriggerParameters batchStepTriggerParameters;
39      
40      /**
41       * BatchStepTrigger is instantiated for each step in a brte script. 
42       * The value used when exiting the system tells the brte script how to handle failures.
43       * 0: okay, 4: the step returned false, 8: an exception occurred in the execution of the step or the trigger
44       *
45       * - verify that the batch container is running, exit with an 8 if not
46       * - for each step
47       *   - write a RUN semaphore file
48       *   - wait and listen for a result file (either SUCCESS or ERROR)
49       *   - if the result file is null the batch container is not running so remove the run file and exit with an 8
50       *   - if the result file is an ERROR
51       *     - if the file is EMPTY remove the result file and exit with a 4
52       *     - otherwise log the error contained in the result file, remove the result file, and exit with an 8
53       *   - otherwise remove the result file and exit with a 0  
54       * 
55       * @param args - refer to BatchStepTriggerParameters for details
56       */
57      public static void main(String[] args) {
58          try {            
59              Log4jConfigurer.configureLogging(false);
60              
61          	batchStepTriggerParameters = new BatchStepTriggerParameters(args);
62          	
63          	String[] stepNames = getStepNames();            
64              String jobName = getJobName();            
65              int stepIndex = getStepIndex();
66              long sleepInterval = getSleepInterval();            
67              BatchContainerDirectory batchContainerDirectory = getBatchContainerDirectory();
68              
69              LOG.info("Executing Job: " + jobName + ", STEP"+ stepIndex +", Step(s): " + Arrays.toString(stepNames));
70              
71  			if (!batchContainerDirectory.isBatchContainerRunning()) {
72  				//an instance of the batch container is not running - exit. Exit status: 8
73  				LOG.error("The BatchContainer is not running - exiting without executing the steps: "+ Arrays.toString(stepNames));
74  	            LOG.info("Exit status: 8");
75  				System.exit(8);
76  			}
77              
78  			//Need to humanize 'i'; the index 
79              for (int i = (stepIndex-1); i < stepNames.length; i++) {
80                  String stepName = stepNames[i];
81              	BatchStepFileDescriptor batchStepFile = new BatchStepFileDescriptor(jobName, stepName, BatchStepFileDescriptor.getFileExtensionRun());
82              	
83              	//write step start file
84              	batchContainerDirectory.writeBatchStepRunFile(batchStepFile, i);
85  
86              	//wait for a result file from BatchContainer
87              	BatchStepFileDescriptor resultFile = listenForResultFile(batchContainerDirectory, batchStepFile, sleepInterval);
88              	
89              	if (resultFile == null) {
90              		//result file is null - something unexpected happened. Exit status: 8             		
91              		batchContainerDirectory.removeBatchStepFileFromSystem(batchStepFile);            		
92              		
93              		LOG.error("No result files were returned- exiting without knowing whether the Step was executed");
94                      LOG.info("Exit status: 8");
95          			System.exit(8);
96              	}
97              	
98              	if (resultFile.isStepFileAnErrorResultFile()) {            		
99              		
100             		if (batchContainerDirectory.isFileEmpty(resultFile)) {
101                 		//do not execute any more steps, but job should succeed. Exit status: 4                		
102             			LOG.error(batchStepFile +" failed");
103             			batchContainerDirectory.removeBatchStepFileFromSystem(resultFile);
104             			
105                         LOG.info("Exit status: 4");
106                 		System.exit(4);
107             		}
108             		else {
109             			
110                 		//if file is not empty do not execute any more steps and fail job (write exception to log). Exit status: 8
111             			LOG.error(batchStepFile +" failed with the following error message: ");
112             			batchContainerDirectory.logFileContents(resultFile, LOG);
113             			batchContainerDirectory.removeBatchStepFileFromSystem(resultFile);
114 
115                         LOG.info("Exit status: 8");
116                     	System.exit(8);            		            			
117             		}
118             	}
119             	
120     			batchContainerDirectory.removeBatchStepFileFromSystem(resultFile);
121                 LOG.info("Exiting "+ batchStepFile);
122                 
123             }
124             
125             //continue executing steps in the job. Exit status: 0
126             LOG.info("Exit status: 0");
127             
128             System.exit(0);
129         }
130         catch (Throwable t) {
131             System.err.println("ERROR: Exception caught: ");
132             t.printStackTrace(System.err);
133             LOG.error(t);
134             
135             System.exit(8);
136         }
137     }
138     
139     /**
140      * Loop - look for a result file in the directory, if none is found then sleep. 
141      * If the batch container is not running then write an error result file and return it.
142      * 
143      * @param batchContainerDirectory the directory in which the semaphore files are located
144      * @param batchStepFile the step descriptor for the current step
145      * @param sleepInterval the amount of time to sleep while waiting for a result file
146      * @return the step descriptor of the result file
147      */
148     private static BatchStepFileDescriptor listenForResultFile(BatchContainerDirectory batchContainerDirectory, BatchStepFileDescriptor batchStepFile, long sleepInterval) {
149         if (LOG.isDebugEnabled()) {
150             LOG.debug("Waiting for result file for "+ batchStepFile);
151         }
152     	
153     	while (true) {    		
154 	    	//look for a result file in file-system
155 	    	BatchStepFileDescriptor resultFile = batchContainerDirectory.getResultFile(batchStepFile);
156 	    	if (resultFile != null) {
157     	        LOG.info("Found result file: "+ resultFile.getName());
158 	    		
159 	    		return resultFile;
160 	    	}
161 	    	
162 	    	if (batchContainerDirectory.isBatchContainerRunning()) {	    	
163 		    	sleep(sleepInterval);    		    		
164 	    	}
165 	    	else {
166 	    		//the batch container is not running - return an error file with an exception
167 	    		batchContainerDirectory.writeBatchStepErrorResultFile(batchStepFile, new RuntimeException("The BatchContainer is not running - exiting without knowing whether the Step executed"));
168 	    		
169 	    		resultFile = batchContainerDirectory.getResultFile(batchStepFile);
170     			return resultFile;
171 	    	}
172     	}     	
173     }    
174     
175     /**
176      * Sleep for the specified amount of time
177      * 
178      * @param sleepInterval the amount of time (in milliseconds) to wait before looking for a result file
179      */
180     private static void sleep(long sleepInterval) {
181         if (LOG.isDebugEnabled()) {
182             LOG.debug("Sleeping...");
183         }
184         try {
185             Thread.sleep(sleepInterval);
186         }
187         catch (InterruptedException e) {
188             throw new RuntimeException("BatchStepTrigger encountered interrupt exception while trying to wait for the specified batch step semaphore processing interval", e);
189         }
190     }
191     
192     /**
193      * @return the names of the steps to be executed
194      */
195     private static String[] getStepNames() {
196     	return batchStepTriggerParameters.getStepNames();
197     }
198     
199     /**
200      * @return the name of the job in which the steps are running
201      */
202     private static String getJobName() {
203     	return batchStepTriggerParameters.getJobName();
204     }
205     
206     /**
207      * @return the index of the step in the job
208      */
209     private static int getStepIndex() {
210         return batchStepTriggerParameters.getStepIndex();
211     }
212     
213     /**
214      * @return the amount of time to sleep (in milliseconds) while waiting for a result file
215      */
216     private static long getSleepInterval() {
217     	return batchStepTriggerParameters.getSleepInterval();
218     }
219     
220     /**
221      * @return the directory in which the semaphore files are located
222      */
223     private static BatchContainerDirectory getBatchContainerDirectory() {
224     	return batchStepTriggerParameters.getBatchContainerDirectory();
225     }
226 }