
package org.jgroups.protocols;


import bsh.EvalError;
import bsh.Interpreter;
import org.jgroups.annotations.*;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Util;
import org.jgroups.Global;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;


/**
 * Beanshell (<a href=http://www.beanshell.org>www.beanshell.org</a>) interpreter class.
 * The <tt>eval()</tt> method receives Java code, executes it and returns the
 * result of the evaluation (or an exception).<p/>
 * This protocol is experimental
 * User: Bela
 * Date: Mar 8, 2003
 * Time: 1:57:07 PM
 * @author Bela Ban
 */
@Experimental
@MBean(description="Protocol which has a running Beanshell (beanshell.org) interpreter, to which clients can " +
  "connect via TCP and submit any Java commands")
public class BSH extends Protocol implements Runnable {
    protected Interpreter interpreter=null;
    protected ServerSocket srv_sock;
    protected Thread acceptor;
    protected final List<Socket> sockets=new ArrayList<Socket>();


    @Property(description="Port on which the interpreter should listen for requests. 0 is an ephemeral port")
    int bind_port=0;


    public BSH() {
    }

    @ManagedAttribute(description="beanshell port",writable=false)
    public int getCurrentPort() {
        return srv_sock.getLocalPort();
    }

    public void start() throws Exception {
        srv_sock=Util.createServerSocket(getSocketFactory(), "jgroups.bsh.srv_sock", null, bind_port);
        log.debug("Server socket listening at " + srv_sock.getLocalSocketAddress());
        acceptor=new Thread(this);
        acceptor.start();
    }

    public void stop() {
        Util.close(srv_sock);

        if(acceptor != null && acceptor.isAlive())
            acceptor.interrupt();

        Util.sleep(500);
        if(!sockets.isEmpty()) {
            for(Socket sock: sockets)
                Util.close(sock);
        }
    }


    public void run() {
        while(srv_sock != null && !srv_sock.isClosed()) {
            try {
                final Socket sock=srv_sock.accept();
                sockets.add(sock);

                createInterpreter();

                new Thread() {
                    public void run() {
                        try {
                            InputStream input=sock.getInputStream();
                            OutputStream out=sock.getOutputStream();
                            BufferedReader reader=new BufferedReader(new InputStreamReader(input));

                            while(!sock.isClosed()) {
                                String line=reader.readLine();
                                if(line == null || line.length() == 0)
                                    continue;
                                try {
                                    Object retval=interpreter.eval(line);
                                    if(retval != null) {
                                        String rsp=retval.toString();
                                        byte[] buf=rsp.getBytes();
                                        out.write(buf, 0, buf.length);
                                        out.flush();
                                    }
                                    if(log.isTraceEnabled()) {
                                        log.trace(line);
                                        if(retval != null)
                                            log.trace(retval);
                                    }
                                }
                                catch(EvalError evalError) {
                                    evalError.printStackTrace();
                                }
                            }
                        }
                        catch(IOException e) {
                            e.printStackTrace();
                        }
                        finally {
                            Util.close(sock);
                            sockets.remove(sock);
                        }
                    }
                }.start();
            }
            catch(IOException e) {
            }
        }
    }


    synchronized void createInterpreter() {
        // create interpreter just-in-time
        if(interpreter == null) {
            interpreter=new Interpreter();
            try {
                interpreter.set("bsh_prot", this);
            }
            catch(EvalError evalError) {
            }
        }
    }



}
