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}