/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.iiop;

import java.io.InputStream;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

import org.jboss.system.ServiceMBeanSupport;
import org.jboss.system.server.ServerConfigUtil;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Policy;
import org.omg.PortableServer.IdAssignmentPolicy;
import org.omg.PortableServer.IdAssignmentPolicyValue;
import org.omg.PortableServer.LifespanPolicy;
import org.omg.PortableServer.LifespanPolicyValue;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import org.w3c.dom.Element;

import org.jboss.iiop.naming.ORBInitialContextFactory;
import org.jboss.metadata.MetaData;
import org.jboss.security.SecurityDomain;
import org.jboss.system.Registry;

/**
 *  This is a JMX service that provides the default CORBA ORB
 *  for JBoss to use.
 *      
 *  @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
 *  @author <a href="mailto:reverbel@ime.usp.br">Francisco Reverbel</a>
 *  @version $Revision: 1.20.2.13 $
 */
public class CorbaORBService
      extends ServiceMBeanSupport
      implements CorbaORBServiceMBean, ObjectFactory
{
   // Constants -----------------------------------------------------

   public static String ORB_NAME = "JBossCorbaORB";
   public static String POA_NAME = "JBossCorbaPOA";
   public static String IR_POA_NAME = "JBossCorbaInterfaceRepositoryPOA";
   public static String SSL_DOMAIN = "JBossCorbaSSLDomain";
    
   // Attributes ----------------------------------------------------

   private String orbClass = null;
   private String orbSingletonClass = null;
   private String orbSingletonDelegate = null;
   private String orbPropertiesFileName = "orb-properties-file-not-defined";
   private Element portableInterceptorInitializers = null;
   private int port = 0;
   private int sslPort = 0;
   private String sslDomain = null;

   private boolean sunJDK14IsLocalBugFix = true;

   // Static --------------------------------------------------------

   private static ORB orb;
   private static POA poa;
   private static POA irPoa;
   
   // ServiceMBeanSupport overrides ---------------------------------

   protected void startService() 
      throws Exception 
   {

      Properties props = new Properties();

      // Read orb properties file into props
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      InputStream is = cl.getResourceAsStream(orbPropertiesFileName);
      props.load(is);
      String oaiAddr = props.getProperty("OAIAddr");
      if (oaiAddr == null)
         oaiAddr = ServerConfigUtil.getSpecificBindAddress();
      if (oaiAddr != null)
         props.setProperty("OAIAddr", oaiAddr);
      log.debug("Using OAIAddr=" + oaiAddr);

      // Set ORB initialization properties
      Properties systemProps = System.getProperties();
      if (orbClass != null) {
         props.put("org.omg.CORBA.ORBClass", orbClass);
         systemProps.put("org.omg.CORBA.ORBClass", orbClass);
      }
      if (orbSingletonClass != null) {
         props.put("org.omg.CORBA.ORBSingletonClass", orbSingletonClass);
         systemProps.put("org.omg.CORBA.ORBSingletonClass", orbSingletonClass);
      }
      if (orbSingletonDelegate != null)
         systemProps.put(org.jboss.system.ORBSingleton.DELEGATE_CLASS_KEY,
                         orbSingletonDelegate);

      // JacORB-specific hack: add jacorb.log.loggerFactory, 
      // jacorb.log.default.verbosity, jacorb.log.verbosity, and
      // jacorb.orb.log.verbosity to the system properties,
      String str;
      str = props.getProperty("jacorb.log.loggerFactory");
      if (str != null)
         systemProps.put("jacorb.log.loggerFactory", str);
      str = props.getProperty("jacorb.log.default.verbosity");
      if (str != null)
         systemProps.put("jacorb.log.default.verbosity", str);
      str = props.getProperty("jacorb.log.verbosity");
      if (str != null)
         systemProps.put("jacorb.log.verbosity", str);
      str = props.getProperty("jacorb.orb.log.verbosity");
      if (str != null)
         systemProps.put("jacorb.orb.log.verbosity", str);

      // This is for SUN JDK bug with isLocal
      if (sunJDK14IsLocalBugFix)
      {
         /* Validate that the class can actually be loaded. It cannot be used
         under java 5 for example because the SunJDK14IsLocalBugFix base class
         does not exist
         */
         try
         {
            Class SunJDK14IsLocalBugFix = cl.loadClass("org.jboss.iiop.SunJDK14IsLocalBugFix");
            log.debug("Was able to load SunJDK14IsLocalBugFix, class="+SunJDK14IsLocalBugFix);
            // Its loadable to use it
            systemProps.put("javax.rmi.CORBA.UtilClass", "org.jboss.iiop.SunJDK14IsLocalBugFix");
         }
         catch(Throwable t)
         {
            log.debug("Ignoring sunJDK14IsLocalBugFix=true due to inability to load org.jboss.iiop.SunJDK14IsLocalBugFix", t);
         }
      }

      System.setProperties(systemProps);
      
      // Add portable interceptor initializers
      Iterator initializerElements = 
            MetaData.getChildrenByTagName(portableInterceptorInitializers, 
                                          "initializer");
      if (initializerElements != null)
      {
         while (initializerElements.hasNext())
         {
            Element initializerElement = (Element) initializerElements.next();
            String portableInterceptorInitializerClassName = 
                  MetaData.getElementContent(initializerElement);
                  log.debug("Adding portable interceptor initializer: " +
                           portableInterceptorInitializerClassName);
            if (portableInterceptorInitializerClassName != null 
                  && !portableInterceptorInitializerClassName.equals(""))
               props.put("org.omg.PortableInterceptor.ORBInitializerClass."
                         + portableInterceptorInitializerClassName, "");
         }
      }

      // Allows overriding of OAPort/OASSLPort 
      // from config file through MBean config
      if (port != 0)
         props.put("OAPort", Integer.toString(this.port));
      if (sslPort != 0)
         props.put("OASSLPort", Integer.toString(this.sslPort));

      // Allow SSL domain to be specified through MBean config
      if (sslDomain != null) 
      {
         InitialContext ctx = new InitialContext();
         log.debug("sslDomain: " + sslDomain);
         try 
         {
            SecurityDomain domain = (SecurityDomain) ctx.lookup(sslDomain);

            // Make SSL domain available to socket factories
            Registry.bind(SSL_DOMAIN, domain);
         }
         catch (Exception e)
         {
            log.warn("Security domain " + sslDomain + " not found");
            if (log.isDebugEnabled())
               log.debug("Exception looking up " + sslDomain + ": ", e);
         }
      }

      // Initialize the ORB
      orb = ORB.init(new String[0], props);
      bind(ORB_NAME, "org.omg.CORBA.ORB");
      CorbaORB.setInstance(orb);
      ORBInitialContextFactory.setORB(orb);

      // Initialize the POA
      poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
      poa.the_POAManager().activate();
      bind(POA_NAME, "org.omg.PortableServer.POA");

      // Make the ORB work
      new Thread(
         new Runnable() {
            public void run() {
               orb.run();
            }
         }, "ORB thread"
      ).start(); 

      // Create a POA for interface repositories
      try {
         LifespanPolicy lifespanPolicy = 
            poa.create_lifespan_policy(LifespanPolicyValue.PERSISTENT);
         IdAssignmentPolicy idAssignmentPolicy = 
            poa.create_id_assignment_policy(IdAssignmentPolicyValue.USER_ID);

         irPoa = poa.create_POA("IR", null,
                                new Policy[]{lifespanPolicy, 
                                             idAssignmentPolicy});
         bind(IR_POA_NAME, "org.omg.PortableServer.POA");
         
         // Activate the poa
         irPoa.the_POAManager().activate();
         
      } catch (Exception ex) {
         getLog().error("Error in IR POA initialization", ex);
      }
   }

   protected void stopService() throws Exception
   {
      /*
      if( mJSR77 != null )
      {
          RMI_IIOPResource.destroy(
             getServer(),
             ORB_NAME
          );
          mJSR77 = null;
      }
      */
      try {
         // Unbind from JNDI
         unbind(ORB_NAME);
         unbind(POA_NAME);
         unbind(IR_POA_NAME);

         // Stop ORB
         orb.shutdown(false);
         
         // Unbind SSL domain
         Registry.unbind(SSL_DOMAIN);
      } catch (Exception e) {
         log.error("Exception while stopping ORB service", e);
      }
   }

   // CorbaORBServiceMBean implementation ---------------------------

   public ORB getORB()
   {
      return orb;
   }
   
   public String getORBClass()
   {
      return orbClass;
   }

   public void setORBClass(String orbClass)
   {
      this.orbClass = orbClass;
   }

   public String getORBSingletonClass()
   {
      return orbSingletonClass;
   }

   public void setORBSingletonClass(String orbSingletonClass)
   {
      this.orbSingletonClass = orbSingletonClass;
   }

   public String getORBSingletonDelegate()
   {
      return orbSingletonDelegate;
   }

   public void setORBSingletonDelegate(String orbSingletonDelegate)
   {
      this.orbSingletonDelegate = orbSingletonDelegate;
   }

   public void setORBPropertiesFileName(String orbPropertiesFileName)
   {
      this.orbPropertiesFileName = orbPropertiesFileName;
   }

   public String getORBPropertiesFileName()
   {
      return orbPropertiesFileName;
   }

   public Element getPortableInterceptorInitializers()
   {
      return portableInterceptorInitializers;
   }

   public void setPortableInterceptorInitializers(
                                       Element portableInterceptorInitializers)
   {
      this.portableInterceptorInitializers = portableInterceptorInitializers;
   }

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

   public int getPort()
   {
      return this.port;
   }

   public void setSSLPort(int sslPort)
   {
      this.sslPort = sslPort;
   }

   public int getSSLPort()
   {
      return this.sslPort;
   }

   public void setSecurityDomain(String sslDomain)
   {
      this.sslDomain = sslDomain;
   }

   public String getSecurityDomain()
   {
      return this.sslDomain;
   }

   public boolean getSunJDK14IsLocalBugFix()
   {
      return sunJDK14IsLocalBugFix;
   }

   public void setSunJDK14IsLocalBugFix(boolean sunJDK14IsLocalBugFix)
   {
      this.sunJDK14IsLocalBugFix = sunJDK14IsLocalBugFix;
   }

   // ObjectFactory implementation ----------------------------------

   public Object getObjectInstance(Object obj, Name name,
                                   Context nameCtx, Hashtable environment)
      throws Exception
   {
      String s = name.toString();
      if (getLog().isTraceEnabled())
         getLog().trace("getObjectInstance: obj.getClass().getName=\"" +
                        obj.getClass().getName() +
                        "\n                   name=" + s);
      if (ORB_NAME.equals(s))
         return orb;
      if (POA_NAME.equals(s))
         return poa;
      if (IR_POA_NAME.equals(s))
         return irPoa;
      return null;
   }


   // Private -------------------------------------------------------

   private void bind(String name, String className)
      throws Exception
   {
      Reference ref = new Reference(className, getClass().getName(), null);
      new InitialContext().bind("java:/"+name, ref);
   }

   private void unbind(String name)
      throws Exception
   {
      new InitialContext().unbind("java:/"+name);
   }

}
