/***************************************
 *                                     *
 *  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.CannotConnectException;
import org.jboss.remoting.ConnectionFailedException;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.RemoteClientInvoker;
import org.jboss.remoting.marshal.Marshaller;
import org.jboss.remoting.marshal.UnMarshaller;
import org.jboss.remoting.marshal.serializable.SerializableMarshaller;

import java.io.IOException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Map;

/**
 * RMIClientInvoker
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @author <a href="mailto:telrod@vocalocity.net">Tom Elrod</a>
 * @version $Revision: 1.4.10.2 $
 */
public class RMIClientInvoker extends RemoteClientInvoker
{
   private RMIServerInvokerInf server;

   /**
    * Need flag to indicate if have been able to lookup registry and set stub.
    * Can't do this in the constructor, as need to throw CannotConnectException so
    * for clustering capability.
    * @param locator
    */
   private boolean connected = false;

   public RMIClientInvoker(InvokerLocator locator)
   {
      super(locator);
   }

   private int getRegistryPort(InvokerLocator locator)
   {
      int port = RMIServerInvoker.DEFAULT_REGISTRY_PORT;

      // See if locator contains a specific registry port
      Map params = locator.getParameters();
      if(params != null)
      {
         String value = (String)params.get(RMIServerInvoker.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.");
            }
         }
      }
      return port;
   }

   /**
    * get the server stub
    *
    * @param server
    */
   public void setServerStub(RMIServerInvokerInf server)
   {
      this.server = server;
      System.err.println(this.server);
   }

   /**
    * return the RMI server stub
    *
    * @return
    */
   public RMIServerInvokerInf getServerStub()
   {
      return this.server;
   }

   /**
    * subclasses must implement this method to provide a hook to connect to the remote server, if this applies
    * to the specific transport. However, in some transport implementations, this may not make must difference since
    * the connection is not persistent among invocations, such as SOAP.  In these cases, the method should
    * silently return without any processing.
    *
    * @throws ConnectionFailedException
    */
   protected void handleConnect()
         throws ConnectionFailedException
   {
      //TODO: -TME Need to figure this out a little better as am now dealing with
      // with 2 ports, the rmi server and the registry.
      try
      {
         String host = locator.getHost();
         int port = getRegistryPort(locator);
         Registry regsitry = LocateRegistry.getRegistry(host, port);
         Remote remoteObj = regsitry.lookup("remoting/RMIServerInvoker/" + locator.getPort());
         log.debug("Remote RMI Stub: " + remoteObj);
         setServerStub((RMIServerInvokerInf)remoteObj);
         connected = true;
      }
      catch(Exception e)
      {
         connected = false;
         log.debug("Error connecting RMI invoker client.", e);
         throw new CannotConnectException("Error connecting RMI invoker client", e);
      }
   }

   /**
    * subclasses must implement this method to provide a hook to disconnect from the remote server, if this applies
    * to the specific transport. However, in some transport implementations, this may not make must difference since
    * the connection is not persistent among invocations, such as SOAP.  In these cases, the method should
    * silently return without any processing.
    */
   protected void handleDisconnect()
   {
   }

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

   protected Object transport(String sessionId, Object invocation, Map metadata, Marshaller marshaller, UnMarshaller unmarshaller)
         throws IOException, ConnectionFailedException
   {
      if (this.server == null)
      {
         log.error("Server stub has not been set in RMI invoker client.  See previous errors for details.");
         //throw new IOException("Server stub hasn't been set!");
         throw new CannotConnectException("Server stub has not been set.");
      }
      try
      {
         return server.transport(invocation);
      }
      catch (RemoteException e)
      {
         throw new CannotConnectException("Error making invocation in RMI client invoker.", e);
      }
   }
}