继上一篇
Process应用之惑
后,继续在为此不断修改,后来因为需求变化,又开始了process的进步一发掘。
先交代下背景。第三方软件发布了命令接口,根据执行发布的命令,可以得到第三方硬件的信息。而我现在需要软件直接执行我排好序的命令,以便获取硬件信息,这时需要做个远程的命令登陆,然后远程执行命令,再退出。这其实就好比要模拟telnet、ftp等的客户端,当远程登陆后执行一个指令,然后返回一大堆执行结果,从而实现与客户端的命令交互。
起先,去下了个common-net.jar的源码看了看,发现telnet、ftp等通信协议都是有专门的消息通道,产生专门的端口来做通讯传输。而这个第三方的命令接口只是个在dos下能执行命令的客户端,再说,即使人家做成了类似telnet的通信协议,俺也不可能知道人家用的端口啥啥的。那现在唯一能做的是,模拟DOS窗口,进行命令的交互。
有了这个思路,就又想到Process可是能执行命令的,那这个是不是可以呢!Process是为了执行命令而产生的一个独立的执行线程。理论上如果只要这个线程不被销毁,那么会一直可以执行命令。可是Process三个流InputStream、OutputStream、ErrorStream在API上说的是提供输出信息的,未知获得了这输入、输出流是否能干出一番事业呢!
1.自动执行命令的交互
试试直接就退出了,证明传进去的exit命令起作用了。之所以用sqlplus做替代的命令测试,因为其和我要做的第三方接口命令类似。
然后依据这个简单的测试封装了一个命令交互的类。直接上代码:
我在配置文件里设置如下:
测试方法:
测试结果如下:
真正做到了自动执行命令,并且获取到该命令的结果。
2.如果是想直接敲命令的互动,可是尝试如下:
看看测试结果:
和在dos下执行是完全一样的
产生process = Runtime.getRuntime().exec()时为什么要选择这种方式呢?为什么不是ProcessBuilder了呢?
我们现在不论使用哪种方式产生的命令执行进程都会读取进程的流,所以就不会再有流堵塞而导致无法执行下去的问题了。
ProcessBuilder在加入命令时是以数组的形式,如果是sqlplus orcl/orcl@db就需要分为两个参数加入,而现在我们更希望是一个命令是一个字符串。
有什么问题希望指正。
先交代下背景。第三方软件发布了命令接口,根据执行发布的命令,可以得到第三方硬件的信息。而我现在需要软件直接执行我排好序的命令,以便获取硬件信息,这时需要做个远程的命令登陆,然后远程执行命令,再退出。这其实就好比要模拟telnet、ftp等的客户端,当远程登陆后执行一个指令,然后返回一大堆执行结果,从而实现与客户端的命令交互。
起先,去下了个common-net.jar的源码看了看,发现telnet、ftp等通信协议都是有专门的消息通道,产生专门的端口来做通讯传输。而这个第三方的命令接口只是个在dos下能执行命令的客户端,再说,即使人家做成了类似telnet的通信协议,俺也不可能知道人家用的端口啥啥的。那现在唯一能做的是,模拟DOS窗口,进行命令的交互。
有了这个思路,就又想到Process可是能执行命令的,那这个是不是可以呢!Process是为了执行命令而产生的一个独立的执行线程。理论上如果只要这个线程不被销毁,那么会一直可以执行命令。可是Process三个流InputStream、OutputStream、ErrorStream在API上说的是提供输出信息的,未知获得了这输入、输出流是否能干出一番事业呢!
1.自动执行命令的交互
- public class Demo {
- public static void main(String[] args) {
- Process process=null;
- BufferedOutputStream out=null;
- BufferedInputStream in=null;
- try {
- process=Runtime.getRuntime().exec("sqlplus ethiopia1103/ethiopia1103@db90");
- out=new BufferedOutputStream(process.getOutputStream());
- in=new BufferedInputStream(process.getInputStream());
- out.write("exit".getBytes());
- out.write("\r\n".getBytes());
- out.flush();
- BufferedReader br=new BufferedReader(new InputStreamReader(in));
- String line=null;
- while((line=br.readLine())!=null){
- if(line.indexOf(">")!=-1) break;
- System.out.println(line);
- }
- }catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }finally{
- try {
- if(null!=out){
- out.close();
- out=null;
- }
- if(null!=in){
- in.close();
- in=null;
- }
- int value=process.waitFor();
- if(null!=process)
- process.destroy();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
试试直接就退出了,证明传进去的exit命令起作用了。之所以用sqlplus做替代的命令测试,因为其和我要做的第三方接口命令类似。
然后依据这个简单的测试封装了一个命令交互的类。直接上代码:
- public class DosCommandInteraction {
- private static Logger logger=Logger.getLogger(DosCommandInteraction.class);
- private static final String ENTER="\r\n"; //每次输入命令后回车,然后命令执行,出结果
- private static final String END="> "; //遇到>时就退出,证明上一个命令已经执行完
- private static final String ERROR="ERROR"; //登录时报ERROR就证明已经登录失败
- private Process process=null;
- private BufferedOutputStream out=null;
- private BufferedInputStream in=null;
- /**
- * 登录到该命令下,创建执行命令的环境进程
- * @param command 登陆命令
- */
- public boolean loggin(String command){
- boolean flag=true;
- try {
- process=Runtime.getRuntime().exec(command);
- out=new BufferedOutputStream(process.getOutputStream());
- in=new BufferedInputStream(process.getInputStream());
- String result=writeCommandResult(in); //把登录时的信息取出来,判断是否登录成功!其实也为后面能正常输出命令结果做了清理
- String[] lines=result.split(ENTER);
- for(String line :lines){
- if(line.indexOf(ERROR)!=-1){
- flag=false;
- break;
- }
- }
- }catch (IOException e) {
- // TODO Auto-generated catch block
- logger.error(e);
- close();
- }
- if(!flag) close();
- return flag;
- }
- /**
- * 将输入的命令转化为流执行命令得到执行的记录
- * @param command
- * @return
- */
- public List<String> execCommand(String command){
- logger.info("exec command : "+command);
- InputStream input = new ByteArrayInputStream((command+ENTER).getBytes());
- readerCommand(out,input);
- String result=writeCommandResult(in);
- logger.info(result);
- try {
- input.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return Arrays.asList(result.split(ENTER));
- }
- /**
- * 将命令写入输出流
- * @param outs 全局输出流
- * @param ins 输入命令的流
- */
- private void readerCommand(OutputStream outs,InputStream ins){
- int ch;
- try {
- while ((ch = ins.read()) != -1) {
- outs.write(ch);
- outs.flush();
- }
- } catch (IOException e) {
- close();
- logger.error("readerCommand",e);
- }
- }
- /**
- * 读取命令返回的结果
- * @param ins 全局的输入流
- * @return 命令结果
- */
- private String writeCommandResult(InputStream ins){
- int length = -1;
- byte[] buffer = new byte[10240];
- String readLine = null;
- StringBuilder readResult = new StringBuilder("");
- try {
- while((length=ins.read(buffer))>0){
- readLine = new String(buffer, 0 , length,"gbk");
- readResult.append(readLine);
- if(readLine.indexOf(ERROR)!=-1) break;
- if(readResult.toString().endsWith(END)||readResult.toString().endsWith(END.trim()))
- break;
- }
- } catch (IOException e) {
- close();
- logger.error("writeCommandResult",e);
- }
- return readResult.toString();
- }
- /**
- * 所有命令执行完成后推出命令,关闭进程
- */
- public void quit(){
- execCommand("quit");
- close();
- }
- /**
- * 关闭所有的流和进程
- */
- private void close(){
- try {
- if(null!=out){
- out.close();
- out=null;
- }
- if(null!=in){
- in.close();
- in=null;
- }
- int value=process.waitFor();
- logger.info("process end state :" +value);
- if(null!=process)
- process.destroy();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- logger.error(e);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
我在配置文件里设置如下:
- <bean id="command" class="java.util.ArrayList">
- <constructor-arg>
- <list>
- <value>sqlplus orcl/orcl@db</value>
- <value>select 1 from dual;</value>
- <value>select 2 from dual;</value>
- <value>select 3 from dual;</value>
- <value>select 4 from dual;</value>
- </list>
- </constructor-arg>
- </bean>
测试方法:
- public void handler(){
- List<String> commList=(List<String>) SpringUtil.getObject("command");
- logger.info("start.................");
- DosCommandInteraction dos=new DosCommandInteraction();
- if(!dos.loggin(commList.get(0))){
- logger.info("connection error!");
- return;
- }
- for(int i=1;i<commList.size();i++)
- dos.execCommand(commList.get(i));
- dos.quit();
- logger.info("end.................");
- }
测试结果如下:
- main [2011-06-27 16:33:59] - start.................
- main [2011-06-27 16:33:59] - exec command : select 1 from dual;
- main [2011-06-27 16:33:59] -
- 1
- ----------
- 1
- SQL>
- main [2011-06-27 16:33:59] - exec command : select 2 from dual;
- main [2011-06-27 16:33:59] -
- 2
- ----------
- 2
- SQL>
- main [2011-06-27 16:33:59] - exec command : select 3 from dual;
- main [2011-06-27 16:33:59] -
- 3
- ----------
- 3
- SQL>
- main [2011-06-27 16:33:59] - exec command : select 4 from dual;
- main [2011-06-27 16:33:59] -
- 4
- ----------
- 4
- SQL>
- main [2011-06-27 16:33:59] - exec command : quit
- main [2011-06-27 16:33:59] - 从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
- With the Partitioning, OLAP and Data Mining options 断开
- main [2011-06-27 16:33:59] - process end state :0
- main [2011-06-27 16:33:59] - end.................
真正做到了自动执行命令,并且获取到该命令的结果。
2.如果是想直接敲命令的互动,可是尝试如下:
- public class TwoDemo {
- public static void main(String[] args) {
- Process process = null;
- BufferedOutputStream out = null;
- BufferedInputStream in = null;
- try {
- process = Runtime.getRuntime().exec(
- "sqlplus orcl/orcl@db");
- out = new BufferedOutputStream(process.getOutputStream());
- in = new BufferedInputStream(process.getInputStream());
- readWrite(in, out, System.in, System.out); //该方法借用common-net的测试例子部分的一个工具类的方法
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- try {
- if (null != out) {
- out.close();
- out = null;
- }
- if (null != in) {
- in.close();
- in = null;
- }
- int value = process.waitFor();
- if (null != process)
- process.destroy();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- public static final void readWrite(final InputStream remoteInput,
- final OutputStream remoteOutput, final InputStream localInput,
- final OutputStream localOutput) {
- Thread reader, writer;
- reader = new Thread() {
- @Override
- public void run() {
- int ch;
- try {
- while (!interrupted() && (ch = localInput.read()) != -1) {
- remoteOutput.write(ch);
- remoteOutput.flush();
- }
- } catch (IOException e) {
- // e.printStackTrace();
- }
- }
- };
- writer = new Thread() {
- @Override
- public void run() {
- try {
- Util.copyStream(remoteInput, localOutput);
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
- };
- writer.setPriority(Thread.currentThread().getPriority() + 1);
- writer.start();
- reader.setDaemon(true);
- reader.start();
- try {
- writer.join();
- reader.interrupt();
- } catch (InterruptedException e) {
- }
- }
- }
看看测试结果:
- SQL*Plus: Release 10.2.0.1.0 - Production on 星期一 6月 27 16:45:58 2011
- Copyright (c) 1982, 2005, Oracle. All rights reserved.
- 连接到:
- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
- With the Partitioning, OLAP and Data Mining options
- SQL> select * from dual;
- DU
- --
- X
- SQL> select 1 from dual;
- 1
- ----------
- 1
- SQL> exit
- 从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
- With the Partitioning, OLAP and Data Mining options 断开
和在dos下执行是完全一样的
产生process = Runtime.getRuntime().exec()时为什么要选择这种方式呢?为什么不是ProcessBuilder了呢?
我们现在不论使用哪种方式产生的命令执行进程都会读取进程的流,所以就不会再有流堵塞而导致无法执行下去的问题了。
ProcessBuilder在加入命令时是以数组的形式,如果是sqlplus orcl/orcl@db就需要分为两个参数加入,而现在我们更希望是一个命令是一个字符串。
有什么问题希望指正。