Skip to content

Commit

Permalink
Merge cc4cb52 into 166b435
Browse files Browse the repository at this point in the history
  • Loading branch information
robinmeiss committed Oct 30, 2014
2 parents 166b435 + cc4cb52 commit 840c669
Show file tree
Hide file tree
Showing 4 changed files with 362 additions and 1 deletion.
30 changes: 30 additions & 0 deletions owner/src/main/java/org/aeonbits/owner/JMXBean.java
@@ -0,0 +1,30 @@
package org.aeonbits.owner;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.ReflectionException;

public interface JMXBean extends Accessible, DynamicMBean {

public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException,
ReflectionException;

public void setAttribute(Attribute attribute)
throws AttributeNotFoundException, InvalidAttributeValueException,
MBeanException, ReflectionException;

public AttributeList getAttributes(String[] attributes);

public AttributeList setAttributes(AttributeList attributes);

public Object invoke(String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException;

public MBeanInfo getMBeanInfo();

}
109 changes: 108 additions & 1 deletion owner/src/main/java/org/aeonbits/owner/PropertiesManager.java
Expand Up @@ -47,6 +47,18 @@
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ReflectionException;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.Collections.synchronizedList;
Expand All @@ -63,7 +75,7 @@
*
* @author Luigi R. Viggiano
*/
class PropertiesManager implements Reloadable, Accessible, Mutable {
class PropertiesManager implements Reloadable, Accessible, Mutable, JMXBean {

private final Class<? extends Config> clazz;
private final Map<?, ?>[] imports;
Expand Down Expand Up @@ -581,4 +593,99 @@ public int hashCode() {
}
}

@Delegate
public Object getAttribute(String attribute)
throws AttributeNotFoundException, MBeanException,
ReflectionException {
return getProperty(attribute);
}

@Delegate
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException, InvalidAttributeValueException,
MBeanException, ReflectionException {
setProperty(attribute.getName(), (String) attribute.getValue());
}

@Delegate
public AttributeList getAttributes(String[] attributes) {
List<Attribute> attrList = new LinkedList<Attribute>();
for (String propertyName : attributes) {
attrList.add(new Attribute(propertyName, properties.getProperty(propertyName)));
}
return new AttributeList(attrList);
}

@Delegate
public AttributeList setAttributes(AttributeList attributes) {
for (Attribute attr : attributes.asList()) {
properties.setProperty(attr.getName(), (String) attr.getValue());
}
return attributes;
}

@Delegate
public Object invoke(String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException {
if (actionName.equals("getProperty") &&
params != null && params.length == 1) {
return properties.getProperty((String) params[0]);
}
else if (actionName.equals("setProperty") &&
params != null && params.length == 2) {
properties.setProperty((String) params[0], (String) params[1]);
return null;
}
else if (actionName.equals("reload") &&
(params == null || params.length == 0) &&
(params == null || params.length == 0)) {
reload();
return null;
}
throw new ReflectionException(new NoSuchMethodException(actionName));
}

@Delegate
public MBeanInfo getMBeanInfo() {
List<MBeanAttributeInfo> attrList = new ArrayList<MBeanAttributeInfo>();
Enumeration<?> propertyEnum = properties.propertyNames();
while (propertyEnum.hasMoreElements()) {
String key = (String) propertyEnum.nextElement();
attrList.add(new MBeanAttributeInfo(key, "String", key, true, true,
false));
}
MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[] {};
attributes = attrList.toArray(attributes);

MBeanParameterInfo paramsKey = new MBeanParameterInfo("Propertykey",
"java.lang.String", "Key of the property");
MBeanParameterInfo paramsValue = new MBeanParameterInfo(
"Propertyvalue", "java.lang.String", "Value of the property");


MBeanOperationInfo getOperation = new MBeanOperationInfo("getProperty", "getProperties",
new MBeanParameterInfo[] { paramsKey }, "java.lang.String", MBeanOperationInfo.INFO);
MBeanOperationInfo setOperation = new MBeanOperationInfo("setProperty", "setProperties",
new MBeanParameterInfo[] { paramsKey, paramsValue },"void", MBeanOperationInfo.ACTION);
MBeanOperationInfo reloadOperation = new MBeanOperationInfo("reload", "Reload properties",
null, // no parameters
"void", MBeanOperationInfo.ACTION);

List<MBeanOperationInfo> operationsList = new ArrayList<MBeanOperationInfo>();
if(Accessible.class.isAssignableFrom(clazz)){
operationsList.add(getOperation);
}
if(Mutable.class.isAssignableFrom(clazz)){
operationsList.add(setOperation);
}
if(Reloadable.class.isAssignableFrom(clazz)){
operationsList.add(reloadOperation);
}
MBeanOperationInfo[] operations = new MBeanOperationInfo[operationsList.size()];
operations = operationsList.toArray(operations);

return new MBeanInfo(clazz.getName(), "Owner MBean", attributes, null,
operations, null);
}

}
33 changes: 33 additions & 0 deletions owner/src/test/java/org/aeonbits/owner/jmx/InvalidJMXBeanTest.java
@@ -0,0 +1,33 @@
package org.aeonbits.owner.jmx;

import java.lang.management.ManagementFactory;
import java.util.Properties;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

import org.aeonbits.owner.Config;
import org.aeonbits.owner.ConfigFactory;
import org.junit.Test;

public class InvalidJMXBeanTest {

private static interface ConfigNoJMX extends Config {
@DefaultValue("1")
int number();
}

@Test(expected=NotCompliantMBeanException.class)
public void testConfigWithoutJMXInterface() throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException{
Properties props = new Properties();
ConfigNoJMX config = ConfigFactory.create(ConfigNoJMX.class, props);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mbeanName = new ObjectName("org.aeonbits.owner.jmx:type=testConfigWithoutJMXInterface,id=ConfigNoJMX");
mbs.registerMBean(config, mbeanName);
}

}
191 changes: 191 additions & 0 deletions owner/src/test/java/org/aeonbits/owner/jmx/JMXMBeanTest.java
@@ -0,0 +1,191 @@
package org.aeonbits.owner.jmx;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;

import java.lang.management.ManagementFactory;
import java.util.Properties;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.ReflectionException;

import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.JMXBean;
import org.aeonbits.owner.Mutable;
import org.aeonbits.owner.Reloadable;
import org.junit.Test;

public class JMXMBeanTest {

private static interface JMXConfigMutableReloadable extends JMXBean, Mutable, Reloadable {
@DefaultValue("8080")
int port();

@DefaultValue("http://localhost")
String hostname();

@DefaultValue("42")
int maxThreads();
}

private static interface JMXConfigOnlyAccessible extends JMXBean {
@DefaultValue("1")
int number();
}

private static interface JMXConfigMutableNoReload extends JMXBean, Mutable {
@DefaultValue("1")
int number();
}

/**
*
* Simple test case for JMX accessible mbeans.
*
* Registers a config of JMXConfigMutableReloadable.class
* under object name org.aeonbits.owner.jmx:type=testBeanHandling,id=JMXConfigMutableReloadable and
* tests getAttribute(s) methods and invokes setProperty actions with it.
*
* @throws MalformedObjectNameException
* @throws AttributeNotFoundException
* @throws InstanceNotFoundException
* @throws MBeanException
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws NotCompliantMBeanException
*/
@Test
public void testBeanHandling() throws MalformedObjectNameException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, InstanceAlreadyExistsException, NotCompliantMBeanException {
Properties props = new Properties();
JMXConfigMutableReloadable config = ConfigFactory.create(JMXConfigMutableReloadable.class, props);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mbeanName = new ObjectName(
"org.aeonbits.owner.jmx:type=testBeanHandling,id=JMXConfigMutableReloadable");
mbs.registerMBean(config, mbeanName);

assertEquals("8080", mbs.getAttribute(mbeanName, "port"));
AttributeList attrList = new AttributeList();
attrList.add(new Attribute("port", "8080"));
attrList.add(new Attribute("hostname", "http://localhost"));
attrList.add(new Attribute("maxThreads", "42"));
assertEquals(attrList, mbs.getAttributes(mbeanName, new String[] { "port", "hostname", "maxThreads"}));

mbs.invoke(mbeanName, "setProperty", new String[] { "port", "7878" },
null);
assertEquals("7878", mbs.getAttribute(mbeanName, "port"));
assertEquals("7878", mbs.invoke(mbeanName, "getProperty", new String[] { "port"}, null));

mbs.invoke(mbeanName, "reload", null, null);
assertEquals(attrList, mbs.getAttributes(mbeanName, new String[] { "port", "hostname", "maxThreads"}));
}

/**
*
* Test case for registering multiple mbeans with same configuration object.
*
* @throws MalformedObjectNameException
* @throws AttributeNotFoundException
* @throws InstanceNotFoundException
* @throws MBeanException
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws NotCompliantMBeanException
* @throws InvalidAttributeValueException
*/
@Test
public void testMultipleBeanHandling() throws MalformedObjectNameException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, InstanceAlreadyExistsException, NotCompliantMBeanException, InvalidAttributeValueException {
Properties props = new Properties();

JMXConfigMutableReloadable config = ConfigFactory.create(JMXConfigMutableReloadable.class, props);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mbeanName1 = new ObjectName(
"org.aeonbits.owner.jmx:type=testMultipleBeanHandling,id=JMXConfigMutableReloadable");
ObjectName mbeanName2 = new ObjectName(
"org.aeonbits.owner.jmx:type=testMultipleBeanHandling2,id=JMXConfigMutableReloadable");
mbs.registerMBean(config, mbeanName1);
mbs.registerMBean(config, mbeanName2);
mbs.setAttribute(mbeanName1, new Attribute("port", "7878"));
assertEquals("7878", mbs.getAttribute(mbeanName1, "port"));
assertEquals(mbs.getAttribute(mbeanName2, "port"), mbs.getAttribute(mbeanName1, "port"));
}

/**
* Test of MBeanInfo description of immutable (and not reloadable) config instance
*
* @throws MalformedObjectNameException
* @throws AttributeNotFoundException
* @throws InstanceNotFoundException
* @throws MBeanException
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws NotCompliantMBeanException
* @throws IntrospectionException
*/
@Test
public void testBeanNotMutableAndReloadable() throws MalformedObjectNameException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, InstanceAlreadyExistsException, NotCompliantMBeanException, IntrospectionException {
Properties props = new Properties();
JMXConfigOnlyAccessible config = ConfigFactory.create(JMXConfigOnlyAccessible.class, props);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mbeanName = new ObjectName(
"org.aeonbits.owner.jmx:type=testBeanNotReloadable,id=JMXConfigOnlyAccessible");
mbs.registerMBean(config, mbeanName);
MBeanInfo info = mbs.getMBeanInfo(mbeanName);

MBeanOperationInfo[] operationsInfo = new MBeanOperationInfo[]{
new MBeanOperationInfo("getProperty", "getProperties",
new MBeanParameterInfo[] { new MBeanParameterInfo("Propertykey", "java.lang.String", "Key of the property") },
"java.lang.String", MBeanOperationInfo.INFO)
};
assertArrayEquals(operationsInfo, info.getOperations());
}

/**
* Test of MBeanInfo description of not reloadable config instance
*
* @throws MalformedObjectNameException
* @throws AttributeNotFoundException
* @throws InstanceNotFoundException
* @throws MBeanException
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws NotCompliantMBeanException
* @throws IntrospectionException
*/
@Test
public void testBeanNotReloadable() throws MalformedObjectNameException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, InstanceAlreadyExistsException, NotCompliantMBeanException, IntrospectionException {
Properties props = new Properties();
JMXConfigMutableNoReload config = ConfigFactory.create(JMXConfigMutableNoReload.class, props);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mbeanName = new ObjectName(
"org.aeonbits.owner.jmx:type=testBeanNotReloadable,id=JMXConfigMutableNoReload");
mbs.registerMBean(config, mbeanName);
MBeanInfo info = mbs.getMBeanInfo(mbeanName);

MBeanOperationInfo[] operationsInfo = new MBeanOperationInfo[]{
new MBeanOperationInfo("getProperty", "getProperties",
new MBeanParameterInfo[] { new MBeanParameterInfo("Propertykey", "java.lang.String", "Key of the property") },
"java.lang.String", MBeanOperationInfo.INFO),
new MBeanOperationInfo("setProperty", "setProperties",
new MBeanParameterInfo[] {
new MBeanParameterInfo("Propertykey", "java.lang.String", "Key of the property"),
new MBeanParameterInfo("Propertyvalue", "java.lang.String", "Value of the property")
},
"void", MBeanOperationInfo.ACTION)
};
assertArrayEquals(operationsInfo, info.getOperations());
}
}

0 comments on commit 840c669

Please sign in to comment.