You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ds...@apache.org on 2016/10/28 21:40:26 UTC

[12/98] [abbrv] [partial] incubator-geode git commit: Added Spotless plugin to enforce formatting standards. Added Google Java Style guide formatter templates, removed existing formatter templates.

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8bf39571/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/MX4JModelMBean.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/MX4JModelMBean.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/MX4JModelMBean.java
index 81f543d..d3f4ab2 100755
--- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/MX4JModelMBean.java
+++ b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/MX4JModelMBean.java
@@ -1,9 +1,8 @@
 /*
- * Copyright (C) MX4J.
- * All rights reserved.
+ * Copyright (C) MX4J. All rights reserved.
  *
- * This software is distributed under the terms of the MX4J License version 1.0.
- * See the terms of the MX4J License in the documentation provided with this software.
+ * This software is distributed under the terms of the MX4J License version 1.0. See the terms of
+ * the MX4J License in the documentation provided with this software.
  */
 
 package org.apache.geode.admin.jmx.internal;
@@ -64,1176 +63,1170 @@ import org.apache.geode.internal.i18n.LocalizedStrings;
  * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
  * @version $Revision: 1.14 $
  */
-public class MX4JModelMBean implements ModelMBean, MBeanRegistration, NotificationEmitter
-{
-   private static final String OBJECT_RESOURCE_TYPE = "ObjectReference";
-
-   private static final int ALWAYS_STALE = 1;
-   private static final int NEVER_STALE = 2;
-   private static final int STALE = 3;
-   private static final int NOT_STALE = 4;
-
-   private static final int PERSIST_NEVER = -1;
-   private static final int PERSIST_ON_TIMER = -2;
-   private static final int PERSIST_ON_UPDATE = -3;
-   private static final int PERSIST_NO_MORE_OFTEN_THAN = -4;
-
-   private MBeanServer m_mbeanServer;
-   private Object m_managedResource;
-   private boolean m_canBeRegistered;
-   private ModelMBeanInfo m_modelMBeanInfo;
-   private NotificationBroadcasterSupport m_attributeChangeBroadcaster = new NotificationBroadcasterSupport();
-   private NotificationBroadcasterSupport m_generalBroadcaster = new NotificationBroadcasterSupport();
-
-   public MX4JModelMBean() throws MBeanException, RuntimeOperationsException
-   {
-      try
-      {
-         load();
+public class MX4JModelMBean implements ModelMBean, MBeanRegistration, NotificationEmitter {
+  private static final String OBJECT_RESOURCE_TYPE = "ObjectReference";
+
+  private static final int ALWAYS_STALE = 1;
+  private static final int NEVER_STALE = 2;
+  private static final int STALE = 3;
+  private static final int NOT_STALE = 4;
+
+  private static final int PERSIST_NEVER = -1;
+  private static final int PERSIST_ON_TIMER = -2;
+  private static final int PERSIST_ON_UPDATE = -3;
+  private static final int PERSIST_NO_MORE_OFTEN_THAN = -4;
+
+  private MBeanServer m_mbeanServer;
+  private Object m_managedResource;
+  private boolean m_canBeRegistered;
+  private ModelMBeanInfo m_modelMBeanInfo;
+  private NotificationBroadcasterSupport m_attributeChangeBroadcaster =
+      new NotificationBroadcasterSupport();
+  private NotificationBroadcasterSupport m_generalBroadcaster =
+      new NotificationBroadcasterSupport();
+
+  public MX4JModelMBean() throws MBeanException, RuntimeOperationsException {
+    try {
+      load();
+    } catch (Exception x) {
+      Logger logger = getLogger();
+      logger.warn(LocalizedStrings.MX4JModelMBean_CANNOT_RESTORE_PREVIOUSLY_SAVED_STATUS
+          .toLocalizedString(), x);
+    }
+  }
+
+  public MX4JModelMBean(ModelMBeanInfo info) throws MBeanException, RuntimeOperationsException {
+    if (info == null)
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_PARAMETER_CANT_BE_NULL
+              .toLocalizedString()));
+    else
+      setModelMBeanInfo(info);
+  }
+
+  private Logger getLogger() {
+    return Log.getLogger(getClass().getName());
+  }
+
+  public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
+    if (m_canBeRegistered) {
+      m_mbeanServer = server;
+      return name;
+    } else {
+      throw new MBeanRegistrationException(new IllegalStateException(
+          LocalizedStrings.MX4JModelMBean_MODELMBEAN_CANNOT_BE_REGISTERED_UNTIL_SETMODELMBEANINFO_HAS_BEEN_CALLED
+              .toLocalizedString()));
+    }
+  }
+
+  public void postRegister(Boolean registrationDone) {
+    if (!registrationDone.booleanValue())
+      clear();
+  }
+
+  public void preDeregister() throws Exception {}
+
+  public void postDeregister() {
+    clear();
+  }
+
+  private void clear() {
+    m_mbeanServer = null;
+    m_managedResource = null;
+    m_modelMBeanInfo = null;
+    m_generalBroadcaster = null;
+    m_attributeChangeBroadcaster = null;
+    // PENDING: also remove generic listeners, attribute change listeners, log4j appenders...
+  }
+
+  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 boolean isModelMBeanInfoValid(ModelMBeanInfo info) {
+    if (info == null || info.getClassName() == null)
+      return false;
+    // PENDING: maybe more checks are needed
+    return true;
+  }
+
+  public void setManagedResource(Object resource, String resourceType) throws MBeanException,
+      RuntimeOperationsException, InstanceNotFoundException, InvalidTargetObjectTypeException {
+    if (resource == null)
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_MANAGED_RESOURCE_CANNOT_BE_NULL.toLocalizedString()));
+    if (!isResourceTypeSupported(resourceType))
+      throw new InvalidTargetObjectTypeException(resourceType);
+
+    Logger logger = getLogger();
+    if (logger.isEnabledFor(Logger.DEBUG))
+      logger.debug("Setting managed resource to be: " + resource);
+    m_managedResource = resource;
+  }
+
+  private boolean isResourceTypeSupported(String resourceType) {
+    // For now only object reference is supported
+    return OBJECT_RESOURCE_TYPE.equals(resourceType);
+  }
+
+  private Object getManagedResource() {
+    return m_managedResource;
+  }
+
+  public MBeanInfo getMBeanInfo() {
+    return m_modelMBeanInfo == null ? null : (MBeanInfo) m_modelMBeanInfo.clone();
+  }
+
+  public void addAttributeChangeNotificationListener(NotificationListener listener,
+      String attributeName, Object handback)
+      throws MBeanException, RuntimeOperationsException, IllegalArgumentException {
+    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"));
       }
-      catch (Exception x)
-      {
-         Logger logger = getLogger();
-         logger.warn(LocalizedStrings.MX4JModelMBean_CANNOT_RESTORE_PREVIOUSLY_SAVED_STATUS.toLocalizedString(), x);
+    }
+
+    getAttributeChangeBroadcaster().addNotificationListener(listener, filter, handback);
+
+    Logger logger = getLogger();
+    if (logger.isEnabledFor(Logger.DEBUG))
+      logger.debug("Listener " + listener + " for attribute " + attributeName
+          + " added successfully, handback is " + handback);
+  }
+
+  public void addNotificationListener(NotificationListener listener, NotificationFilter filter,
+      Object handback) throws IllegalArgumentException {
+    m_generalBroadcaster.addNotificationListener(listener, filter, handback);
+  }
+
+  public MBeanNotificationInfo[] getNotificationInfo() {
+    return m_modelMBeanInfo.getNotifications();
+  }
+
+  public void removeAttributeChangeNotificationListener(NotificationListener listener,
+      String attributeName) throws RuntimeOperationsException, ListenerNotFoundException {
+    try {
+      removeAttributeChangeNotificationListener(listener, attributeName, null);
+    } catch (MBeanException e) {
+      throw new RuntimeOperationsException(new RuntimeException(e.getMessage()));
+    }
+  }
+
+  // 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"));
       }
-   }
-
-   public MX4JModelMBean(ModelMBeanInfo info) throws MBeanException, RuntimeOperationsException
-   {
-      if (info == null)
-         throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_PARAMETER_CANT_BE_NULL.toLocalizedString()));
-      else
-         setModelMBeanInfo(info);
-   }
-
-   private Logger getLogger()
-   {
-      return Log.getLogger(getClass().getName());
-   }
-
-   public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception
-   {
-      if (m_canBeRegistered)
-      {
-         m_mbeanServer = server;
-         return 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);
+  }
+
+  public void removeNotificationListener(NotificationListener listener)
+      throws RuntimeOperationsException, ListenerNotFoundException {
+    m_generalBroadcaster.removeNotificationListener(listener);
+  }
+
+  public void removeNotificationListener(NotificationListener listener, NotificationFilter filter,
+      Object handback) throws RuntimeOperationsException, ListenerNotFoundException {
+    m_generalBroadcaster.removeNotificationListener(listener, filter, handback);
+  }
+
+  public void sendAttributeChangeNotification(Attribute oldAttribute, Attribute newAttribute)
+      throws MBeanException, RuntimeOperationsException {
+    if (oldAttribute == null || newAttribute == null)
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_CANNOT_BE_NULL.toLocalizedString()));
+    if (!oldAttribute.getName().equals(newAttribute.getName()))
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_NAMES_CANNOT_BE_DIFFERENT.toLocalizedString()));
+
+    // TODO: the source must be the object name of the MBean if the listener was registered through
+    // MBeanServer
+    Object oldValue = oldAttribute.getValue();
+    AttributeChangeNotification n = new AttributeChangeNotification(this, 1,
+        System.currentTimeMillis(), "Attribute value changed", oldAttribute.getName(),
+        oldValue == null ? null : oldValue.getClass().getName(), oldValue, newAttribute.getValue());
+    sendAttributeChangeNotification(n);
+  }
+
+  public void sendAttributeChangeNotification(AttributeChangeNotification notification)
+      throws MBeanException, RuntimeOperationsException {
+    if (notification == null)
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_NOTIFICATION_CANNOT_BE_NULL.toLocalizedString()));
+
+    getAttributeChangeBroadcaster().sendNotification(notification);
+
+    Logger modelMBeanLogger = getModelMBeanLogger(notification.getType());
+    if (modelMBeanLogger != null)
+      if (modelMBeanLogger.isEnabledFor(Logger.DEBUG))
+        modelMBeanLogger.debug("ModelMBean log: " + new Date() + " - " + notification);
+
+    Logger logger = getLogger();
+    if (logger.isEnabledFor(Logger.DEBUG))
+      logger.debug("Attribute change notification " + notification + " sent");
+  }
+
+  public void sendNotification(String message) throws MBeanException, RuntimeOperationsException {
+    Notification notification = new Notification("jmx.modelmbean.general", this, 1, message);
+    sendNotification(notification);
+  }
+
+  public void sendNotification(Notification notification)
+      throws MBeanException, RuntimeOperationsException {
+    if (m_generalBroadcaster != null) {
+      m_generalBroadcaster.sendNotification(notification);
+    }
+  }
+
+  public AttributeList getAttributes(String[] attributes) {
+    if (attributes == null)
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_NAMES_CANNOT_BE_NULL.toLocalizedString()));
+
+    Logger logger = getLogger();
+
+    AttributeList list = new AttributeList();
+    for (int i = 0; i < attributes.length; ++i) {
+      String attrName = attributes[i];
+      Attribute attribute = null;
+      try {
+        Object value = getAttribute(attrName);
+        attribute = new Attribute(attrName, value);
+        list.add(attribute);
+      } catch (Exception x) {
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("getAttribute for attribute " + attrName + " failed", x);
+        // And go on with the next attribute
       }
-      else
-      {
-         throw new MBeanRegistrationException(new IllegalStateException(LocalizedStrings.MX4JModelMBean_MODELMBEAN_CANNOT_BE_REGISTERED_UNTIL_SETMODELMBEANINFO_HAS_BEEN_CALLED.toLocalizedString()));
+    }
+    return list;
+  }
+
+  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");
 
-   public void postRegister(Boolean registrationDone)
-   {
-      if (!registrationDone.booleanValue()) clear();
-   }
-
-   public void preDeregister() throws Exception
-   {
-   }
-
-   public void postDeregister()
-   {
-      clear();
-   }
+      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());
 
-   private void clear()
-   {
-      m_mbeanServer = null;
-      m_managedResource = null;
-      m_modelMBeanInfo = null;
-      m_generalBroadcaster = null;
-      m_attributeChangeBroadcaster = null;
-      // PENDING: also remove generic listeners, attribute change listeners, log4j appenders...
-   }
-
-   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 boolean isModelMBeanInfoValid(ModelMBeanInfo info)
-   {
-      if (info == null || info.getClassName() == null) return false;
-      // PENDING: maybe more checks are needed
-      return true;
-   }
-
-   public void setManagedResource(Object resource, String resourceType) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException, InvalidTargetObjectTypeException
-   {
-      if (resource == null) throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_MANAGED_RESOURCE_CANNOT_BE_NULL.toLocalizedString()));
-      if (!isResourceTypeSupported(resourceType)) throw new InvalidTargetObjectTypeException(resourceType);
-
-      Logger logger = getLogger();
-      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Setting managed resource to be: " + resource);
-      m_managedResource = resource;
-   }
-
-   private boolean isResourceTypeSupported(String resourceType)
-   {
-      // For now only object reference is supported
-      return OBJECT_RESOURCE_TYPE.equals(resourceType);
-   }
-
-   private Object getManagedResource()
-   {
-      return m_managedResource;
-   }
-
-   public MBeanInfo getMBeanInfo()
-   {
-      return m_modelMBeanInfo == null ? null : (MBeanInfo)m_modelMBeanInfo.clone();
-   }
-
-   public void addAttributeChangeNotificationListener(NotificationListener listener, String attributeName, Object handback) throws MBeanException, RuntimeOperationsException, IllegalArgumentException
-   {
-      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);
+        checkAssignability(returned, declared);
       }
-      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().addNotificationListener(listener, filter, handback);
 
-      Logger logger = getLogger();
-      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Listener " + listener + " for attribute " + attributeName + " added successfully, handback is " + handback);
-   }
-
-   public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException
-   {
-      m_generalBroadcaster.addNotificationListener(listener, filter, handback);
-   }
-
-   public MBeanNotificationInfo[] getNotificationInfo()
-   {
-      return m_modelMBeanInfo.getNotifications();
-   }
-
-   public void removeAttributeChangeNotificationListener(NotificationListener listener, String attributeName) throws RuntimeOperationsException, ListenerNotFoundException
-   {
-      try
-      {
-         removeAttributeChangeNotificationListener(listener, attributeName, null);
-      }
-      catch (MBeanException e)
-      {
-         throw new RuntimeOperationsException(new RuntimeException(e.getMessage()));
-      }
-   }
-
-   // 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"));
-         }
+      if (logger.isEnabledFor(Logger.DEBUG))
+        logger.debug(
+            "getAttribute for attribute " + attribute + " returns cached value: " + returnValue);
+    }
+
+    // Puff, everything went ok
+    return returnValue;
+  }
+
+  public AttributeList setAttributes(AttributeList attributes) {
+    if (attributes == null)
+      throw new RuntimeOperationsException(new IllegalArgumentException(
+          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_LIST_CANNOT_BE_NULL.toLocalizedString()));
+
+    Logger logger = getLogger();
+
+    AttributeList list = new AttributeList();
+    for (Iterator i = attributes.iterator(); i.hasNext();) {
+      Attribute attribute = (Attribute) i.next();
+      String name = attribute.getName();
+      try {
+        setAttribute(attribute);
+        list.add(attribute);
+      } catch (Exception x) {
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("setAttribute for attribute " + name + " failed", x);
+        // And go on with the next one
       }
-
-      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);
-   }
-
-   public void removeNotificationListener(NotificationListener listener) throws RuntimeOperationsException, ListenerNotFoundException
-   {
-      m_generalBroadcaster.removeNotificationListener(listener);
-   }
-
-   public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws RuntimeOperationsException, ListenerNotFoundException
-   {
-      m_generalBroadcaster.removeNotificationListener(listener, filter, handback);
-   }
-
-   public void sendAttributeChangeNotification(Attribute oldAttribute, Attribute newAttribute) throws MBeanException, RuntimeOperationsException
-   {
-      if (oldAttribute == null || newAttribute == null) throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_ATTRIBUTE_CANNOT_BE_NULL.toLocalizedString()));
-      if (!oldAttribute.getName().equals(newAttribute.getName())) throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_ATTRIBUTE_NAMES_CANNOT_BE_DIFFERENT.toLocalizedString()));
-
-      // TODO: the source must be the object name of the MBean if the listener was registered through MBeanServer
-      Object oldValue = oldAttribute.getValue();
-      AttributeChangeNotification n = new AttributeChangeNotification(this,
-                                                                      1,
-                                                                      System.currentTimeMillis(),
-                                                                      "Attribute value changed",
-                                                                      oldAttribute.getName(),
-                                                                      oldValue == null ? null : oldValue.getClass().getName(),
-                                                                      oldValue,
-                                                                      newAttribute.getValue());
-      sendAttributeChangeNotification(n);
-   }
-
-   public void sendAttributeChangeNotification(AttributeChangeNotification notification) throws MBeanException, RuntimeOperationsException
-   {
-      if (notification == null) throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_NOTIFICATION_CANNOT_BE_NULL.toLocalizedString()));
-
-      getAttributeChangeBroadcaster().sendNotification(notification);
-
-      Logger modelMBeanLogger = getModelMBeanLogger(notification.getType());
-      if (modelMBeanLogger != null) if (modelMBeanLogger.isEnabledFor(Logger.DEBUG)) modelMBeanLogger.debug("ModelMBean log: " + new Date() + " - " + notification);
-
-      Logger logger = getLogger();
-      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Attribute change notification " + notification + " sent");
-   }
-
-   public void sendNotification(String message) throws MBeanException, RuntimeOperationsException
-   {
-      Notification notification = new Notification("jmx.modelmbean.general", this, 1, message);
-      sendNotification(notification);
-   }
-
-   public void sendNotification(Notification notification) throws MBeanException, RuntimeOperationsException
-   {
-     if (m_generalBroadcaster != null) {
-       m_generalBroadcaster.sendNotification(notification);
-     }
-   }
-
-   public AttributeList getAttributes(String[] attributes)
-   {
-      if (attributes == null) throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_ATTRIBUTE_NAMES_CANNOT_BE_NULL.toLocalizedString()));
-
-      Logger logger = getLogger();
-
-      AttributeList list = new AttributeList();
-      for (int i = 0; i < attributes.length; ++i)
-      {
-         String attrName = attributes[i];
-         Attribute attribute = null;
-         try
-         {
-            Object value = getAttribute(attrName);
-            attribute = new Attribute(attrName, value);
-            list.add(attribute);
-         }
-         catch (Exception x)
-         {
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("getAttribute for attribute " + attrName + " failed", x);
-            // And go on with the next attribute
-         }
+    }
+    return list;
+  }
+
+  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);
       }
-      return list;
-   }
-
-   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";
+      // 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 || 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);
+      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());
 
-      // Puff, everything went ok
-      return returnValue;
-   }
-
-   public AttributeList setAttributes(AttributeList attributes)
-   {
-      if (attributes == null) throw new RuntimeOperationsException(new IllegalArgumentException(LocalizedStrings.MX4JModelMBean_ATTRIBUTE_LIST_CANNOT_BE_NULL.toLocalizedString()));
-
-      Logger logger = getLogger();
-
-      AttributeList list = new AttributeList();
-      for (Iterator i = attributes.iterator(); i.hasNext();)
-      {
-         Attribute attribute = (Attribute)i.next();
-         String name = attribute.getName();
-         try
-         {
-            setAttribute(attribute);
-            list.add(attribute);
-         }
-         catch (Exception x)
-         {
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("setAttribute for attribute " + name + " failed", x);
-            // And go on with the next one
-         }
+        checkAssignability(parameter, declared);
       }
-      return list;
-   }
-
-   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);
+      // 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);
       }
-      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");
-         }
+    }
+  }
+
+  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);
       }
-      else
-      {
-         if (attrValue != null)
-         {
-            Class parameter = attrValue.getClass();
-            Class declared = loadClassWithContextClassLoader(attrInfo.getType());
 
-            checkAssignability(parameter, declared);
-         }
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace("Invoking operation...");
 
-         // Always store the value in the descriptor: no setMethod
-         attributeDescriptor.setField("value", attrValue);
-      }
+      // Find target object
+      Object target = resolveTargetObject(operationDescriptor);
+      returnValue = invokeMethod(target, method, parameters, arguments);
 
-      // And now replace the descriptor with the updated clone
-      info.setDescriptor(attributeDescriptor, "attribute");
+      if (logger.isEnabledFor(Logger.DEBUG))
+        logger.debug("Returned value is: " + returnValue);
 
-      // Send notifications to listeners
-      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Sending attribute change notifications");
-      sendAttributeChangeNotification(new Attribute(attrName, oldValue), attribute);
+      if (returnValue != null) {
+        Class parameter = returnValue.getClass();
+        Class declared = loadClassWithContextClassLoader(operInfo.getReturnType());
 
-      // 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);
-         }
+        checkAssignability(parameter, declared);
       }
-   }
-
-   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);
+      // 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");
+        }
 
-      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);
+        // And now replace the descriptor with the updated clone
+        info.setDescriptor(operationDescriptor, "operation");
       }
-      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 invoked value: " + returnValue);
+    } else {
+      // Return cached value
+      returnValue = operationDescriptor.getFieldValue("lastReturnedValue");
 
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("invoke for operation " + method + " returns cached value: " + returnValue);
-      }
+      if (returnValue != null) {
+        Class parameter = returnValue.getClass();
+        Class declared = loadClassWithContextClassLoader(operInfo.getReturnType());
 
-      // 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);
-         }
+        checkAssignability(parameter, declared);
       }
 
-      return returnValue;
-   }
-
-   private Object resolveTargetObject(Descriptor descriptor) throws MBeanException
-   {
-      Logger logger = getLogger();
-      Object target = descriptor.getFieldValue("targetObject");
-      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("targetObject is: " + target);
-      if (target == null)
-      {
-         target = getManagedResource();
-      }
-      else
-      {
-         String targetObjectType = (String)descriptor.getFieldValue("targetObjectType");
-         if (logger.isEnabledFor(Logger.TRACE)) logger.trace("targetObjectType is: " + targetObjectType);
-         if (targetObjectType == null)
-         {
-            // Not defined, assume object reference
-            targetObjectType = OBJECT_RESOURCE_TYPE;
-         }
-
-         if (!isResourceTypeSupported(targetObjectType)) throw new MBeanException(new InvalidTargetObjectTypeException(targetObjectType));
-      }
-      return target;
-   }
-
-   public void load() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException
-   {
-      PersisterMBean persister = findPersister();
-      if (persister != null)
-      {
-         ModelMBeanInfo info = (ModelMBeanInfo)persister.load();
-         setModelMBeanInfo(info);
+      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);
       }
-   }
-
-   public void store() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException
-   {
-      PersisterMBean persister = findPersister();
-      if (persister != null)
-      {
-         // Take a clone to avoid synchronization problems
-         ModelMBeanInfo info = (ModelMBeanInfo)getMBeanInfo();
-         persister.store(info);
+    }
+
+    return returnValue;
+  }
+
+  private Object resolveTargetObject(Descriptor descriptor) throws MBeanException {
+    Logger logger = getLogger();
+    Object target = descriptor.getFieldValue("targetObject");
+    if (logger.isEnabledFor(Logger.TRACE))
+      logger.trace("targetObject is: " + target);
+    if (target == null) {
+      target = getManagedResource();
+    } else {
+      String targetObjectType = (String) descriptor.getFieldValue("targetObjectType");
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace("targetObjectType is: " + targetObjectType);
+      if (targetObjectType == null) {
+        // Not defined, assume object reference
+        targetObjectType = OBJECT_RESOURCE_TYPE;
       }
-   }
 
-   protected ClassLoaderRepository getClassLoaderRepository()
-   {
-      if (m_mbeanServer != null)
-         return m_mbeanServer.getClassLoaderRepository();
-      else
-         return null;
-   }
+      if (!isResourceTypeSupported(targetObjectType))
+        throw new MBeanException(new InvalidTargetObjectTypeException(targetObjectType));
+    }
+    return target;
+  }
+
+  public void load() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException {
+    PersisterMBean persister = findPersister();
+    if (persister != null) {
+      ModelMBeanInfo info = (ModelMBeanInfo) persister.load();
+      setModelMBeanInfo(info);
+    }
+  }
+
+  public void store() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException {
+    PersisterMBean persister = findPersister();
+    if (persister != null) {
+      // Take a clone to avoid synchronization problems
+      ModelMBeanInfo info = (ModelMBeanInfo) getMBeanInfo();
+      persister.store(info);
+    }
+  }
+
+  protected ClassLoaderRepository getClassLoaderRepository() {
+    if (m_mbeanServer != null)
+      return m_mbeanServer.getClassLoaderRepository();
+    else
+      return null;
+  }
 
 
-   private boolean shouldPersistNow(Descriptor attribute, Descriptor mbean, String lastUpdateField)
-   {
-      int persist = getPersistPolicy(attribute, mbean);
-      if (persist == PERSIST_NO_MORE_OFTEN_THAN)
-      {
-         Long period = getFieldTimeValue(attribute, mbean, "persistPeriod");
-         long now = System.currentTimeMillis();
-         Long lastUpdate = (Long)attribute.getFieldValue(lastUpdateField);
-         if (now - lastUpdate.longValue() < period.longValue())
-            return false;
-         else
-            return true;
-      }
-      else if (persist == PERSIST_NEVER)
-      {
-         return false;
-      }
-      else if (persist == PERSIST_ON_TIMER)
-      {
-         return false;
-      }
-      else if (persist == PERSIST_ON_UPDATE)
-      {
-         return true;
-      }
+  private boolean shouldPersistNow(Descriptor attribute, Descriptor mbean, String lastUpdateField) {
+    int persist = getPersistPolicy(attribute, mbean);
+    if (persist == PERSIST_NO_MORE_OFTEN_THAN) {
+      Long period = getFieldTimeValue(attribute, mbean, "persistPeriod");
+      long now = System.currentTimeMillis();
+      Long lastUpdate = (Long) attribute.getFieldValue(lastUpdateField);
+      if (now - lastUpdate.longValue() < period.longValue())
+        return false;
       else
-      {
-         throw new ImplementationException(LocalizedStrings.MX4JModelMBean_INVALID_PERSIST_VALUE.toLocalizedString());
-      }
-   }
-
-   private int getPersistPolicy(Descriptor descriptor, Descriptor mbean)
-   {
-      Logger logger = getLogger();
-
-      String persist = (String)descriptor.getFieldValue("persistPolicy");
-      if (persist == null && mbean != null) persist = (String)mbean.getFieldValue("persistPolicy");
-      if (persist == null)
-      {
-         if (logger.isEnabledFor(Logger.TRACE)) logger.trace("No persist policy defined, assuming Never");
-         return PERSIST_NEVER;
+        return true;
+    } else if (persist == PERSIST_NEVER) {
+      return false;
+    } else if (persist == PERSIST_ON_TIMER) {
+      return false;
+    } else if (persist == PERSIST_ON_UPDATE) {
+      return true;
+    } else {
+      throw new ImplementationException(
+          LocalizedStrings.MX4JModelMBean_INVALID_PERSIST_VALUE.toLocalizedString());
+    }
+  }
+
+  private int getPersistPolicy(Descriptor descriptor, Descriptor mbean) {
+    Logger logger = getLogger();
+
+    String persist = (String) descriptor.getFieldValue("persistPolicy");
+    if (persist == null && mbean != null)
+      persist = (String) mbean.getFieldValue("persistPolicy");
+    if (persist == null) {
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace("No persist policy defined, assuming Never");
+      return PERSIST_NEVER;
+    } else {
+      if (persist.equals("Never")) {
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Persist never");
+        return PERSIST_NEVER;
+      } else if (persist.equals("OnUpdate")) {
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Persist on update");
+        return PERSIST_ON_UPDATE;
+      } else if (persist.equals("OnTimer")) {
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Persist on update");
+        return PERSIST_ON_TIMER;
+      } else if (persist.equals("NoMoreOftenThan")) {
+        if (logger.isEnabledFor(Logger.TRACE)) {
+          Long period = getFieldTimeValue(descriptor, mbean, "persistPeriod");
+          logger.trace("Persist no more often than " + period);
+        }
+        return PERSIST_NO_MORE_OFTEN_THAN;
+      } else {
+        // Garbage, assuming Never
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Invalid persist policy, assuming persist never");
+        return PERSIST_NEVER;
       }
-      else
+    }
+  }
+
+  private int getStaleness(Descriptor attribute, Descriptor mbean, String lastUpdateField) {
+    Logger logger = getLogger();
+
+    Long currencyTimeLimit = getFieldTimeValue(attribute, mbean, "currencyTimeLimit");
+    if (currencyTimeLimit == null) {
+      // No time limit defined
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace("No currencyTimeLimit defined, assuming always stale");
+      return ALWAYS_STALE;
+    } else {
+      long ctl = currencyTimeLimit.longValue() * 1000;
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace("currencyTimeLimit is (ms): " + ctl);
+
+      if (ctl == 0) {
+        // Never stale
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Never stale");
+        return NEVER_STALE;
+      } else if (ctl < 0) // this should be == -1 but the other cases are in the air
       {
-         if (persist.equals("Never"))
-         {
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persist never");
-            return PERSIST_NEVER;
-         }
-         else if (persist.equals("OnUpdate"))
-         {
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persist on update");
-            return PERSIST_ON_UPDATE;
-         }
-         else if (persist.equals("OnTimer"))
-         {
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persist on update");
-            return PERSIST_ON_TIMER;
-         }
-         else if (persist.equals("NoMoreOftenThan"))
-         {
+        // Always stale
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Always stale");
+        return ALWAYS_STALE;
+      } else {
+        Long timestamp = (Long) attribute.getFieldValue(lastUpdateField);
+        long luts = 0;
+
+        if (timestamp != null)
+          luts = timestamp.longValue();
+        if (logger.isEnabledFor(Logger.DEBUG))
+          logger.debug(lastUpdateField + " is: " + luts);
+
+        long now = System.currentTimeMillis();
+        if (now < luts + ctl) {
+          // Seems to be not stale, but has been set at least once ?
+          if (timestamp == null) {
+            // Return stale to call it the first time
             if (logger.isEnabledFor(Logger.TRACE))
-            {
-               Long period = getFieldTimeValue(descriptor, mbean, "persistPeriod");
-               logger.trace("Persist no more often than " + period);
-            }
-            return PERSIST_NO_MORE_OFTEN_THAN;
-         }
-         else
-         {
-            // Garbage, assuming Never
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Invalid persist policy, assuming persist never");
-            return PERSIST_NEVER;
-         }
-      }
-   }
-
-   private int getStaleness(Descriptor attribute, Descriptor mbean, String lastUpdateField)
-   {
-      Logger logger = getLogger();
-
-      Long currencyTimeLimit = getFieldTimeValue(attribute, mbean, "currencyTimeLimit");
-      if (currencyTimeLimit == null)
-      {
-         // No time limit defined
-         if (logger.isEnabledFor(Logger.TRACE)) logger.trace("No currencyTimeLimit defined, assuming always stale");
-         return ALWAYS_STALE;
-      }
-      else
-      {
-         long ctl = currencyTimeLimit.longValue() * 1000;
-         if (logger.isEnabledFor(Logger.TRACE)) logger.trace("currencyTimeLimit is (ms): " + ctl);
-
-         if (ctl == 0)
-         {
-            // Never stale
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Never stale");
-            return NEVER_STALE;
-         }
-         else if (ctl < 0) // this should be == -1 but the other cases are in the air
-         {
-            // Always stale
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Always stale");
-            return ALWAYS_STALE;
-         }
-         else
-         {
-            Long timestamp = (Long)attribute.getFieldValue(lastUpdateField);
-            long luts = 0;
-
-            if (timestamp != null) luts = timestamp.longValue();
-            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug(lastUpdateField + " is: " + luts);
-
-            long now = System.currentTimeMillis();
-            if (now < luts + ctl)
-            {
-               // Seems to be not stale, but has been set at least once ?
-               if (timestamp == null)
-               {
-                  // Return stale to call it the first time
-                  if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Stale since was never set");
-                  return STALE;
-               }
-               else
-               {
-                  if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Not stale");
-                  return NOT_STALE;
-               }
-            }
-            else
-            {
-               // Stale
-               if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Stale");
-               return STALE;
-            }
-         }
-      }
-   }
-
-   private Long getFieldTimeValue(Descriptor descriptor, Descriptor mbean, String field)
-   {
-      Logger logger = getLogger();
-
-      Object value = descriptor.getFieldValue(field);
-      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Descriptor's " + field + " field: " + value);
-
-      if (value == null && mbean != null)
-      {
-         value = mbean.getFieldValue(field);
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean's " + field + " field: " + value);
-         if (value == null) return null;
-      }
-
-      if (value instanceof Number) return Long.valueOf(((Number)value).longValue());
-
-      if (value instanceof String)
-      {
-         try
-         {
-            long ctl = Long.parseLong((String)value);
-            return Long.valueOf(ctl);
-         }
-         catch (NumberFormatException x)
-         {
-            return Long.valueOf(0);
-         }
-      }
-      return Long.valueOf(0);
-   }
-
-   private Object invokeMethod(Object target, String methodName, Class[] params, Object[] args) throws MBeanException, ReflectionException
-   {
-      // First try on this instance, then on the target
-      Object realTarget = null;
-      Method method = null;
-      try
-      {
-         realTarget = this;
-         method = realTarget.getClass().getMethod(methodName, params);
-      }
-      catch (NoSuchMethodException x)
-      {
-         realTarget = target;
-      }
-
-      if (realTarget == null) throw new MBeanException(new ServiceNotFoundException(LocalizedStrings.MX4JModelMBean_COULD_NOT_FIND_TARGET.toLocalizedString()));
-
-      if (method == null)
-      {
-         try
-         {
-            method = realTarget.getClass().getMethod(methodName, params);
-         }
-         catch (NoSuchMethodException x)
-         {
-            throw new ReflectionException(x);
-         }
+              logger.trace("Stale since was never set");
+            return STALE;
+          } else {
+            if (logger.isEnabledFor(Logger.TRACE))
+              logger.trace("Not stale");
+            return NOT_STALE;
+          }
+        } else {
+          // Stale
+          if (logger.isEnabledFor(Logger.TRACE))
+            logger.trace("Stale");
+          return STALE;
+        }
       }
+    }
+  }
 
-      try
-      {
-         Object value = method.invoke(realTarget, args);
-         Logger logger = getLogger();
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Method invocation returned value: " + value);
-         return value;
-      }
-      catch (IllegalAccessException x)
-      {
-         throw new ReflectionException(x);
-      }
-      catch (IllegalArgumentException x)
-      {
-         throw new MBeanException(x);
-      }
-      catch (InvocationTargetException x)
-      {
-         Throwable t = x.getTargetException();
-         if (t instanceof Error)
-            throw new MBeanException(new RuntimeErrorException((Error)t));
-         else
-            throw new MBeanException((Exception)t);
-      }
-   }
+  private Long getFieldTimeValue(Descriptor descriptor, Descriptor mbean, String field) {
+    Logger logger = getLogger();
 
-   private Logger getModelMBeanLogger(String notificationType) throws MBeanException
-   {
-      // Get a copy to avoid synchronization
-      ModelMBeanInfo info = getModelMBeanInfo();
+    Object value = descriptor.getFieldValue(field);
+    if (logger.isEnabledFor(Logger.DEBUG))
+      logger.debug("Descriptor's " + field + " field: " + value);
 
-      // 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 (value == null && mbean != null) {
+      value = mbean.getFieldValue(field);
+      if (logger.isEnabledFor(Logger.DEBUG))
+        logger.debug("MBean's " + field + " field: " + value);
+      if (value == null)
+        return null;
+    }
+
+    if (value instanceof Number)
+      return Long.valueOf(((Number) value).longValue());
+
+    if (value instanceof String) {
+      try {
+        long ctl = Long.parseLong((String) value);
+        return Long.valueOf(ctl);
+      } catch (NumberFormatException x) {
+        return Long.valueOf(0);
       }
-
-      if (modelMBeanLogger == null)
-      {
-         descriptor = info.getMBeanDescriptor();
-         modelMBeanLogger = findLogger(descriptor);
-         if (modelMBeanLogger != null) return modelMBeanLogger;
+    }
+    return Long.valueOf(0);
+  }
+
+  private Object invokeMethod(Object target, String methodName, Class[] params, Object[] args)
+      throws MBeanException, ReflectionException {
+    // First try on this instance, then on the target
+    Object realTarget = null;
+    Method method = null;
+    try {
+      realTarget = this;
+      method = realTarget.getClass().getMethod(methodName, params);
+    } catch (NoSuchMethodException x) {
+      realTarget = target;
+    }
+
+    if (realTarget == null)
+      throw new MBeanException(new ServiceNotFoundException(
+          LocalizedStrings.MX4JModelMBean_COULD_NOT_FIND_TARGET.toLocalizedString()));
+
+    if (method == null) {
+      try {
+        method = realTarget.getClass().getMethod(methodName, params);
+      } catch (NoSuchMethodException x) {
+        throw new ReflectionException(x);
       }
+    }
 
-      return null;
-   }
-
-   private Logger findLogger(Descriptor descriptor)
-   {
+    try {
+      Object value = method.invoke(realTarget, args);
       Logger logger = getLogger();
-
-      if (descriptor == null)
-      {
-         if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Can't find MBean logger, descriptor is null");
-         return null;
-      }
-
-      String log = (String)descriptor.getFieldValue("log");
-      String location = (String)descriptor.getFieldValue("logFile");
-
-      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Log fields: log=" + log + ", file=" + location);
-
-      if (log == null || !Boolean.valueOf(log).booleanValue())
-      {
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Logging is not supported by this ModelMBean");
-         return null;
-      }
-      // Logger is supported, where log to ?
-      if (location == null)
-      {
-         // As an extension, see if the field logMBean has been defined
-         location = (String)descriptor.getFieldValue("logMBean");
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Log fields: mbean=" + location);
-
-         if (location == null)
-         {
-            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Logging is not supported by this ModelMBean");
-            return null;
-         }
-
-         // It seems that the user wants to delegate a registered mbean to log
-         try
-         {
-            ObjectName objectName = new ObjectName(location);
-            MBeanServer server = getMBeanServer();
-            if (server == null) throw new MBeanException(new IllegalStateException(LocalizedStrings.MX4JModelMBean_MX4JMODELMBEAN_IS_NOT_REGISTERED.toLocalizedString()));
-            if (server.isRegistered(objectName))
-            {
-               MBeanLogger l = new MBeanLogger(server, objectName);
-               if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBean log supported by delegating to this MBean: " + objectName);
-               return l;
-            }
-
-            return null;
-         }
-         catch (MalformedObjectNameException x)
-         {
-            // Ah, was not a correct object name
-            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Specified logMBean field does not contain a valid ObjectName: " + location);
-            return null;
-         }
-         catch (MBeanException x)
-         {
-            if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("logMBean field does not specify an MBean that supports logging delegation", x);
-            return null;
-         }
-      }
+      if (logger.isEnabledFor(Logger.DEBUG))
+        logger.debug("Method invocation returned value: " + value);
+      return value;
+    } catch (IllegalAccessException x) {
+      throw new ReflectionException(x);
+    } catch (IllegalArgumentException x) {
+      throw new MBeanException(x);
+    } catch (InvocationTargetException x) {
+      Throwable t = x.getTargetException();
+      if (t instanceof Error)
+        throw new MBeanException(new RuntimeErrorException((Error) t));
       else
-      {
-         // User decided to log to a file
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBean log supported on file system");
-         return new FileLogger(location);
-      }
-   }
-
-   private NotificationBroadcasterSupport getAttributeChangeBroadcaster()
-   {
-      return m_attributeChangeBroadcaster;
-   }
-
-   private MBeanServer getMBeanServer()
-   {
-      return m_mbeanServer;
-   }
-
-   private ModelMBeanInfo getModelMBeanInfo()
-   {
-      // No cloning performed
-      return m_modelMBeanInfo;
-   }
-
-   private PersisterMBean findPersister() throws MBeanException, InstanceNotFoundException
-   {
-      Logger logger = getLogger();
+        throw new MBeanException((Exception) t);
+    }
+  }
+
+  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;
+  }
+
+  private Logger findLogger(Descriptor descriptor) {
+    Logger logger = getLogger();
+
+    if (descriptor == null) {
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace("Can't find MBean logger, descriptor is null");
+      return null;
+    }
 
-      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 log = (String) descriptor.getFieldValue("log");
+    String location = (String) descriptor.getFieldValue("logFile");
 
-      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 (logger.isEnabledFor(Logger.DEBUG))
+      logger.debug("Log fields: log=" + log + ", file=" + location);
 
-      if (mbeanName == null && name == null)
-      {
-         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Persistence is not supported by this ModelMBean");
-         return null;
-      }
+    if (log == null || !Boolean.valueOf(log).booleanValue()) {
+      if (logger.isEnabledFor(Logger.DEBUG))
+        logger.debug("Logging is not supported by this ModelMBean");
+      return null;
+    }
+    // Logger is supported, where log to ?
+    if (location == null) {
+      // As an extension, see if the field logMBean has been defined
+      location = (String) descriptor.getFieldValue("logMBean");
+      if (logger.isEnabledFor(Logger.DEBUG))
+        logger.debug("Log fields: mbean=" + location);
 
-      // 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;
+      if (location == null) {
+        if (logger.isEnabledFor(Logger.TRACE))
+          logger.trace("Logging is not supported by this ModelMBean");
+        return null;
       }
-   }
 
-   private Class loadClassWithContextClassLoader(String name)
-   {
-      try
-      {
-         return Utils.loadClass(Thread.currentThread().getContextClassLoader(), name);
-      }
-      catch (ClassNotFoundException x)
-      {
-         Logger logger = getLogger();
-         if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Cannot find attribute's declared return class", x);
-         return null;
+      // It seems that the user wants to delegate a registered mbean to log
+      try {
+        ObjectName objectName = new ObjectName(location);
+        MBeanServer server = getMBeanServer();
+        if (server == null)
+          throw new MBeanException(new IllegalStateException(
+              LocalizedStrings.MX4JModelMBean_MX4JMODELMBEAN_IS_NOT_REGISTERED
+                  .toLocalizedString()));
+        if (server.isRegistered(objectName)) {
+          MBeanLogger l = new MBeanLogger(server, objectName);
+          if (logger.isEnabledFor(Logger.DEBUG))
+            logger.debug("ModelMBean log supported by delegating to this MBean: " + objectName);
+          return l;
+        }
+
+        return null;
+      } catch (MalformedObjectNameException x) {
+ 

<TRUNCATED>