001: /*
002: * Copyright 2004-2005 OpenSymphony
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy
006: * of the License at
007: *
008: * http://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, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations
014: * under the License.
015: *
016: */
017:
018: /*
019: * Previously Copyright (c) 2001-2004 James House
020: */
021:
022: package org.quartz.jobs;
023:
024: import java.io.BufferedReader;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.quartz.Job;
032: import org.quartz.JobDataMap;
033: import org.quartz.JobExecutionContext;
034: import org.quartz.JobExecutionException;
035:
036: /*
037: * <p> Built in job for executing native executables in a separate process.</p>
038: *
039: * If PROP_WAIT_FOR_PROCESS is true, then the Integer exit value of the process
040: * will be saved as the job execution result in the JobExecutionContext.
041: *
042: * @see #PROP_COMMAND
043: * @see #PROP_PARAMETERS
044: * @see #PROP_WAIT_FOR_PROCESS
045: * @see #PROP_CONSUME_STREAMS
046: *
047: * @author Matthew Payne
048: * @author James House
049: * @author Steinar Overbeck Cook
050: * @date Sep 17, 2003 @Time: 11:27:13 AM
051: */
052: public class NativeJob implements Job {
053:
054: private final Log log = LogFactory.getLog(getClass());
055:
056: /*
057: *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
058: *
059: * Constants.
060: *
061: *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
062: */
063:
064: /**
065: * Required parameter that specifies the name of the command (executable)
066: * to be ran.
067: */
068: public static final String PROP_COMMAND = "command";
069:
070: /**
071: * Optional parameter that specifies the parameters to be passed to the
072: * executed command.
073: */
074: public static final String PROP_PARAMETERS = "parameters";
075:
076: /**
077: * Optional parameter (value should be 'true' or 'false') that specifies
078: * whether the job should wait for the execution of the native process to
079: * complete before it completes.
080: *
081: * <p>Defaults to <code>true</code>.</p>
082: */
083: public static final String PROP_WAIT_FOR_PROCESS = "waitForProcess";
084:
085: /**
086: * Optional parameter (value should be 'true' or 'false') that specifies
087: * whether the spawned process's stdout and stderr streams should be
088: * consumed. If the process creates output, it is possible that it might
089: * 'hang' if the streams are not consumed.
090: *
091: * <p>Defaults to <code>false</code>.</p>
092: */
093: public static final String PROP_CONSUME_STREAMS = "consumeStreams";
094:
095: /*
096: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
097: *
098: * Interface.
099: *
100: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101: */
102:
103: public void execute(JobExecutionContext context)
104: throws JobExecutionException {
105:
106: JobDataMap data = context.getMergedJobDataMap();
107:
108: String command = data.getString(PROP_COMMAND);
109:
110: String parameters = data.getString(PROP_PARAMETERS);
111:
112: if (parameters == null) {
113: parameters = "";
114: }
115:
116: boolean wait = true;
117: if (data.containsKey(PROP_WAIT_FOR_PROCESS)) {
118: wait = data.getBooleanValue(PROP_WAIT_FOR_PROCESS);
119: }
120: boolean consumeStreams = false;
121: if (data.containsKey(PROP_CONSUME_STREAMS)) {
122: consumeStreams = data.getBooleanValue(PROP_CONSUME_STREAMS);
123: }
124:
125: Integer exitCode = this .runNativeCommand(command, parameters,
126: wait, consumeStreams);
127: context.setResult(exitCode);
128:
129: }
130:
131: protected Log getLog() {
132: return log;
133: }
134:
135: private Integer runNativeCommand(String command, String parameters,
136: boolean wait, boolean consumeStreams)
137: throws JobExecutionException {
138:
139: String[] cmd = null;
140: String[] args = new String[2];
141: Integer result = null;
142: args[0] = command;
143: args[1] = parameters;
144:
145: try {
146: //with this variable will be done the swithcing
147: String osName = System.getProperty("os.name");
148:
149: //only will work with Windows NT
150: if (osName.equals("Windows NT")) {
151: if (cmd == null) {
152: cmd = new String[args.length + 2];
153: }
154: cmd[0] = "cmd.exe";
155: cmd[1] = "/C";
156: for (int i = 0; i < args.length; i++) {
157: cmd[i + 2] = args[i];
158: }
159: } else if (osName.equals("Windows 95")) { //only will work with Windows 95
160: if (cmd == null) {
161: cmd = new String[args.length + 2];
162: }
163: cmd[0] = "command.com";
164: cmd[1] = "/C";
165: for (int i = 0; i < args.length; i++) {
166: cmd[i + 2] = args[i];
167: }
168: } else if (osName.equals("Windows 2003")) { //only will work with Windows 2003
169: if (cmd == null) {
170: cmd = new String[args.length + 2];
171: }
172: cmd[0] = "cmd.exe";
173: cmd[1] = "/C";
174:
175: for (int i = 0; i < args.length; i++) {
176: cmd[i + 2] = args[i];
177: }
178: } else if (osName.equals("Windows 2000")) { //only will work with Windows 2000
179: if (cmd == null) {
180: cmd = new String[args.length + 2];
181: }
182: cmd[0] = "cmd.exe";
183: cmd[1] = "/C";
184:
185: for (int i = 0; i < args.length; i++) {
186: cmd[i + 2] = args[i];
187: }
188: } else if (osName.equals("Windows XP")) { //only will work with Windows XP
189: if (cmd == null) {
190: cmd = new String[args.length + 2];
191: }
192: cmd[0] = "cmd.exe";
193: cmd[1] = "/C";
194:
195: for (int i = 0; i < args.length; i++) {
196: cmd[i + 2] = args[i];
197: }
198: } else { //will work with the rest (including Linux)
199: cmd = args;
200: }
201:
202: Runtime rt = Runtime.getRuntime();
203: // Executes the command
204: getLog().info("About to run" + cmd[0] + cmd[1]);
205: Process proc = rt.exec(cmd);
206: // Consumes the stdout from the process
207: StreamConsumer stdoutConsumer = new StreamConsumer(proc
208: .getInputStream(), "stdout");
209:
210: // Consumes the stderr from the process
211: if (consumeStreams) {
212: StreamConsumer stderrConsumer = new StreamConsumer(proc
213: .getErrorStream(), "stderr");
214: stdoutConsumer.start();
215: stderrConsumer.start();
216: }
217:
218: if (wait) {
219: result = new Integer(proc.waitFor());
220: }
221: // any error message?
222:
223: } catch (Exception x) {
224: throw new JobExecutionException(
225: "Error launching native command: ", x, false);
226: }
227:
228: return result;
229: }
230:
231: /**
232: * Consumes data from the given input stream until EOF and prints the data to stdout
233: *
234: * @author cooste
235: * @author jhouse
236: */
237: class StreamConsumer extends Thread {
238: InputStream is;
239: String type;
240:
241: /**
242: *
243: */
244: public StreamConsumer(InputStream inputStream, String type) {
245: this .is = inputStream;
246: this .type = type;
247: }
248:
249: /**
250: * Runs this object as a separate thread, printing the contents of the InputStream
251: * supplied during instantiation, to either stdout or stderr
252: */
253: public void run() {
254: BufferedReader br = null;
255: try {
256: br = new BufferedReader(new InputStreamReader(is));
257: String line = null;
258:
259: while ((line = br.readLine()) != null) {
260: if (type.equalsIgnoreCase("stderr")) {
261: getLog().warn(type + ">" + line);
262: } else {
263: getLog().info(type + ">" + line);
264: }
265: }
266: } catch (IOException ioe) {
267: getLog().error(
268: "Error consuming " + type
269: + " stream of spawned process.", ioe);
270: } finally {
271: if (br != null) {
272: try {
273: br.close();
274: } catch (Exception ignore) {
275: }
276: }
277: }
278: }
279: }
280:
281: }
|