001package dk.netarkivet.harvester.heritrix3; 002 003import java.io.BufferedReader; 004import java.io.File; 005import java.io.IOException; 006import java.io.InputStreamReader; 007import java.io.PrintWriter; 008import java.io.StringWriter; 009import java.util.Arrays; 010import java.util.Map; 011 012import org.netarchivesuite.heritrix3wrapper.LaunchResultHandlerAbstract; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016public class BlockingCommandLauncher { 017 018 /** The logger for this class. */ 019 private static final Logger log = LoggerFactory.getLogger(BlockingCommandLauncher.class); 020 021 public ProcessBuilder processBuilder; 022 023 public Map<String, String> env; 024 025 public Process process; 026 027 public BlockingCommandLauncher(File basedir, String[] cmd) { 028 processBuilder = new ProcessBuilder(Arrays.asList(cmd)); 029 processBuilder.directory(basedir); 030 env = processBuilder.environment(); 031 } 032 033 public Integer start(LaunchResultHandlerAbstract resultHandler) throws IOException { 034 035 process = processBuilder.start(); 036 037 BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream())); 038 Thread outputSinkThread = new Thread(new OutputSinkThread(outputReader, resultHandler)); 039 outputSinkThread.setDaemon(true); 040 outputSinkThread.start(); 041 042 BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); 043 Thread errorSinkThread = new Thread(new ErrorSinkThread(errorReader, resultHandler)); 044 errorSinkThread.setDaemon(true); 045 errorSinkThread.start(); 046 047 int exitValue = Integer.MIN_VALUE; 048 try { 049 exitValue = process.waitFor(); 050 } catch (InterruptedException e) { 051 log.error("Interrupted error:", e); 052 } 053 if (resultHandler != null) { 054 resultHandler.exitValue(exitValue); 055 } 056 return exitValue; 057 } 058 059 protected class OutputSinkThread implements Runnable { 060 LaunchResultHandlerAbstract resultHandler; 061 BufferedReader reader; 062 063 protected OutputSinkThread(BufferedReader reader, LaunchResultHandlerAbstract resultHandler) { 064 this.reader = reader; 065 this.resultHandler = resultHandler; 066 } 067 068 @Override 069 public void run() { 070 try { 071 if (resultHandler == null) { 072 return; 073 } 074 String line; 075 try { 076 while ((line = reader.readLine()) != null) { 077 resultHandler.output(line); 078 } 079 } catch (IOException e) { 080 resultHandler.output(getStackTrace(e)); 081 } finally { 082 resultHandler.closeOutput(); 083 } 084 } finally { 085 try { 086 reader.close(); 087 } catch (IOException e) { 088 log.warn("Exception on closing reader", e); 089 } 090 } 091 } 092 } 093 094 protected String getStackTrace(IOException e) { 095 String stackTrace = null; 096 try (StringWriter stringWriter = new StringWriter(); 097 PrintWriter writer = new PrintWriter(stringWriter)) { 098 e.printStackTrace(writer); 099 writer.flush(); 100 stackTrace = stringWriter.toString(); 101 } catch (IOException ex) { 102 log.warn("Error from string writer...", ex); 103 } 104 return stackTrace; 105 } 106 107 protected class ErrorSinkThread implements Runnable { 108 LaunchResultHandlerAbstract resultHandler; 109 BufferedReader reader; 110 protected ErrorSinkThread(BufferedReader reader, LaunchResultHandlerAbstract resultHandler) { 111 this.reader = reader; 112 this.resultHandler = resultHandler; 113 } 114 115 @Override 116 public void run() { 117 try { 118 if (resultHandler == null) { 119 return; 120 } 121 String line; 122 try { 123 while ((line = reader.readLine()) != null) { 124 resultHandler.error(line); 125 } 126 } catch (IOException e) { 127 resultHandler.error(getStackTrace(e)); 128 } finally { 129 resultHandler.closeError(); 130 } 131 } finally { 132 try { 133 reader.close(); 134 } catch (IOException e) { 135 log.warn("Exception on closing reader", e); 136 } 137 } 138 } 139 } 140 141}