摘要:還有,需要支持字符編碼設置,在下對象調試程序很有幫助,因此,我們可以列表表示整個需求。第二種是無法設置字符編碼的,而第一種是獲得了整個標準輸出和錯誤輸出后再設置字符編碼的。
概述
寫這篇的主要目的是為了整理和記錄,歸檔以便以后查閱。
我之前在SF上提問了一個問題:如何正確使用PipedInputStream和PipedOutputStream
問題中提到的Apache Commons Execs這個庫,相比我們原來使用原生的Runtime和Process有不少優點。
對比我之前寫過的代碼,總結一下:
簡化路徑處理
如果要調用的腳本的路徑存在空格,Apache Commons Execs會自動幫忙加上轉義字符
兼容Windows環境
使用原生Runtime和Process方式時,必須手工為調用bat腳本加上cmd /c,比如把test.bat腳本拼接成cmd /c才向Runtime.exec方法傳入這個腳本作為第一個參數
支持超時設置
原生的Runtime和Process并沒有直接支持超時的設置,但網上也有在原生基礎上做的超時功能的封裝,大概是基于循環定期檢查的機制。在SF上也有類似的文章,其中的代碼大可參考一下,我要提醒的是,需要注意異步線程不能給及時返回結果的問題。
在我的項目需求中,規定要獲得腳本的退出碼,標準輸出、錯誤輸出。另外,還有可能要從標注輸出中解析得到一個描述成功或失敗的結果,大概就是過濾腳本的標準輸出,捕獲感興趣的某一行,最后要預留超時設置的接口。還有,需要支持字符編碼設置,在Windows下對象調試程序很有幫助,因此,我們可以列表表示整個需求。
序號 | 需求 | 是否必須 |
---|---|---|
1 | 退出碼、標準輸出、錯誤輸出 | 是 |
2 | 獲得腳本提供的結果描述 | 是 |
3 | 設置超時 | 否 |
4 | 設置字符編碼 | 否 |
public abstract class AbstractCommonExecs { private String bin; //腳本 private Listarguments; //參數 //Constructor method //封裝返回結果 public ExecResult exec() throws IOException { try{ Executor executor = getExecutor(); //執行線程 CommandLine cmdLine = getCommandLine(); //腳本命令參數等 if(supportWatchdog()) { //是否支持監視 用于設置超時等 executor.setWatchdog(getWatchdog()); } executor.setStreamHandler(streamHandler); //設置處理標注輸出和錯誤輸出的Handler int ret = executor.execute(cmdLine); //獲得退出碼 }catch(ExecuteException e) { int ret = e.getExitValue(); //如果出現異常還能獲得退出碼 關于這個仔細想想 } } }
1.1 抽象類接收腳本和參數,類型和形式還可以是別的形式
1.2 對外提供的exec方法返回的是退出碼、標準輸出、錯誤輸出和腳本提供的結果描述
1.3 通過getXXX方法的形式可以將具體的實現交給具體實現類來完成
2. 如何處理輸出為了從Executor中獲得標準輸出和錯誤輸出,是需要向Executor傳入一個streamHandler的是,這是一個基于字節流式的Handler,為了支持字符編碼的設計,
最終處理時我們還需要將它轉成字符流并設置目標字符編碼,比如在Windows開發環境下設置為GBK。
executor.setStreamHandler(streamHandler); //設置處理標注輸出和錯誤輸出的Handler
這里先提兩種非常有效的做法,一種是基于ByteArrayOutStream的,一種是官方封裝的LogOutputStream。第一種,
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream,errorStream); executor.setStreamHandler(streamHandler); exec.execute(cmdline); String out = outputStream.toString("gbk"); //設置編碼 String error = errorStream.toString("gbk"); //設置編碼
第二種,參考這個答案。
第二種是無法設置字符編碼的,而第一種是獲得了整個標準輸出和錯誤輸出后再設置字符編碼的。
如果采用這種方式,為了滿足從標準輸出解析某個特殊結果是需要對這個標準輸出做切分,再循環判斷的。
最后我采用的是PipedInputStream和PipedOutStream的方式,這也是為什么會有這個問題如何正確使用PipedInputStream和PipedOutputStream
。為了讓處理標注輸出、錯誤輸出和結果描述看起來比較統一,我使用了回調的方式。
private void readInputStream(PipedInputStream pis, OutputCallback ...cbs) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(pis, getEncoding())); String line = null; while((line = br.readLine()) != null) { for(OutputCallback cb : cbs) { cb.parse(line); //這里可以獲得結果描述 } } }4. 說明
整體思路上的抽象已經做到了,但是還不夠徹底,抽象類exec方法體內業務邏輯還是過于耦合的。
完整代碼ExecResult代碼,
public class ExecResult { private int exitCode; private String stdout; private String stderr; private String codeInfo; //getter and setter }
OutputCallback接口代碼,
public interface OutputCallback { public void parse(String line); }
AbstractCommonExecs代碼,
public abstract class AbstractCommonExecs { private Logger log = LoggerFactory.getLogger(AbstractCommonExecs.class); private static final String DEFAULT_ENCODING = "UTF-8"; private String encoding = DEFAULT_ENCODING; private String bin; private List測試 1. 支持字符編碼設置的測試arguments; public AbstractCommonExecs(String bin, List arguments) { this.bin = bin; this.arguments = arguments; } public ExecResult exec() throws IOException{ ExecResult er = new ExecResult(); //ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PipedOutputStream outputStream = new PipedOutputStream(); PipedInputStream pis = new PipedInputStream(outputStream); ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); CodeInfoCallback codeInfoCb = new CodeInfoCallback(); StdOutputCallback stdoutCb = new StdOutputCallback(); ErrorOutputCallback stderrCb = new ErrorOutputCallback(); String stdout = null; String stderr = null; try { Executor executor = getExecutor(); CommandLine cmdLine = getCommandLine(); log.info("Executing script {}",cmdLine.toString()); if(supportWatchdog()) { executor.setWatchdog(getWatchdog()); } PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream,errorStream); executor.setStreamHandler(streamHandler); int ret = executor.execute(cmdLine); readInputStream(pis, stdoutCb, codeInfoCb); pis.close(); readErrorStream(errorStream, stderrCb); stdout = join(stdoutCb.getLines()); stderr = stderrCb.getErrors(); log.info("output from script {} is {}", this.bin, stdout); log.info("error output from script {} is {}", this.bin, stderr); log.info("exit code from script {} is {}", this.bin, ret); er.setStdout(stdout); er.setStderr(stderr); er.setCodeInfo(codeInfoCb.getCodeInfo()); er.setExitCode(ret); return er; } catch (ExecuteException e) { if(pis != null) { readInputStream(pis, stdoutCb, codeInfoCb); pis.close(); } if(errorStream != null) { readErrorStream(errorStream, stderrCb); } stdout = join(stdoutCb.getLines()); stderr = stderrCb.getErrors(); int ret = e.getExitValue(); log.info("output from script {} is {}", this.bin, stdout); log.info("error output from script {} is {}", this.bin, stderr); log.info("exit code from script {} is {}", this.bin, ret); er.setStdout(stdout); er.setStderr(stderr); er.setCodeInfo(codeInfoCb.getCodeInfo()); er.setExitCode(ret); return er; } } /** * 接口回調的方式解析腳本的錯誤輸出 * @param baos * @param cbs * @throws IOException */ private void readErrorStream(ByteArrayOutputStream baos, OutputCallback ...cbs) throws IOException { String err = baos.toString(getEncoding()); for(OutputCallback cb : cbs) { cb.parse(err); } } /** * 接口回調的方式解析腳本的標準輸出 * @param pis * @param cbs * @throws IOException */ private void readInputStream(PipedInputStream pis, OutputCallback ...cbs) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(pis, getEncoding())); String line = null; while((line = br.readLine()) != null) { for(OutputCallback cb : cbs) { cb.parse(line); } } } public Executor getExecutor() { Executor executor = new DefaultExecutor(); executor.setWorkingDirectory(new File(this.bin).getParentFile()); return executor; } public CommandLine getCommandLine() { String fullCommand = bin + join(arguments); return CommandLine.parse(fullCommand); } protected String join(List arguments) { if(arguments == null || arguments.isEmpty()) { return ""; } StringBuilder sb = new StringBuilder(); for(String arg : arguments) { sb.append(" ").append(arg); } return sb.toString(); } /** * @return the encoding */ protected String getEncoding() { return encoding; } /** * @param encoding the encoding to set */ public void setEncoding(String encoding) { this.encoding = encoding; } /** * @return the bin */ protected String getBin() { return bin; } /** * @param bin the bin to set */ public void setBin(String bin) { this.bin = bin; } /** * @return the arguments */ protected List getArguments() { return arguments; } /** * @param arguments the arguments to set */ public void setArguments(List arguments) { this.arguments = arguments; } public abstract boolean supportWatchdog(); public abstract ExecuteWatchdog getWatchdog(); }
public class GbkCommonExecs extends AbstractCommonExecs{ /** * @param bin * @param arguments */ public GbkCommonExecs(String bin, List2. 支持超時設置的測試arguments) { super(bin, arguments); } /* (non-Javadoc) * @see com.bingosoft.proxy.helper.AbstractCommonExecs#supportWatchdog() */ @Override public boolean supportWatchdog() { // TODO implement AbstractCommonExecs.supportWatchdog return false; } /* (non-Javadoc) * @see com.bingosoft.proxy.helper.AbstractCommonExecs#getWatchdog() */ @Override public ExecuteWatchdog getWatchdog() { // TODO implement AbstractCommonExecs.getWatchdog return null; } //提供這個編碼即可 public String getEncoding() { return "GBK"; } public static void main(String[] args) throws IOException { String bin = "ping"; String arg1 = "127.0.0.1"; List arguments = new ArrayList (); arguments.add(arg1); AbstractCommonExecs executable = new GbkCommonExecs(bin, arguments); ExecResult er = executable.exec(); System.out.println(er.getExitCode()); System.out.println(er.getStdout()); System.out.println(er.getStderr()); } }
設置監視狗就能設置超時
public class TimeoutCommonExecs extends AbstractCommonExecs{ private Logger log = LoggerFactory.getLogger(TimeoutCommonExecs.class); private long timeout = 10 * 1000; // 10 seconds public TimeoutCommonExecs(String bin, Listarguments) { super(bin, arguments); } public TimeoutCommonExecs(String bin, List arguments, long timeout) { super(bin, arguments); this.timeout = timeout; } public boolean supportWatchdog() { return true; // 使用監視狗 監視腳本執行超時的情況 } public ExecuteWatchdog getWatchdog() { ExecuteWatchdog watchdog = new ExecuteWatchdog(this.timeout); return watchdog; } /** * @return the timeout */ public long getTimeout() { return timeout; } /** * @param timeout the timeout to set */ public void setTimeout(long timeout) { this.timeout = timeout; } }
為了方便在Windows下測試
public class TimeoutGbkCommonExecs extends TimeoutCommonExecs{ public TimeoutGbkCommonExecs(String bin, Listarguments, long timeout) { super(bin, arguments, timeout); } //字符編碼設置 public String getEncoding() { return "GBK"; } public static void main(String[] args) throws IOException { String bin = "ping"; String arg1 = "-t"; //不斷ping String arg2 = "127.0.0.1"; List arguments = new ArrayList (); arguments.add(arg1); arguments.add(arg2); AbstractCommonExecs executable = new TimeoutGbkCommonExecs(bin, arguments, 5 * 1000); ExecResult er = executable.exec(); System.out.println(er.getExitCode()); System.out.println(er.getStdout()); System.out.println(er.getStderr()); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65396.html
摘要:第二集非必選持續集成編譯打包部署到線上環境集成前言在持續集成第一集中講解了關于如果編譯,打包,發布包到私服。在下面一集中,也就是第二集中,開始講解如何持續集成可運行的服務包到測試和正式環境。 第二集 hudson+gradle+git+maven(非必選)持續集成編譯,打包,部署到線上環境集成 前言 在持續集成第一集中,講解了關于如果編譯,打包,發布jar包到maven私服。在下面一集...
摘要:操作系統只用權限才能監聽已下的端口。雖然我們可以讓啟動后以用戶的權限工作。我們還可以指定啟動是的總之我們可以更精細的控制的運行方式。只有該版本的是綁定到二進制發行版中。這意味著它要與該版本的一同使用。 jsvc 是個什么是么高端武器呢 全稱:Java Service 還是沒有弄明白是什么,那就繼續往下看。 我們贊不討論 tomcat 應不應該運行在80端口上。 假如我們有需求,需要 ...
摘要:在中,工具類定義了一組公共方法,這篇文章將介紹中使用最頻繁及最通用的工具類。另外,工具類,根據阿里開發手冊,包名如果要使用不能帶,工具類命名為 在Java中,工具類定義了一組公共方法,這篇文章將介紹Java中使用最頻繁及最通用的Java工具類。以下工具類、方法按使用流行度排名,參考數據來源于Github上隨機選取的5萬個開源項目源碼。 一. org.apache.commons.io....
閱讀 3595·2021-09-13 10:28
閱讀 1944·2021-08-10 09:43
閱讀 1015·2019-08-30 15:44
閱讀 3186·2019-08-30 13:14
閱讀 1839·2019-08-29 16:56
閱讀 2944·2019-08-29 16:35
閱讀 2852·2019-08-29 12:58
閱讀 870·2019-08-26 13:46