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);
        }
      }
    }
  }