【问题标题】:Can I write to the Beanshell console from Java?我可以从 Java 写入 Beanshell 控制台吗?
【发布时间】:2009-12-01 21:48:48
【问题描述】:

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

问题是我发现打印到 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 函数

【问题讨论】:

  • 您能否继承相关类,添加您想要的功能,然后实例化该类的实例?
  • 对需要在标准输出上打印的对象使用 String dumpInfo() 方法然后调用 print(object.dumpInfo()) 怎么样?还是您希望它更像记录器?
  • 如果您需要更多类似记录器的功能,您需要在单独构造的特定控制台类上实例化您的解释器,然后对象可以使用 console.print() - beanshell.org/javadoc/bsh/… 写入该控制台跨度>

标签: java beanshell


【解决方案1】:

我将它复制到那里,因为这些天似乎忽略了 cmets。

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

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

【讨论】:

  • 我根据您的建议尝试了 console.print() ,但这似乎将输出发送到我的应用程序的 System.out,而不是控制台:-/
  • 你需要使用你在 telnet 上公开的控制台,解释器初始化是默认的非交互式的
  • 根据我添加到问题中的代码,我如何获取我在 telnet 上公开的控制台?
  • 已更新,修改了 bsh telnet 服务器代码。更改类名或将其放入另一个包中,然后启动它,而不是在脚本中使用 server(port)。
【解决方案2】:

我认为没有 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 以启动这个“新”远程登录服务器(或代替原来的)。 (我猜,这个脚本会启动服务器……)

源代码可以在这里找到:beanshell repository

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-15
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      • 2012-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多