Spring Batch提供了SystemCommandTasklet用来执行系统命令。底层使用JDK Runtime.exec()方法,Process.waitFor()来获取运行结果。
SystemCommandTasklet部分源码:
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { FutureTaskProcess 的Javadoc写到:systemCommandTask = new FutureTask (new Callable () { @Override public Integer call() throws Exception { Process process = Runtime.getRuntime().exec(command, environmentParams, workingDirectory); return process.waitFor(); } }); long t0 = System.currentTimeMillis(); taskExecutor.execute(systemCommandTask); while (true) { Thread.sleep(checkInterval); if (systemCommandTask.isDone()) { contribution.setExitStatus(systemProcessExitCodeMapper.getExitStatus(systemCommandTask.get())); return RepeatStatus.FINISHED; } else if (System.currentTimeMillis() - t0 > timeout) { systemCommandTask.cancel(interruptOnCancel); throw new SystemCommandException("Execution of system command did not finish within the timeout"); } else if (execution.isTerminateOnly()) { systemCommandTask.cancel(interruptOnCancel); throw new JobInterruptedException("Job interrupted while executing system command '" + command + "'"); } } }
The parent process uses these streams to feed input to and get outputfrom the subprocess. Because some native platforms only providelimited buffer size for standard input and output streams, failureto promptly write the input stream or read the output stream ofthe subprocess may cause the subprocess to block, or even deadlock.
SystemCommandTasklet没处理系统命令的输入输出。当输出大于系统限制缓冲区大小时,就会阻塞得不到返回。
例如我测试的在32位Windows下当输入超过512个字母时,进程被卡住不會结束。
修改使其处理执行系统命令时的输出(不太可能有输入所以没处理):
Process process = Runtime.getRuntime().exec(command, environmentParams, workingDirectory); final InputStream inputStream = process.getInputStream(); final InputStream errorStream = process.getErrorStream(); Runnable r1 = new Runnable() { public void run() { BufferedReader br = null; try { if (logger.isInfoEnabled()) { br = new BufferedReader(new InputStreamReader(inputStream)); for (String read = br.readLine(); read != null; read = br.readLine()) { logger.info("Command out - " + read); } } } catch (Exception e) { logger.error(e.getMessage(), e); } finally { try { if (br != null) { br.close(); } } catch (IOException e1) { // ignore } } } }; Runnable r2 = new Runnable() { public void run() { BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(errorStream)); for (String read = br.readLine(); read != null; read = br.readLine()) { logger.error("Command error - " + read); } } catch (Exception e) { logger.error(e.getMessage(), e); } finally { try { if (br != null) { br.close(); } } catch (IOException e1) { // ignore } } } }; r1.run(); r2.run(); return process.waitFor();