/*
 * Copyright (C) The MX4J Contributors.
 * All rights reserved.
 *
 * This software is distributed under the terms of the MX4J License version 1.0.
 * See the terms of the MX4J License in the documentation provided with this software.
 */

package mx4j.remote.resolver.iiop;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIIIOPServerImpl;
import javax.management.remote.rmi.RMIServer;
import javax.management.remote.rmi.RMIServerImpl;
import javax.rmi.CORBA.Stub;
import javax.rmi.PortableRemoteObject;

import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.ORB;

/**
 * @version $Revision: 1.3 $
 */
public class Resolver extends mx4j.remote.resolver.rmi.Resolver
{
   private static final String IOR_CONTEXT = "/ior/";

   private ORB orb;
   private static final String ORB_KEY = "java.naming.corba.orb";


//********************************************************************************************************************//
// CLIENT METHODS

   protected RMIServer lookupStubInJNDI(JMXServiceURL url, Map environment) throws IOException
   {
      checkORB(environment);
      return super.lookupStubInJNDI(url, environment);
   }

   protected RMIServer decodeStub(JMXServiceURL url, Map environment) throws IOException
   {
      String path = url.getURLPath();
      String ior = IOR_CONTEXT;
      if (path.startsWith(ior))
      {
         String encoded = path.substring(ior.length());
         ORB orb = getORB(environment);
         Object object = orb.string_to_object(encoded);
         return narrowRMIServerStub(object);
      }
      throw new MalformedURLException("Unsupported binding: " + url);
   }

   protected RMIServer narrowRMIServerStub(Object stub)
   {
      return (RMIServer)PortableRemoteObject.narrow(stub, RMIServer.class);
   }

   public Object bindClient(Object client, Map environment) throws IOException
   {
      Stub stub = (Stub)client;
      ORB orb = null;
      try
      {
         orb = stub._orb();
      }
      catch (BAD_OPERATION x)
      {
         // The stub is not connected to an ORB, go on
      }

      if (orb == null)
      {
         orb = getORB(environment);
         stub.connect(orb);
      }
      return stub;
   }

//********************************************************************************************************************//
// SERVER METHODS


   protected RMIServerImpl createRMIServer(JMXServiceURL url, Map environment) throws IOException
   {
      return new RMIIIOPServerImpl(environment);
   }

   public JMXServiceURL bindServer(Object server, JMXServiceURL url, Map environment) throws IOException
   {
      RMIServerImpl rmiServer = (RMIServerImpl)server;
      Stub stub = (Stub)PortableRemoteObject.toStub(rmiServer);
      stub.connect(getORB(environment));
      return super.bindServer(server, url, environment);
   }

   protected String encodeStub(RMIServerImpl rmiServer, Map environment) throws IOException
   {
      Stub stub = (Stub)bindClient(rmiServer.toStub(), environment);
      String ior = getORB(environment).object_to_string(stub);
      return IOR_CONTEXT + ior;
   }

   private ORB checkORB(Map environment)
   {
      if (environment == null) return null;
      Object candidateORB = environment.get(ORB_KEY);
      if (candidateORB != null)
      {
         // Throw as required by the spec
         if (!(candidateORB instanceof ORB)) throw new IllegalArgumentException("Property " + ORB_KEY + " must specify a " + ORB.class.getName() + ", not " + candidateORB.getClass().getName());
         return (ORB)candidateORB;
      }
      return null;
   }

   /**
    * Creates a new ORB, if not already created.
    * This method is accessed from both client and server.
    */
   private synchronized ORB getORB(Map environment)
   {
      if (orb == null)
      {
         orb = checkORB(environment);
         if (orb == null)
         {
            Properties props = new Properties();
            // Using putAll() on a Properties is discouraged, since it expects only Strings
            for (Iterator i = environment.entrySet().iterator(); i.hasNext();)
            {
               Map.Entry entry = (Map.Entry)i.next();
               Object key = entry.getKey();
               Object value = entry.getValue();
               if (key instanceof String && value instanceof String)
               {
                  props.setProperty((String)key, (String)value);
               }
            }
            orb = ORB.init((String[])null, props);
         }
      }
      return orb;
   }

   protected boolean isEncodedForm(JMXServiceURL url)
   {
      String path = url.getURLPath();
      if (path != null && path.startsWith(IOR_CONTEXT)) return true;
      return super.isEncodedForm(url);
   }

   public void destroyServer(Object server, JMXServiceURL url, Map environment) throws IOException
   {
      if (!isEncodedForm(url)) return;
      if (orb != null)
      {
         orb.shutdown(true);
         orb.destroy();
      }
   }
}
