001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.utils;
019
020
021
022import io.stallion.services.Log;
023import org.apache.commons.io.IOUtils;
024
025import java.io.BufferedReader;
026import java.io.File;
027import java.io.IOException;
028import java.io.InputStreamReader;
029import java.util.Map;
030import java.util.concurrent.TimeUnit;
031
032import static io.stallion.utils.Literals.*;
033
034/**
035 * A helper class for running external programs.
036 */
037public class ProcessHelper {
038    private String[] args;
039    private String directory = null;
040    private String input;
041    private Boolean showDotsWhileWaiting = true;
042    private boolean inheritIO = true;
043    private boolean quietMode = false;
044
045
046    public static CommandResult run(String...args) {
047        return new ProcessHelper(args).run();
048    }
049
050
051    public ProcessHelper(String...args) {
052        this.args = args;
053    }
054
055    public ProcessHelper withDirectory(String directory) {
056        this.directory = directory;
057        return this;
058    }
059
060    public CommandResult run() {
061
062        String cmdString = String.join(" ", args);
063        System.out.printf("----- Execute command: %s ----\n", cmdString);
064        ProcessBuilder pb = new ProcessBuilder(args);
065        if (!empty(directory)) {
066            pb.directory(new File(directory));
067        }
068        Map<String, String> env = pb.environment();
069        CommandResult commandResult = new CommandResult();
070        Process p = null;
071        try {
072            if (showDotsWhileWaiting == null) {
073                showDotsWhileWaiting = !inheritIO;
074            }
075
076            if (inheritIO) {
077                p = pb.inheritIO().start();
078            } else {
079                p = pb.start();
080            }
081
082            BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
083            BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
084
085            if (!empty(input)) {
086                Log.info("Writing input to pipe {0}", input);
087                IOUtils.write(input, p.getOutputStream(), UTF8);
088                p.getOutputStream().flush();
089            }
090
091            while (p.isAlive()) {
092                p.waitFor(1000, TimeUnit.MILLISECONDS);
093                if (showDotsWhileWaiting == true) {
094                    System.out.printf(".");
095                }
096            }
097
098            commandResult.setErr(IOUtils.toString(err));
099            commandResult.setOut(IOUtils.toString(out));
100            commandResult.setCode(p.exitValue());
101
102            if (commandResult.succeeded()) {
103                info("\n---- Command execution completed ----\n");
104            } else {
105                Log.warn("Command failed with error code: " + commandResult.getCode());
106            }
107
108        } catch (IOException e) {
109            Log.exception(e, "Error running command: " + cmdString);
110            commandResult.setCode(999);
111            commandResult.setEx(e);
112        } catch (InterruptedException e) {
113            Log.exception(e, "Error running command: " + cmdString);
114            commandResult.setCode(998);
115            commandResult.setEx(e);
116        }
117        Log.fine("\n\n----Start shell command result----:\nCommand:  {0}\nexitCode: {1}\n----------STDOUT---------\n{2}\n\n----------STDERR--------\n{3}\n\n----end shell command result----\n",
118                cmdString,
119                commandResult.getCode(),
120                commandResult.getOut(),
121                commandResult.getErr());
122        return commandResult;
123    }
124
125    protected void info(String msg) {
126        if (quietMode == true) {
127
128        }
129        System.out.printf(msg + "\n");
130    }
131
132    public static class CommandResult {
133        private int code;
134        private String out;
135        private String err;
136        private Throwable ex;
137
138        public boolean succeeded() {
139            return code == 0;
140        }
141
142        public int getCode() {
143            return code;
144        }
145
146        public void setCode(int code) {
147            this.code = code;
148        }
149
150        public String getOut() {
151            return out;
152        }
153
154        public void setOut(String out) {
155            this.out = out;
156        }
157
158        public String getErr() {
159            return err;
160        }
161
162        public void setErr(String err) {
163            this.err = err;
164        }
165
166        public Throwable getEx() {
167            return ex;
168        }
169
170        public void setEx(Throwable ex) {
171            this.ex = ex;
172        }
173    }
174
175
176    public String getInput() {
177        return input;
178    }
179
180    public ProcessHelper setInput(String input) {
181        this.input = input;
182        return this;
183    }
184
185    public boolean isInheritIO() {
186        return inheritIO;
187    }
188
189    public ProcessHelper setInheritIO(boolean inheritIO) {
190        this.inheritIO = inheritIO;
191        return this;
192    }
193
194    public boolean isShowDotsWhileWaiting() {
195        return showDotsWhileWaiting;
196    }
197
198    public ProcessHelper setShowDotsWhileWaiting(boolean showDotsWhileWaiting) {
199        this.showDotsWhileWaiting = showDotsWhileWaiting;
200        return this;
201    }
202}