/* * 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 org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Ignore; import org.junit.Test; import javax.management.DynamicMBean; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import java.util.HashMap; import java.util.Map; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class JmxMBeansOperationsTest { @Test public void itShouldCollectInformationAbountJMXOperationsToMBeanInfo() throws Exception { MonitorableStubWithOperations monitorable = new MonitorableStubWithOperations(); DynamicMBean mbean = JmxMBeans.factory().createFor(asList(monitorable), MBeanSettings.defaultSettings(), false); MBeanInfo mBeanInfo = mbean.getMBeanInfo(); MBeanOperationInfo[] operations = mBeanInfo.getOperations(); Map<String, MBeanOperationInfo> nameToOperation = new HashMap<>(); for (MBeanOperationInfo operation : operations) { nameToOperation.put(operation.getName(), operation); } assertThat(nameToOperation, hasKey("increment")); assertThat(nameToOperation, hasKey("addInfo")); assertThat(nameToOperation, hasKey("multiplyAndAdd")); MBeanOperationInfo incrementOperation = nameToOperation.get("increment"); MBeanOperationInfo addInfoOperation = nameToOperation.get("addInfo"); MBeanOperationInfo multiplyAndAddOperation = nameToOperation.get("multiplyAndAdd"); assertThat(incrementOperation, hasReturnType("void")); assertThat(addInfoOperation, hasParameter("information", String.class.getName())); assertThat(addInfoOperation, hasReturnType("void")); // parameter names are not annotated assertThat(multiplyAndAddOperation, hasParameter("arg0", "long")); assertThat(multiplyAndAddOperation, hasParameter("arg1", "long")); assertThat(multiplyAndAddOperation, hasReturnType("void")); } @Test public void itShouldInvokeAnnotanedOperationsThroughDynamicMBeanInterface() throws Exception { MonitorableStubWithOperations monitorable = new MonitorableStubWithOperations(); DynamicMBean mbean = JmxMBeans.factory().createFor(asList(monitorable), MBeanSettings.defaultSettings(), false); mbean.invoke("increment", null, null); mbean.invoke("increment", null, null); mbean.invoke("addInfo", new Object[]{"data1"}, new String[]{String.class.getName()}); mbean.invoke("addInfo", new Object[]{"data2"}, new String[]{String.class.getName()}); mbean.invoke("multiplyAndAdd", new Object[]{120, 150}, new String[]{"long", "long"}); assertEquals(monitorable.getCount(), 2); assertEquals(monitorable.getInfo(), "data1data2"); assertEquals(monitorable.getSum(), 120 * 150); } @Ignore @Test public void itShouldBroadcastOperationCallToAllMonitorables() throws Exception { MonitorableStubWithOperations monitorable_1 = new MonitorableStubWithOperations(); MonitorableStubWithOperations monitorable_2 = new MonitorableStubWithOperations(); DynamicMBean mbean = JmxMBeans.factory().createFor(asList(monitorable_1, monitorable_2), MBeanSettings.defaultSettings(), false); // set manually init value for second monitorable to be different from first monitorable_2.inc(); monitorable_2.inc(); monitorable_2.inc(); monitorable_2.addInfo("second"); monitorable_2.multiplyAndAdd(10, 15); mbean.invoke("increment", null, null); mbean.invoke("increment", null, null); mbean.invoke("addInfo", new Object[]{"data1"}, new String[]{String.class.getName()}); mbean.invoke("addInfo", new Object[]{"data2"}, new String[]{String.class.getName()}); mbean.invoke("multiplyAndAdd", new Object[]{120, 150}, new String[]{"long", "long"}); // check first monitorable assertEquals(monitorable_1.getCount(), 2); assertEquals(monitorable_1.getInfo(), "data1data2"); assertEquals(monitorable_1.getSum(), 120 * 150); // check second monitorable assertEquals(monitorable_2.getCount(), 2 + 3); assertEquals(monitorable_2.getInfo(), "second" + "data1data2"); assertEquals(monitorable_2.getSum(), 10 * 15 + 120 * 150); } @Test public void operationReturnsValueInCaseOfSingleMBeanInPool() throws Exception { MBeanWithOperationThatReturnsValue mbeanOpWithValue = new MBeanWithOperationThatReturnsValue(); DynamicMBean mbean = JmxMBeans.factory().createFor(asList(mbeanOpWithValue), MBeanSettings.defaultSettings(), false); assertEquals(15, (int) mbean.invoke("sum", new Object[]{7, 8}, new String[]{"int", "int"})); } @Ignore @Test public void operationReturnsNullInCaseOfSeveralMBeansInPool() throws Exception { MBeanWithOperationThatReturnsValue mbeanOpWithValue_1 = new MBeanWithOperationThatReturnsValue(); MBeanWithOperationThatReturnsValue mbeanOpWithValue_2 = new MBeanWithOperationThatReturnsValue(); DynamicMBean mbean = JmxMBeans.factory().createFor(asList(mbeanOpWithValue_1, mbeanOpWithValue_2), MBeanSettings.defaultSettings(), false); assertEquals(null, mbean.invoke("sum", new Object[]{7, 8}, new String[]{"int", "int"})); } // helpers public static class MonitorableStubWithOperations implements ConcurrentJmxMBean { private int count = 0; private String info = ""; private long sum = 0; public int getCount() { return count; } public String getInfo() { return info; } public long getSum() { return sum; } @JmxOperation(name = "increment") public void inc() { count++; } @JmxOperation public void addInfo(@JmxParameter("information") String info) { this.info += info; } @JmxOperation public void multiplyAndAdd(long valueOne, long valueTwo) { sum += valueOne * valueTwo; } } public static final class MBeanWithOperationThatReturnsValue implements ConcurrentJmxMBean { @JmxOperation public int sum(int a, int b) { return a + b; } } // custom matchers public static <T> Matcher<Map<T, ?>> hasKey(final T key) { return new BaseMatcher<Map<T, ?>>() { @Override public void describeTo(Description description) { description.appendText("has key \"" + key.toString() + "\""); } @Override public boolean matches(Object item) { if (item == null) { return false; } Map<T, ?> map = (Map<T, ?>) item; return map.containsKey(key); } }; } public static Matcher<MBeanOperationInfo> hasParameter(final String name, final String type) { return new BaseMatcher<MBeanOperationInfo>() { @Override public boolean matches(Object item) { if (item == null) { return false; } MBeanOperationInfo operation = (MBeanOperationInfo) item; for (MBeanParameterInfo param : operation.getSignature()) { if (param.getName().equals(name) && param.getType().equals(type)) { return true; } } return false; } @Override public void describeTo(Description description) { description.appendText("has parameter with name \"" + name + "\" and type \"" + type + "\""); } }; } public static Matcher<MBeanOperationInfo> hasReturnType(final String type) { return new BaseMatcher<MBeanOperationInfo>() { @Override public boolean matches(Object item) { if (item == null) { return false; } MBeanOperationInfo operation = (MBeanOperationInfo) item; return operation.getReturnType().equals(type); } @Override public void describeTo(Description description) { description.appendText("has return type " + type); } }; } }