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;