Можно ли писать в консоль Beanshell из Java? - PullRequest
3 голосов
/ 19 ноября 2009

Я использую Beanshell как встроенный инструмент отладки в моем приложении. Это означает, что я могу подключиться к своему приложению через telnet и поработать с его внутренними компонентами во время его работы (обычно я оборачиваю сеанс telnet с помощью rlwrap).

Проблема заключается в том, что единственный способ печати на консоли Beanshell, а не на stdout самого приложения, - это метод print () в Beanshell.

Но я хотел бы написать код на Java, который я могу вызывать из Beanshell, который будет выводить на консоль Beanshell - т.е. это будет показано в моем сеансе telnet, а не отправлено на стандартный вывод приложения, как это происходит, если вы попытаетесь использовать System.out или System.err.

Возможно ли это?


edit: Чтобы уточнить, я настраиваю сервер 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);
    }
}

Как мне изменить это так, чтобы я мог написать функцию Java, которая выводит данные в сеанс telnet (а не в System.out моего приложения)

Ответы [ 3 ]

2 голосов
/ 26 ноября 2009

Я скопирую его туда, поскольку кажется, что комментарии игнорируются в эти дни.

вы можете: вместо метода, который выводит отладочную информацию в стандартный вывод, возвращается эта отладочная информация:

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

и затем вызывая его из бобовой оболочки

print(clients.debugCientList());

выдаст вам вывод по вашему телнету

или, если вам нужен более похожий логгер, вам нужно напрямую взаимодействовать с объектом 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); }
    }
}
0 голосов
/ 01 декабря 2009

Я думаю, что это невозможно без взлома ... извините, адаптация реализации BSH для сервера telnet.

Класс, на который мы смотрим, это bsh.util.Sessiond. После запуска он открывает и поддерживает сервер telnet. Когда он получает команду, он создает новый рабочий поток, он создает новый bsh.Interpreter с правильными входными и выходными потоками (полученными из сокета) и запускает интерпретатор.

Таким образом, имеет смысл, что клиенту telnet отправляется только вывод интерпретированных команд, поскольку System.out и System.err не перенаправляются.

Но это именно то, что нужно сделать в вашем случае: перенаправить System.out и System.err в поток вывода сокетов до того, как интерпретатор выполнит команду, и сбросить потоки после завершения.

Я бы посоветовал скопировать класс bsh.util.Sessiond во что-то вроде mybsh.util.DebuggerSessiond, применить код перенаправления к методу run внутреннего класса SessiondConnection и изменить bsh/commands/server.bsh, чтобы дополнительно запустить этот «новый» сервер telnet (или вместо оригинального). (Полагаю, этот скрипт запускает серверы ...)

Исходный код можно найти здесь: хранилище бобов

0 голосов
/ 26 ноября 2009

Если все выходные данные вашего приложения в любом случае записываются с использованием какой-либо среды ведения журналов, вы можете написать собственный обработчик / обработчик, который помимо ведения журнала, чтобы сказать, что файл будет дополнительно записывать в консоль beanshell? Возможно включение и отключение ведения журнала консоли после выполнения какой-либо команды beanshell.

(я раньше не знал о бобовой оболочке, но она кажется полезной!)

...