/**
   * Create MBean for Object. Attempts to create an MBean for the object by searching the package
   * and class name space. For example an object of the type
   *
   * <PRE>
   *   class com.acme.MyClass extends com.acme.util.BaseClass
   * </PRE>
   *
   * Then this method would look for the following classes:
   *
   * <UL>
   *   <LI>com.acme.MyClassMBean
   *   <LI>com.acme.jmx.MyClassMBean
   *   <LI>com.acme.util.BaseClassMBean
   *   <LI>com.acme.util.jmx.BaseClassMBean
   * </UL>
   *
   * @param o The object
   * @return A new instance of an MBean for the object or null.
   */
  public static ModelMBean mbeanFor(Object o) {
    try {
      Class oClass = o.getClass();
      ClassLoader loader = oClass.getClassLoader();

      ModelMBean mbean = null;
      boolean jmx = false;
      Class[] interfaces = null;
      int i = 0;

      while (mbean == null && oClass != null) {
        Class focus = interfaces == null ? oClass : interfaces[i];
        String pName = focus.getPackage().getName();
        String cName = focus.getName().substring(pName.length() + 1);
        String mName = pName + (jmx ? ".jmx." : ".") + cName + "MBean";

        try {
          Class mClass = loader.loadClass(mName);
          if (log.isTraceEnabled()) log.trace("mbeanFor " + o + " mClass=" + mClass);
          mbean = (ModelMBean) mClass.newInstance();
          mbean.setManagedResource(o, "objectReference");
          if (log.isDebugEnabled()) log.debug("mbeanFor " + o + " is " + mbean);
          return mbean;
        } catch (ClassNotFoundException e) {
          if (e.toString().endsWith("MBean")) {
            if (log.isTraceEnabled()) log.trace(e.toString());
          } else log.warn(LogSupport.EXCEPTION, e);
        } catch (Error e) {
          log.warn(LogSupport.EXCEPTION, e);
          mbean = null;
        } catch (Exception e) {
          log.warn(LogSupport.EXCEPTION, e);
          mbean = null;
        }

        if (jmx) {
          if (interfaces != null) {
            i++;
            if (i >= interfaces.length) {
              interfaces = null;
              oClass = oClass.getSuperclass();
            }
          } else {
            interfaces = oClass.getInterfaces();
            i = 0;
            if (interfaces == null || interfaces.length == 0) {
              interfaces = null;
              oClass = oClass.getSuperclass();
            }
          }
        }
        jmx = !jmx;
      }
    } catch (Exception e) {
      LogSupport.ignore(log, e);
    }
    return null;
  }
  /* Find MBean descriptions.
   * MBean descriptions are searched for in ResourceBundles. Bundles
   * are looked for in a mbean.property files within each package of
   * the MBean class inheritance hierachy.
   * Once a bundle is found, the key is added to object names in the
   * following order: fully qualied managed resource class name, tail
   * managed resource class name, tail mbean class name. The string
   * "MBean" is stripped from the tail of any name.
   * <P>For example, if the class a.b.C is managed by a MBean
   * p.q.RMBean which is derived from p.SMBean, then the seach order
   * for a key x is as follows:<PRE>
   *   bundle: p.q.mbean    name: a.b.C.x
   *   bundle: p.q.mbean    name: C.x
   *   bundle: p.q.mbean    name: R.x
   *   bundle: p.mbean      name: a.b.C.x
   *   bundle: p.mbean      name: C.x
   *   bundle: p.mbean      name: S.x
   * </PRE>
   * <P>The convention used for keys passed to this method are:<PRE>
   *   null or empty         - Object description
   *   xxx                   - Attribute xxx description
   *   xxx()                 - Simple operation xxx description
   *   xxx(type,..)          - Operation xxx with signature desciption
   *   xxx(type,..)[n]       - Param n of operation xxx description
   * </PRE>
   * @param key
   * @return Description string.
   */
  private String findDescription(String key) {
    Class lookIn = this.getClass();

    // Array of possible objectNames
    String[] objectNames = new String[3];
    objectNames[0] = _object.getClass().getName();
    if (objectNames[0].indexOf(".") >= 0)
      objectNames[1] = objectNames[0].substring(objectNames[0].lastIndexOf(".") + 1);

    while (lookIn != null) {
      String pkg = lookIn.getName();
      int lastDot = pkg.lastIndexOf(".");
      if (lastDot > 0) {
        objectNames[2] = pkg.substring(lastDot + 1);
        pkg = pkg.substring(0, lastDot);
      } else {
        objectNames[2] = pkg;
        pkg = null;
      }

      String resource = (pkg == null ? "mbean" : (pkg.replace('.', '/') + "/mbean"));
      if (log.isTraceEnabled()) log.trace("Look for: " + resource);

      try {
        ResourceBundle bundle =
            ResourceBundle.getBundle(
                resource, Locale.getDefault(), _object.getClass().getClassLoader());

        if (log.isTraceEnabled()) log.trace("Bundle " + resource);

        for (int i = 0; i < objectNames.length; i++) {
          String name = objectNames[i];

          if (name == null) continue;
          if (name.endsWith("MBean")) name = name.substring(0, name.length() - 5);
          if (key != null && key.length() > 0) name += "." + key;

          try {
            String description = bundle.getString(name);
            if (description != null && description.length() > 0) return description;
          } catch (Exception e) {
            if (log.isTraceEnabled()) log.trace(e.toString());
          }
        }
      } catch (Exception e) {
        if (log.isTraceEnabled()) log.trace(e.toString());
      }

      lookIn = lookIn.getSuperclass();
    }

    if (key == null || key.length() == 0) return objectNames[0];

    return key;
  }