public static boolean start(RootDoc rootDoc) throws Exception {
    List<Class<?>> mbeanIspnClasses = getMBeanClasses();
    List<Class<?>> globalClasses = new ArrayList<Class<?>>();
    List<Class<?>> namedCacheClasses = new ArrayList<Class<?>>();
    for (Class<?> clazz : mbeanIspnClasses) {
      Scope scope = clazz.getAnnotation(Scope.class);
      if (scope != null && scope.value() == Scopes.GLOBAL) {
        debug("Add as global class " + clazz);
        globalClasses.add(clazz);
      } else {
        debug("Add as named cache class " + clazz);
        namedCacheClasses.add(clazz);
      }
    }

    // Init the Javassist class pool.
    classPool = ClassPool.getDefault();
    classPool.insertClassPath(new ClassClassPath(RhqPluginXmlGenerator.class));
    PluginGen pg = new PluginGen();

    Props root = new Props();
    root.setPluginName("Infinispan");
    root.setPluginDescription("Supports management and monitoring of Infinispan");
    root.setName("Infinispan Cache Manager");
    root.setPkg("org.infinispan.rhq");
    root.setDependsOnJmxPlugin(true);
    root.setDiscoveryClass("CacheManagerDiscovery");
    root.setComponentClass("CacheManagerComponent");
    root.setSingleton(false);
    root.setCategory(ResourceCategory.SERVICE);
    Set<TypeKey> servers = new HashSet<TypeKey>();
    servers.add(new TypeKey("JMX Server", "JMX"));
    servers.add(new TypeKey("JBossAS Server", "JBossAS"));
    servers.add(new TypeKey("JBossAS Server", "JBossAS5"));
    root.setRunsInsides(servers);
    populateMetricsAndOperations(globalClasses, root, false);

    Props cache = new Props();
    cache.setName("Infinispan Cache");
    cache.setPkg("org.infinispan.rhq");
    cache.setDependsOnJmxPlugin(true);
    cache.setDiscoveryClass("CacheDiscovery");
    cache.setComponentClass("CacheComponent");
    cache.setSingleton(false);
    cache.setCategory(ResourceCategory.SERVICE);
    populateMetricsAndOperations(namedCacheClasses, cache, true);

    root.getChildren().add(cache);

    String metaInfDir = "../../../src/main/resources/META-INF";
    new File(metaInfDir).mkdirs();
    String targetMetaInfDir = "../../../target/classes/META-INF";
    new File(targetMetaInfDir).mkdirs();

    pg.createFile(root, "descriptor", "rhq-plugin.xml", metaInfDir);
    copyFile(
        new File(metaInfDir + "/rhq-plugin.xml"), new File(targetMetaInfDir + "/rhq-plugin.xml"));

    return true;
  }
  private static void populateMetricsAndOperations(
      List<Class<?>> classes, Props props, boolean withNamePrefix) throws Exception {
    props.setHasOperations(true);
    props.setHasMetrics(true);
    for (Class<?> clazz : classes) {
      MBean mbean = clazz.getAnnotation(MBean.class);
      String prefix = withNamePrefix ? mbean.objectName() + '.' : "";
      CtClass ctClass = classPool.get(clazz.getName());

      CtMethod[] ctMethods = ctClass.getMethods();
      for (CtMethod ctMethod : ctMethods) {
        ManagedAttribute managedAttr =
            (ManagedAttribute) ctMethod.getAnnotation(ManagedAttribute.class);
        ManagedOperation managedOp =
            (ManagedOperation) ctMethod.getAnnotation(ManagedOperation.class);

        Metric rhqMetric = (Metric) ctMethod.getAnnotation(Metric.class);
        if (rhqMetric != null) {
          debug("Metric annotation found " + rhqMetric);
          // Property and description resolution are the reason why annotation scanning is done
          // here.
          // These two fields are calculated from either the method name or the Managed*
          // annotations,
          // and so, only the infinispan side knows about that.
          String property = prefix + getPropertyFromBeanConvention(ctMethod);
          if (!rhqMetric.property().isEmpty()) {
            property = prefix + rhqMetric.property();
          }
          MetricProps metric = new MetricProps(property);
          String displayName =
              withNamePrefix
                  ? "[" + mbean.objectName() + "] " + rhqMetric.displayName()
                  : rhqMetric.displayName();
          metric.setDisplayName(displayName);
          metric.setDisplayType(rhqMetric.displayType());
          metric.setDataType(rhqMetric.dataType());
          metric.setUnits(rhqMetric.units());
          if (managedAttr != null) {
            debug("Metric has ManagedAttribute annotation " + managedAttr);
            metric.setDescription(managedAttr.description());
          } else if (managedOp != null) {
            debug("Metric has ManagedOperation annotation " + managedOp);
            metric.setDescription(managedOp.description());
          } else {
            log.debug(
                "Metric has no managed annotations, so take the description from the display name.");
            metric.setDescription(rhqMetric.displayName());
          }
          props.getMetrics().add(metric);
        }

        Operation rhqOperation = (Operation) ctMethod.getAnnotation(Operation.class);
        if (rhqOperation != null) {
          debug("Operation annotation found " + rhqOperation);
          String name;
          if (!rhqOperation.name().isEmpty()) {
            name = prefix + rhqOperation.name();
          } else {
            name = prefix + ctMethod.getName();
          }
          OperationProps operation = new OperationProps(name);
          String displayName =
              withNamePrefix
                  ? "[" + mbean.objectName() + "] " + rhqOperation.displayName()
                  : rhqOperation.displayName();
          operation.setDisplayName(displayName);
          if (managedAttr != null) {
            debug("Operation has ManagedAttribute annotation " + managedAttr);
            operation.setDescription(managedAttr.description());
          } else if (managedOp != null) {
            debug("Operation has ManagedOperation annotation " + managedOp);
            operation.setDescription(managedOp.description());
          } else {
            debug(
                "Operation has no managed annotations, so take the description from the display name.");
            operation.setDescription(rhqOperation.displayName());
          }

          Object[][] paramAnnotations = ctMethod.getParameterAnnotations();
          int i = 0;
          for (Object[] paramAnnotationsInEach : paramAnnotations) {
            boolean hadParameter = false;
            for (Object annot : paramAnnotationsInEach) {
              debug("Parameter annotation " + annot);
              if (annot instanceof Parameter) {
                Parameter param = (Parameter) annot;
                SimpleProperty prop = new SimpleProperty(param.name());
                prop.setDescription(param.description());
                operation.getParams().add(prop);
                hadParameter = true;
              }
            }
            if (!hadParameter) {
              operation.getParams().add(new SimpleProperty("p" + i++));
            }
          }
          CtClass returnType = ctMethod.getReturnType();
          if (!returnType.equals(CtClass.voidType)) {
            if (!returnType.equals(Void.TYPE)) {
              SimpleProperty prop = new SimpleProperty("operationResult");
              operation.setResult(prop);
            }
          }
          props.getOperations().add(operation);
        }
      }

      CtField[] ctFields = ctClass.getDeclaredFields();
      for (CtField ctField : ctFields) {
        debug("Inspecting field " + ctField);

        Metric rhqMetric = (Metric) ctField.getAnnotation(Metric.class);
        if (rhqMetric != null) {
          debug("Field " + ctField + " contains Metric annotation " + rhqMetric);
          String property;
          if (!rhqMetric.property().isEmpty()) {
            property = prefix + rhqMetric.property();
          } else {
            property = prefix + getPropertyFromBeanConvention(ctField);
          }
          MetricProps metric = new MetricProps(property);
          String displayName =
              withNamePrefix
                  ? "[" + mbean.objectName() + "] " + rhqMetric.displayName()
                  : rhqMetric.displayName();
          metric.setDisplayName(displayName);
          metric.setDisplayType(rhqMetric.displayType());
          metric.setDataType(rhqMetric.dataType());
          metric.setUnits(rhqMetric.units());
          ManagedAttribute managedAttr =
              (ManagedAttribute) ctField.getAnnotation(ManagedAttribute.class);
          if (managedAttr != null) {
            debug("Metric has ManagedAttribute annotation " + managedAttr);
            metric.setDescription(managedAttr.description());
          } else {
            log.debug(
                "Metric has no managed annotations, so take the description from the display name.");
            metric.setDescription(rhqMetric.displayName());
          }
          props.getMetrics().add(metric);
        }
      }
    }
  }