/* JFox, the OpenSource J2EE Application Server
 *
 * Copyright (C) 2002 huihoo.org
 * Distributable under GNU LGPL license
 * See the GNU Lesser General Public License for more details.
 */

package org.huihoo.jfox.service;

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

import javax.rmi.PortableRemoteObject;
import org.huihoo.jfox.logging.Logger;

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

public class ServiceWrapper extends ServiceSupport implements ServiceWrapperMBean, Runnable {
  private int port = 0;
  private Remote remoteObject = null;
  private ServerSocket ssocket = null;

  public ServiceWrapper(Remote remoteObject, int port) {
    String className = remoteObject.getClass().getName();
    this.setName(parseName(className) + "[ServiceWrapper]");
    this.setLogger(Logger.getLogger(className + "[ServiceWrapper]"));
    this.remoteObject = remoteObject;
    this.setPort(port);
  }

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

    if(remoteObjectClass.isAssignableFrom(Remote.class)){
//      throw new RuntimeException(remoteObjectClass.getName() + " is not a Remote class");
      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);
  }

  public ServiceWrapper(String remoteObjectClassName, int port){
    this.setName(parseName(remoteObjectClassName) + "[ServiceWrapper]");
    this.setLogger(Logger.getLogger(remoteObjectClassName + "[ServiceWrapper]"));

    Class claz = null;
    try {
      claz = Thread.currentThread().getContextClassLoader().loadClass(remoteObjectClassName);
      if(claz.isAssignableFrom(Remote.class)){
//      throw new RuntimeException(remoteObjectClass.getName() + " is not a Remote class");
        logger.fatal(claz.getName() + " is not a Remote class");
        return;
      }

      this.remoteObject = (Remote)claz.newInstance();
    }
    catch(ClassNotFoundException e){
      logger.fatal("Class " + remoteObjectClassName + " not found!" + e);
    }
    catch(Exception e){
      logger.fatal(claz.getName() + " instantiator error", e);
    }
    this.setPort(port);
//    System.out.println("ServiceWrapper.constructor");
  }

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

    }
    ssocket = new ServerSocket(port);
  }

  protected void doStart() throws Exception {
    PortableRemoteObject.exportObject(remoteObject);
    new Thread(this).start();
  }

  protected void doStop() throws Exception {
    PortableRemoteObject.unexportObject(remoteObject);
  }

  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(isRunning()) {
      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;
    }

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

}
