You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/10/27 17:19:10 UTC

[18/50] [abbrv] incubator-geode git commit: GEODE-288: move admin packages to internal

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JModelMBean.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JModelMBean.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JModelMBean.java
new file mode 100755
index 0000000..2facc17
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JModelMBean.java
@@ -0,0 +1,1232 @@
+/*
+ * 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.
+ */
+
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.management.Attribute;
+import javax.management.AttributeChangeNotification;
+import javax.management.AttributeChangeNotificationFilter;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.Descriptor;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeErrorException;
+import javax.management.RuntimeOperationsException;
+import javax.management.ServiceNotFoundException;
+import javax.management.loading.ClassLoaderRepository;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+import javax.management.modelmbean.ModelMBean;
+import javax.management.modelmbean.ModelMBeanAttributeInfo;
+import javax.management.modelmbean.ModelMBeanInfo;
+import javax.management.modelmbean.ModelMBeanOperationInfo;
+
+import mx4j.ImplementationException;
+import mx4j.log.FileLogger;
+import mx4j.log.Log;
+import mx4j.log.Logger;
+import mx4j.log.MBeanLogger;
+import mx4j.persist.FilePersister;
+import mx4j.persist.MBeanPersister;
+import mx4j.persist.PersisterMBean;
+import mx4j.util.Utils;
+
+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();
+    } 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"));
+      }
+    }
+
+    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"));
+      }
+    }
+
+    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 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 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
+      }
+    }
+    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);
+      }
+
+      // 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 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;
+  }
+
+  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);
+    }
+  }
+
+  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;
+    } 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;
+      }
+    }
+  }
+
+  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);
+      }
+    }
+
+    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 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;
+    }
+
+    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;
+      }
+    } 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();
+
+    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;
+    }
+  }
+
+  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;
+    }
+  }
+
+  private void checkAssignability(Class parameter, Class declared) throws MBeanException {
+    Logger logger = getLogger();
+
+    if (logger.isEnabledFor(Logger.DEBUG)) {
+      logger.debug("The class of the parameter is: " + parameter);
+      if (parameter != null)
+        logger.debug("The classloder of the parameter's class is: " + parameter.getClassLoader());
+      logger.debug("The class declared as type of the attribute is: " + declared);
+      if (declared != null)
+        logger.debug(
+            "The classloader of the declared parameter's class is: " + declared.getClassLoader());
+    }
+
+    boolean assignable = false;
+
+    if (declared == null || parameter == null)
+      assignable = false;
+    else if (declared == boolean.class && parameter == Boolean.class)
+      assignable = true;
+    else if (declared == byte.class && parameter == Byte.class)
+      assignable = true;
+    else if (declared == char.class && parameter == Character.class)
+      assignable = true;
+    else if (declared == short.class && parameter == Short.class)
+      assignable = true;
+    else if (declared == int.class && parameter == Integer.class)
+      assignable = true;
+    else if (declared == long.class && parameter == Long.class)
+      assignable = true;
+    else if (declared == float.class && parameter == Float.class)
+      assignable = true;
+    else if (declared == double.class && parameter == Double.class)
+      assignable = true;
+    else
+      assignable = declared.isAssignableFrom(parameter);
+
+    if (!assignable) {
+      if (logger.isEnabledFor(Logger.TRACE))
+        logger.trace(
+            "Parameter value's class and attribute's declared return class are not assignable");
+      throw new MBeanException(new InvalidAttributeValueException(
+          LocalizedStrings.MX4JModelMBean_RETURNED_TYPE_AND_DECLARED_TYPE_ARE_NOT_ASSIGNABLE
+              .toLocalizedString()));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JServerSocketFactory.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JServerSocketFactory.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JServerSocketFactory.java
new file mode 100644
index 0000000..cf9df4a
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MX4JServerSocketFactory.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import org.apache.geode.internal.admin.api.DistributedSystemConfig;
+import org.apache.geode.internal.admin.api.impl.InetAddressUtil;
+import org.apache.geode.distributed.internal.DistributionConfig;
+import org.apache.geode.internal.net.SocketCreator;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.net.SocketCreatorFactory;
+
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.Properties;
+
+/**
+ * Creates <code>ServerSockets</code> for JMX adaptors.
+ * <p>
+ * The interface {@link mx4j.tools.adaptor.AdaptorServerSocketFactory} is implemented in order to
+ * support securing of {@link mx4j.tools.adaptor.http.HttpAdaptor}.
+ * <p>
+ * The interface {@link java.rmi.server.RMIServerSocketFactory} is implemented to support the
+ * securing of {@link javax.management.remote.JMXConnectorServer}. See
+ * {@link javax.management.remote.rmi.RMIConnectorServer} for the actual subclass that is used for
+ * the JMX RMI adaptor.
+ * <p>
+ * Complete info on JSSE, including debugging, can be found at
+ * <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html">
+ * http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html</a>
+ *
+ * @since GemFire 3.5 (old name was SSLAdaptorServerSocketFactory)
+ */
+public class MX4JServerSocketFactory implements mx4j.tools.adaptor.AdaptorServerSocketFactory,
+    java.rmi.server.RMIServerSocketFactory {
+
+  private static final Logger logger = LogService.getLogger();
+
+  private static final int DEFAULT_BACKLOG = 50;
+
+  private final SocketCreator socketCreator;
+  private String bindAddress = DistributedSystemConfig.DEFAULT_BIND_ADDRESS;
+  private int backlog = DEFAULT_BACKLOG;
+
+  /**
+   * Constructs new instance of MX4JServerSocketFactory.
+   * 
+   * @param useSSL true if ssl is to be enabled
+   * @param needClientAuth true if client authentication is required
+   * @param protocols space-delimited list of ssl protocols to use
+   * @param ciphers space-delimited list of ssl ciphers to use
+   * @param gfsecurityProps vendor properties passed in through gfsecurity.properties
+   */
+  public MX4JServerSocketFactory(boolean useSSL, boolean needClientAuth, String protocols,
+      String ciphers, Properties gfsecurityProps) {
+    if (protocols == null || protocols.length() == 0) {
+      protocols = DistributionConfig.DEFAULT_SSL_PROTOCOLS;
+    }
+    if (ciphers == null || ciphers.length() == 0) {
+      ciphers = DistributionConfig.DEFAULT_SSL_CIPHERS;
+    }
+    this.socketCreator = SocketCreatorFactory.createNonDefaultInstance(useSSL, needClientAuth,
+        protocols, ciphers, gfsecurityProps);
+  }
+
+  /**
+   * Constructs new instance of MX4JServerSocketFactory.
+   * 
+   * @param useSSL true if ssl is to be enabled
+   * @param needClientAuth true if client authentication is required
+   * @param protocols space-delimited list of ssl protocols to use
+   * @param ciphers space-delimited list of ssl ciphers to use
+   * @param bindAddress host or address to bind to (bind-address)
+   * @param backlog how many connections are queued
+   * @param gfsecurityProps vendor properties passed in through gfsecurity.properties
+   */
+  public MX4JServerSocketFactory(boolean useSSL, boolean needClientAuth, String protocols,
+      String ciphers, String bindAddress, // optional for RMI impl
+      int backlog, // optional for RMI impl
+      Properties gfsecurityProps) {
+    this(useSSL, needClientAuth, protocols, ciphers, gfsecurityProps);
+    this.bindAddress = bindAddress;
+    this.backlog = backlog;
+  }
+
+  // -------------------------------------------------------------------------
+  // mx4j.tools.adaptor.AdaptorServerSocketFactory impl...
+  // -------------------------------------------------------------------------
+
+  public ServerSocket createServerSocket(int port, int backlog, String bindAddress)
+      throws IOException {
+    if ("".equals(bindAddress)) {
+      return socketCreator.createServerSocket(port, backlog);
+
+    } else {
+      return socketCreator.createServerSocket(port, backlog,
+          InetAddressUtil.toInetAddress(bindAddress));
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // java.rmi.server.RMIServerSocketFactory impl...
+  // -------------------------------------------------------------------------
+
+  public ServerSocket createServerSocket(int port) throws IOException {
+    ServerSocket sock = null;
+    if ("".equals(bindAddress)) {
+      sock = socketCreator.createServerSocket(port, this.backlog);
+    } else {
+      sock = socketCreator.createServerSocket(port, this.backlog,
+          InetAddressUtil.toInetAddress(this.bindAddress));
+    }
+
+    if (logger.isDebugEnabled()) {
+      logger.debug(
+          "MX4JServerSocketFactory RMIServerSocketFactory, INetAddress {}, LocalPort {}, LocalSocketAddress {}",
+          sock.getInetAddress(), sock.getLocalPort(), sock.getLocalSocketAddress());
+    }
+    return sock;
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MailManager.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MailManager.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MailManager.java
new file mode 100755
index 0000000..92e6f2d
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MailManager.java
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.SystemFailure;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.log4j.LocalizedMessage;
+
+/**
+ * Provides the ways to send emails to all the registered email id It also provides the way to
+ * add/remove email ids. Can be used to send email in case of any alerts raised / warning / failure
+ * in gemfire.
+ * 
+ * @since GemFire 5.1
+ */
+public class MailManager {
+
+  private static final Logger logger = LogService.getLogger();
+
+  public MailManager() {}
+
+  public MailManager(Properties mailProperties) {
+    setMailProperties(mailProperties);
+  }
+
+  public MailManager(File mailPropertiesFile) throws IOException {
+    Properties prop = new Properties();
+    FileInputStream fio = new FileInputStream(mailPropertiesFile);
+    try {
+      prop.load(fio);
+    } finally {
+      fio.close();
+    }
+    setMailProperties(prop);
+  }
+
+  public MailManager(String mailHost, String mailFrom) {
+    this.mailHost = mailHost;
+    this.mailFrom = mailFrom;
+  }
+
+  /**
+   * Send email to all the registered email id with given subject and message
+   */
+  public void sendEmail(String subject, String message) {
+    processEmail(new EmailData(subject, message));
+  }
+
+  /**
+   * Send Emails to all the registered email id
+   * 
+   * @param emailData Instance of EmailData
+   */
+  // Why a separate method & class EmailData needed???
+  private void processEmail(EmailData emailData) {
+    if (logger.isTraceEnabled()) {
+      logger.trace("Entered MailManager:processEmail");
+    }
+
+    if (mailHost == null || mailHost.length() == 0 || emailData == null
+        || mailToAddresses.length == 0) {
+      logger.error(LocalizedMessage
+          .create(LocalizedStrings.MailManager_REQUIRED_MAILSERVER_CONFIGURATION_NOT_SPECIFIED));
+      if (logger.isDebugEnabled()) {
+        logger.debug("Exited MailManager:processEmail: Not sending email as conditions not met");
+      }
+      return;
+    }
+
+    Session session = Session.getDefaultInstance(getMailHostConfiguration());
+    MimeMessage mimeMessage = new MimeMessage(session);
+    String subject = emailData.subject;
+    String message = emailData.message;
+    String mailToList = getMailToAddressesAsString();
+
+    try {
+      for (int i = 0; i < mailToAddresses.length; i++) {
+        mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(mailToAddresses[i]));
+      }
+
+      if (subject == null) {
+        subject = LocalizedStrings.MailManager_ALERT_MAIL_SUBJECT.toLocalizedString();
+      }
+      mimeMessage.setSubject(subject);
+
+      if (message == null) {
+        message = "";
+      }
+      mimeMessage.setText(message);
+
+      Transport.send(mimeMessage);
+      logger.info(
+          LocalizedMessage.create(LocalizedStrings.MailManager_EMAIL_ALERT_HAS_BEEN_SENT_0_1_2,
+              new Object[] {mailToList, subject, message}));
+    } catch (VirtualMachineError err) {
+      SystemFailure.initiateFailure(err);
+      // If this ever returns, rethrow the error. We're poisoned
+      // now, so don't let this thread continue.
+      throw err;
+    } catch (Throwable ex) {
+      // Whenever you catch Error or Throwable, you must also
+      // catch VirtualMachineError (see above). However, there is
+      // _still_ a possibility that you are dealing with a cascading
+      // error condition, so you also need to check to see if the JVM
+      // is still usable:
+      SystemFailure.checkFailure();
+      StringBuilder buf = new StringBuilder();
+      buf.append(LocalizedStrings.MailManager_AN_EXCEPTION_OCCURRED_WHILE_SENDING_EMAIL
+          .toLocalizedString());
+      buf.append(
+          LocalizedStrings.MailManager_UNABLE_TO_SEND_EMAIL_PLEASE_CHECK_YOUR_EMAIL_SETTINGS_AND_LOG_FILE
+              .toLocalizedString());
+      buf.append("\n\n").append(
+          LocalizedStrings.MailManager_EXCEPTION_MESSAGE_0.toLocalizedString(ex.getMessage()));
+      buf.append("\n\n").append(
+          LocalizedStrings.MailManager_FOLLOWING_EMAIL_WAS_NOT_DELIVERED.toLocalizedString());
+      buf.append("\n\t")
+          .append(LocalizedStrings.MailManager_MAIL_HOST_0.toLocalizedString(mailHost));
+      buf.append("\n\t").append(LocalizedStrings.MailManager_FROM_0.toLocalizedString(mailFrom));
+      buf.append("\n\t").append(LocalizedStrings.MailManager_TO_0.toLocalizedString(mailToList));
+      buf.append("\n\t").append(LocalizedStrings.MailManager_SUBJECT_0.toLocalizedString(subject));
+      buf.append("\n\t").append(LocalizedStrings.MailManager_CONTENT_0.toLocalizedString(message));
+
+      logger.error(buf.toString(), ex);
+    }
+    if (logger.isTraceEnabled()) {
+      logger.trace("Exited MailManager:processEmail");
+    }
+  }
+
+  /**
+   * Not yet implemented
+   */
+  public void close() {}
+
+  /**
+   * @return All the registered email id as string
+   */
+  private String getMailToAddressesAsString() {
+    StringBuffer mailToList = new StringBuffer();
+    for (int i = 0; i < mailToAddresses.length; i++) {
+      mailToList.append(mailToAddresses[i]);
+      mailToList.append(", ");
+    }
+    return mailToList.toString();
+  }
+
+  /**
+   * 
+   * @return Properties consisting mailHost and mailFrom property
+   */
+  private Properties getMailHostConfiguration() {
+    Properties result = new Properties();
+    if (mailHost == null) {
+      mailHost = "";
+    }
+    if (mailFrom == null) {
+      mailFrom = "";
+    }
+    result.setProperty("mail.host", mailHost);
+    result.put("mail.from", mailFrom);
+    return result;
+  }
+
+  /**
+   * 
+   * @param host mail host server name
+   */
+  public void setMailHost(String host) {
+    this.mailHost = host;
+  }
+
+  /**
+   * 
+   * @return mail host server name
+   */
+  public String getMailHost() {
+    return this.mailHost;
+  }
+
+  /**
+   * 
+   * @param fromAddress mailFrom email id
+   */
+  public void setMailFromAddress(String fromAddress) {
+    mailFrom = fromAddress;
+  }
+
+  /**
+   * 
+   * @return mailFrom email id
+   */
+  public String getMailFromAddress() {
+    return mailFrom;
+  }
+
+  /**
+   * add new mail id to ToList
+   */
+  public void addMailToAddress(String toAddress) {
+    mailToSet.add(toAddress);
+    mailToAddresses = getAllToAddresses();
+  }
+
+  /**
+   * remove given mail id from ToList
+   */
+  public void removeMailToAddress(String toAddress) {
+    mailToSet.remove(toAddress);
+    mailToAddresses = getAllToAddresses();
+  }
+
+  /**
+   * @return list all the registered email id
+   */
+  public String[] getAllToAddresses() {
+    return (String[]) mailToSet.toArray(new String[0]);
+  }
+
+  /**
+   * remove all the registered email ids from ToList
+   */
+  public void removeAllMailToAddresses() {
+    mailToSet.clear();
+    mailToAddresses = new String[0];
+  }
+
+  /**
+   * Set the mail properties, e.g mail host, mailFrom, MailTo etc
+   */
+  public void setMailProperties(Properties mailProperties) {
+    mailHost = mailProperties.getProperty(PROPERTY_MAIL_HOST);
+    mailFrom = mailProperties.getProperty(PROPERTY_MAIL_FROM);
+    String mailList = mailProperties.getProperty(PROPERTY_MAIL_TO_LIST, "");
+    String split[] = mailList.split(",");
+    removeAllMailToAddresses();
+    for (int i = 0; i < split.length; i++) {
+      addMailToAddress(split[i].trim());
+    }
+  }
+
+  @Override
+  public String toString() {
+    StringBuffer buffer = new StringBuffer(200);
+    buffer.append("[Mail Host: ");
+    buffer.append(getMailHost());
+    buffer.append("]");
+    buffer.append(" [Mail From: ");
+    buffer.append(getMailFromAddress());
+    buffer.append("]");
+    buffer.append(" [Mail To: ");
+    if (mailToAddresses.length > 0) {
+
+      for (int i = 0; i < mailToAddresses.length; i++) {
+        buffer.append(mailToAddresses[i]);
+        buffer.append(", ");
+      }
+      buffer.replace(buffer.length() - 2, buffer.length(), "");
+    } else {
+      buffer.append(" Undefined");
+    }
+    buffer.append("]");
+    return buffer.toString();
+  }
+
+  private HashSet mailToSet = new HashSet();
+
+  private String mailToAddresses[] = new String[0];
+
+  protected String mailHost;
+
+  protected String mailFrom;
+
+  public final static String PROPERTY_MAIL_HOST = "mail.host";
+
+  public final static String PROPERTY_MAIL_FROM = "mail.from";
+
+  public final static String PROPERTY_MAIL_TO_LIST = "mail.toList";
+
+  /**
+   * Incorporating subject and message of email
+   * 
+   * 
+   */
+  static private class EmailData {
+    String subject;
+
+    String message;
+
+    EmailData(String subject, String message) {
+      this.subject = subject;
+      this.message = message;
+    }
+  }
+
+  public static void main(String args[]) {
+    MailManager mailManager = new MailManager("mailsrv1.gemstone.com", "hkhanna@gemstone.com");
+    mailManager.sendEmail("Alert!", "Test");
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResource.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResource.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResource.java
new file mode 100755
index 0000000..99c9ed8
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResource.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+
+/**
+ * Represents a component or resource that is managed by a
+ * {@link javax.management.modelmbean.ModelMBean}.
+ *
+ * @since GemFire 3.5
+ *
+ */
+public interface ManagedResource {
+
+  /**
+   * The prefix of MBean names. Note: this is NOT used by Members, Stats, or any other MBean that
+   * has it's own domain.
+   *
+   * @see #getMBeanName
+   */
+  public static final String MBEAN_NAME_PREFIX = "GemFire:type=";
+
+  /**
+   * Returns the name of the ModelMBean that will manage this resource. They [some] are of the form
+   *
+   * <PRE>
+   * MBEAN_NAME_PREFIX + typeName + ",id=" + id
+   * </PRE>
+   *
+   * @see #MBEAN_NAME_PREFIX
+   */
+  public String getMBeanName();
+
+  /** Returns the ModelMBean that is configured to manage this resource */
+  public ModelMBean getModelMBean();
+
+  /** Sets the ModelMBean that is configured to manage this resource */
+  public void setModelMBean(ModelMBean modelMBean);
+
+  /**
+   * Returns the enumerated ManagedResourceType of this resource.
+   *
+   * @see ManagedResourceType
+   */
+  public ManagedResourceType getManagedResourceType();
+
+  /**
+   * Returns the JMX <code>ObjectName</code> of this managed resource.
+   *
+   * @see #getMBeanName
+   */
+  public ObjectName getObjectName();
+
+  /**
+   * Perform any cleanup necessary before stopping management of this resource.
+   */
+  public void cleanupResource();
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResourceType.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResourceType.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResourceType.java
new file mode 100755
index 0000000..48da3a0
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ManagedResourceType.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import org.apache.commons.lang.StringUtils;
+
+import org.apache.geode.internal.admin.api.AdminDistributedSystem;
+import org.apache.geode.internal.admin.api.CacheVm;
+import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig;
+import org.apache.geode.internal.admin.api.DistributionLocator;
+import org.apache.geode.internal.admin.api.GemFireHealth;
+import org.apache.geode.internal.admin.api.GemFireHealthConfig;
+import org.apache.geode.internal.admin.api.StatisticResource;
+import org.apache.geode.internal.admin.api.SystemMember;
+import org.apache.geode.internal.admin.api.SystemMemberCache;
+import org.apache.geode.internal.admin.api.SystemMemberCacheServer;
+import org.apache.geode.internal.admin.api.SystemMemberRegion;
+import org.apache.geode.internal.admin.api.jmx.Agent;
+
+/**
+ * Type-safe definition for ModelMBean managed resources. The class type ({@link #getClassTypeName})
+ * must match the fully qualified class name listed in the type descriptor in
+ * mbeans-descriptors.xml.
+ *
+ * @since GemFire 3.5
+ *
+ */
+public class ManagedResourceType implements java.io.Serializable {
+  private static final long serialVersionUID = 3752874768667480449L;
+
+  /** Agent managed resource type */
+  public static final ManagedResourceType AGENT = new ManagedResourceType("Agent", Agent.class);
+
+  /** DistributedSystem managed resource type */
+  public static final ManagedResourceType DISTRIBUTED_SYSTEM =
+      new ManagedResourceType("AdminDistributedSystem", AdminDistributedSystem.class);
+
+  /** SystemMember managed resource type */
+  public static final ManagedResourceType SYSTEM_MEMBER =
+      new ManagedResourceType("SystemMember", SystemMember.class);
+
+  /** SystemMemberCache managed resource type */
+  public static final ManagedResourceType SYSTEM_MEMBER_CACHE =
+      new ManagedResourceType("SystemMemberCache", SystemMemberCache.class);
+
+  /** SystemMemberCache managed resource type */
+  public static final ManagedResourceType SYSTEM_MEMBER_REGION =
+      new ManagedResourceType("SystemMemberRegion", SystemMemberRegion.class);
+
+  /** SystemMemberCacheServer managed resource type */
+  public static final ManagedResourceType SYSTEM_MEMBER_CACHE_SERVER =
+      new ManagedResourceType("SystemMemberCacheServer", SystemMemberCacheServer.class);
+
+  /** CacheVm managed resource type */
+  public static final ManagedResourceType CACHE_VM =
+      new ManagedResourceType("CacheVm", CacheVm.class);
+
+  /** StatisticResource managed resource type */
+  public static final ManagedResourceType STATISTIC_RESOURCE =
+      new ManagedResourceType("StatisticResource", StatisticResource.class);
+
+  public static final ManagedResourceType GEMFIRE_HEALTH =
+      new ManagedResourceType("GemFireHealth", GemFireHealth.class);
+
+  public static final ManagedResourceType DISTRIBUTED_SYSTEM_HEALTH_CONFIG =
+      new ManagedResourceType("DistributedSystemHealthConfig", DistributedSystemHealthConfig.class);
+
+  public static final ManagedResourceType GEMFIRE_HEALTH_CONFIG =
+      new ManagedResourceType("GemFireHealthConfig", GemFireHealthConfig.class);
+
+  public static final ManagedResourceType DISTRIBUTION_LOCATOR =
+      new ManagedResourceType("DistributionLocator", DistributionLocator.class);
+
+  //////////////////// Instance Fields ////////////////////
+
+  /** The display-friendly name of this managed resource type. */
+  private final transient String name;
+
+  /**
+   * The interface/class used to externally represent this type. Note: this must match the mbean
+   * type descriptor in mbeans-descriptors.xml.
+   */
+  private final transient Class clazz;
+
+  // The 4 declarations below are necessary for serialization
+  /** int used as ordinal to represent this Scope */
+  public final int ordinal = nextOrdinal++;
+
+  private static int nextOrdinal = 0;
+
+  private static final ManagedResourceType[] VALUES =
+      {AGENT, DISTRIBUTED_SYSTEM, SYSTEM_MEMBER, SYSTEM_MEMBER_CACHE, SYSTEM_MEMBER_REGION,
+          SYSTEM_MEMBER_CACHE_SERVER, CACHE_VM, STATISTIC_RESOURCE, GEMFIRE_HEALTH,
+          DISTRIBUTED_SYSTEM_HEALTH_CONFIG, GEMFIRE_HEALTH_CONFIG, DISTRIBUTION_LOCATOR};
+
+  private Object readResolve() throws java.io.ObjectStreamException {
+    return VALUES[ordinal]; // Canonicalize
+  }
+
+  /** Creates a new instance of ManagedResourceType. */
+  private ManagedResourceType(String name, Class clazz) {
+    this.name = name;
+    this.clazz = clazz;
+  }
+
+  /** Returns the ManagedResourceType represented by specified ordinal */
+  public static ManagedResourceType fromOrdinal(int ordinal) {
+    return VALUES[ordinal];
+  }
+
+  /** Returns the display-friendly name of this managed resource type */
+  public String getName() {
+    return this.name;
+  }
+
+  /** Returns the interface/class used to externally represent this type */
+  public Class getClassType() {
+    return this.clazz;
+  }
+
+  /**
+   * Returns the fully qualified name of the interface/class used to externally represent this type
+   */
+  public String getClassTypeName() {
+    return this.clazz.getName();
+  }
+
+  /** Returns true if this is <code>AGENT</code>. */
+  public boolean isAgent() {
+    return this.equals(AGENT);
+  }
+
+  /** Returns true if this is <code>DISTRIBUTED_SYSTEM</code>. */
+  public boolean isDistributedSystem() {
+    return this.equals(DISTRIBUTED_SYSTEM);
+  }
+
+  /** Returns true if this is <code>SYSTEM_MEMBER</code>. */
+  public boolean isSystemMember() {
+    return this.equals(SYSTEM_MEMBER);
+  }
+
+  /** Returns whether this is <code>STATISTIC_RESOURCE</code>. */
+  public boolean isStatisticResource() {
+    return this.equals(STATISTIC_RESOURCE);
+  }
+
+  /** Return whether this is <code>GEMFIRE_HEALTH</code>. */
+  public boolean isGemFireHealth() {
+    return this.equals(GEMFIRE_HEALTH);
+  }
+
+  /**
+   * Returns a string representation for this type.
+   */
+  @Override
+  public String toString() {
+    return this.name;
+  }
+
+  /**
+   * Indicates whether some other object is "equal to" this one.
+   *
+   * @param other the reference object with which to compare.
+   * @return true if this object is the same as the obj argument; false otherwise.
+   */
+  @Override
+  public boolean equals(Object other) {
+    if (other == this)
+      return true;
+    if (other == null)
+      return false;
+    if (!(other instanceof ManagedResourceType))
+      return false;
+    final ManagedResourceType that = (ManagedResourceType) other;
+
+    if (!StringUtils.equals(this.name, that.name))
+      return false;
+    if (this.clazz != that.clazz && !(this.clazz != null && this.clazz.equals(that.clazz)))
+      return false;
+
+    return true;
+  }
+
+  /**
+   * Returns a hash code for the object. This method is supported for the benefit of hashtables such
+   * as those provided by java.util.Hashtable.
+   *
+   * @return the integer 0 if description is null; otherwise a unique integer.
+   */
+  @Override
+  public int hashCode() {
+    int result = 17;
+    final int mult = 37;
+
+    result = mult * result + (this.name == null ? 0 : this.name.hashCode());
+    result = mult * result + (this.clazz == null ? 0 : this.clazz.hashCode());
+
+    return result;
+  }
+
+}
+