/**
   * 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;
  }
  public static void main(String[] args) throws Exception {
    Class thisClass = MethodResultTest.class;
    Class exoticClass = Exotic.class;
    String exoticClassName = Exotic.class.getName();
    ClassLoader testClassLoader = thisClass.getClassLoader();
    if (!(testClassLoader instanceof URLClassLoader)) {
      System.out.println("TEST INVALID: Not loaded by a " + "URLClassLoader: " + testClassLoader);
      System.exit(1);
    }

    URLClassLoader tcl = (URLClassLoader) testClassLoader;
    URL[] urls = tcl.getURLs();
    ClassLoader shadowLoader =
        new ShadowLoader(
            urls,
            testClassLoader,
            new String[] {
              exoticClassName, ExoticMBeanInfo.class.getName(), ExoticException.class.getName()
            });
    Class cl = shadowLoader.loadClass(exoticClassName);
    if (cl == exoticClass) {
      System.out.println(
          "TEST INVALID: Shadow class loader loaded " + "same class as test class loader");
      System.exit(1);
    }
    Thread.currentThread().setContextClassLoader(shadowLoader);

    ObjectName on = new ObjectName("a:b=c");
    MBeanServer mbs = MBeanServerFactory.newMBeanServer();
    mbs.createMBean(Thing.class.getName(), on);

    final String[] protos = {"rmi", "iiop", "jmxmp"};

    boolean ok = true;
    for (int i = 0; i < protos.length; i++) {
      try {
        ok &= test(protos[i], mbs, on);
        System.out.println();
      } catch (Exception e) {
        System.out.println("TEST FAILED WITH EXCEPTION:");
        e.printStackTrace(System.out);
        ok = false;
      }
    }

    if (ok) System.out.println("Test passed");
    else {
      System.out.println("TEST FAILED");
      System.exit(1);
    }
  }
  public static void main(String[] args) throws Exception {
    System.out.println(
        "Checking that all known MBeans that are "
            + "NotificationBroadcasters have sane "
            + "MBeanInfo.getNotifications()");

    System.out.println("Checking platform MBeans...");
    checkPlatformMBeans();

    URL codeBase = ClassLoader.getSystemResource("javax/management/MBeanServer.class");
    if (codeBase == null) {
      throw new Exception("Could not determine codeBase for " + MBeanServer.class);
    }

    System.out.println();
    System.out.println("Looking for standard MBeans...");
    String[] classes = findStandardMBeans(codeBase);

    System.out.println("Testing standard MBeans...");
    for (int i = 0; i < classes.length; i++) {
      String name = classes[i];
      Class<?> c;
      try {
        c = Class.forName(name);
      } catch (Throwable e) {
        System.out.println(name + ": cannot load (not public?): " + e);
        continue;
      }
      if (!NotificationBroadcaster.class.isAssignableFrom(c)) {
        System.out.println(name + ": not a NotificationBroadcaster");
        continue;
      }
      if (Modifier.isAbstract(c.getModifiers())) {
        System.out.println(name + ": abstract class");
        continue;
      }

      NotificationBroadcaster mbean;
      Constructor<?> constr;
      try {
        constr = c.getConstructor();
      } catch (Exception e) {
        System.out.println(name + ": no public no-arg constructor: " + e);
        continue;
      }
      try {
        mbean = (NotificationBroadcaster) constr.newInstance();
      } catch (Exception e) {
        System.out.println(name + ": no-arg constructor failed: " + e);
        continue;
      }

      check(mbean);
    }

    System.out.println();
    System.out.println("Testing some explicit cases...");

    check(new RelationService(false));
    /*
      We can't do this:
        check(new RequiredModelMBean());
      because the Model MBean spec more or less forces us to use the
      names GENERIC and ATTRIBUTE_CHANGE for its standard notifs.
    */
    checkRMIConnectorServer();

    System.out.println();
    if (!suspicious.isEmpty()) System.out.println("SUSPICIOUS CLASSES: " + suspicious);

    if (failed.isEmpty()) System.out.println("TEST PASSED");
    else {
      System.out.println("TEST FAILED: " + failed);
      System.exit(1);
    }
  }