/*******************************************************************************
 * Copyright (c), 2001, 2002 N2 Broadband, Inc.  All Rights Reserved.
 *
 * This module contains unpublished, confidential, proprietary
 * material.  The use and dissemination of this material are
 * governed by a license.  The above copyright notice does not
 * evidence any actual or intended publication of this material.
 *
 * Author:  Drake H. Henderson
 * Created:  11-12-01
 *
 ******************************************************************************/

package com.n2bb.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.*;

import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;

/**
 * Orb manager.  Creates orb and gets factory objects.
 *
 * @author dhenderson
 * @author kmatsuoka
 * @version $Id: OrbManager.java,v 1.1 2006/06/17 00:48:48 rao Exp $
 */
public class OrbManager {
  private static Log n2bbLog = LogFactory.getLog(OrbManager.class);

  ORB m_orb;
  NamingContextExt m_nameCtxExt;

  /** Singleton instance of this class. */
  private static OrbManager instance;
  /** Orb properties. */
  private static Properties orbProps;

  /**
   * Constructor for singleton.  Only called from getInstance()
   */
  private OrbManager() throws Exception {
    n2bbLog.debug("enter");

    if (orbProps == null) {
      n2bbLog.warn("Orb properties not set, using defaults!");
      orbProps = new Properties();
    }

    String orbType = orbProps.getProperty("orb", "JacORB");
    if (orbType.equalsIgnoreCase("JacORB")) {

      String nameService = orbProps.getProperty("NameService", "corbaloc::NameServer:5000/NameService");
      String timeout1 = orbProps.getProperty("jacorb.client.pending_reply_timeout", "30000");
      String timeout2 = orbProps.getProperty("jacorb.connection.client_idle_timeout", "30000");
      String timeout3 = orbProps.getProperty("jacorb.connection.server_timeout", "30000");

      String args[] = {
      };

      n2bbLog.info("creating JacORB orb");
      n2bbLog.info("NameService... " + nameService);
      n2bbLog.info("jacorb.client.pending_reply_timeout=" + timeout1);
      n2bbLog.info("jacorb.connection.client_idle_timeout=" + timeout2);
      n2bbLog.info("jacorb.connection.server_timeout=" + timeout3);

      Properties props = new Properties();
      String settingsService = orbProps.getProperty("SettingsServer");
      if (settingsService != null) {
        n2bbLog.info("SettingsService... " + settingsService);
        props.setProperty("ORBInitRef.SettingsService", settingsService);
      }

      props.setProperty("org.omg.CORBA.ORBClass", "org.jacorb.orb.ORB");
      props.setProperty("org.omg.CORBA.ORBSingletonClass", "org.jacorb.orb.ORBSingleton");
      props.setProperty("ORBInitRef.NameService", nameService);
      props.setProperty("jacorb.client.pending_reply_timeout", timeout1);
      props.setProperty("jacorb.connection.client_idle_timeout", timeout2);
      props.setProperty("jacorb.connection.server_timeout", timeout3);

      m_orb = org.omg.CORBA.ORB.init(args, props);

    } else if (orbType.equalsIgnoreCase("Orbacus")) {
      n2bbLog.info("creating Orbabus orb");
      String nameService = orbProps.getProperty("NameService", "corbaloc::NameServer:5000/NameService");
      String timeout = orbProps.getProperty("ooc.orb.policy.timeout", "30000");
      String connectTimeout = orbProps.getProperty("ooc.orb.policy.connect_timeout", "30000");
      String requestTimeout = orbProps.getProperty("ooc.orb.policy.request_timeout", "30000");
      String[] args = null;
      String settingsService = orbProps.getProperty("SettingsServer");
      if (settingsService != null) {
        n2bbLog.info("SettingsService... " + settingsService);
        String props[] =
          {
            "-ORBproperty",
            "ooc.orb.service.NameService=" + nameService,
            "-ORBproperty",
            "ooc.orb.policy.timeout=" + timeout,
            "-ORBproperty",
            "ooc.orb.policy.connect_timeout=" + connectTimeout,
            "-ORBproperty",
            "ooc.orb.policy.request_timeout=" + requestTimeout,
            "-ORBInitRef",
            "SettingsService=" + settingsService };
        args = props;

      } else {
        String props[] =
          {
            "-ORBproperty",
            "ooc.orb.service.NameService=" + nameService,
            "-ORBproperty",
            "ooc.orb.policy.timeout=" + timeout,
            "-ORBproperty",
            "ooc.orb.policy.connect_timeout=" + connectTimeout,
            "-ORBproperty",
            "ooc.orb.policy.request_timeout=" + requestTimeout,
            "-ORBInitRef" };
        args = props;
      }

      n2bbLog.info("NameService... " + nameService);
      n2bbLog.info("ooc.orb.policy.timeout=" + timeout);
      n2bbLog.info("ooc.orb.policy.connect_timeout=" + connectTimeout);
      n2bbLog.info("ooc.orb.policy.request_timeout=" + requestTimeout);

      Properties props = new Properties();
      props.setProperty("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
      props.setProperty("org.omg.CORBA.ORBSingletonClass", "com.ooc.CORBA.ORBSingleton");
      m_orb = org.omg.CORBA.ORB.init(args, props);

    } else {
      n2bbLog.error("unknown orb type - must be JacOrb or Orbacus");
      throw new N2bbException("");
    }

    org.omg.CORBA.Object ns = m_orb.resolve_initial_references("NameService");
    m_nameCtxExt = NamingContextExtHelper.narrow(ns);
  }

  /**
   * Sets orb properties to be used in constructor.
   *
   * @param props   orb properties
   */
  public static synchronized void setProperties(Properties props) {
    n2bbLog.debug("enter");

    if (instance == null) {
      orbProps = props;
    } else {
      n2bbLog.warn("Orb manager already initialized.");
      n2bbLog.warn("  To update properties, first call OrbManager.destroy()");
    }
  }

  /**
   * Gets singleton instance of this class.
   *
   * @return singleton instance of this class
   */
  public static synchronized OrbManager getInstance() {
    n2bbLog.debug("enter");

    if (instance == null) {
      try {
        instance = new OrbManager();
      } catch (Exception e) {
        n2bbLog.error("failed to initialize orb manager", e);
      }
    }

    return instance;
  }

  /**
   * Destroys orb, and singleton instance of this class.
   */
  public static synchronized void destroy() {
    n2bbLog.debug("enter");
    if (instance != null) {
      instance.destroyORB();
      instance = null;
      orbProps = null;
    }
  }

  /*******************
   *
   *******************/
  public org.omg.CORBA.Object getFactoryObject(String factoryName) throws N2bbException {
    n2bbLog.debug("enter");

    String[] path = { "Factories", factoryName };
    return getFactoryObject(path);
  }

    /**
     * Gets a CORBA factory object.
     *
     * @param strFullPath full path to factory object
     * @return factory object
     * @throws N2bbException if unable to get factory, or if factory is null
     */
    public org.omg.CORBA.Object getFactoryObject(String[] strFullPath) throws N2bbException {
        n2bbLog.debug("enter");
        try {
            NamingContextExt ctx = getContext(strFullPath, strFullPath.length - 1);
            NameComponent[] nc = new NameComponent[1];
            nc[0] = new NameComponent(strFullPath[strFullPath.length - 1], "Factory");
            org.omg.CORBA.Object obj = ctx.resolve(nc);
            if (obj == null) {
                throw new RuntimeException("Factory was null");
            }
            return obj;
        }
        catch (Exception e) {
            n2bbLog.error(e.getMessage(), e);
            throw new N2bbException("error.corba.findFactory");
        }
    }

  /*******************
   *
   *******************/
  private NamingContextExt getContext(String[] strFullPath, int iLen) throws N2bbException {
    try {
      /* WTF
      for (int i=1; i<iLen; i++) {
        getContext(strFullPath, i);
      }
      */
      NameComponent[] nc = new NameComponent[iLen];
      for (int i = 0; i < iLen; i++) {
        nc[i] = new NameComponent(strFullPath[i], "Context");
      }

      org.omg.CORBA.Object ctx = m_nameCtxExt.resolve(nc);
      return NamingContextExtHelper.narrow(ctx);

    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }
  }

  /*******************
   *  Alerts
   *******************/
  public org.omg.CORBA.Object getRootLevelObject(NameComponent[] nc) throws N2bbException {
    try {
      org.omg.CORBA.Object obj = m_nameCtxExt.resolve(nc);
      return obj;
    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }
  }

  /*******************
   *  Applications
   *******************/
  public Vector getRootLevelContextObjects(String context) throws N2bbException {
    Vector objs = new Vector();

    try {
      NamingContextExt cxt = getRootLevelContext(context);
      if (cxt == null)
        return objs;

      Vector bindings = getBindings(cxt);
      for (int i = 0; i < bindings.size(); i++) {
        try {
          objs.addElement(cxt.resolve(((Binding) bindings.elementAt(i)).binding_name));
        } catch (Exception e) {
          n2bbLog.error("failed to resolve a binding - message... " + e.getMessage(), e);
          n2bbLog.error("  skipping this binding");
        }
      }
    } catch (N2bbException e) {
      throw e;
    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }
    return objs;
  }

  /*******************
   *  Sessions, Content Stores
   *******************/
  public HashMap getRootLevelContextObjectsMap(String context) throws N2bbException {
    HashMap nameObjMap = new HashMap();

    try {
      NamingContextExt cxt = getRootLevelContext(context);
      if (cxt == null)
        return nameObjMap;

      Vector bindings = getBindings(cxt);
      for (int i = 0; i < bindings.size(); i++) {
        try {
            NameComponent[] nc = ((Binding) bindings.elementAt(i)).binding_name;
            nameObjMap.put(nc[nc.length-1].id, cxt.resolve(nc));
        } catch (Exception e) {
          n2bbLog.error("failed to resolve a binding - message... " + e.getMessage(), e);
          n2bbLog.error("  skipping this binding");
        }
      }
    } catch (N2bbException e) {
      throw e;
    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }

    return nameObjMap;
  }

  /*******************
   *
   *******************/
  private NamingContextExt getRootLevelContext(String contextName) throws N2bbException {
    NamingContextExt context = null;
    try {
      boolean found = false;
      Binding bind = null;
      BindingListHolder blh = new BindingListHolder();
      BindingIteratorHolder bih = new BindingIteratorHolder();

      int blockSize = 50;
      m_nameCtxExt.list(blockSize, blh, bih);

      for (int i = 0; i < blh.value.length; i++) {
        bind = blh.value[i];
        if (bind.binding_name[0].id.compareTo(contextName) == 0) {
          found = true;
          break;
        }
      }

      if (!found && bih.value != null) {
        n2bbLog.debug("more bindings");

        BindingIterator bi = bih.value;
        while (bi.next_n(blockSize, blh)) {
          n2bbLog.debug("more bindings while");
          for (int i = 0; i < blh.value.length; i++) {
            bind = blh.value[i];
            if (bind.binding_name[0].id.compareTo(contextName) == 0) {
              found = true;
              break;
            }
          }
        }
      }

      if (!found)
        return null;
      //throw new N2bbException("error.orb.noContext");

      return context = NamingContextExtHelper.narrow(m_nameCtxExt.resolve(bind.binding_name));

    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }
  }

  /*******************
   *
   *******************/
  private Vector getBindings(NamingContextExt cxt) throws N2bbException {
    n2bbLog.debug("enter");
    Vector bindings = new Vector();
    try {
      BindingListHolder blh = new BindingListHolder();
      BindingIteratorHolder bih = new BindingIteratorHolder();

      int blockSize = 50;
      cxt.list(blockSize, blh, bih);

      if (blh.value.length == 1 && blh.value[0] == null)
        return bindings;

      for (int i = 0; i < blh.value.length; i++) {
        bindings.addElement(blh.value[i]);
      }

      if (bih.value != null) {
        n2bbLog.debug("more bindings iter");

        BindingIterator bi = bih.value;
        while (bi.next_n(blockSize, blh)) {
          n2bbLog.debug("more bindings while");
          for (int i = 0; i < blh.value.length; i++) {
            bindings.addElement(blh.value[i]);
          }
        }
      }
    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }
    return bindings;
  }

  /*******************
   *
   *******************/
  private void destroyORB() {
    n2bbLog.debug("enter");
    try {
      if (m_orb != null) {
        m_orb.destroy();
      }
    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
    }
  }

  /*******************
   *  CORBAAction, Package send notification
   *******************/
  public ORB getORB() {
    return m_orb;
  }

  /*******************
   *  CORBAAction
   *******************/
  public NamingContextExt getNamingContextExt() {
    return m_nameCtxExt;
  }

  /*******************
   *
   *******************/
  public void deleteBinding(NameComponent[] nc) throws N2bbException {
    n2bbLog.debug("enter");
    try {
      m_nameCtxExt.unbind(nc);
    } catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.other");
    }
  }

}
