/***************************************
 *                                     *
 *  JBoss: The OpenSource J2EE WebOS   *
 *                                     *
 *  Distributable under LGPL license.  *
 *  See terms of license at gnu.org.   *
 *                                     *
 ***************************************/
package org.jboss.remoting.transport.rmi;

import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.ServerInvoker;
import org.jboss.remoting.marshal.serializable.SerializableMarshaller;

import java.io.IOException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ExportException;
import java.rmi.server.RemoteStub;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;

/**
 * RMIServerInvoker
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
 * @version $Revision: 1.3.10.2 $
 */
public class RMIServerInvoker extends ServerInvoker implements RMIServerInvokerInf
{
   private final RemoteStub stub;

      public static final int DEFAULT_REGISTRY_PORT = 3455;
   public static final String REGISTRY_PORT_KEY = "registryPort";

   public RMIServerInvoker(InvokerLocator locator)
         throws RemoteException
   {
      super(locator);

      try
      {
         if (locator.getPort() != -1)
         {
            Registry registry = null;
            //TODO: -TME Need to find a better way to do this.  Now have 2 ports to worry about.
            registry = getRegistry(locator);
            stub = (RemoteStub) UnicastRemoteObject.exportObject(this, locator.getPort());
            if(log.isDebugEnabled())
            {
               log.debug("Binding registry to " + "remoting/RMIServerInvoker/" + locator.getPort());
            }
            registry.rebind("remoting/RMIServerInvoker/" + locator.getPort(), this);
         }
         else
         {
            stub = UnicastRemoteObject.exportObject(this);
         }
      }
      catch (RemoteException th)
      {
         th.printStackTrace();
         throw th;
      }

   }

   public RMIServerInvoker(InvokerLocator locator, Map configuration) throws RemoteException
   {
      super(locator, configuration);
      //TODO: -TME Need to setup configuration for RMI
      try
      {
         if (locator.getPort() != -1)
         {
            Registry registry = null;
            //TODO: -TME Need to find a better way to do this.  Now have 2 ports to worry about.
            registry = getRegistry(locator);
            stub = (RemoteStub) UnicastRemoteObject.exportObject(this, locator.getPort());
            if(log.isDebugEnabled())
            {
               log.debug("Binding registry to " + "remoting/RMIServerInvoker/" + locator.getPort());
            }
            registry.rebind("remoting/RMIServerInvoker/" + locator.getPort(), this);
         }
         else
         {
            stub = UnicastRemoteObject.exportObject(this);
         }
      }
      catch (RemoteException th)
      {
         th.printStackTrace();
         throw th;
      }
   }

   private Registry getRegistry(InvokerLocator locator)
         throws RemoteException
   {
      Registry registry = null;

      int port = DEFAULT_REGISTRY_PORT;

      // See if locator contains a specific registry port
      Map params = locator.getParameters();
      if(params != null)
      {
         String value = (String)params.get(REGISTRY_PORT_KEY);
         if(value != null)
         {
            try
            {
               port = Integer.parseInt(value);
               log.debug("Using port " + port + " for rmi registry.");
            }
            catch (NumberFormatException e)
            {
               log.error("Can not set the RMIServerInvoker RMI registry to port " + value + ".  This is not a valid port number.");
            }
         }
      }

      try
      {
         if(log.isDebugEnabled())
         {
            log.debug("Creating registry for " + port);
         }
         registry = LocateRegistry.createRegistry(port);
      }
      catch( ExportException exportEx)
      {
         if(log.isDebugEnabled())
         {
            log.debug("Locating registry for " + port);
         }
         // Probably means that the registry already exists, so just get it.
         registry = LocateRegistry.getRegistry(port);
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
      if(log.isDebugEnabled())
      {
         log.debug("Got registry: " + registry);
      }
      return registry;
   }

   protected String getDefaultDataType()
   {
      return SerializableMarshaller.DATATYPE;
   }

   /**
    * destroy the RMI Server Invoker, which will unexport the RMI server
    */
   public void destroy()
   {
      super.destroy();
      try
      {
         try
         {
            if(log.isDebugEnabled())
            {
               log.debug("unbinding " + "remoting/RMIServerInvoker/" + locator.getPort() + " from registry running on port " + (locator.getPort() + 1));
            }
            Registry registry = getRegistry(locator);
            registry.unbind("remoting/RMIServerInvoker/" + locator.getPort());
         }
         catch (RemoteException e)
         {
            e.printStackTrace();
         }
         catch (NotBoundException e)
         {
            e.printStackTrace();
         }

         UnicastRemoteObject.unexportObject(this, true);

      }
      catch (java.rmi.NoSuchObjectException e)
      {

      }
   }

   public String getMBeanObjectName()
   {
      return "jboss.remoting:service=invoker,transport=rmi";
   }

   protected void finalize() throws Throwable
   {
      destroy();
      super.finalize();
   }

   /**
    * returns true if the transport is bi-directional in nature, for example,
    * SOAP in unidirectional and SOCKETs are bi-directional (unless behind a firewall
    * for example).
    *
    * @return
    */
   public boolean isTransportBiDirectional()
   {
      return true;
   }

   public final RemoteStub getStub()
   {
      return stub;
   }

   public Object transport(Object invocation)
         throws RemoteException, IOException
   {
      return invoke(invocation);
   }
}