public void testAttributeHasCorrespondingOperations() throws Exception {
    ModelMBeanInfo info = getMBeanInfoFromAssembler();

    ModelMBeanOperationInfo get = info.getOperation("getName");
    assertNotNull("get operation should not be null", get);
    assertEquals(
        "get operation should have visibility of four",
        (Integer) get.getDescriptor().getFieldValue("visibility"),
        new Integer(4));
    assertEquals(
        "get operation should have role \"getter\"",
        "getter",
        get.getDescriptor().getFieldValue("role"));

    ModelMBeanOperationInfo set = info.getOperation("setName");
    assertNotNull("set operation should not be null", set);
    assertEquals(
        "set operation should have visibility of four",
        (Integer) set.getDescriptor().getFieldValue("visibility"),
        new Integer(4));
    assertEquals(
        "set operation should have role \"setter\"",
        "setter",
        set.getDescriptor().getFieldValue("role"));
  }
  @Test
  public void testNickNameIsExposed() throws Exception {
    ModelMBeanInfo inf = (ModelMBeanInfo) getMBeanInfo();
    MBeanAttributeInfo attr = inf.getAttribute("NickName");

    assertNickName(attr);
  }
  @Test
  public void testGetAgeIsReadOnly() throws Exception {
    ModelMBeanInfo info = getMBeanInfoFromAssembler();
    ModelMBeanAttributeInfo attr = info.getAttribute(AGE_ATTRIBUTE);

    assertTrue("Age is not readable", attr.isReadable());
    assertFalse("Age is not writable", attr.isWritable());
  }
  /* Check that all descriptors have been returned */
  private static void checkDescriptors(
      ModelMBeanInfo modelMBeanInfo, Descriptor[] descriptors, String string) {
    int errCount = 0;
    final ArrayList<Descriptor> list = new ArrayList<Descriptor>(descriptors.length);
    list.addAll(Arrays.asList(descriptors));
    System.out.println("Got " + list.size() + " descriptors for " + string);

    // checks that MBean's descriptor is returned.
    //
    final Descriptor mbd = ((MBeanInfo) modelMBeanInfo).getDescriptor();
    if (!mbd.equals(remove(list, mbd))) {
      System.err.println("modelMBeanInfo.getDescriptor(): not found");
      errCount++;
    }

    // checks that MBean's attributes descriptors are returned.
    //
    final MBeanAttributeInfo[] attrs = modelMBeanInfo.getAttributes();
    for (MBeanAttributeInfo att : attrs) {
      final Descriptor ad = att.getDescriptor();
      final String name = att.getName();
      if (!ad.equals(remove(list, ad))) {
        System.err.println("attInfo.getDescriptor(): not found for " + name);
        errCount++;
      }
    }

    // checks that MBean's operations descriptors are returned.
    //
    final MBeanOperationInfo[] ops = modelMBeanInfo.getOperations();
    for (MBeanOperationInfo op : ops) {
      final Descriptor od = op.getDescriptor();
      final String name = op.getName();
      if (!od.equals(remove(list, od))) {
        System.err.println("opInfo.getDescriptor(): not found for " + name);
        errCount++;
      }
    }

    // checks that MBean's notifications descriptors are returned.
    //
    final MBeanNotificationInfo[] ntfs = modelMBeanInfo.getNotifications();
    for (MBeanNotificationInfo ntf : ntfs) {
      final Descriptor nd = ntf.getDescriptor();
      final String name = ntf.getName();
      if (!nd.equals(remove(list, nd))) {
        System.err.println("notifInfo.getDescriptor(): not found for " + name);
        errCount++;
      }
    }
    if (errCount > 0) {
      throw new RuntimeException(string + ": failed with " + errCount + " errors");
    } else if (list.size() != 0) {
      // Check that there are no additional descriptors
      //
      throw new RuntimeException(string + ": Unexpected remaining descriptors: " + list);
    } else System.out.println(string + ": PASSED");
  }
  public void testAttributeInfoHasDescriptors() throws Exception {
    ModelMBeanInfo info = getMBeanInfoFromAssembler();

    ModelMBeanAttributeInfo attr = info.getAttribute(NAME_ATTRIBUTE);
    Descriptor desc = attr.getDescriptor();
    assertNotNull("getMethod field should not be null", desc.getFieldValue("getMethod"));
    assertNotNull("setMethod field should not be null", desc.getFieldValue("setMethod"));
    assertEquals("getMethod field has incorrect value", "getName", desc.getFieldValue("getMethod"));
    assertEquals("setMethod field has incorrect value", "setName", desc.getFieldValue("setMethod"));
  }
  @Test
  public void testWithFallThrough() throws Exception {
    MethodNameBasedMBeanInfoAssembler assembler =
        getWithMapping("foobar", "add,myOperation,getName,setName,getAge");
    assembler.setManagedMethods(new String[] {"getNickName", "setNickName"});

    ModelMBeanInfo inf = assembler.getMBeanInfo(getBean(), getObjectName());
    MBeanAttributeInfo attr = inf.getAttribute("NickName");

    assertNickName(attr);
  }
  public void testGetMBeanOperationInfo() throws Exception {
    ModelMBeanInfo info = getMBeanInfoFromAssembler();
    MBeanOperationInfo[] inf = info.getOperations();
    assertEquals("Invalid number of Operations returned", getExpectedOperationCount(), inf.length);

    for (int x = 0; x < inf.length; x++) {
      assertNotNull("MBeanOperationInfo should not be null", inf[x]);
      assertNotNull(
          "Description for MBeanOperationInfo should not be null", inf[x].getDescription());
    }
  }
  public void testNotificationMetadata() throws Exception {
    ModelMBeanInfo info = (ModelMBeanInfo) getMBeanInfo();
    MBeanNotificationInfo[] notifications = info.getNotifications();
    assertEquals("Incorrect number of notifications", 1, notifications.length);
    assertEquals("Incorrect notification name", "My Notification", notifications[0].getName());

    String[] notifTypes = notifications[0].getNotifTypes();

    assertEquals("Incorrect number of notification types", 2, notifTypes.length);
    assertEquals("Notification type.foo not found", "type.foo", notifTypes[0]);
    assertEquals("Notification type.bar not found", "type.bar", notifTypes[1]);
  }
  // Not in the spec but needed
  private void removeAttributeChangeNotificationListener(
      NotificationListener listener, String attributeName, Object handback)
      throws MBeanException, RuntimeOperationsException, ListenerNotFoundException {
    if (listener == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_LISTENER_CANNOT_BE_NULL.toLocalizedString()));
    AttributeChangeNotificationFilter filter = new AttributeChangeNotificationFilter();
    if (attributeName != null) {
      filter.enableAttribute(attributeName);
    } else {
      MBeanAttributeInfo[] ai = m_modelMBeanInfo.getAttributes();
      for (int i = 0; i < ai.length; i++) {
        Descriptor d = ((ModelMBeanAttributeInfo) ai[i]).getDescriptor();
        filter.enableAttribute((String) d.getFieldValue("name"));
      }
    }

    getAttributeChangeBroadcaster().removeNotificationListener(listener, filter, handback);

    Logger logger = getLogger();
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug(
          "Listener "
              + listener
              + " for attribute "
              + attributeName
              + " removed successfully, handback is "
              + handback);
  }
  private Logger getModelMBeanLogger(String notificationType) throws MBeanException {
    // Get a copy to avoid synchronization
    ModelMBeanInfo info = getModelMBeanInfo();

    // First look if there is a suitable notification descriptor, otherwise use MBean descriptor
    Descriptor descriptor = null;
    Logger modelMBeanLogger = null;
    if (notificationType != null) {
      descriptor = info.getDescriptor(notificationType, "notification");
      modelMBeanLogger = findLogger(descriptor);
    }

    if (modelMBeanLogger == null) {
      descriptor = info.getMBeanDescriptor();
      modelMBeanLogger = findLogger(descriptor);
      if (modelMBeanLogger != null) return modelMBeanLogger;
    }

    return null;
  }
  public void setModelMBeanInfo(ModelMBeanInfo modelMBeanInfo)
      throws MBeanException, RuntimeOperationsException {
    if (modelMBeanInfo == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_CANNOT_BE_NULL.toLocalizedString()));
    if (!isModelMBeanInfoValid(modelMBeanInfo))
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_INVALID.toLocalizedString()));

    m_modelMBeanInfo = (ModelMBeanInfo) modelMBeanInfo.clone();

    Logger logger = getLogger();
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("ModelMBeanInfo successfully set to: " + m_modelMBeanInfo);
    // Only now the MBean can be registered in the MBeanServer
    m_canBeRegistered = true;
  }
 private static void test(int testno) throws Exception {
   // com.sun.jmx.trace.TraceImplementation.init(2);
   Resource resource = new Resource();
   Class resourceClass = Resource.class;
   Class rmmbClass = RequiredModelMBean.class;
   Method setManagedResource =
       rmmbClass.getMethod("setManagedResource", new Class[] {Object.class, String.class});
   Method sendNotification =
       rmmbClass.getMethod("sendNotification", new Class[] {Notification.class});
   Method addAttributeChangeNL =
       rmmbClass.getMethod(
           "addAttributeChangeNotificationListener",
           new Class[] {NotificationListener.class, String.class, Object.class});
   Method getArray = resourceClass.getMethod("getArray", new Class[0]);
   Method getNumber = resourceClass.getMethod("getNumber", new Class[0]);
   Method setNumber = resourceClass.getMethod("setNumber", new Class[] {Integer.TYPE});
   Method tweakArray = resourceClass.getMethod("tweakArray", new Class[] {Object[].class});
   Method addOne = resourceClass.getMethod("addOne", new Class[] {Integer.TYPE});
   MBeanServer mbs = MBeanServerFactory.newMBeanServer();
   ObjectName on = new ObjectName("a:b=c");
   Descriptor attrDescr = new DescriptorSupport();
   attrDescr.setField("name", "Array");
   attrDescr.setField("descriptorType", "attribute");
   attrDescr.setField("getMethod", "getArray");
   ModelMBeanAttributeInfo attrInfo =
       new ModelMBeanAttributeInfo("Array", "array attr", getArray, null, attrDescr);
   Descriptor attrDescr2 = new DescriptorSupport();
   attrDescr2.setField("name", "Number");
   attrDescr2.setField("descriptorType", "attribute");
   attrDescr2.setField("getMethod", "getNumber");
   attrDescr2.setField("setMethod", "setNumber");
   ModelMBeanAttributeInfo attrInfo2 =
       new ModelMBeanAttributeInfo("Number", "number attr", getNumber, setNumber, attrDescr2);
   Descriptor attrDescr3 = new DescriptorSupport();
   attrDescr3.setField("name", "Local");
   attrDescr3.setField("descriptorType", "attribute");
   attrDescr3.setField("currencyTimeLimit", "" + Integer.MAX_VALUE);
   ModelMBeanAttributeInfo attrInfo3 =
       new ModelMBeanAttributeInfo(
           "Local", "java.lang.String", "local attr", true, true, false, attrDescr3);
   Descriptor attrDescr4 = new DescriptorSupport();
   attrDescr4.setField("name", "Local2");
   attrDescr4.setField("descriptorType", "attribute");
   ModelMBeanAttributeInfo attrInfo4 =
       new ModelMBeanAttributeInfo(
           "Local2", "java.lang.String", "local attr 2", true, true, false, attrDescr4);
   ModelMBeanAttributeInfo[] attrs =
       new ModelMBeanAttributeInfo[] {attrInfo, attrInfo2, attrInfo3, attrInfo4};
   ModelMBeanOperationInfo operInfo = new ModelMBeanOperationInfo("getArray descr", getArray);
   ModelMBeanOperationInfo operInfo2 = new ModelMBeanOperationInfo("getNumber descr", getNumber);
   ModelMBeanOperationInfo operInfo3 = new ModelMBeanOperationInfo("addOne descr", addOne);
   ModelMBeanOperationInfo operInfo4 = new ModelMBeanOperationInfo("setNumber descr", setNumber);
   ModelMBeanOperationInfo operInfo5 = new ModelMBeanOperationInfo("tweakArray descr", tweakArray);
   ModelMBeanOperationInfo operInfoSetManagedResource =
       new ModelMBeanOperationInfo("setManagedResource descr", setManagedResource);
   ModelMBeanOperationInfo operInfoSendNotification =
       new ModelMBeanOperationInfo("sendNotification descr", sendNotification);
   ModelMBeanOperationInfo operInfoAddAttributeChangeNL =
       new ModelMBeanOperationInfo("AddAttributeChangeNL descr", addAttributeChangeNL);
   ModelMBeanOperationInfo[] opers =
       new ModelMBeanOperationInfo[] {
         operInfo,
         operInfo2,
         operInfo3,
         operInfo4,
         operInfo5,
         operInfoSetManagedResource,
         operInfoSendNotification,
         operInfoAddAttributeChangeNL
       };
   ModelMBeanInfo info =
       new ModelMBeanInfoSupport(
           Resource.class.getName(), "Resourcish resource", attrs, null, opers, null, null);
   mbs.createMBean(
       RequiredModelMBean.class.getName(),
       on,
       new Object[] {info},
       new String[] {ModelMBeanInfo.class.getName()});
   mbs.invoke(
       on,
       "setManagedResource",
       new Object[] {resource, "objectReference"},
       new String[] {"java.lang.Object", "java.lang.String"});
   switch (testno) {
     case 0:
       {
         /* Check  getDescriptors("") on original MBeanInfo */
         final Descriptor[] desc = info.getDescriptors("");
         checkDescriptors(info, desc, "info.getDescriptors(\"\")");
         break;
       }
     case 1:
       {
         /* Check  getDescriptors(null) on original MBeanInfo */
         final Descriptor[] desc = info.getDescriptors(null);
         checkDescriptors(info, desc, "info.getDescriptors(null)");
         break;
       }
     case 2:
       {
         /* Check  getDescriptors("") on retrieved MBeanInfo */
         final MBeanInfo mbi = mbs.getMBeanInfo(on);
         final ModelMBeanInfo model = (ModelMBeanInfo) mbi;
         final Descriptor[] desc = model.getDescriptors("");
         checkDescriptors(info, desc, "model.getDescriptors(\"\")");
         break;
       }
     case 3:
       {
         /* Check  getDescriptors(null) on retrieved MBeanInfo */
         final MBeanInfo mbi = mbs.getMBeanInfo(on);
         final ModelMBeanInfo model = (ModelMBeanInfo) mbi;
         final Descriptor[] desc = model.getDescriptors(null);
         checkDescriptors(info, desc, "model.getDescriptors(null)");
         break;
       }
     default:
       System.err.println("UNKNOWN TEST NUMBER " + testno);
       break;
   }
 }
 private boolean isModelMBeanInfoValid(ModelMBeanInfo info) {
   if (info == null || info.getClassName() == null) return false;
   // PENDING: maybe more checks are needed
   return true;
 }
  public void testDescriptionNotNull() throws Exception {
    ModelMBeanInfo info = getMBeanInfoFromAssembler();

    assertNotNull("The MBean description should not be null", info.getDescription());
  }
 public MBeanInfo getMBeanInfo() {
   return m_modelMBeanInfo == null ? null : (MBeanInfo) m_modelMBeanInfo.clone();
 }
  private PersisterMBean findPersister() throws MBeanException, InstanceNotFoundException {
    Logger logger = getLogger();

    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null) {
      // Not yet initialized
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("Can't find persister, ModelMBeanInfo is null");
      return null;
    }
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null) {
      // This is normally should not happen if ModelMBeanInfoSupport is used
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("Can't find persister, MBean descriptor is null");
      return null;
    }

    String location = (String) mbeanDescriptor.getFieldValue("persistLocation");
    String name = (String) mbeanDescriptor.getFieldValue("persistName");
    String mbeanName = (String) mbeanDescriptor.getFieldValue("name");
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Persistence fields: location=" + location + ", name=" + name);

    if (mbeanName == null && name == null) {
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Persistence is not supported by this ModelMBean");
      return null;
    }

    // Try to see if this mbean should delegate to another mbean
    if (name != null) {
      try {
        ObjectName objectName = new ObjectName(name.trim());
        // OK, a valid object name
        MBeanServer server = getMBeanServer();
        if (server == null)
          throw new MBeanException(
              new IllegalStateException(
                  LocalizedStrings.MX4JModelMBean_MX4JMODELMBEAN_IS_NOT_REGISTERED
                      .toLocalizedString()));

        if (server.isRegistered(objectName)
            && server.isInstanceOf(objectName, PersisterMBean.class.getName())) {
          // OK, the given mbean is registered with this mbean server
          PersisterMBean persister = new MBeanPersister(server, objectName);
          if (logger.isEnabledFor(Logger.DEBUG))
            logger.debug("Persistence is delegated to this MBean: " + objectName);
          return persister;
        } else {
          throw new InstanceNotFoundException(objectName.toString());
        }
      } catch (MalformedObjectNameException ignored) {
        // It does not delegates to another mbean, use default
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Persistence is not delegated to another MBean");
      }

      // Default is serialization to file
      FilePersister persister = new FilePersister(location, name);
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Persistence is realized through file system in " + persister.getFileName());
      return persister;
    } else {
      // Only location given, use MBean name
      FilePersister persister = new FilePersister(location, mbeanName);
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Persistence is realized through file system in " + persister.getFileName());
      return persister;
    }
  }
  public Object invoke(String method, Object[] arguments, String[] params)
      throws MBeanException, ReflectionException {
    if (method == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_METHOD_NAME_CANNOT_BE_NULL.toLocalizedString()));
    if (arguments == null) arguments = new Object[0];
    if (params == null) params = new String[0];

    Logger logger = getLogger();

    // Find operation descriptor
    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_NULL.toLocalizedString()));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo is: " + info);

    // This is a clone, we use it read only
    ModelMBeanOperationInfo operInfo = info.getOperation(method);
    if (operInfo == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_CANNOT_FIND_MODELMBEANOPERATIONINFO_FOR_OPERATION_0
                  .toLocalizedString(method)));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Operation info is: " + operInfo);

    // This descriptor is a clone
    Descriptor operationDescriptor = operInfo.getDescriptor();
    if (operationDescriptor == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_OPERATION_DESCRIPTOR_FOR_OPERATION_0_CANNOT_BE_NULL
                  .toLocalizedString(method)));
    String role = (String) operationDescriptor.getFieldValue("role");
    if (role == null || !role.equals("operation"))
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings
                  .MX4JModelMBean_OPERATION_DESCRIPTOR_FIELD_ROLE_MUST_BE_OPERATION_NOT_0
                  .toLocalizedString(role)));
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Operation descriptor is: " + operationDescriptor);

    // This returns a clone of the mbean descriptor, we use it read only
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_MBEAN_DESCRIPTOR_CANNOT_BE_NULL.toLocalizedString()));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean descriptor is: " + mbeanDescriptor);

    Object returnValue = null;

    String lastUpdateField = "lastReturnedTimeStamp";

    // Check if the method should be invoked given the cache settings
    int staleness = getStaleness(operationDescriptor, mbeanDescriptor, lastUpdateField);

    if (staleness == ALWAYS_STALE || staleness == STALE) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Value is stale");

      // Find parameters classes
      Class[] parameters = null;
      try {
        parameters = Utils.loadClasses(Thread.currentThread().getContextClassLoader(), params);
      } catch (ClassNotFoundException x) {
        logger.error(LocalizedStrings.MX4JModelMBean_CANNOT_FIND_OPERATIONS_PARAMETER_CLASSES, x);
        throw new ReflectionException(x);
      }

      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Invoking operation...");

      // Find target object
      Object target = resolveTargetObject(operationDescriptor);
      returnValue = invokeMethod(target, method, parameters, arguments);

      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Returned value is: " + returnValue);

      if (returnValue != null) {
        Class parameter = returnValue.getClass();
        Class declared = loadClassWithContextClassLoader(operInfo.getReturnType());

        checkAssignability(parameter, declared);
      }

      // Cache the new value only if caching is needed
      if (staleness != ALWAYS_STALE) {
        operationDescriptor.setField("lastReturnedValue", returnValue);
        operationDescriptor.setField(lastUpdateField, Long.valueOf(System.currentTimeMillis()));
        if (logger.isEnabledFor(Logger.TRACE)) {
          logger.trace("Returned value has been cached");
        }

        // And now replace the descriptor with the updated clone
        info.setDescriptor(operationDescriptor, "operation");
      }

      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("invoke for operation " + method + " returns invoked value: " + returnValue);
    } else {
      // Return cached value
      returnValue = operationDescriptor.getFieldValue("lastReturnedValue");

      if (returnValue != null) {
        Class parameter = returnValue.getClass();
        Class declared = loadClassWithContextClassLoader(operInfo.getReturnType());

        checkAssignability(parameter, declared);
      }

      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("invoke for operation " + method + " returns cached value: " + returnValue);
    }

    // As an extension, persist this model mbean also after operation invocation, but using only
    // settings provided in the operation descriptor, without falling back to defaults set in
    // the MBean descriptor
    boolean persistNow = shouldPersistNow(operationDescriptor, null, lastUpdateField);
    int impact = operInfo.getImpact();
    if (persistNow && impact != MBeanOperationInfo.INFO) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persisting this ModelMBean...");
      try {
        store();
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("ModelMBean persisted successfully");
      } catch (Exception x) {
        logger.error(
            LocalizedStrings.MX4JModelMBean_CANNOT_STORE_MODELMBEAN_AFTER_OPERATION_INVOCATION, x);
        if (x instanceof MBeanException) throw (MBeanException) x;
        else throw new MBeanException(x);
      }
    }

    return returnValue;
  }
  public void setAttribute(Attribute attribute)
      throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException,
          ReflectionException {
    if (attribute == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_ATTRIBUTE_CANNOT_BE_NULL.toLocalizedString()));

    Logger logger = getLogger();

    // No need to synchronize: I work mostly on clones
    // I want the real info, not its clone
    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo is: " + info);

    String attrName = attribute.getName();
    Object attrValue = attribute.getValue();

    // This is a clone, we use it read only
    ModelMBeanAttributeInfo attrInfo = info.getAttribute(attrName);
    if (attrInfo == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_CANNOT_FIND_MODELMBEANATTRIBUTEINFO_FOR_ATTRIBUTE_0
              .toLocalizedString(attrName));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Attribute info is: " + attrInfo);

    if (!attrInfo.isWritable())
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_0_IS_NOT_WRITABLE.toLocalizedString(attrName));

    // This returns a clone of the mbean descriptor, we use it read only
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MBEAN_DESCRIPTOR_CANNOT_BE_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean descriptor is: " + mbeanDescriptor);

    // This descriptor is a clone
    Descriptor attributeDescriptor = attrInfo.getDescriptor();
    if (attributeDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_DESCRIPTOR_FOR_ATTRIBUTE_0_CANNOT_BE_NULL
              .toLocalizedString(attrName));
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Attribute descriptor is: " + attributeDescriptor);

    String lastUpdateField = "lastUpdatedTimeStamp";

    Object oldValue = null;
    try {
      oldValue = getAttribute(attrName);
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Previous value of attribute " + attrName + ": " + oldValue);
    } catch (Exception x) {
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Cannot get previous value of attribute " + attrName, x);
    }

    // Check if setMethod is present
    String method = (String) attributeDescriptor.getFieldValue("setMethod");
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("setMethod field is: " + method);
    if (method != null) {
      Class declared = loadClassWithContextClassLoader(attrInfo.getType());
      if (attrValue != null) {
        Class parameter = attrValue.getClass();
        checkAssignability(parameter, declared);
      }

      // As an extension, allow attributes to be called on target objects also
      Object target = resolveTargetObject(attributeDescriptor);
      invokeMethod(target, method, new Class[] {declared}, new Object[] {attrValue});

      // Cache the value only if currencyTimeLimit is not 0, ie it is not always stale
      int staleness = getStaleness(attributeDescriptor, mbeanDescriptor, lastUpdateField);
      if (staleness != ALWAYS_STALE) {
        attributeDescriptor.setField("value", attrValue);
        attributeDescriptor.setField(lastUpdateField, Long.valueOf(System.currentTimeMillis()));
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Attribute's value has been cached");
      } else {
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Always stale, avoiding to cache attribute's value");
      }
    } else {
      if (attrValue != null) {
        Class parameter = attrValue.getClass();
        Class declared = loadClassWithContextClassLoader(attrInfo.getType());

        checkAssignability(parameter, declared);
      }

      // Always store the value in the descriptor: no setMethod
      attributeDescriptor.setField("value", attrValue);
    }

    // And now replace the descriptor with the updated clone
    info.setDescriptor(attributeDescriptor, "attribute");

    // Send notifications to listeners
    if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Sending attribute change notifications");
    sendAttributeChangeNotification(new Attribute(attrName, oldValue), attribute);

    // Persist this ModelMBean
    boolean persistNow = shouldPersistNow(attributeDescriptor, mbeanDescriptor, lastUpdateField);
    if (persistNow) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persisting this ModelMBean...");
      try {
        store();
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("ModelMBean persisted successfully");
      } catch (Exception x) {
        logger.error(LocalizedStrings.MX4JModelMBean_CANNOT_STORE_MODELMBEAN_AFTER_SETATTRIBUTE, x);
        if (x instanceof MBeanException) throw (MBeanException) x;
        else throw new MBeanException(x);
      }
    }
  }
  public Object getAttribute(String attribute)
      throws AttributeNotFoundException, MBeanException, ReflectionException {
    if (attribute == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_ATTRIBUTE_NAME_CANNOT_BE_NULL.toLocalizedString()));

    Logger logger = getLogger();

    // I want the real info, not its clone
    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo is: " + info);

    // This is a clone, we use it read only
    ModelMBeanAttributeInfo attrInfo = info.getAttribute(attribute);
    if (attrInfo == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_CANNOT_FIND_MODELMBEANATTRIBUTEINFO_FOR_ATTRIBUTE_0
              .toLocalizedString(attribute));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Attribute info is: " + attrInfo);
    if (!attrInfo.isReadable())
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_0_IS_NOT_READABLE.toLocalizedString(attribute));

    // This returns a clone of the mbean descriptor, we use it read only
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MBEAN_DESCRIPTOR_CANNOT_BE_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean descriptor is: " + mbeanDescriptor);

    // This descriptor is a clone
    Descriptor attributeDescriptor = attrInfo.getDescriptor();
    if (attributeDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_DESCRIPTOR_FOR_ATTRIBUTE_0_CANNOT_BE_NULL
              .toLocalizedString(attribute));
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Attribute descriptor is: " + attributeDescriptor);

    Object returnValue = null;

    String lastUpdateField = "lastUpdatedTimeStamp";

    int staleness = getStaleness(attributeDescriptor, mbeanDescriptor, lastUpdateField);

    if (staleness == ALWAYS_STALE || staleness == STALE) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Value is stale");

      String getter = (String) attributeDescriptor.getFieldValue("getMethod");
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("getMethod field is: " + getter);
      if (getter == null) {
        // No getter, use default value
        returnValue = attributeDescriptor.getFieldValue("default");

        if (returnValue != null) {
          // Check if the return type is of the same type
          // As an extension allow covariant return type
          Class returned = returnValue.getClass();
          Class declared = loadClassWithContextClassLoader(attrInfo.getType());

          checkAssignability(returned, declared);
        }

        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug(
              "getAttribute for attribute " + attribute + " returns default value: " + returnValue);
      } else {
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Invoking attribute getter...");
        // As an extension, allow attributes to be called on target objects also
        Object target = resolveTargetObject(attributeDescriptor);
        returnValue = invokeMethod(target, getter, new Class[0], new Object[0]);
        if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Returned value is: " + returnValue);

        if (returnValue != null) {
          // Check if the return type is of the same type
          // As an extension allow covariant return type
          Class returned = returnValue.getClass();
          Class declared = loadClassWithContextClassLoader(attrInfo.getType());

          checkAssignability(returned, declared);
        }

        // Cache the new value only if caching is needed
        if (staleness != ALWAYS_STALE) {
          attributeDescriptor.setField("value", returnValue);
          attributeDescriptor.setField(lastUpdateField, Long.valueOf(System.currentTimeMillis()));
          if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Returned value has been cached");

          // And now replace the descriptor with the updated clone
          info.setDescriptor(attributeDescriptor, "attribute");
        }

        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug(
              "getAttribute for attribute " + attribute + " returns invoked value: " + returnValue);
      }
    } else {
      // Return cached value
      returnValue = attributeDescriptor.getFieldValue("value");

      if (returnValue != null) {
        // Check if the return type is of the same type
        // As an extension allow covariant return type
        Class returned = returnValue.getClass();
        Class declared = loadClassWithContextClassLoader(attrInfo.getType());

        checkAssignability(returned, declared);
      }

      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug(
            "getAttribute for attribute " + attribute + " returns cached value: " + returnValue);
    }

    // Puff, everything went ok
    return returnValue;
  }
 public MBeanNotificationInfo[] getNotificationInfo() {
   return m_modelMBeanInfo.getNotifications();
 }