001/* 002 * Copyright 2007 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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 * http://www.opensource.org/licenses/ecl2.php 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.kuali.ole.sys.context; 017 018 019import java.util.Arrays; 020 021 022/** 023 * BatchStepTrigger writes .run files containing a job name and step name for BatchContainerStep to read. 024 * 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. 025 * 026 * BatchStepTrigger also checks for BatchContainerStep's .runlock file. If it doesn't find one (indicating the batch container is not running) it exits. 027 * BatchStepTrigger adds a ConsoleAppender to its Logger if one hasn't been configured. 028 * 029 * Note that this class runs without starting the SpringContext. KFS Services and Beans are not available for use. 030 * 031 */ 032public class BatchStepTrigger { 033 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BatchStepTrigger.class); 034 035 private static BatchStepTriggerParameters batchStepTriggerParameters; 036 037 /** 038 * BatchStepTrigger is instantiated for each step in a brte script. 039 * The value used when exiting the system tells the brte script how to handle failures. 040 * 0: okay, 4: the step returned false, 8: an exception occurred in the execution of the step or the trigger 041 * 042 * - verify that the batch container is running, exit with an 8 if not 043 * - for each step 044 * - write a RUN semaphore file 045 * - wait and listen for a result file (either SUCCESS or ERROR) 046 * - if the result file is null the batch container is not running so remove the run file and exit with an 8 047 * - if the result file is an ERROR 048 * - if the file is EMPTY remove the result file and exit with a 4 049 * - otherwise log the error contained in the result file, remove the result file, and exit with an 8 050 * - otherwise remove the result file and exit with a 0 051 * 052 * @param args - refer to BatchStepTriggerParameters for details 053 */ 054 public static void main(String[] args) { 055 try { 056 Log4jConfigurer.configureLogging(false); 057 BatchLogger.addConsoleAppender(LOG); 058 059 batchStepTriggerParameters = new BatchStepTriggerParameters(args); 060 061 String[] stepNames = getStepNames(); 062 String jobName = getJobName(); 063 int stepIndex = getStepIndex(); 064 long sleepInterval = getSleepInterval(); 065 BatchContainerDirectory batchContainerDirectory = getBatchContainerDirectory(); 066 067 LOG.info("Executing Job: " + jobName + ", STEP"+ stepIndex +", Step(s): " + Arrays.toString(stepNames)); 068 069 if (!batchContainerDirectory.isBatchContainerRunning()) { 070 //an instance of the batch container is not running - exit. Exit status: 8 071 LOG.error("The BatchContainer is not running - exiting without executing the steps: "+ Arrays.toString(stepNames)); 072 LOG.info("Exit status: 8"); 073 System.exit(8); 074 } 075 076 //Need to humanize 'i'; the index 077 for (int i = (stepIndex-1); i < stepNames.length; i++) { 078 String stepName = stepNames[i]; 079 BatchStepFileDescriptor batchStepFile = new BatchStepFileDescriptor(jobName, stepName, BatchStepFileDescriptor.getFileExtensionRun()); 080 081 //write step start file 082 batchContainerDirectory.writeBatchStepRunFile(batchStepFile, i); 083 084 //wait for a result file from BatchContainer 085 BatchStepFileDescriptor resultFile = listenForResultFile(batchContainerDirectory, batchStepFile, sleepInterval); 086 087 if (resultFile == null) { 088 //result file is null - something unexpected happened. Exit status: 8 089 batchContainerDirectory.removeBatchStepFileFromSystem(batchStepFile); 090 091 LOG.error("No result files were returned- exiting without knowing whether the Step was executed"); 092 LOG.info("Exit status: 8"); 093 System.exit(8); 094 } 095 096 if (resultFile.isStepFileAnErrorResultFile()) { 097 098 if (batchContainerDirectory.isFileEmpty(resultFile)) { 099 //do not execute any more steps, but job should succeed. Exit status: 4 100 LOG.error(batchStepFile +" failed"); 101 batchContainerDirectory.removeBatchStepFileFromSystem(resultFile); 102 103 LOG.info("Exit status: 4"); 104 System.exit(4); 105 } 106 else { 107 108 //if file is not empty do not execute any more steps and fail job (write exception to log). Exit status: 8 109 LOG.error(batchStepFile +" failed with the following error message: "); 110 batchContainerDirectory.logFileContents(resultFile, LOG); 111 batchContainerDirectory.removeBatchStepFileFromSystem(resultFile); 112 113 LOG.info("Exit status: 8"); 114 System.exit(8); 115 } 116 } 117 118 batchContainerDirectory.removeBatchStepFileFromSystem(resultFile); 119 LOG.info("Exiting "+ batchStepFile); 120 121 } 122 123 //continue executing steps in the job. Exit status: 0 124 LOG.info("Exit status: 0"); 125 126 System.exit(0); 127 } 128 catch (Throwable t) { 129 System.err.println("ERROR: Exception caught: "); 130 t.printStackTrace(System.err); 131 LOG.error(t); 132 133 System.exit(8); 134 } 135 } 136 137 /** 138 * Loop - look for a result file in the directory, if none is found then sleep. 139 * If the batch container is not running then write an error result file and return it. 140 * 141 * @param batchContainerDirectory the directory in which the semaphore files are located 142 * @param batchStepFile the step descriptor for the current step 143 * @param sleepInterval the amount of time to sleep while waiting for a result file 144 * @return the step descriptor of the result file 145 */ 146 private static BatchStepFileDescriptor listenForResultFile(BatchContainerDirectory batchContainerDirectory, BatchStepFileDescriptor batchStepFile, long sleepInterval) { 147 if (LOG.isDebugEnabled()) { 148 LOG.debug("Waiting for result file for "+ batchStepFile); 149 } 150 151 while (true) { 152 //look for a result file in file-system 153 BatchStepFileDescriptor resultFile = batchContainerDirectory.getResultFile(batchStepFile); 154 if (resultFile != null) { 155 LOG.info("Found result file: "+ resultFile.getName()); 156 157 return resultFile; 158 } 159 160 if (batchContainerDirectory.isBatchContainerRunning()) { 161 sleep(sleepInterval); 162 } 163 else { 164 //the batch container is not running - return an error file with an exception 165 batchContainerDirectory.writeBatchStepErrorResultFile(batchStepFile, new RuntimeException("The BatchContainer is not running - exiting without knowing whether the Step executed")); 166 167 resultFile = batchContainerDirectory.getResultFile(batchStepFile); 168 return resultFile; 169 } 170 } 171 } 172 173 /** 174 * Sleep for the specified amount of time 175 * 176 * @param sleepInterval the amount of time (in milliseconds) to wait before looking for a result file 177 */ 178 private static void sleep(long sleepInterval) { 179 if (LOG.isDebugEnabled()) { 180 LOG.debug("Sleeping..."); 181 } 182 try { 183 Thread.sleep(sleepInterval); 184 } 185 catch (InterruptedException e) { 186 throw new RuntimeException("BatchStepTrigger encountered interrupt exception while trying to wait for the specified batch step semaphore processing interval", e); 187 } 188 } 189 190 /** 191 * @return the names of the steps to be executed 192 */ 193 private static String[] getStepNames() { 194 return batchStepTriggerParameters.getStepNames(); 195 } 196 197 /** 198 * @return the name of the job in which the steps are running 199 */ 200 private static String getJobName() { 201 return batchStepTriggerParameters.getJobName(); 202 } 203 204 /** 205 * @return the index of the step in the job 206 */ 207 private static int getStepIndex() { 208 return batchStepTriggerParameters.getStepIndex(); 209 } 210 211 /** 212 * @return the amount of time to sleep (in milliseconds) while waiting for a result file 213 */ 214 private static long getSleepInterval() { 215 return batchStepTriggerParameters.getSleepInterval(); 216 } 217 218 /** 219 * @return the directory in which the semaphore files are located 220 */ 221 private static BatchContainerDirectory getBatchContainerDirectory() { 222 return batchStepTriggerParameters.getBatchContainerDirectory(); 223 } 224}