我在我的应用中使用Beanshell作为嵌入式调试工具。这意味着我可以telnet到我的应用程序,并在其运行时与其内部进行调整(我通常用rlwrap包装telnet会话)。
问题是,我发现打印到Beanshell控制台的唯一方法是Beanshell中的print()方法,而不是应用程序本身的stdout。
但是我想用Java编写代码,我可以从Beanshell调用它,它将输出到Beanshell控制台 - 即。它将显示在我的telnet会话中,而不是发送到应用程序的stdout,就像你尝试使用System.out或System.err一样。
这可能吗?
编辑:为了进一步澄清,我正在设置一个Beanshell服务器,如下所示:
public static void setUpBeanshell() {
try {
i.setShowResults(true);
i.eval(new InputStreamReader(Bsh.class.getResourceAsStream("init.bsh")));
i.eval("server(" + Main.globalConfig.beanShellPort + ");");
} catch (final EvalError e) {
Main.log.error("Error generated while starting BeanShell server", e);
}
}
我如何修改它,以便我可以编写一个输出到telnet会话的Java函数(而不是我的应用程序的System.out)
答案 0 :(得分:2)
我会把它复制到那里,因为这些日子的评论似乎被忽视了。
你可以: 而不是将调试信息打印到标准输出的方法返回该调试信息:class ClientList {
Integer clients = 0;
public String debugClientList() {
return clients.toString();
}
然后从beanshell中调用它
print(clients.debugCientList());
将在您的telnet上为您提供输出
或者如果你需要更多的记录器,你需要直接与Interpreter对象进行交互
InterpreterSingleton {
public static final void Console console = new Interpreter();
}
....
class ClientList {
Integer clients = 0;
public void addClient(Client c) {
....
InterpreterSingleton.console.print("Client added, clients now are " + clients);
}
我在那里回复评论,因为它需要更多编码; telnet实现为每个连接使用不同的解释器,因此您必须将该解释器公开给对象以便打印到telnet客户端。最快的方法是更改默认telnet服务器中的一些位并使用修改后的telnet服务器启动服务器,而不是使用server()脚本命令(它在lgpl或sun许可条款下)
请注意,这种方式为每个连接启动了一个解释器;简单快捷的解决方法是维护所有正在运行的解释器的列表,并向每个解释器打印调试信息,所以:
class InterpreterSingletonList {
public static final void Set<Interpreter> is = new HashSet();
void printToAll(String s) {
for (Interpreter i: is) {
i.print(s);
}
}
}
package bsh.util;
import java.io.*;
import java.net.Socket;
import java.net.ServerSocket;
import bsh.*;
/**
BeanShell remote session server.
Starts instances of bsh for client connections.
Note: the sessiond effectively maps all connections to the same interpreter
(shared namespace).
*/
public class Sessiond extends Thread
{
private ServerSocket ss;
NameSpace globalNameSpace;
public Sessiond(NameSpace globalNameSpace, int port) throws IOException
{
ss = new ServerSocket(port);
this.globalNameSpace = globalNameSpace;
}
public void run()
{
try
{
while(true)
new SessiondConnection(globalNameSpace, ss.accept()).start();
}
catch(IOException e) { System.out.println(e); }
}
}
class SessiondConnection extends Thread
{
NameSpace globalNameSpace;
Socket client;
SessiondConnection(NameSpace globalNameSpace, Socket client)
{
this.client = client;
this.globalNameSpace = globalNameSpace;
}
public void run()
{
try
{
InputStream in = client.getInputStream();
PrintStream out = new PrintStream(client.getOutputStream());
/* this is the one you're looking for */
Interpreter i = new Interpreter(
new InputStreamReader(in), out, out, true, globalNameSpace);
i.setExitOnEOF( false ); // don't exit interp
/*store the interpreter on the list*/
InterpreterSingletonList.is.add(i);
i.run();
/*remove it (i.run() blocks)*/
InterpreterSingletonList.is.remove(i);
}
catch(IOException e) { System.out.println(e); }
}
}
答案 1 :(得分:0)
我认为不可能没有黑客... ...抱歉,调整BSH的telnet服务器实现。
我们正在研究的课程是bsh.util.Sessiond
。一旦启动,它将打开并维护一个telnet服务器。当它收到命令时,它会创建一个新的工作线程,这将创建一个新的bsh.Interpreter,其中包含正确的输入和输出流(从套接字派生)并运行解释器。
所以有意义的是,只有解释命令的输出才会发送到telnet客户端,因为System.out
和System.err
不会被重定向。
但这恰恰是你需要做的事情:在解释器运行命令之前将System.out
和System.err
重定向到套接字输出流,并在完成后重置流。
我建议你将bsh.util.Sessiond
类复制到类似mybsh.util.DebuggerSessiond
的类,将重定向代码应用于内部类SessiondConnection的run方法,并修改bsh/commands/server.bsh
以启动它'新的'telnet服务器另外(或代替原来的)。 (我想,这个脚本启动服务器......)
可在此处找到源代码:beanshell repository
答案 2 :(得分:-1)
如果您的所有应用程序的输出都是使用某些日志记录框架编写的,您可以编写一个自定义的appender / handler,除了记录之外还要写一个文件会写入beanshell控制台吗?执行一些beanshell命令后,可能会启用和禁用控制台日志记录。
(之前我没有发现过豆壳,但它看起来很有用!)