/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed 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 io.datakernel.jmx;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.jmx.helper.JmxStatsStub;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import javax.management.Attribute;
import javax.management.DynamicMBean;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import java.util.*;
import static io.datakernel.eventloop.FatalErrorHandlers.rethrowOnAnyError;
import static io.datakernel.jmx.MBeanSettings.defaultSettings;
import static io.datakernel.jmx.helper.Utils.nameToAttribute;
import static java.util.Arrays.asList;
import static org.junit.Assert.*;
public class JmxMBeansAttributesTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
public static final Eventloop eventloop = Eventloop.create();
@Test
public void retreivesProperMBeanInfo() throws Exception {
MBeanWithSimpleAttrsAndPojo mbeanOneSample = new MBeanWithSimpleAttrsAndPojo("data", new SamplePojo(5, 100));
DynamicMBean mbean = createDynamicMBeanFor(mbeanOneSample);
MBeanInfo mBeanInfo = mbean.getMBeanInfo();
MBeanAttributeInfo[] attributesInfoArr = mBeanInfo.getAttributes();
Map<String, MBeanAttributeInfo> nameToAttr = nameToAttribute(attributesInfoArr);
assertEquals(3, nameToAttr.size());
assertTrue(nameToAttr.containsKey("info"));
assertTrue(nameToAttr.get("info").isReadable());
assertTrue(nameToAttr.containsKey("details_count"));
assertTrue(nameToAttr.get("details_count").isReadable());
assertTrue(nameToAttr.containsKey("details_sum"));
assertTrue(nameToAttr.get("details_sum").isReadable());
}
@Test
public void retreivesProperAttributeValues() throws Exception {
MBeanWithSimpleAttrsAndPojo mbeanOneSample = new MBeanWithSimpleAttrsAndPojo("data", new SamplePojo(5, 100L));
DynamicMBean mbean = createDynamicMBeanFor(mbeanOneSample);
assertEquals("data", mbean.getAttribute("info"));
assertEquals(5, mbean.getAttribute("details_count"));
assertEquals(100L, mbean.getAttribute("details_sum"));
}
/*
* tests for aggregation
*/
@Test
public void ifAppropriateAttributesInDifferentMBeansHaveSameValueReturnsThatValue() throws Exception {
MBeanWithSingleIntAttr mbean_1 = new MBeanWithSingleIntAttr(1);
MBeanWithSingleIntAttr mbean_2 = new MBeanWithSingleIntAttr(1);
DynamicMBean mbean = createDynamicMBeanFor(mbean_1, mbean_2);
assertEquals(1, mbean.getAttribute("value"));
}
@Test
public void ifAppropriateAttributesInDifferentMBeansHaveDifferentValueReturnsNull() throws Exception {
MBeanWithSingleIntAttr mbean_1 = new MBeanWithSingleIntAttr(1);
MBeanWithSingleIntAttr mbean_2 = new MBeanWithSingleIntAttr(2);
DynamicMBean mbean = createDynamicMBeanFor(mbean_1, mbean_2);
assertEquals(null, mbean.getAttribute("value"));
}
@Test
public void aggregatesJmxStatsUsingTheirAggregationPolicy() throws Exception {
JmxStatsStub stats_1 = new JmxStatsStub();
stats_1.recordValue(100);
stats_1.recordValue(20);
JmxStatsStub stats_2 = new JmxStatsStub();
stats_2.recordValue(5);
MBeanWithJmxStats mBeanWithJmxStats_1 = new MBeanWithJmxStats(stats_1);
MBeanWithJmxStats mBeanWithJmxStats_2 = new MBeanWithJmxStats(stats_2);
DynamicMBean mbean = createDynamicMBeanFor(mBeanWithJmxStats_1, mBeanWithJmxStats_2);
assertEquals(3, mbean.getAttribute("jmxStatsStub_count"));
assertEquals(125L, mbean.getAttribute("jmxStatsStub_sum"));
}
@Test
public void concatenatesListAttributesFromDifferentMBeans() throws Exception {
MBeanWithListAttr mBeanWithListAttr_1 = new MBeanWithListAttr(asList("a", "b"));
MBeanWithListAttr mBeanWithListAttr_2 = new MBeanWithListAttr(new ArrayList<String>());
MBeanWithListAttr mBeanWithListAttr_3 = new MBeanWithListAttr(asList("w"));
DynamicMBean mbean = createDynamicMBeanFor(mBeanWithListAttr_1, mBeanWithListAttr_2, mBeanWithListAttr_3);
assertArrayEquals(new String[]{"a", "b", "w"}, (String[]) mbean.getAttribute("list"));
}
@Test
public void properlyAggregateMapsByKeyAccordingToTheirValueAggregationPolicy() throws Exception {
Map<String, Integer> map_1 = new HashMap<>();
map_1.put("a", 1);
map_1.put("b", 2);
map_1.put("c", 100);
Map<String, Integer> map_2 = new HashMap<>();
map_2.put("b", 2);
map_2.put("c", 200);
map_2.put("d", 5);
MBeanWithMap mBeanWithMap_1 = new MBeanWithMap(map_1);
MBeanWithMap mBeanWithMap_2 = new MBeanWithMap(map_2);
DynamicMBean mbean = createDynamicMBeanFor(mBeanWithMap_1, mBeanWithMap_2);
TabularData tabularData = (TabularData) mbean.getAttribute("nameToNumber");
assertEquals(4, tabularData.size());
CompositeData row_1 = tabularData.get(keyForTabularData("a"));
assertEquals(1, row_1.get("value"));
CompositeData row_2 = tabularData.get(keyForTabularData("b"));
assertEquals(2, row_2.get("value"));
CompositeData row_3 = tabularData.get(keyForTabularData("c"));
assertEquals(null, row_3.get("value"));
CompositeData row_4 = tabularData.get(keyForTabularData("d"));
assertEquals(5, row_4.get("value"));
}
// test empty names
@Test
public void handlesEmptyAttributeNamesProperly() throws Exception {
MBeanWithEmptyNames mBeanWithEmptyNames =
new MBeanWithEmptyNames(
new SamplePojo_L_1_1(10),
new SamplePojo_L_1_2(
new SamplePojo_L_2(25)
)
);
DynamicMBean mbean = createDynamicMBeanFor(mBeanWithEmptyNames);
MBeanAttributeInfo[] attributesInfoArr = mbean.getMBeanInfo().getAttributes();
Map<String, MBeanAttributeInfo> nameToAttr = nameToAttribute(attributesInfoArr);
assertEquals(2, nameToAttr.size());
assertTrue(nameToAttr.containsKey("group_count"));
assertTrue(nameToAttr.containsKey("group_total"));
assertEquals(10, mbean.getAttribute("group_count"));
assertEquals(25, mbean.getAttribute("group_total"));
}
// test empty leaf names
@Test
public void handlesAttributeWithEmptyLeafNamesProperly() throws Exception {
MBeanWithEmptyLeafName monitorable = new MBeanWithEmptyLeafName();
DynamicMBean mbean = createDynamicMBeanFor(monitorable);
MBeanAttributeInfo[] attributesInfoArr = mbean.getMBeanInfo().getAttributes();
Map<String, MBeanAttributeInfo> nameToAttr = nameToAttribute(attributesInfoArr);
assertEquals(1, nameToAttr.size());
assertTrue(nameToAttr.containsKey("pojo"));
assertEquals(10, mbean.getAttribute("pojo"));
}
// test setters
@Test
public void returnsInfoAboutWritableAttributesInMBeanInfo() throws Exception {
MBeanWithSettableAttributes settableMBean = new MBeanWithSettableAttributes(10, 20, "data");
DynamicMBean mbean = createDynamicMBeanFor(settableMBean);
MBeanInfo mBeanInfo = mbean.getMBeanInfo();
MBeanAttributeInfo[] attributesInfoArr = mBeanInfo.getAttributes();
Map<String, MBeanAttributeInfo> nameToAttr = nameToAttribute(attributesInfoArr);
assertEquals(3, nameToAttr.size());
assertTrue(nameToAttr.containsKey("notSettableInt"));
assertFalse(nameToAttr.get("notSettableInt").isWritable());
assertTrue(nameToAttr.containsKey("settableInt"));
assertTrue(nameToAttr.get("settableInt").isWritable());
assertTrue(nameToAttr.containsKey("settableStr"));
assertTrue(nameToAttr.get("settableStr").isWritable());
}
@Test
public void setsWritableAttributes() throws Exception {
MBeanWithSettableAttributes settableMBean = new MBeanWithSettableAttributes(10, 20, "data");
DynamicMBean mbean = createDynamicMBeanFor(settableMBean);
mbean.setAttribute(new Attribute("settableInt", 125));
mbean.setAttribute(new Attribute("settableStr", "data-2"));
assertEquals(125, mbean.getAttribute("settableInt"));
assertEquals("data-2", mbean.getAttribute("settableStr"));
}
@Test
public void setsWrittableAttributesInInnerPojo() throws Exception {
PojoWithSettableAttribute pojo = new PojoWithSettableAttribute(20);
MBeanWithPojoWithSettableAttribute settableMBean = new MBeanWithPojoWithSettableAttribute(pojo);
DynamicMBean mbean = createDynamicMBeanFor(settableMBean);
mbean.setAttribute(new Attribute("pojo_sum", 155L));
assertEquals(155L, mbean.getAttribute("pojo_sum"));
}
@Test
public void worksProperlyWithIsGetters() throws Exception {
MBeanWithIsGetter mBeanWithIsGetter = new MBeanWithIsGetter();
DynamicMBean mbean = createDynamicMBeanFor(mBeanWithIsGetter);
MBeanInfo mBeanInfo = mbean.getMBeanInfo();
MBeanAttributeInfo[] attributesInfoArr = mBeanInfo.getAttributes();
assertEquals(1, attributesInfoArr.length);
MBeanAttributeInfo booleanAttr = attributesInfoArr[0];
assertEquals("active", booleanAttr.getName());
assertTrue(booleanAttr.isReadable());
assertTrue(booleanAttr.isIs());
assertTrue((boolean) mbean.getAttribute("active"));
}
@Test
public void returns_toString_representationOfObjectsThatAreNeitherSupportedTypesNorPojosWithJmxAttributes()
throws Exception {
ArbitraryType arbitraryType = new ArbitraryType("String representation");
Date date = new Date();
MBeanWithJmxAttributesOfArbitraryTypes obj =
new MBeanWithJmxAttributesOfArbitraryTypes(arbitraryType, date);
DynamicMBean mbean = createDynamicMBeanFor(obj);
assertEquals(arbitraryType.toString(), mbean.getAttribute("arbitraryType"));
assertEquals(date.toString(), mbean.getAttribute("date"));
}
@Test
public void handlesProperlyDefaultGetter() throws Exception {
DynamicMBean mbean = createDynamicMBeanFor(new MBeanWithPojoWithDefaultGetter());
Map<String, MBeanAttributeInfo> attrs = nameToAttribute(mbean.getMBeanInfo().getAttributes());
assertEquals(1, attrs.size());
assertTrue(attrs.containsKey("pojo"));
assertEquals("summary", mbean.getAttribute("pojo"));
}
// region helper methods
public static DynamicMBean createDynamicMBeanFor(Object... objects) throws Exception {
boolean refreshEnabled = false;
return JmxMBeans.factory().createFor(asList(objects), defaultSettings(), refreshEnabled);
}
public static Object[] keyForTabularData(String key) {
return new String[]{key};
}
// endregion
// region helper classes
public static final class SamplePojo {
private final int count;
private final long sum;
public SamplePojo(int count, long sum) {
this.count = count;
this.sum = sum;
}
@JmxAttribute
public int getCount() {
return count;
}
@JmxAttribute
public long getSum() {
return sum;
}
}
public static final class MBeanWithSimpleAttrsAndPojo implements ConcurrentJmxMBean {
private final String info;
private final SamplePojo samplePojo;
public MBeanWithSimpleAttrsAndPojo(String info, SamplePojo samplePojo) {
this.info = info;
this.samplePojo = samplePojo;
}
@JmxAttribute
public String getInfo() {
return info;
}
@JmxAttribute(name = "details")
public SamplePojo getSamplePojo() {
return samplePojo;
}
}
public static final class MBeanWithListAttr implements EventloopJmxMBean {
private final List<String> list;
public MBeanWithListAttr(List<String> list) {
this.list = list;
}
@JmxAttribute
public List<String> getList() {
return list;
}
@Override
public Eventloop getEventloop() {
return Eventloop.create().withFatalErrorHandler(rethrowOnAnyError());
}
}
public static final class MBeanWithSingleIntAttr implements EventloopJmxMBean {
private final int value;
public MBeanWithSingleIntAttr(int value) {
this.value = value;
}
@JmxAttribute
public int getValue() {
return value;
}
@Override
public Eventloop getEventloop() {
return Eventloop.create().withFatalErrorHandler(rethrowOnAnyError());
}
}
public static final class MBeanWithJmxStats implements EventloopJmxMBean {
private final JmxStatsStub jmxStatsStub;
public MBeanWithJmxStats(JmxStatsStub jmxStatsStub) {
this.jmxStatsStub = jmxStatsStub;
}
@JmxAttribute
public JmxStatsStub getJmxStatsStub() {
return jmxStatsStub;
}
@Override
public Eventloop getEventloop() {
return Eventloop.create().withFatalErrorHandler(rethrowOnAnyError());
}
}
public static final class MBeanWithMap implements EventloopJmxMBean {
private final Map<String, Integer> nameToNumber;
public MBeanWithMap(Map<String, Integer> nameToNumber) {
this.nameToNumber = nameToNumber;
}
@JmxAttribute
public Map<String, Integer> getNameToNumber() {
return nameToNumber;
}
@Override
public Eventloop getEventloop() {
return Eventloop.create().withFatalErrorHandler(rethrowOnAnyError());
}
}
// classes for empty names tests
public static final class MBeanWithEmptyNames implements ConcurrentJmxMBean {
private final SamplePojo_L_1_1 pojo1;
private final SamplePojo_L_1_2 pojo2;
public MBeanWithEmptyNames(SamplePojo_L_1_1 pojo1, SamplePojo_L_1_2 pojo2) {
this.pojo1 = pojo1;
this.pojo2 = pojo2;
}
@JmxAttribute(name = "group")
public SamplePojo_L_1_1 getPojo1() {
return pojo1;
}
@JmxAttribute(name = "")
public SamplePojo_L_1_2 getPojo2() {
return pojo2;
}
}
public static final class SamplePojo_L_1_1 {
private final int count;
public SamplePojo_L_1_1(int count) {
this.count = count;
}
@JmxAttribute(name = "count")
public int getCount() {
return count;
}
}
public static final class SamplePojo_L_1_2 {
private final SamplePojo_L_2 pojo;
public SamplePojo_L_1_2(SamplePojo_L_2 pojo) {
this.pojo = pojo;
}
@JmxAttribute(name = "group")
public SamplePojo_L_2 getPojo() {
return pojo;
}
}
public static final class SamplePojo_L_2 {
private final int total;
public SamplePojo_L_2(int total) {
this.total = total;
}
@JmxAttribute(name = "total")
public int getTotal() {
return total;
}
}
// classes for empty leaf names
public static final class MBeanWithEmptyLeafName implements ConcurrentJmxMBean {
private final PojoWithEmptyLeafName pojo = new PojoWithEmptyLeafName();
@JmxAttribute
public PojoWithEmptyLeafName getPojo() {
return pojo;
}
}
public static final class PojoWithEmptyLeafName {
@JmxAttribute(name = "")
public int getNumber() {
return 10;
}
}
// classes for setter tests
public static final class MBeanWithSettableAttributes implements ConcurrentJmxMBean {
private final int notSettableInt;
private int settableInt;
private String settableStr;
public MBeanWithSettableAttributes(int notSettableInt, int settableInt, String settableStr) {
this.notSettableInt = notSettableInt;
this.settableInt = settableInt;
this.settableStr = settableStr;
}
@JmxAttribute
public int getNotSettableInt() {
return notSettableInt;
}
@JmxAttribute
public int getSettableInt() {
return settableInt;
}
@JmxAttribute
public String getSettableStr() {
return settableStr;
}
@JmxAttribute
public void setSettableInt(int settableInt) {
this.settableInt = settableInt;
}
@JmxAttribute
public void setSettableStr(String settableStr) {
this.settableStr = settableStr;
}
}
public static final class MBeanWithPojoWithSettableAttribute implements ConcurrentJmxMBean {
private final PojoWithSettableAttribute pojo;
public MBeanWithPojoWithSettableAttribute(PojoWithSettableAttribute pojo) {
this.pojo = pojo;
}
@JmxAttribute
public PojoWithSettableAttribute getPojo() {
return pojo;
}
}
public static final class PojoWithSettableAttribute {
private long sum;
public PojoWithSettableAttribute(long sum) {
this.sum = sum;
}
@JmxAttribute
public long getSum() {
return sum;
}
@JmxAttribute
public void setSum(long sum) {
this.sum = sum;
}
}
public static final class MBeanWithIsGetter implements ConcurrentJmxMBean {
@JmxAttribute
public boolean isActive() {
return true;
}
}
public static final class MBeanWithJmxAttributesOfArbitraryTypes implements ConcurrentJmxMBean {
private final ArbitraryType arbitraryType;
private final Date date;
public MBeanWithJmxAttributesOfArbitraryTypes(ArbitraryType arbitraryType, Date date) {
this.arbitraryType = arbitraryType;
this.date = date;
}
@JmxAttribute
public ArbitraryType getArbitraryType() {
return arbitraryType;
}
@JmxAttribute
public Date getDate() {
return date;
}
}
public static final class ArbitraryType {
private final String str;
public ArbitraryType(String str) {
this.str = str;
}
@Override
public String toString() {
return str;
}
}
// classes for default attribute test (@JmxAttribute get())
public static final class MBeanWithPojoWithDefaultGetter implements EventloopJmxMBean {
private final PojoWithDefaultGetter pojo = new PojoWithDefaultGetter();
@JmxAttribute
public PojoWithDefaultGetter getPojo() {
return pojo;
}
@Override
public Eventloop getEventloop() {
return eventloop;
}
}
public static final class PojoWithDefaultGetter {
@JmxAttribute(optional = true)
public int getValue() {
return 10;
}
@JmxAttribute
public String get() {
return "summary";
}
}
// endregion
}