/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.system;

import java.io.BufferedOutputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.Remote;
import java.rmi.server.UnicastRemoteObject;

import org.jfox.framework.logger.Logger;

/**
 * wrap a remote system and export it's stub to a speicific port
 *
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class ServiceWrapper extends AbstractService {
    private int port = 0;
    private Remote remoteObject = null;
    private ServerSocket ssocket = null;
    private Remote stub = null;
    private int exportPort = 0;

    public ServiceWrapper(Remote remoteObject, int port) {
        this(remoteObject, port, 0);
    }

    /**
     * @param remoteObject
     * @param port
     * @param exportPort   remote object export port
     */
    public ServiceWrapper(Remote remoteObject, int port, int exportPort) {
        String className = remoteObject.getClass().getName();
        this.setName(ServiceWrapper.class.getName() + "#" + parseName(className));
        this.setLogger(Logger.getLogger(ServiceWrapper.class.getName() + "#" + className + ""));
        this.remoteObject = remoteObject;
        this.setPort(port);
        if(exportPort > 0) {
            this.exportPort = exportPort;
        }
    }


    public ServiceWrapper(Class remoteObjectClass, int port) {
        this(remoteObjectClass, port, 0);
    }

    public ServiceWrapper(Class remoteObjectClass, int port, int exportPort) {
        String className = remoteObjectClass.getName();
        this.setName(ServiceWrapper.class.getName() + "#" + parseName(className));
        this.setLogger(Logger.getLogger(ServiceWrapper.class.getName() + "#" + className));

//    if(!Remote.class.isAssignableFrom(remoteObjectClass)){
//      logger.fatal(remoteObjectClass.getName() + " is not a Remote class");
//      return;
//    }

        try {
            this.remoteObject = (Remote) remoteObjectClass.newInstance();
        }
        catch(Exception e) {
            logger.fatal(remoteObjectClass.getName() + " instantiator error", e);
        }

        this.setPort(port);
        if(exportPort > 0) {
            this.exportPort = exportPort;
        }
    }

    protected void doInit() throws Exception {
        try {
            UnicastRemoteObject.unexportObject(remoteObject, true);
        }
        catch(Exception e) {

        }
        ssocket = new ServerSocket(port);
    }

    protected void doStart() throws Exception {
        //TODO: set the JNDI Remote Service port
        stub = UnicastRemoteObject.exportObject(remoteObject, exportPort);
        new Thread(this, "ServiceWrapper[" + remoteObject.getClass().getName() + "]").start();
    }

    protected void doStop() throws Exception {
        UnicastRemoteObject.unexportObject(remoteObject, false);
    }

    protected void doDestroy() throws Exception {
        ssocket.close();
        ssocket = null;
    }

    private void setPort(int port) {
        this.port = port;
    }

    public int getPort() {
        return port;
    }

    public Remote getRemoteObject() {
        return remoteObject;
    }

    public void run() {
        while(isStarted()) {
            try {
                Socket socket = ssocket.accept();
                new Sender(socket).start();
            }
            catch(Exception e) {
                logger.warn(e.getMessage());
            }
        }
    }

    /**
     * Thread class to send stub
     */
    private class Sender extends Thread {
        private Socket socket = null;

        public Sender(Socket socket) {
            this.socket = socket;
            setName("ServiceWrapper Sender[" + remoteObject.getClass().getName() + "]");
        }

        public void run() {
            try {
                ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()));
                out.writeObject(stub);
                out.flush();
                socket.close();
            }
            catch(Exception e) {
                logger.warn(e.getMessage(), e);
            }
        }
    }

}
