PATCH: Changes to load() and store() for RMMB

Roland Huss <[email protected]>
Newsgroups gmane.comp.java.mx4j.devel
Message-ID <[email protected]>
Hi,

Please find attachted a patch for letting RMMB store and load the current values of the 
managed bean's attributes (instead of storing the more or less static metadata from 
ModelMBeanInfo). The setAttribute() and getAttribute() of the RMMB are used to 
initialize/retrieve attribute values from the underlying managed bean.

The changes in detail:

* Changed 'store()' and 'load()' to not store the MBeanInfo but the attributes of the
   bean managed. This is done by using getAttribute() and setAttribute() to obtain the
   current attribute values

* Write-only attributes are cached in a member m_writeOnlyAttributes which are then stored
   during a store()

* Read-only attributes are neither stored nor loaded.

* 'store()' and 'load()' call the Persister with a java.util.Set object containing the
   Attributes.

* load() is called during RMMB.setManagedResource() to initialize a managed bean during
   setup.

* Added three unit-tests to RequireModelMBeanTest for verifying the behaviour described
   above

* Changed 'private findPersister()' to be protected. Needed here only for easier testing,
   but comes in handy if one inherits RMMB and want to provided a different strategy for
   locating a persister.

Frankly, I don't understand exactly what the spec means when talking about 'ModelMBean 
persistence'. Is it the MBean itself (which is rather static) or is this the attributes' 
state of the managed resources ? If the latter is the case (and that is what IMHO makes 
the most sense), then the patch might be valuable for you ....

ciao (and thanx for your great piece of software) ....
-- 
                                          ...roland huss
                                               consol.de
rmmb_store.patch (text/x-patch, 16.4 KB)
diff -r -x CVS -u -N mx4j/src/core/javax/management/modelmbean/RequiredModelMBean.java mx4j.new/src/core/javax/management/modelmbean/RequiredModelMBean.java
--- mx4j/src/core/javax/management/modelmbean/RequiredModelMBean.java	2005-05-14 10:43:29.000000000 +0200
+++ mx4j.new/src/core/javax/management/modelmbean/RequiredModelMBean.java	2005-08-03 17:12:05.000000000 +0200
@@ -12,6 +12,8 @@
 import java.lang.reflect.Method;
 import java.util.Date;
 import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
 import javax.management.Attribute;
 import javax.management.AttributeChangeNotification;
 import javax.management.AttributeChangeNotificationFilter;
@@ -48,6 +50,7 @@
 import mx4j.persist.FilePersister;
 import mx4j.persist.MBeanPersister;
 import mx4j.persist.PersisterMBean;
+import mx4j.persist.PersistAttribute;
 import mx4j.util.Utils;
 
 /**
@@ -93,6 +96,12 @@
    private ModelMBeanInfo m_modelMBeanInfo;
    private NotificationBroadcasterSupport m_generalBroadcaster = new NotificationBroadcasterSupport();
 
+   // cache for write-only attributes for persisting
+   private Set m_writeOnlyAttributes = new HashSet();
+
+   // internal flag used when loading the persisten state
+   private static ThreadLocal m_loadFlag = new ThreadLocal();
+
    public RequiredModelMBean() throws MBeanException, RuntimeOperationsException
    {
       m_modelMBeanInfo = DEFAULT_INFO;
@@ -164,6 +173,14 @@
       Logger logger = getLogger();
       if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Setting managed resource to be: " + resource);
       m_managedResource = resource;
+      try
+      {
+         load();
+      }
+      catch (InstanceNotFoundException e)
+      {
+         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("No ModelMBeanInfo stored");
+      }
    }
 
    private boolean isResourceTypeSupported(String resourceType)
@@ -511,6 +528,13 @@
       if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Attribute info is: " + attrInfo);
 
       if (!attrInfo.isWritable()) throw new AttributeNotFoundException("Attribute " + attrName + " is not writable");
+      if (!attrInfo.isReadable())
+      {
+          // Remember write-only properties for later persistent storage
+          PersistAttribute mbeanAttr = new PersistAttribute(attribute);
+          m_writeOnlyAttributes.remove(mbeanAttr);
+          m_writeOnlyAttributes.add(mbeanAttr);
+      }
 
       // This returns a clone of the mbean descriptor, we use it read only
       Descriptor mbeanDescriptor = info.getMBeanDescriptor();
@@ -781,22 +805,94 @@
 
    public void load() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException
    {
-      PersisterMBean persister = findPersister();
-      if (persister != null)
+      m_loadFlag.set(Boolean.TRUE);
+      try
       {
-         ModelMBeanInfo info = (ModelMBeanInfo)persister.load();
-         setModelMBeanInfo(info);
+         PersisterMBean persister = findPersister();
+         if (persister != null)
+         {
+            Set attributes = (Set) persister.load();
+            if (attributes == null)
+            {
+               throw new InstanceNotFoundException("Can not load MBean");
+            }
+            m_writeOnlyAttributes = new HashSet();
+            ModelMBeanInfo info = (ModelMBeanInfo)getMBeanInfo();
+            for (Iterator iterator = attributes.iterator(); iterator.hasNext();)
+            {
+               try
+               {
+                  PersistAttribute attribute = (PersistAttribute) iterator.next();
+                  ModelMBeanAttributeInfo attrInfo =
+                          info.getAttribute(attribute.getName());
+                  if (attrInfo != null && attrInfo.isWritable())
+                  {
+                     setAttribute(attribute.getAttribute());
+                  }
+               }
+               catch (AttributeNotFoundException e)
+               {
+                  throw new MBeanException(e);
+               }
+               catch (InvalidAttributeValueException e)
+               {
+                  throw new MBeanException(e);
+               }
+               catch (ReflectionException e) {
+                  throw new MBeanException(e);
+               }
+            }
+         }
+         Logger logger = getLogger();
+         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo successfully loaded");
+      }
+      finally
+      {
+         m_loadFlag.set(Boolean.FALSE);
       }
    }
 
+
    public void store() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException
    {
-      PersisterMBean persister = findPersister();
-      if (persister != null)
+      // Only if no already loading....
+      if ( m_loadFlag.get() == null || !((Boolean) m_loadFlag.get()).booleanValue())
       {
-         // Take a clone to avoid synchronization problems
-         ModelMBeanInfo info = (ModelMBeanInfo)getMBeanInfo();
-         persister.store(info);
+         PersisterMBean persister = findPersister();
+         if (persister != null)
+         {
+            // Take a clone to avoid synchronization problems
+            ModelMBeanInfo info = (ModelMBeanInfo)getMBeanInfo();
+            MBeanAttributeInfo attrInfos[] = info.getAttributes();
+            Set attributes = new HashSet();
+            for (int i = 0; i < attrInfos.length; i++)
+            {
+               MBeanAttributeInfo attributeInfo = attrInfos[i];
+               try
+               {
+                  // Only if readable:
+                  if (attributeInfo.isReadable())
+                  {
+                     String name = attributeInfo.getName();
+                     Object value = getAttribute(name);
+                     attributes.add(new PersistAttribute(name,value));
+                  }
+                  // Add write-only attributes:
+                  attributes.addAll(m_writeOnlyAttributes);
+               }
+               catch (AttributeNotFoundException e)
+               {
+                  throw new MBeanException(e);
+               }
+               catch (ReflectionException e)
+               {
+                  throw new MBeanException(e);
+               }
+            }
+            persister.store(attributes);
+         }
+         Logger logger = getLogger();
+         if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo successfully stored");
       }
    }
 
@@ -1141,7 +1237,7 @@
       return m_modelMBeanInfo;
    }
 
-   private PersisterMBean findPersister() throws MBeanException, InstanceNotFoundException
+   protected PersisterMBean findPersister() throws MBeanException, InstanceNotFoundException
    {
       Logger logger = getLogger();
 
diff -r -x CVS -u -N mx4j/src/core/mx4j/persist/FilePersister.java mx4j.new/src/core/mx4j/persist/FilePersister.java
--- mx4j/src/core/mx4j/persist/FilePersister.java	2004-11-13 17:38:44.000000000 +0100
+++ mx4j.new/src/core/mx4j/persist/FilePersister.java	2005-08-03 17:12:05.000000000 +0200
@@ -75,6 +75,11 @@
       ObjectInputStream clois = null;
       Object result = null;
 
+      if (!m_store.exists())
+      {
+         throw new InstanceNotFoundException("No file " + m_store + " exists");
+      }
+
       synchronized (this)
       {
          try
diff -r -x CVS -u -N mx4j/src/core/mx4j/persist/PersistAttribute.java mx4j.new/src/core/mx4j/persist/PersistAttribute.java
--- mx4j/src/core/mx4j/persist/PersistAttribute.java	1970-01-01 01:00:00.000000000 +0100
+++ mx4j.new/src/core/mx4j/persist/PersistAttribute.java	2005-08-03 17:20:47.000000000 +0200
@@ -0,0 +1,85 @@
+package mx4j.persist;
+
+import javax.management.Attribute;
+import java.io.Serializable;
+
+/**
+ * Helper class used for storing attributes. This extension to
+ * the plain Attribute is necessary in order to provide an own
+ * equals and hash method which is used for set semantics.
+ *
+ * @author roland
+ * @since 02.08.2005
+ */
+public class PersistAttribute implements Serializable {
+
+    private String name;
+    private Object value;
+
+    /**
+     * No-arg constructor needed for derserializing
+     */
+    public PersistAttribute() {  }
+
+    /**
+     * Create attributes
+     *
+     * @param pName name of attributes
+     * @param pValue value of attributes
+     */
+    public PersistAttribute(String pName, Object pValue) {
+        if (pName == null) {
+            throw new IllegalArgumentException("Name cannot be null");
+        }
+        name = pName;
+        value = pValue;
+    }
+
+    /**
+     * Copy constructor
+     * @param pAttribute attribute to store
+     */
+    public PersistAttribute(Attribute pAttribute) {
+        if (pAttribute == null) {
+            throw new IllegalArgumentException("Attribute cannot be null");
+        }
+        name = pAttribute.getName();
+        value = pAttribute.getValue();
+    }
+
+
+    public Attribute getAttribute() {
+        return new Attribute(name,value);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PersistAttribute)) return false;
+
+        final PersistAttribute mBeanAttribute = (PersistAttribute) o;
+
+        if (name != null ? !name.equals(mBeanAttribute.name) :
+                mBeanAttribute.name != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (name != null ? name.hashCode() : 0);
+    }
+
+    public String toString() {
+        return new StringBuffer("[PersistAttribute - ").
+                append("name: ").append(name).
+                append("value: ").append(value).
+                append("]").toString();
+    }
+}
diff -r -x CVS -u -N mx4j/src/test/test/javax/management/modelmbean/RequiredModelMBeanTest.java mx4j.new/src/test/test/javax/management/modelmbean/RequiredModelMBeanTest.java
--- mx4j/src/test/test/javax/management/modelmbean/RequiredModelMBeanTest.java	2005-02-13 17:36:16.000000000 +0100
+++ mx4j.new/src/test/test/javax/management/modelmbean/RequiredModelMBeanTest.java	2005-08-03 14:20:14.000000000 +0200
@@ -8,7 +8,7 @@
 
 package test.javax.management.modelmbean;
 
-import java.util.List;
+import java.util.*;
 import javax.management.*;
 import javax.management.modelmbean.*;
 
@@ -16,6 +16,8 @@
 import test.MutableBoolean;
 import test.MutableInteger;
 import test.javax.management.modelmbean.support.ModelMBeanTarget;
+import mx4j.persist.PersisterMBean;
+import mx4j.persist.PersistAttribute;
 
 /**
  * @version $Revision: 1.14 $
@@ -646,6 +648,158 @@
       assertEquals(3, notificationInfos.length);
    }
 
+   public void testStoreLoadReadWrite() throws Exception
+   {
+       Attribute attribute = new Attribute("MutableContent","42");
+       String attributeName = attribute.getName();
+       ObjectName name = new ObjectName(":type=test");
+       StoreLoadTestRMMB rmmb = createStoreLoadTestMBean(
+               name, attribute,true /* get */,true /* set */);
+
+       rmmb.verify(1,0,"42");
+       assertEquals("42",rmmb.getAttribute(attributeName));
+       Attribute attr = new Attribute(attributeName,"85");
+       m_server.setAttribute(name,attr);
+       rmmb.verify(1,1,"85");
+       assertEquals("85",m_server.getAttribute(name,attributeName));
+       rmmb.load();
+       assertEquals("85",m_server.getAttribute(name,attributeName));
+       rmmb.verify(2,1,"85");
+       m_server.setAttribute(name,new Attribute(attributeName,"16"));
+       rmmb.verify(2,2,"16");
+   }
+
+   public void testStoreLoadReadOnly() throws Exception
+   {
+       Attribute attribute = new Attribute("FixedContent","YUPPI");
+       String attributeName = attribute.getName();
+       ObjectName name = new ObjectName(":type=test");
+       StoreLoadTestRMMB rmmb = createStoreLoadTestMBean(
+               name, attribute,true /* get */,false /* set */);
+
+       rmmb.verify(1,0,"YUPPI");
+       assertEquals("FIXED_CONTENT",rmmb.getAttribute(attributeName));
+       try
+       {
+           Attribute attr = new Attribute(attributeName,"85");
+           m_server.setAttribute(name,attr);
+           fail();
+       }
+       catch (AttributeNotFoundException exp) {}
+       rmmb.verify(1,0,"YUPPI");
+   }
+
+   public void testStoreLoadWriteOnly() throws Exception
+   {
+       Attribute attribute = new Attribute("MutableContent","42");
+       String attributeName = attribute.getName();
+       ObjectName name = new ObjectName(":type=test");
+       StoreLoadTestRMMB rmmb = createStoreLoadTestMBean(
+               name, attribute,false /* get */,true /* set */);
+
+       rmmb.verify(1,0,"42");
+       try
+       {
+           rmmb.getAttribute(attributeName);
+           fail();
+       }
+       catch (AttributeNotFoundException exp) {}
+       Attribute attr = new Attribute(attributeName,"85");
+       m_server.setAttribute(name,attr);
+       rmmb.verify(1,1,"85");
+       rmmb.load();
+       rmmb.verify(2,1,"85");
+       m_server.setAttribute(name,new Attribute(attributeName,"16"));
+       rmmb.verify(2,2,"16");
+   }
+
+   // =======================================================================
+   private StoreLoadTestRMMB createStoreLoadTestMBean(ObjectName name,Attribute attribute, boolean get, boolean set) throws Exception
+   {
+       PersistAttribute mAttribute = new PersistAttribute(attribute);
+       StoreLoadTestRMMB mmb = new StoreLoadTestRMMB(mAttribute);
+
+       MutableInteger counter = new MutableInteger(0);
+       ModelMBeanTarget bean = new ModelMBeanTarget(counter);
+       ModelMBeanInfo info = getMBeanInfo(attribute.getName(),get,set);
+       mmb.setModelMBeanInfo(info);
+       mmb.setManagedResource(bean, "ObjectReference");
+       m_server.registerMBean(mmb, name);
+       return mmb;
+   }
+
+   // Info for various methods
+   private ModelMBeanInfoSupport getMBeanInfo(String attrName, boolean get, boolean set)
+   {
+       ArrayList names = new ArrayList(Arrays.asList(
+               new String[] {
+                   "name", "descriptorType",
+                   "value", "iterable","displayName",
+                   "persistPolicy","currencyTimeLimit","default"}));
+       ArrayList values = new ArrayList(Arrays.asList(
+               new Object[]{
+                   attrName, "attribute",
+                   null, "false", "",
+                   "OnUpdate","-1","default"
+               }));
+       if (get) {
+           names.add("getMethod");
+           values.add("get" + attrName);
+       }
+       if (set) {
+           names.add("setMethod");
+           values.add("set" + attrName);
+       }
+       DescriptorSupport attrDescr1 =
+               new DescriptorSupport(
+                       (String[]) names.toArray(new String[0]),
+                       values.toArray(new Object[0]));
+
+       ModelMBeanAttributeInfo attrInfo1 = new ModelMBeanAttributeInfo(attrName, String.class.getName(), "", get, set, false, attrDescr1);
+       ModelMBeanInfoSupport info = new ModelMBeanInfoSupport(ModelMBeanTarget.class.getName(), "", new ModelMBeanAttributeInfo[]{attrInfo1}, null, null, null);
+       return info;
+   }
+
+   class StoreLoadTestRMMB extends RequiredModelMBean
+   {
+       private int storeCalled;
+       private int loadCalled;
+       PersistAttribute attribute;
+
+       public StoreLoadTestRMMB(PersistAttribute attribute) throws MBeanException {
+           this.attribute = attribute;
+           storeCalled = 0;
+           loadCalled = 0;
+       }
+
+       protected PersisterMBean findPersister() throws MBeanException, InstanceNotFoundException {
+           return new PersisterMBean() {
+
+               public Object load() {
+                   Set ret = new HashSet();
+                   ret.add(attribute);
+                   loadCalled++;
+                   return ret;
+               }
+
+               public void store(Object data) {
+                   assertTrue(data instanceof Set);
+                   Set setData = (Set) data;
+                   assertEquals(setData.size(),1);
+                   assertTrue(setData.contains(attribute));
+                   attribute = (PersistAttribute) setData.iterator().next();
+                   storeCalled++;
+               }
+           };
+       }
+
+       public void verify(int loadExpected,int storeExpected,String value) {
+           assertEquals(loadExpected,loadCalled);
+           assertEquals(storeExpected,storeCalled);
+           assertEquals(value,attribute.getValue());
+       }
+   }
+
    public static class StoreTesterRMMB extends RequiredModelMBean
    {
       private MutableBoolean m_stored;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.