我可以从 Java 写入 Beanshell 控制台吗?

发布于 2024-08-12 06:36:53 字数 838 浏览 6 评论 0原文

我在我的应用程序中使用 Beanshell 作为嵌入式调试工具。这意味着我可以 telnet 到我的应用程序并在其运行时查看其内部结构(我通常使用 rlwrap 包装 telnet 会话)。

问题是我发现打印到 Beanshell 控制台(而不是应用程序本身的标准输出)的唯一方法是 Beanshell 中的 print() 方法。

但我想用 Java 编写可以从 Beanshell 调用的代码,该代码将输出到 Beanshell 控制台 - 即。它将显示在我的 telnet 会话中,而不是发送到应用程序的标准输出,就像您尝试使用 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 会话(而不是我的应用程序的 System.out)的 Java 函数

I'm using Beanshell as an embedded debugging tool in my app. It means I can telnet to my app and poke around with its internals while it is running (I typically wrap the telnet session with rlwrap).

The problem is that the only way I've found to print to the Beanshell console, rather than stdout of the application itself, is the print() method within Beanshell.

But I'd like to write code in Java that I can call from Beanshell, which will output to the Beanshell console - ie. it will be shown in my telnet session, not sent to stdout of the application, as happens if you try to use System.out or System.err.

Is this possible?


edit: To further clarify, I'm setting up a Beanshell server as follows:

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);
    }
}

How would I modify this such that I can write a Java function that outputs to the telnet session (rather than to System.out of my application)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

西瓜 2024-08-19 06:36:53

我会将其复制到那里,因为现在看来评论已被忽略。

你可以:
而不是使用将调试信息打印到标准输出的方法返回该调试信息:

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 服务器中的一些位并使用修改后的服务器来启动服务器,而不是使用 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); }
    }
}

I'll copy it there as it seems that comments are disregarded this days.

you can:
instead of having a method which print debug information to the standard output returns that debug information:

class ClientList {
 Integer clients = 0;
 public String debugClientList() {
   return clients.toString();
 }

and then calling it from beanshell

print(clients.debugCientList());

will give you an output on your telnet

or if you need it more logger like, you need to interact with the Interpreter object directly

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);
 }

I'm replying there to the comment as it will need some more coding; the telnet implementation uses a different interpreter for each connection, so you have to expose that interpreter to the objects for printing to the telnet client. The quickest way is to change some bit in the default telnet server and use the modified one to start your server, instead of using the server() scripted command (it's under lgpl or sun license terms)

note that this way have an interpreter started for each connection; the easy and quick fix is to maintain a list of all the running interpreters and print to each one the debugging information, so:

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); }
    }
}
美羊羊 2024-08-19 06:36:53

我认为没有 hack 是不可能的...,抱歉,调整了 BSH 的 telnet 服务器实现。

我们正在查看的类是bsh.util.Sessiond。一旦启动,它就会打开并维护一个 telnet 服务器。当它收到命令时,它会创建一个新的工作线程,这会创建一个具有正确输入和输出流(从套接字派生)的新 bsh.Interpreter 并运行解释器。

因此,只有解释命令的输出才会发送到 telnet 客户端,因为 System.outSystem.err 不会被重定向。

但这正是您的情况必须要做的:在解释器运行命令并重置流之前将 System.outSystem.err 重定向到套接字输出流完成后。

我建议您将 bsh.util.Sessiond 类复制到 mybsh.util.DebuggerSessiond 之类的内容,将重定向代码应用于内部类 SessiondConnection 的 run 方法并修改 bsh/commands/server.bsh 以另外启动此“新”telnet 服务器(或代替原始服务器)。 (我猜,这个脚本启动服务器...)

源代码可以在这里找到: beanshell 存储库

I think it's not possible with out hack..., sorry, adapting the telnet server implementation of BSH.

The class we're looking at is bsh.util.Sessiond. Once started, it opens and maintains a telnet server. When ever it receives a command, it creates a new worker thread, this on creates a new bsh.Interpreter with the correct input and output streams (derived from the socket) and runs the interpreter.

So it makes sense, that only output of interpreted commands is send to the telnet client, because System.out and System.err are not redirected.

But that exactly is what has to be done in your case: redirect System.out and System.err to the sockets output stream before the interpreter runs the command and reset the streams after completion.

I'd suggest, you copy the bsh.util.Sessiond class to something like mybsh.util.DebuggerSessiond, apply the redirection code to the run method of the inner class SessiondConnection and modify bsh/commands/server.bsh to start this 'new' telnet server in addition (or instead of the original one). (I guess, this script starts the servers...)

Source code can be found here: beanshell repository

紫瑟鸿黎 2024-08-19 06:36:53

如果您的应用程序的所有输出都是使用某些日志记录框架编写的,那么您可以编写一个自定义附加程序/处理程序,除了记录日志之外,还可以将文件写入 beanshell 控制台吗?执行某些 beanshell 命令后可能启用和禁用控制台日志记录。

(我以前不知道beanshell,但它似乎很有用!)

If all your application's output is written using some logging framework anyways, you could write a custom appender/handler which besides logging to say a file would write to the beanshell console in addition? Possibly enabling and disabling the console-logging after executing some beanshell command.

(I wasn't aware of beanshell before, but it seems useful!)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文