1 /*
2 * Copyright 2007 The Kuali Foundation
3 *
4 * Licensed under the Educational Community License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/ecl2.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.kuali.ole.sys.context;
17
18
19 import java.util.Arrays;
20
21
22 /**
23 * BatchStepTrigger writes .run files containing a job name and step name for BatchContainerStep to read.
24 * 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.
25 *
26 * BatchStepTrigger also checks for BatchContainerStep's .runlock file. If it doesn't find one (indicating the batch container is not running) it exits.
27 * BatchStepTrigger adds a ConsoleAppender to its Logger if one hasn't been configured.
28 *
29 * Note that this class runs without starting the SpringContext. KFS Services and Beans are not available for use.
30 *
31 */
32 public class BatchStepTrigger {
33 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BatchStepTrigger.class);
34
35 private static BatchStepTriggerParameters batchStepTriggerParameters;
36
37 /**
38 * BatchStepTrigger is instantiated for each step in a brte script.
39 * The value used when exiting the system tells the brte script how to handle failures.
40 * 0: okay, 4: the step returned false, 8: an exception occurred in the execution of the step or the trigger
41 *
42 * - verify that the batch container is running, exit with an 8 if not
43 * - for each step
44 * - write a RUN semaphore file
45 * - wait and listen for a result file (either SUCCESS or ERROR)
46 * - if the result file is null the batch container is not running so remove the run file and exit with an 8
47 * - if the result file is an ERROR
48 * - if the file is EMPTY remove the result file and exit with a 4
49 * - otherwise log the error contained in the result file, remove the result file, and exit with an 8
50 * - otherwise remove the result file and exit with a 0
51 *
52 * @param args - refer to BatchStepTriggerParameters for details
53 */
54 public static void main(String[] args) {
55 try {
56 Log4jConfigurer.configureLogging(false);
57 BatchLogger.addConsoleAppender(LOG);
58
59 batchStepTriggerParameters = new BatchStepTriggerParameters(args);
60
61 String[] stepNames = getStepNames();
62 String jobName = getJobName();
63 int stepIndex = getStepIndex();
64 long sleepInterval = getSleepInterval();
65 BatchContainerDirectory batchContainerDirectory = getBatchContainerDirectory();
66
67 LOG.info("Executing Job: " + jobName + ", STEP"+ stepIndex +", Step(s): " + Arrays.toString(stepNames));
68
69 if (!batchContainerDirectory.isBatchContainerRunning()) {
70 //an instance of the batch container is not running - exit. Exit status: 8
71 LOG.error("The BatchContainer is not running - exiting without executing the steps: "+ Arrays.toString(stepNames));
72 LOG.info("Exit status: 8");
73 System.exit(8);
74 }
75
76 //Need to humanize 'i'; the index
77 for (int i = (stepIndex-1); i < stepNames.length; i++) {
78 String stepName = stepNames[i];
79 BatchStepFileDescriptor batchStepFile = new BatchStepFileDescriptor(jobName, stepName, BatchStepFileDescriptor.getFileExtensionRun());
80
81 //write step start file
82 batchContainerDirectory.writeBatchStepRunFile(batchStepFile, i);
83
84 //wait for a result file from BatchContainer
85 BatchStepFileDescriptor resultFile = listenForResultFile(batchContainerDirectory, batchStepFile, sleepInterval);
86
87 if (resultFile == null) {
88 //result file is null - something unexpected happened. Exit status: 8
89 batchContainerDirectory.removeBatchStepFileFromSystem(batchStepFile);
90
91 LOG.error("No result files were returned- exiting without knowing whether the Step was executed");
92 LOG.info("Exit status: 8");
93 System.exit(8);
94 }
95
96 if (resultFile.isStepFileAnErrorResultFile()) {
97
98 if (batchContainerDirectory.isFileEmpty(resultFile)) {
99 //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 }