继上一篇[url=http://zzldn.iteye.com/blog/1007831]Process应用之惑[/url]后,继续在为此不断修改,后来因为需求变化,又开始了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就需要分为两个参数加入,而现在我们更希望是一个命令是一个字符串。
有什么问题希望指正。