Path: blob/master/test/jdk/javax/management/mxbean/MXBeanTest.java
41149 views
/*1* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 6175517 6278707 6318827 6305746 6392303 6600709 801028526* @summary General MXBean test.27* @author Eamonn McManus28* @author Jaroslav Bachorik29* @modules java.management.rmi30* @run clean MXBeanTest MerlinMXBean TigerMXBean31* @run build MXBeanTest MerlinMXBean TigerMXBean32* @run main MXBeanTest33*/3435import java.lang.reflect.Array;36import java.lang.reflect.Field;37import java.lang.reflect.InvocationHandler;38import java.lang.reflect.Method;39import java.lang.reflect.Proxy;40import java.util.Arrays;41import java.util.Collection;42import java.util.HashMap;43import java.util.Iterator;44import java.util.Map;45import java.util.SortedMap;46import javax.management.JMX;47import javax.management.MBeanAttributeInfo;48import javax.management.MBeanInfo;49import javax.management.MBeanOperationInfo;50import javax.management.MBeanParameterInfo;51import javax.management.MBeanServer;52import javax.management.MBeanServerConnection;53import javax.management.MBeanServerFactory;54import javax.management.MBeanServerInvocationHandler;55import javax.management.NotCompliantMBeanException;56import javax.management.ObjectName;57import javax.management.StandardMBean;58import javax.management.openmbean.ArrayType;59import javax.management.openmbean.CompositeData;60import javax.management.openmbean.CompositeDataInvocationHandler;61import javax.management.openmbean.OpenType;62import javax.management.openmbean.SimpleType;63import javax.management.openmbean.TabularData;64import javax.management.openmbean.TabularType;65import javax.management.remote.JMXConnector;66import javax.management.remote.JMXConnectorFactory;67import javax.management.remote.JMXConnectorServer;68import javax.management.remote.JMXConnectorServerFactory;69import javax.management.remote.JMXServiceURL;7071public class MXBeanTest {72public static void main(String[] args) throws Exception {73testInterface(MerlinMXBean.class, false);74testInterface(TigerMXBean.class, false);75testInterface(MerlinMXBean.class, true);76testInterface(TigerMXBean.class, true);77testExplicitMXBean();78testSubclassMXBean();79testIndirectMXBean();80testNonCompliantMXBean("Private", new Private());81testNonCompliantMXBean("NonCompliant", new NonCompliant());8283if (failures == 0)84System.out.println("Test passed");85else86throw new Exception("TEST FAILURES: " + failures);87}8889private static int failures = 0;9091private static interface PrivateMXBean {92public int[] getInts();93}9495public static class Private implements PrivateMXBean {96public int[] getInts() {97return new int[]{1,2,3};98}99}100101public static interface NonCompliantMXBean {102public boolean getInt();103public boolean isInt();104public void setInt(int a);105public void setInt(long b);106}107108public static class NonCompliant implements NonCompliantMXBean {109public boolean getInt() {110return false;111}112113public boolean isInt() {114return true;115}116117public void setInt(int a) {118}119120public void setInt(long b) {121}122}123124public static interface ExplicitMXBean {125public int[] getInts();126}127public static class Explicit implements ExplicitMXBean {128public int[] getInts() {129return new int[] {1, 2, 3};130}131}132public static class Subclass133extends StandardMBean134implements ExplicitMXBean {135public Subclass() {136super(ExplicitMXBean.class, true);137}138139public int[] getInts() {140return new int[] {1, 2, 3};141}142}143public static interface IndirectInterface extends ExplicitMXBean {}144public static class Indirect implements IndirectInterface {145public int[] getInts() {146return new int[] {1, 2, 3};147}148}149150private static void testNonCompliantMXBean(String type, Object bean) throws Exception {151System.out.println(type + " MXBean test...");152MBeanServer mbs = MBeanServerFactory.newMBeanServer();153ObjectName on = new ObjectName("test:type=" + type);154try {155mbs.registerMBean(bean, on);156failure(bean.getClass().getInterfaces()[0].getName() + " is not a compliant "157+ "MXBean interface");158} catch (NotCompliantMBeanException e) {159success("Non-compliant MXBean not registered");160}161}162163private static void testExplicitMXBean() throws Exception {164System.out.println("Explicit MXBean test...");165MBeanServer mbs = MBeanServerFactory.newMBeanServer();166ObjectName on = new ObjectName("test:type=Explicit");167Explicit explicit = new Explicit();168mbs.registerMBean(explicit, on);169testMXBean(mbs, on);170}171172private static void testSubclassMXBean() throws Exception {173System.out.println("Subclass MXBean test...");174MBeanServer mbs = MBeanServerFactory.newMBeanServer();175ObjectName on = new ObjectName("test:type=Subclass");176Subclass subclass = new Subclass();177mbs.registerMBean(subclass, on);178testMXBean(mbs, on);179}180181private static void testIndirectMXBean() throws Exception {182System.out.println("Indirect MXBean test...");183MBeanServer mbs = MBeanServerFactory.newMBeanServer();184ObjectName on = new ObjectName("test:type=Indirect");185Indirect indirect = new Indirect();186mbs.registerMBean(indirect, on);187testMXBean(mbs, on);188}189190private static void testMXBean(MBeanServer mbs, ObjectName on)191throws Exception {192MBeanInfo mbi = mbs.getMBeanInfo(on);193MBeanAttributeInfo[] attrs = mbi.getAttributes();194int nattrs = attrs.length;195if (mbi.getAttributes().length != 1)196failure("wrong number of attributes: " + attrs);197else {198MBeanAttributeInfo mbai = attrs[0];199if (mbai.getName().equals("Ints")200&& mbai.isReadable() && !mbai.isWritable()201&& mbai.getDescriptor().getFieldValue("openType")202.equals(new ArrayType<int[]>(SimpleType.INTEGER, true))203&& attrs[0].getType().equals("[I"))204success("MBeanAttributeInfo");205else206failure("MBeanAttributeInfo: " + mbai);207}208209int[] ints = (int[]) mbs.getAttribute(on, "Ints");210if (equal(ints, new int[] {1, 2, 3}, null))211success("getAttribute");212else213failure("getAttribute: " + Arrays.toString(ints));214215ExplicitMXBean proxy =216JMX.newMXBeanProxy(mbs, on, ExplicitMXBean.class);217int[] pints = proxy.getInts();218if (equal(pints, new int[] {1, 2, 3}, null))219success("getAttribute through proxy");220else221failure("getAttribute through proxy: " + Arrays.toString(pints));222}223224private static class NamedMXBeans extends HashMap<ObjectName, Object> {225private static final long serialVersionUID = 0;226227NamedMXBeans(MBeanServerConnection mbsc) {228this.mbsc = mbsc;229}230231MBeanServerConnection getMBeanServerConnection() {232return mbsc;233}234235private final MBeanServerConnection mbsc;236}237238/* This is the core of the test. Given the MXBean interface c, we239make an MXBean object that implements that interface by240constructing a dynamic proxy. If the interface defines an241attribute Foo (with getFoo and setFoo methods), then it must242also contain a field (constant) Foo of the same type, and a243field (constant) FooType that is an OpenType. The field Foo is244a reference value for this case. We check that the attribute245does indeed have the given OpenType. The dynamically-created246MXBean will return the reference value from the getFoo()247method, and we check that that value survives the mapping to248open values and back when the attribute is accessed through an249MXBean proxy. The MXBean will also check in its setFoo method250that the value being set is equal to the reference value, which251tests that the mapping and unmapping also works in the other252direction. The interface should define an operation opFoo with253two parameters and a return value all of the same type as the254attribute. The MXBean will check that the two parameters are255equal to the reference value, and will return that value. The256test checks that calling the operation through an MXBean proxy257returns the reference value, again after mapping to and back258from open values.259260If any field (constant) in the MXBean interface has a name that261ends with ObjectName, say FooObjectName, then its value must be262a String containing an ObjectName value. There must be a field263(constant) called Foo that is a valid MXBean, and that MXBean264will be registered in the MBean Server with the given name before265the test starts. This enables us to test that inter-MXBean266references are correctly converted to ObjectNames and back.267*/268private static <T> void testInterface(Class<T> c, boolean nullTest)269throws Exception {270271System.out.println("Testing " + c.getName() +272(nullTest ? " for null values" : "") + "...");273274MBeanServer mbs = MBeanServerFactory.newMBeanServer();275276JMXServiceURL url = new JMXServiceURL("rmi", null, 0);277JMXConnectorServer cs =278JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);279cs.start();280JMXServiceURL addr = cs.getAddress();281JMXConnector cc = JMXConnectorFactory.connect(addr);282MBeanServerConnection mbsc = cc.getMBeanServerConnection();283284NamedMXBeans namedMXBeans = new NamedMXBeans(mbsc);285InvocationHandler ih =286nullTest ? new MXBeanNullImplInvocationHandler(c, namedMXBeans) :287new MXBeanImplInvocationHandler(c, namedMXBeans);288T impl = c.cast(Proxy.newProxyInstance(c.getClassLoader(),289new Class[] {c},290ih));291ObjectName on = new ObjectName("test:type=" + c.getName());292mbs.registerMBean(impl, on);293294System.out.println("Register any MXBeans...");295296Field[] fields = c.getFields();297for (Field field : fields) {298String n = field.getName();299if (n.endsWith("ObjectName")) {300String objectNameString = (String) field.get(null);301String base = n.substring(0, n.length() - 10);302Field f = c.getField(base);303Object mxbean = f.get(null);304ObjectName objectName =305ObjectName.getInstance(objectNameString);306mbs.registerMBean(mxbean, objectName);307namedMXBeans.put(objectName, mxbean);308}309}310311try {312testInterface(c, mbsc, on, namedMXBeans, nullTest);313} finally {314try {315cc.close();316} finally {317cs.stop();318}319}320}321322private static <T> void testInterface(Class<T> c,323MBeanServerConnection mbsc,324ObjectName on,325NamedMXBeans namedMXBeans,326boolean nullTest)327throws Exception {328329System.out.println("Type check...");330331MBeanInfo mbi = mbsc.getMBeanInfo(on);332MBeanAttributeInfo[] mbais = mbi.getAttributes();333for (int i = 0; i < mbais.length; i++) {334MBeanAttributeInfo mbai = mbais[i];335String name = mbai.getName();336Field typeField = c.getField(name + "Type");337OpenType typeValue = (OpenType) typeField.get(null);338OpenType openType =339(OpenType) mbai.getDescriptor().getFieldValue("openType");340if (typeValue.equals(openType))341success("attribute " + name);342else {343final String msg =344"Wrong type attribute " + name + ": " +345openType + " should be " + typeValue;346failure(msg);347}348}349350MBeanOperationInfo[] mbois = mbi.getOperations();351for (int i = 0; i < mbois.length; i++) {352MBeanOperationInfo mboi = mbois[i];353String oname = mboi.getName();354if (!oname.startsWith("op"))355throw new Error();356OpenType retType =357(OpenType) mboi.getDescriptor().getFieldValue("openType");358MBeanParameterInfo[] params = mboi.getSignature();359MBeanParameterInfo p1i = params[0];360MBeanParameterInfo p2i = params[1];361OpenType p1Type =362(OpenType) p1i.getDescriptor().getFieldValue("openType");363OpenType p2Type =364(OpenType) p2i.getDescriptor().getFieldValue("openType");365if (!retType.equals(p1Type) || !p1Type.equals(p2Type)) {366final String msg =367"Parameter and return open types should all be same " +368"but are not: " + retType + " " + oname + "(" + p1Type +369", " + p2Type + ")";370failure(msg);371continue;372}373String name = oname.substring(2);374Field typeField = c.getField(name + "Type");375OpenType typeValue = (OpenType) typeField.get(null);376if (typeValue.equals(retType))377success("operation " + oname);378else {379final String msg =380"Wrong type operation " + oname + ": " +381retType + " should be " + typeValue;382failure(msg);383}384}385386387System.out.println("Mapping check...");388389Object proxy =390JMX.newMXBeanProxy(mbsc, on, c);391392Method[] methods = c.getMethods();393for (int i = 0; i < methods.length; i++) {394final Method method = methods[i];395if (method.getDeclaringClass() != c)396continue; // skip hashCode() etc inherited from Object397final String mname = method.getName();398final int what = getType(method);399final String name = getName(method);400final Field refField = c.getField(name);401if (nullTest && refField.getType().isPrimitive())402continue;403final Field openTypeField = c.getField(name + "Type");404final OpenType openType = (OpenType) openTypeField.get(null);405final Object refValue = nullTest ? null : refField.get(null);406Object setValue = refValue;407try {408Field onField = c.getField(name + "ObjectName");409String refName = (String) onField.get(null);410ObjectName refObjName = ObjectName.getInstance(refName);411Class<?> mxbeanInterface = refField.getType();412setValue = nullTest ? null :413JMX.newMXBeanProxy(mbsc, refObjName, mxbeanInterface);414} catch (Exception e) {415// no xObjectName field, setValue == refValue416}417boolean ok = true;418try {419switch (what) {420case GET:421final Object gotOpen = mbsc.getAttribute(on, name);422if (nullTest) {423if (gotOpen != null) {424failure(mname + " got non-null value " +425gotOpen);426ok = false;427}428} else if (!openType.isValue(gotOpen)) {429if (gotOpen instanceof TabularData) {430// detail the mismatch431TabularData gotTabular = (TabularData) gotOpen;432compareTabularType((TabularType) openType,433gotTabular.getTabularType());434}435failure(mname + " got open data " + gotOpen +436" not valid for open type " + openType);437ok = false;438}439final Object got = method.invoke(proxy, (Object[]) null);440if (!equal(refValue, got, namedMXBeans)) {441failure(mname + " got " + string(got) +442", should be " + string(refValue));443ok = false;444}445break;446447case SET:448method.invoke(proxy, new Object[] {setValue});449break;450451case OP:452final Object opped =453method.invoke(proxy, new Object[] {setValue, setValue});454if (!equal(refValue, opped, namedMXBeans)) {455failure(456mname + " got " + string(opped) +457", should be " + string(refValue)458);459ok = false;460}461break;462463default:464throw new Error();465}466467if (ok)468success(mname);469470} catch (Exception e) {471failure(mname, e);472}473}474}475476477private static void success(String what) {478System.out.println("OK: " + what);479}480481private static void failure(String what) {482System.out.println("FAILED: " + what);483failures++;484}485486private static void failure(String what, Exception e) {487System.out.println("FAILED WITH EXCEPTION: " + what);488e.printStackTrace(System.out);489failures++;490}491492private static class MXBeanImplInvocationHandler493implements InvocationHandler {494MXBeanImplInvocationHandler(Class intf, NamedMXBeans namedMXBeans) {495this.intf = intf;496this.namedMXBeans = namedMXBeans;497}498499public Object invoke(Object proxy, Method method, Object[] args)500throws Throwable {501final String mname = method.getName();502final int what = getType(method);503final String name = getName(method);504final Field refField = intf.getField(name);505final Object refValue = getRefValue(refField);506507switch (what) {508case GET:509assert args == null;510return refValue;511512case SET:513assert args.length == 1;514Object setValue = args[0];515if (!equal(refValue, setValue, namedMXBeans)) {516final String msg =517mname + "(" + string(setValue) +518") does not match ref: " + string(refValue);519throw new IllegalArgumentException(msg);520}521return null;522523case OP:524assert args.length == 2;525Object arg1 = args[0];526Object arg2 = args[1];527if (!equal(arg1, arg2, namedMXBeans)) {528final String msg =529mname + "(" + string(arg1) + ", " + string(arg2) +530"): args not equal";531throw new IllegalArgumentException(msg);532}533if (!equal(refValue, arg1, namedMXBeans)) {534final String msg =535mname + "(" + string(arg1) + ", " + string(arg2) +536"): args do not match ref: " + string(refValue);537throw new IllegalArgumentException(msg);538}539return refValue;540default:541throw new Error();542}543}544545Object getRefValue(Field refField) throws Exception {546return refField.get(null);547}548549private final Class intf;550private final NamedMXBeans namedMXBeans;551}552553private static class MXBeanNullImplInvocationHandler554extends MXBeanImplInvocationHandler {555MXBeanNullImplInvocationHandler(Class intf, NamedMXBeans namedMXBeans) {556super(intf, namedMXBeans);557}558559@Override560Object getRefValue(Field refField) throws Exception {561Class<?> type = refField.getType();562if (type.isPrimitive())563return super.getRefValue(refField);564else565return null;566}567}568569570private static final String[] prefixes = {571"get", "set", "op",572};573private static final int GET = 0, SET = 1, OP = 2;574575private static String getName(Method m) {576return getName(m.getName());577}578579private static String getName(String n) {580for (int i = 0; i < prefixes.length; i++) {581if (n.startsWith(prefixes[i]))582return n.substring(prefixes[i].length());583}584throw new Error();585}586587private static int getType(Method m) {588return getType(m.getName());589}590591private static int getType(String n) {592for (int i = 0; i < prefixes.length; i++) {593if (n.startsWith(prefixes[i]))594return i;595}596throw new Error();597}598599static boolean equal(Object o1, Object o2, NamedMXBeans namedMXBeans) {600if (o1 == o2)601return true;602if (o1 == null || o2 == null)603return false;604if (o1.getClass().isArray()) {605if (!o2.getClass().isArray())606return false;607return deepEqual(o1, o2, namedMXBeans);608}609if (o1 instanceof Map) {610if (!(o2 instanceof Map))611return false;612return equalMap((Map) o1, (Map) o2, namedMXBeans);613}614if (o1 instanceof CompositeData && o2 instanceof CompositeData) {615return compositeDataEqual((CompositeData) o1, (CompositeData) o2,616namedMXBeans);617}618if (Proxy.isProxyClass(o1.getClass())) {619if (Proxy.isProxyClass(o2.getClass()))620return proxyEqual(o1, o2, namedMXBeans);621InvocationHandler ih = Proxy.getInvocationHandler(o1);622// if (ih instanceof MXBeanInvocationHandler) {623// return proxyEqualsObject((MXBeanInvocationHandler) ih,624// o2, namedMXBeans);625if (ih instanceof MBeanServerInvocationHandler) {626return true;627} else if (ih instanceof CompositeDataInvocationHandler) {628return o2.equals(o1);629// We assume the other object has a reasonable equals method630}631} else if (Proxy.isProxyClass(o2.getClass()))632return equal(o2, o1, namedMXBeans);633return o1.equals(o2);634}635636// We'd use Arrays.deepEquals except we want the test to work on 1.4637// Note this code assumes no selfreferential arrays638// (as does Arrays.deepEquals)639private static boolean deepEqual(Object a1, Object a2,640NamedMXBeans namedMXBeans) {641int len = Array.getLength(a1);642if (len != Array.getLength(a2))643return false;644for (int i = 0; i < len; i++) {645Object e1 = Array.get(a1, i);646Object e2 = Array.get(a2, i);647if (!equal(e1, e2, namedMXBeans))648return false;649}650return true;651}652653private static boolean equalMap(Map<?,?> m1, Map<?,?> m2,654NamedMXBeans namedMXBeans) {655if (m1.size() != m2.size())656return false;657if ((m1 instanceof SortedMap) != (m2 instanceof SortedMap))658return false;659for (Object k1 : m1.keySet()) {660if (!m2.containsKey(k1))661return false;662if (!equal(m1.get(k1), m2.get(k1), namedMXBeans))663return false;664}665return true;666}667668// This is needed to work around a bug (5095277)669// in CompositeDataSupport.equals670private static boolean compositeDataEqual(CompositeData cd1,671CompositeData cd2,672NamedMXBeans namedMXBeans) {673if (cd1 == cd2)674return true;675if (!cd1.getCompositeType().equals(cd2.getCompositeType()))676return false;677Collection v1 = cd1.values();678Collection v2 = cd2.values();679if (v1.size() != v2.size())680return false; // should not happen681for (Iterator i1 = v1.iterator(), i2 = v2.iterator();682i1.hasNext(); ) {683if (!equal(i1.next(), i2.next(), namedMXBeans))684return false;685}686return true;687}688689// Also needed for 5095277690private static boolean proxyEqual(Object proxy1, Object proxy2,691NamedMXBeans namedMXBeans) {692if (proxy1.getClass() != proxy2.getClass())693return proxy1.equals(proxy2);694InvocationHandler ih1 = Proxy.getInvocationHandler(proxy1);695InvocationHandler ih2 = Proxy.getInvocationHandler(proxy2);696if (!(ih1 instanceof CompositeDataInvocationHandler)697|| !(ih2 instanceof CompositeDataInvocationHandler))698return proxy1.equals(proxy2);699CompositeData cd1 =700((CompositeDataInvocationHandler) ih1).getCompositeData();701CompositeData cd2 =702((CompositeDataInvocationHandler) ih2).getCompositeData();703return compositeDataEqual(cd1, cd2, namedMXBeans);704}705706// private static boolean proxyEqualsObject(MXBeanInvocationHandler ih,707// Object o,708// NamedMXBeans namedMXBeans) {709// if (namedMXBeans.getMBeanServerConnection() !=710// ih.getMBeanServerConnection())711// return false;712//713// ObjectName on = ih.getObjectName();714// Object named = namedMXBeans.get(on);715// if (named == null)716// return false;717// return (o == named && ih.getMXBeanInterface().isInstance(named));718// }719720/* I wanted to call this method toString(Object), but oddly enough721this meant that I couldn't call it from the inner class722MXBeanImplInvocationHandler, because the inherited Object.toString()723prevented that. */724static String string(Object o) {725if (o == null)726return "null";727if (o instanceof String)728return '"' + (String) o + '"';729if (o instanceof Collection)730return deepToString((Collection) o);731if (o.getClass().isArray())732return deepToString(o);733return o.toString();734}735736private static String deepToString(Object o) {737StringBuffer buf = new StringBuffer();738buf.append("[");739int len = Array.getLength(o);740for (int i = 0; i < len; i++) {741if (i > 0)742buf.append(", ");743Object e = Array.get(o, i);744buf.append(string(e));745}746buf.append("]");747return buf.toString();748}749750private static String deepToString(Collection c) {751return deepToString(c.toArray());752}753754private static void compareTabularType(TabularType t1, TabularType t2) {755if (t1.equals(t2)) {756System.out.println("same tabular type");757return;758}759if (t1.getClassName().equals(t2.getClassName()))760System.out.println("same class name");761if (t1.getDescription().equals(t2.getDescription()))762System.out.println("same description");763else {764System.out.println("t1 description: " + t1.getDescription());765System.out.println("t2 description: " + t2.getDescription());766}767if (t1.getIndexNames().equals(t2.getIndexNames()))768System.out.println("same index names");769if (t1.getRowType().equals(t2.getRowType()))770System.out.println("same row type");771}772}773774775