摘要:還有,需要支持字符編碼設(shè)置,在下對(duì)象調(diào)試程序很有幫助,因此,我們可以列表表示整個(gè)需求。第二種是無(wú)法設(shè)置字符編碼的,而第一種是獲得了整個(gè)標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出后再設(shè)置字符編碼的。
概述
寫(xiě)這篇的主要目的是為了整理和記錄,歸檔以便以后查閱。
我之前在SF上提問(wèn)了一個(gè)問(wèn)題:如何正確使用PipedInputStream和PipedOutputStream
問(wèn)題中提到的Apache Commons Execs這個(gè)庫(kù),相比我們?cè)瓉?lái)使用原生的Runtime和Process有不少優(yōu)點(diǎn)。
對(duì)比我之前寫(xiě)過(guò)的代碼,總結(jié)一下:
簡(jiǎn)化路徑處理
如果要調(diào)用的腳本的路徑存在空格,Apache Commons Execs會(huì)自動(dòng)幫忙加上轉(zhuǎn)義字符
兼容Windows環(huán)境
使用原生Runtime和Process方式時(shí),必須手工為調(diào)用bat腳本加上cmd /c,比如把test.bat腳本拼接成cmd /c才向Runtime.exec方法傳入這個(gè)腳本作為第一個(gè)參數(shù)
支持超時(shí)設(shè)置
原生的Runtime和Process并沒(méi)有直接支持超時(shí)的設(shè)置,但網(wǎng)上也有在原生基礎(chǔ)上做的超時(shí)功能的封裝,大概是基于循環(huán)定期檢查的機(jī)制。在SF上也有類(lèi)似的文章,其中的代碼大可參考一下,我要提醒的是,需要注意異步線程不能給及時(shí)返回結(jié)果的問(wèn)題。
在我的項(xiàng)目需求中,規(guī)定要獲得腳本的退出碼,標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出。另外,還有可能要從標(biāo)注輸出中解析得到一個(gè)描述成功或失敗的結(jié)果,大概就是過(guò)濾腳本的標(biāo)準(zhǔn)輸出,捕獲感興趣的某一行,最后要預(yù)留超時(shí)設(shè)置的接口。還有,需要支持字符編碼設(shè)置,在Windows下對(duì)象調(diào)試程序很有幫助,因此,我們可以列表表示整個(gè)需求。
序號(hào) | 需求 | 是否必須 |
---|---|---|
1 | 退出碼、標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出 | 是 |
2 | 獲得腳本提供的結(jié)果描述 | 是 |
3 | 設(shè)置超時(shí) | 否 |
4 | 設(shè)置字符編碼 | 否 |
public abstract class AbstractCommonExecs { private String bin; //腳本 private Listarguments; //參數(shù) //Constructor method //封裝返回結(jié)果 public ExecResult exec() throws IOException { try{ Executor executor = getExecutor(); //執(zhí)行線程 CommandLine cmdLine = getCommandLine(); //腳本命令參數(shù)等 if(supportWatchdog()) { //是否支持監(jiān)視 用于設(shè)置超時(shí)等 executor.setWatchdog(getWatchdog()); } executor.setStreamHandler(streamHandler); //設(shè)置處理標(biāo)注輸出和錯(cuò)誤輸出的Handler int ret = executor.execute(cmdLine); //獲得退出碼 }catch(ExecuteException e) { int ret = e.getExitValue(); //如果出現(xiàn)異常還能獲得退出碼 關(guān)于這個(gè)仔細(xì)想想 } } }
1.1 抽象類(lèi)接收腳本和參數(shù),類(lèi)型和形式還可以是別的形式
1.2 對(duì)外提供的exec方法返回的是退出碼、標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出和腳本提供的結(jié)果描述
1.3 通過(guò)getXXX方法的形式可以將具體的實(shí)現(xiàn)交給具體實(shí)現(xiàn)類(lèi)來(lái)完成
2. 如何處理輸出為了從Executor中獲得標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出,是需要向Executor傳入一個(gè)streamHandler的是,這是一個(gè)基于字節(jié)流式的Handler,為了支持字符編碼的設(shè)計(jì),
最終處理時(shí)我們還需要將它轉(zhuǎn)成字符流并設(shè)置目標(biāo)字符編碼,比如在Windows開(kāi)發(fā)環(huán)境下設(shè)置為GBK。
executor.setStreamHandler(streamHandler); //設(shè)置處理標(biāo)注輸出和錯(cuò)誤輸出的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"); //設(shè)置編碼 String error = errorStream.toString("gbk"); //設(shè)置編碼
第二種,參考這個(gè)答案。
第二種是無(wú)法設(shè)置字符編碼的,而第一種是獲得了整個(gè)標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出后再設(shè)置字符編碼的。
如果采用這種方式,為了滿足從標(biāo)準(zhǔn)輸出解析某個(gè)特殊結(jié)果是需要對(duì)這個(gè)標(biāo)準(zhǔn)輸出做切分,再循環(huán)判斷的。
最后我采用的是PipedInputStream和PipedOutStream的方式,這也是為什么會(huì)有這個(gè)問(wèn)題如何正確使用PipedInputStream和PipedOutputStream
。為了讓處理標(biāo)注輸出、錯(cuò)誤輸出和結(jié)果描述看起來(lái)比較統(tǒng)一,我使用了回調(diào)的方式。
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); //這里可以獲得結(jié)果描述 } } }4. 說(shuō)明
整體思路上的抽象已經(jīng)做到了,但是還不夠徹底,抽象類(lèi)exec方法體內(nèi)業(yè)務(wù)邏輯還是過(guò)于耦合的。
完整代碼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測(cè)試 1. 支持字符編碼設(shè)置的測(cè)試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; } } /** * 接口回調(diào)的方式解析腳本的錯(cuò)誤輸出 * @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); } } /** * 接口回調(diào)的方式解析腳本的標(biāo)準(zhǔn)輸出 * @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. 支持超時(shí)設(shè)置的測(cè)試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; } //提供這個(gè)編碼即可 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()); } }
設(shè)置監(jiān)視狗就能設(shè)置超時(shí)
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; // 使用監(jiān)視狗 監(jiān)視腳本執(zhí)行超時(shí)的情況 } 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下測(cè)試
public class TimeoutGbkCommonExecs extends TimeoutCommonExecs{ public TimeoutGbkCommonExecs(String bin, Listarguments, long timeout) { super(bin, arguments, timeout); } //字符編碼設(shè)置 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()); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65396.html
摘要:第二集非必選持續(xù)集成編譯打包部署到線上環(huán)境集成前言在持續(xù)集成第一集中講解了關(guān)于如果編譯,打包,發(fā)布包到私服。在下面一集中,也就是第二集中,開(kāi)始講解如何持續(xù)集成可運(yùn)行的服務(wù)包到測(cè)試和正式環(huán)境。 第二集 hudson+gradle+git+maven(非必選)持續(xù)集成編譯,打包,部署到線上環(huán)境集成 前言 在持續(xù)集成第一集中,講解了關(guān)于如果編譯,打包,發(fā)布jar包到maven私服。在下面一集...
摘要:操作系統(tǒng)只用權(quán)限才能監(jiān)聽(tīng)已下的端口。雖然我們可以讓啟動(dòng)后以用戶的權(quán)限工作。我們還可以指定啟動(dòng)是的總之我們可以更精細(xì)的控制的運(yùn)行方式。只有該版本的是綁定到二進(jìn)制發(fā)行版中。這意味著它要與該版本的一同使用。 jsvc 是個(gè)什么是么高端武器呢 全稱(chēng):Java Service 還是沒(méi)有弄明白是什么,那就繼續(xù)往下看。 我們贊不討論 tomcat 應(yīng)不應(yīng)該運(yùn)行在80端口上。 假如我們有需求,需要 ...
摘要:在中,工具類(lèi)定義了一組公共方法,這篇文章將介紹中使用最頻繁及最通用的工具類(lèi)。另外,工具類(lèi),根據(jù)阿里開(kāi)發(fā)手冊(cè),包名如果要使用不能帶,工具類(lèi)命名為 在Java中,工具類(lèi)定義了一組公共方法,這篇文章將介紹Java中使用最頻繁及最通用的Java工具類(lèi)。以下工具類(lèi)、方法按使用流行度排名,參考數(shù)據(jù)來(lái)源于Github上隨機(jī)選取的5萬(wàn)個(gè)開(kāi)源項(xiàng)目源碼。 一. org.apache.commons.io....
閱讀 3588·2021-09-13 10:28
閱讀 1937·2021-08-10 09:43
閱讀 1010·2019-08-30 15:44
閱讀 3178·2019-08-30 13:14
閱讀 1830·2019-08-29 16:56
閱讀 2938·2019-08-29 16:35
閱讀 2843·2019-08-29 12:58
閱讀 864·2019-08-26 13:46