private boolean isMBeanTypeCompliant(MBeanMetaData metadata) {
    Logger logger = getLogger();

    if (metadata.standard && metadata.dynamic) {
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is both standard and dynamic");
      return false;
    }
    if (!metadata.standard && !metadata.dynamic) {
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not standard nor dynamic");
      return false;
    }

    return true;
  }
  private MBeanDescription createMBeanDescription(MBeanMetaData metadata) {
    // This is a non-standard extension

    Logger logger = getLogger();
    if (logger.isEnabledFor(Logger.TRACE))
      logger.trace("Looking for standard MBean description...");

    Class mbeanClass = metadata.mbean.getClass();

    for (Class cls = mbeanClass; cls != null; cls = cls.getSuperclass()) {
      String clsName = cls.getName();
      if (clsName.startsWith("java.")) break;

      // Use full qualified name only
      String descrClassName = clsName + "MBeanDescription";
      // Try to load the class
      try {
        Class descrClass = null;
        ClassLoader loader = metadata.classloader;
        if (loader != null) {
          // Optimize lookup of the description class in case of MLets: we lookup the description
          // class
          // only in the classloader of the mbean, not in the whole CLR (since MLets delegates to
          // the CLR)
          if (loader.getClass() == MLet.class)
            descrClass = ((MLet) loader).loadClass(descrClassName, null);
          else descrClass = loader.loadClass(descrClassName);
        } else {
          descrClass = Class.forName(descrClassName, false, null);
        }

        Object descrInstance = descrClass.newInstance();
        if (descrInstance instanceof MBeanDescription) {
          MBeanDescription description = (MBeanDescription) descrInstance;
          if (logger.isEnabledFor(Logger.TRACE))
            logger.trace("Found provided standard MBean description: " + description);
          return description;
        }
      } catch (ClassNotFoundException ignored) {
      } catch (InstantiationException ignored) {
      } catch (IllegalAccessException ignored) {
      }
    }

    MBeanDescription description = DEFAULT_DESCRIPTION;
    if (logger.isEnabledFor(Logger.TRACE))
      logger.trace("Cannot find standard MBean description, using default: " + description);
    return description;
  }
  private boolean isMBeanInfoCompliant(MBeanMetaData metadata) {
    Logger logger = getLogger();

    if (metadata.info == null) {
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo is null");
      return false;
    }
    return true;
  }
  private MBeanInfo getDynamicMBeanInfo(MBeanMetaData metadata) {
    Logger logger = getLogger();

    MBeanInfo info = null;

    try {
      info = ((DynamicMBean) metadata.mbean).getMBeanInfo();
    } catch (Exception x) {
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("getMBeanInfo threw: " + x.toString());
    }

    if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Dynamic MBeanInfo is: " + info);

    if (info == null) {
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo cannot be null");
      return null;
    }

    return info;
  }
  private MBeanInvoker createInvoker(MBeanMetaData metadata) {
    Logger logger = getLogger();

    if (m_customMBeanInvoker != null) {
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("Custom MBeanInvoker class is: " + m_customMBeanInvoker);
      try {
        MBeanInvoker mbeanInvoker =
            (MBeanInvoker)
                Thread.currentThread()
                    .getContextClassLoader()
                    .loadClass(m_customMBeanInvoker)
                    .newInstance();
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Using custom MBeanInvoker: " + mbeanInvoker);
        return mbeanInvoker;
      } catch (Exception x) {
        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug("Cannot instantiate custom MBeanInvoker, using default", x);
      }
    }

    /* SFR
          if (m_bcelClassesAvailable)
          {
             MBeanInvoker mbeanInvoker = BCELMBeanInvoker.create(metadata);
             if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using default BCEL MBeanInvoker for MBean " + metadata.name + ", " + mbeanInvoker);
             return mbeanInvoker;
          }
          else
          {
    */
    MBeanInvoker mbeanInvoker = new ReflectedMBeanInvoker();
    if (logger.isEnabledFor(Logger.TRACE))
      logger.trace(
          "Using default Reflection MBeanInvoker for MBean " + metadata.name + ", " + mbeanInvoker);
    return mbeanInvoker;
    /* SFR     } */
  }
  private MBeanAttributeInfo[] createMBeanAttributeInfo(
      MBeanMetaData metadata, MBeanDescription description) {
    Logger logger = getLogger();

    HashMap attributes = new HashMap();
    HashMap getterNames = new HashMap();

    Method[] methods = metadata.management.getMethods();
    for (int j = 0; j < methods.length; ++j) {
      Method method = methods[j];
      if (Utils.isAttributeGetter(method)) {
        String name = method.getName();
        boolean isIs = name.startsWith("is");

        String attribute = null;
        if (isIs) attribute = name.substring(2);
        else attribute = name.substring(3);

        String descr = description == null ? null : description.getAttributeDescription(attribute);

        MBeanAttributeInfo info = (MBeanAttributeInfo) attributes.get(attribute);

        if (info != null) {
          // JMX spec does not allow overloading attributes.
          // If an attribute with the same name already exists the MBean is not compliant
          if (!info.getType().equals(method.getReturnType().getName())) {
            if (logger.isEnabledFor(Logger.DEBUG))
              logger.debug("MBean is not compliant: has overloaded attribute " + attribute);
            return null;
          } else {
            // They return the same value,
            if (getterNames.get(name) != null) {
              // This is the case of an attribute being present in multiple interfaces
              // Ignore all but the first, since they resolve to the same method anyways
              continue;
            }

            // there is a chance that one is a get-getter and one is a is-getter
            // for a boolean attribute. In this case, the MBean is not compliant.
            if (info.isReadable()) {
              if (logger.isEnabledFor(Logger.DEBUG))
                logger.debug("MBean is not compliant: has overloaded attribute " + attribute);
              return null;
            }

            // MBeanAttributeInfo is already present due to a setter method, just update its
            // readability
            info =
                new MBeanAttributeInfo(
                    attribute,
                    info.getType(),
                    info.getDescription(),
                    true,
                    info.isWritable(),
                    isIs);
          }
        } else {
          info =
              new MBeanAttributeInfo(
                  attribute, method.getReturnType().getName(), descr, true, false, isIs);
        }

        // Replace if exists
        attributes.put(attribute, info);
        getterNames.put(name, method);
      } else if (Utils.isAttributeSetter(method)) {
        String name = method.getName();
        String attribute = name.substring(3);

        String descr = description == null ? null : description.getAttributeDescription(attribute);

        MBeanAttributeInfo info = (MBeanAttributeInfo) attributes.get(attribute);

        if (info != null) {
          // JMX spec does not allow overloading attributes.
          // If an attribute with the same name already exists the MBean is not compliant
          if (!info.getType().equals(method.getParameterTypes()[0].getName())) {
            if (logger.isEnabledFor(Logger.DEBUG))
              logger.debug("MBean is not compliant: has overloaded attribute " + attribute);
            return null;
          } else {
            // MBeanAttributeInfo is already present due to a getter method, just update its
            // writability
            info =
                new MBeanAttributeInfo(
                    info.getName(),
                    info.getType(),
                    info.getDescription(),
                    info.isReadable(),
                    true,
                    info.isIs());
          }
        } else {
          info =
              new MBeanAttributeInfo(
                  attribute, method.getParameterTypes()[0].getName(), descr, false, true, false);
        }

        // Replace if exists
        attributes.put(attribute, info);
      }
    }

    return (MBeanAttributeInfo[])
        attributes.values().toArray(new MBeanAttributeInfo[attributes.size()]);
  }