d84a7e55be
Reviewed-by: mchung, chegar, alanb
635 lines
24 KiB
Java
635 lines
24 KiB
Java
/*
|
|
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import java.io.InvalidObjectException;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
import javax.management.Attribute;
|
|
import javax.management.ConstructorParameters;
|
|
import javax.management.JMX;
|
|
import javax.management.MBeanException;
|
|
import javax.management.MBeanServer;
|
|
import javax.management.MBeanServerConnection;
|
|
import javax.management.MBeanServerFactory;
|
|
import javax.management.NotCompliantMBeanException;
|
|
import javax.management.ObjectName;
|
|
import javax.management.StandardMBean;
|
|
import javax.management.openmbean.CompositeData;
|
|
import javax.management.openmbean.CompositeDataSupport;
|
|
import javax.management.openmbean.CompositeDataView;
|
|
import javax.management.openmbean.CompositeType;
|
|
import javax.management.openmbean.OpenDataException;
|
|
import javax.management.openmbean.OpenType;
|
|
import javax.management.remote.JMXConnector;
|
|
import javax.management.remote.JMXConnectorServer;
|
|
import javax.management.remote.JMXConnectorServerFactory;
|
|
import javax.management.remote.JMXServiceURL;
|
|
|
|
import org.testng.annotations.DataProvider;
|
|
import org.testng.annotations.Test;
|
|
import static org.testng.Assert.*;
|
|
|
|
/**
|
|
* @test
|
|
* @bug 8264124
|
|
* @run testng RecordsMXBeanTest
|
|
*/
|
|
public class RecordsMXBeanTest {
|
|
// Simple record with open types
|
|
public record Data(List<Integer> ints, Map<String, List<String>> map) {}
|
|
// Used to test case in component names
|
|
public record MixedCases(int Foo, int BarBar, int foo) {}
|
|
// Used to test nested records
|
|
public record DataPoint(Data x, Data y, MixedCases mixed) {}
|
|
// Used to test reconstruction using a non-canonical constructor
|
|
public record Annotated(int x, int y, int z) {
|
|
@ConstructorParameters(value = {"y", "x"})
|
|
public Annotated(int y, int x) {
|
|
this(x,y,-1);
|
|
}
|
|
}
|
|
// Used to test reconstruction using a static `from` method
|
|
public record FromMethod(int x, int y, int z) {
|
|
public static FromMethod from(CompositeData cd) {
|
|
int x = (int) cd.get("x");
|
|
int y = (int) cd.get("y");
|
|
int z = -x -y;
|
|
return new FromMethod(x, y, z);
|
|
}
|
|
}
|
|
// A record that exposes methods that look like
|
|
// getters... These should be ignored - only the
|
|
// record components should be considered.
|
|
public record Trickster(int x, int y) {
|
|
public int getZ() { return -x() -y(); }
|
|
public boolean isTricky() { return true; }
|
|
}
|
|
// A regular class similar to the Trickster,
|
|
// but this time z and tricky should appear
|
|
// in the composite data
|
|
public static class TricksterToo {
|
|
final int x;
|
|
final int y;
|
|
@ConstructorParameters({"x", "y"})
|
|
public TricksterToo(int x, int y) {
|
|
this.x = x; this.y = y;
|
|
}
|
|
public int getX() { return x; }
|
|
public int getY() { return y; }
|
|
public int getZ() { return -x -y; }
|
|
public boolean isTricky() { return true; }
|
|
}
|
|
// A record with a conflicting name getX/x which
|
|
// should ensure that non component getters are ignored
|
|
public record RWithGetter(int x, int y) {
|
|
public int getX() { return x;}
|
|
}
|
|
// A record with an annotated cannonical constructor.
|
|
// Annotation should be ignored
|
|
public record WithAnno(int x, int y) {
|
|
@ConstructorParameters({"y", "x"})
|
|
public WithAnno(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
// A record that implements CompositeDataView
|
|
public record WithCDV(int x, int y) implements CompositeDataView {
|
|
@Override
|
|
public CompositeData toCompositeData(CompositeType ct) {
|
|
if (ct == null) return null;
|
|
try {
|
|
return new CompositeDataSupport(ct, new String[]{"x", "y"}, new Object[]{x() + 1, y() + 2});
|
|
} catch (OpenDataException x) {
|
|
throw new IllegalArgumentException(ct.getTypeName(), x);
|
|
}
|
|
}
|
|
}
|
|
|
|
// A read only MXBean interface
|
|
public interface RecordsMXBean {
|
|
public Data getData();
|
|
public DataPoint getDataPoint();
|
|
public default Map<String, DataPoint> allPoints() {
|
|
return Map.of("allpoints", getDataPoint());
|
|
}
|
|
}
|
|
|
|
// A read-write MXBean interface
|
|
public interface Records2MXBean extends RecordsMXBean {
|
|
public void setDataPoint(DataPoint point);
|
|
}
|
|
|
|
// An implementation of the read-only MXBean interface which is
|
|
// itself a record (this is already supported)
|
|
public record Records(DataPoint point) implements RecordsMXBean {
|
|
@Override
|
|
public Data getData() {
|
|
return point().x();
|
|
}
|
|
|
|
@Override
|
|
public DataPoint getDataPoint() {
|
|
return point();
|
|
}
|
|
|
|
@Override
|
|
public Map<String, DataPoint> allPoints() {
|
|
return Map.of("point", point());
|
|
}
|
|
}
|
|
|
|
// An implementation of the read-write MXBean interface
|
|
public static class Records2 implements Records2MXBean {
|
|
private volatile DataPoint point = new DataPoint(
|
|
new Data(List.of(1, 2), Map.of("foo", List.of("bar"))),
|
|
new Data(List.of(3, 4), Map.of("bar", List.of("foo"))),
|
|
new MixedCases(5, 6, 7)
|
|
);
|
|
|
|
@Override
|
|
public Data getData() {
|
|
return point.x;
|
|
}
|
|
|
|
@Override
|
|
public DataPoint getDataPoint() {
|
|
return point;
|
|
}
|
|
|
|
@Override
|
|
public void setDataPoint(DataPoint point) {
|
|
this.point = point;
|
|
}
|
|
|
|
@Override
|
|
public Map<String, DataPoint> allPoints() {
|
|
return Map.of("point", point);
|
|
}
|
|
}
|
|
|
|
// A complex MXBean interface used to test reconstruction
|
|
// of records through non-canonical annotated constructors
|
|
// and static `from` method
|
|
public interface ComplexMXBean {
|
|
Annotated getAnnotated();
|
|
void setAnnotated(Annotated annotated);
|
|
FromMethod getFromMethod();
|
|
void setFromMethod(FromMethod fromMethod);
|
|
Trickster getTrickster();
|
|
void setTrickster(Trickster trick);
|
|
TricksterToo getTricksterToo();
|
|
void setTricksterToo(TricksterToo trick);
|
|
RWithGetter getR();
|
|
void setR(RWithGetter r);
|
|
WithAnno getWithAnno();
|
|
void setWithAnno(WithAnno r);
|
|
WithCDV getCDV();
|
|
void setCDV(WithCDV cdv);
|
|
}
|
|
|
|
// An implementation of the complex MXBean interface
|
|
public static class Complex implements ComplexMXBean {
|
|
private volatile Annotated annotated = new Annotated(1, 2, 3);
|
|
private volatile FromMethod fromMethod = new FromMethod(1, 2, 3);
|
|
private volatile Trickster trickster = new Trickster(4, 5);
|
|
private volatile TricksterToo too = new TricksterToo(6, 7);
|
|
private volatile RWithGetter r = new RWithGetter(8, 9);
|
|
private volatile WithAnno withAnno = new WithAnno(10, 11);
|
|
private volatile WithCDV withCDV = new WithCDV(12, 13);
|
|
|
|
@Override
|
|
public Annotated getAnnotated() {
|
|
return annotated;
|
|
}
|
|
|
|
@Override
|
|
public void setAnnotated(Annotated annotated) {
|
|
this.annotated = annotated;
|
|
}
|
|
|
|
@Override
|
|
public FromMethod getFromMethod() {
|
|
return fromMethod;
|
|
}
|
|
|
|
@Override
|
|
public void setFromMethod(FromMethod fromMethod) {
|
|
this.fromMethod = fromMethod;
|
|
}
|
|
|
|
@Override
|
|
public Trickster getTrickster() {
|
|
return trickster;
|
|
}
|
|
|
|
@Override
|
|
public void setTrickster(Trickster trickster) {
|
|
this.trickster = trickster;
|
|
}
|
|
|
|
@Override
|
|
public TricksterToo getTricksterToo() {
|
|
return too;
|
|
}
|
|
|
|
@Override
|
|
public void setTricksterToo(TricksterToo trick) {
|
|
too = trick;
|
|
}
|
|
|
|
@Override
|
|
public RWithGetter getR() {
|
|
return r;
|
|
}
|
|
|
|
@Override
|
|
public void setR(RWithGetter r) {
|
|
this.r = r;
|
|
}
|
|
|
|
@Override
|
|
public WithAnno getWithAnno() {
|
|
return withAnno;
|
|
}
|
|
|
|
@Override
|
|
public void setWithAnno(WithAnno r) {
|
|
this.withAnno = r;
|
|
}
|
|
|
|
@Override
|
|
public WithCDV getCDV() {
|
|
return withCDV;
|
|
}
|
|
|
|
@Override
|
|
public void setCDV(WithCDV cdv) {
|
|
withCDV = cdv;
|
|
}
|
|
}
|
|
|
|
public record NonCompliantR1(int x, Object y) {
|
|
public int getX() { return x;}
|
|
}
|
|
public interface NC1MXBean {
|
|
public NonCompliantR1 getNCR1();
|
|
}
|
|
public class NC1 implements NC1MXBean {
|
|
private volatile NonCompliantR1 ncr1 = new NonCompliantR1(1,2);
|
|
|
|
@Override
|
|
public NonCompliantR1 getNCR1() {
|
|
return ncr1;
|
|
}
|
|
}
|
|
|
|
public record NonCompliantR2(int x, List<? super Integer> y) {
|
|
}
|
|
public interface NC2MXBean {
|
|
public NonCompliantR2 getNCR2();
|
|
}
|
|
public class NC2 implements NC2MXBean {
|
|
private volatile NonCompliantR2 ncr2 = new NonCompliantR2(1,List.of(2));
|
|
|
|
@Override
|
|
public NonCompliantR2 getNCR2() {
|
|
return ncr2;
|
|
}
|
|
}
|
|
|
|
public record NonCompliantR3() {
|
|
}
|
|
public interface NC3MXBean {
|
|
public NonCompliantR3 getNCR3();
|
|
}
|
|
public class NC3 implements NC3MXBean {
|
|
private volatile NonCompliantR3 ncr3 = new NonCompliantR3();
|
|
|
|
@Override
|
|
public NonCompliantR3 getNCR3() {
|
|
return ncr3;
|
|
}
|
|
}
|
|
|
|
@DataProvider(name = "wrapInStandardMBean")
|
|
Object[][] wrapInStandardMBean() {
|
|
return new Object[][] {
|
|
new Object[] {"wrapped in StandardMBean", true},
|
|
new Object[] {"not wrapped in StandardMBean", false}
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "wrapInStandardMBean")
|
|
public void testLocal(String desc, boolean standard) throws Exception {
|
|
// test local
|
|
System.out.println("\nTest local " + desc);
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer("test");
|
|
test(mbs, mbs, standard);
|
|
}
|
|
|
|
@Test(dataProvider = "wrapInStandardMBean")
|
|
public void testRemote(String desc, boolean standard) throws Exception {
|
|
// test remote
|
|
System.out.println("\nTest remote " + desc);
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer("test");
|
|
final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
|
|
JMXConnectorServer server =
|
|
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
|
|
server.start();
|
|
try {
|
|
JMXConnector ctor = server.toJMXConnector(null);
|
|
ctor.connect();
|
|
try {
|
|
test(mbs, ctor.getMBeanServerConnection(), standard);
|
|
} finally {
|
|
ctor.close();
|
|
}
|
|
} finally {
|
|
server.stop();
|
|
}
|
|
}
|
|
|
|
private void test(MBeanServer server, MBeanServerConnection connection, boolean standard)
|
|
throws Exception {
|
|
|
|
// test RecordsMXBean via MBeanServerConnection
|
|
assertTrue(JMX.isMXBeanInterface(RecordsMXBean.class));
|
|
Records records = new Records(new DataPoint(
|
|
new Data(List.of(1, 2), Map.of("foo", List.of("bar"))),
|
|
new Data(List.of(3, 4), Map.of("bar", List.of("foo"))),
|
|
new MixedCases(5, 6, 7)
|
|
));
|
|
ObjectName recname = new ObjectName("test:type=Records");
|
|
var mbean = standard
|
|
? new StandardMBean(records, RecordsMXBean.class, true)
|
|
: records;
|
|
server.registerMBean(mbean, recname);
|
|
RecordsMXBean mxBean = JMX.newMXBeanProxy(connection, recname, RecordsMXBean.class);
|
|
Records retrieved = new Records(mxBean.getDataPoint());
|
|
assertEquals(retrieved, records);
|
|
assertEquals(mxBean.allPoints(), records.allPoints());
|
|
|
|
// test Records2MXBean via MBeanServerConnection
|
|
assertTrue(JMX.isMXBeanInterface(Records2MXBean.class));
|
|
Records2 records2 = new Records2();
|
|
assertEquals(records2.allPoints(), records.allPoints());
|
|
ObjectName recname2 = new ObjectName("test:type=Records2");
|
|
var mbean2 = standard
|
|
? new StandardMBean(records2, Records2MXBean.class, true)
|
|
: records2;
|
|
server.registerMBean(mbean2, recname2);
|
|
Records2MXBean mxBean2 = JMX.newMXBeanProxy(connection, recname2, Records2MXBean.class);
|
|
Records retrieved2 = new Records(mxBean2.getDataPoint());
|
|
assertEquals(retrieved2, records);
|
|
assertEquals(mxBean2.allPoints(), records.allPoints());
|
|
|
|
// mutate Records2MXBean via MBeanServerConnection
|
|
DataPoint point2 = new DataPoint(records.point().y(), records.point().x(), records.point().mixed());
|
|
mxBean2.setDataPoint(point2);
|
|
assertEquals(mxBean2.getDataPoint(), point2);
|
|
assertEquals(mxBean2.allPoints(), Map.of("point", point2));
|
|
|
|
// test reconstruction through non-canonical constructor and from method
|
|
Complex complex = new Complex();
|
|
var complexMBean = new StandardMBean(complex, ComplexMXBean.class, true);
|
|
ObjectName recname3 = new ObjectName("test:type=Complex");
|
|
var mbean3 = standard ? complexMBean : complex;
|
|
server.registerMBean(complexMBean, recname3);
|
|
ComplexMXBean mBean5 = JMX.newMXBeanProxy(connection, recname3, ComplexMXBean.class);
|
|
var annotated = mBean5.getAnnotated();
|
|
assertEquals(annotated, complex.getAnnotated());
|
|
// Obtain the CompositeData that corresponds to the Annotated record
|
|
var cd = (CompositeData) complexMBean.getAttribute("Annotated");
|
|
var ct = cd.getCompositeType();
|
|
// Construct a version of the "Annotated" composite data where z is missing
|
|
var nct = new CompositeType(ct.getTypeName(), ct.getDescription(), new String[] {"x", "y"},
|
|
new String[] {ct.getDescription("x"), ct.getDescription("y")},
|
|
new OpenType<?>[] {ct.getType("x"), ct.getType("y")});
|
|
var ncd = new CompositeDataSupport(nct, new String[] {"x", "y"},
|
|
new Object[] {cd.get("x"), cd.get("y")});
|
|
// send the modified composite data to remote, and check
|
|
// that the non-canonical constructor was called (this constructor
|
|
// sets z = -1)
|
|
connection.setAttribute(recname3, new Attribute("Annotated", ncd));
|
|
var annotated2 = mBean5.getAnnotated();
|
|
assertEquals(annotated2.x(), annotated.x());
|
|
assertEquals(annotated2.y(), annotated2.y());
|
|
assertEquals(annotated2.z(), -1);
|
|
// gets the FromMethod record, and check that the `from` method
|
|
// we defined was called. When reconstructed from our `from` method,
|
|
// z will be set to z = -x -y;
|
|
var from = mBean5.getFromMethod();
|
|
assertEquals(from.x(), 1);
|
|
assertEquals(from.y(), 2);
|
|
assertEquals(from.z(), -3);
|
|
mBean5.setFromMethod(new FromMethod(2, 1, 3));
|
|
from = mBean5.getFromMethod();
|
|
assertEquals(from.x(), 2);
|
|
assertEquals(from.y(), 1);
|
|
assertEquals(from.z(), -3);
|
|
// checks that the presence of getter-like methods doesn't
|
|
// prevent the record from being reconstructed.
|
|
var cdtrick = (CompositeData) connection.getAttribute(recname3, "Trickster");
|
|
println("tricky", cdtrick);
|
|
assertEquals(cdtrick.getCompositeType().keySet(), Set.of("x", "y"));
|
|
var trick = mBean5.getTrickster();
|
|
assertEquals(trick.x(), 4);
|
|
assertEquals(trick.y(), 5);
|
|
assertEquals(trick.getZ(), -9);
|
|
assertTrue(trick.isTricky());
|
|
mBean5.setTrickster(new Trickster(5, 4));
|
|
trick = mBean5.getTrickster();
|
|
assertEquals(trick.x(), 5);
|
|
assertEquals(trick.y(), 4);
|
|
assertEquals(trick.getZ(), -9);
|
|
assertTrue(trick.isTricky());
|
|
// get the "TricksterToo" composite data
|
|
var cdtoo = (CompositeData) connection.getAttribute(recname3, "TricksterToo");
|
|
println("tricky too", cdtoo);
|
|
assertEquals(cdtoo.getCompositeType().keySet(), Set.of("x", "y", "tricky", "z"));
|
|
var too = mBean5.getTricksterToo();
|
|
assertEquals(too.getX(), 6);
|
|
assertEquals(too.getY(), 7);
|
|
assertEquals(too.getZ(), -13);
|
|
assertTrue(too.isTricky());
|
|
mBean5.setTricksterToo(new TricksterToo(7, 6));
|
|
too = mBean5.getTricksterToo();
|
|
assertEquals(too.getX(), 7);
|
|
assertEquals(too.getY(), 6);
|
|
assertEquals(too.getZ(), -13);
|
|
assertTrue(too.isTricky());
|
|
|
|
// builds a composite data that contains more fields than
|
|
// the record...
|
|
var cdtype = cdtrick.getCompositeType();
|
|
var itemNames = List.of("x", "y", "z", "tricky").toArray(new String[0]);
|
|
var itemDesc = Stream.of(itemNames)
|
|
.map(cdtoo.getCompositeType()::getDescription)
|
|
.toArray(String[]::new);
|
|
var itemTypes = Stream.of(itemNames)
|
|
.map(cdtoo.getCompositeType()::getType)
|
|
.toArray(OpenType<?>[]::new);
|
|
var cdtype2 = new CompositeType(cdtype.getTypeName(),
|
|
cdtype.getDescription(), itemNames, itemDesc, itemTypes);
|
|
var values = Stream.of(itemNames).map(cdtoo::get).toArray();
|
|
var cdtrick2 = new CompositeDataSupport(cdtype2, itemNames, values);
|
|
// sets the composite data with more fields - the superfluous fields
|
|
// should be ignored...
|
|
connection.setAttribute(recname3, new Attribute("Trickster", cdtrick2));
|
|
// get the composite data we just set
|
|
var cdtrick3 = (CompositeData) connection.getAttribute(recname3, "Trickster");
|
|
assertEquals(cdtrick3.getCompositeType().keySet(), Set.of("x", "y"));
|
|
// get the "Trickster" through the MXBean proxy
|
|
var trick3 = mBean5.getTrickster();
|
|
assertEquals(trick3.x(), 6);
|
|
assertEquals(trick3.y(), 7);
|
|
assertEquals(trick3.getZ(), -13);
|
|
assertEquals(trick3.isTricky(), true);
|
|
// get record that has both x() and getX()
|
|
var rWithGetter = mBean5.getR();
|
|
assertEquals(rWithGetter.x(), rWithGetter.getX());
|
|
assertEquals(rWithGetter.x(), 8);
|
|
assertEquals(rWithGetter.y(), 9);
|
|
mBean5.setR(new RWithGetter(rWithGetter.y(), rWithGetter.x()));
|
|
rWithGetter = mBean5.getR();
|
|
assertEquals(rWithGetter.x(), rWithGetter.getX());
|
|
assertEquals(rWithGetter.x(), 9);
|
|
assertEquals(rWithGetter.y(), 8);
|
|
|
|
var withAnno = mBean5.getWithAnno();
|
|
assertEquals(withAnno.x(), 10);
|
|
assertEquals(withAnno.y(), 11);
|
|
withAnno = new WithAnno(12, 13);
|
|
mBean5.setWithAnno(withAnno);
|
|
withAnno = mBean5.getWithAnno();
|
|
assertEquals(withAnno.x(), 12);
|
|
assertEquals(withAnno.y(), 13);
|
|
|
|
// WithCDV.toCompositeData adds 1 to x and 2 to y,
|
|
// we can check how many time it's been called
|
|
// by looking at the values for x and y.
|
|
var cdv = mBean5.getCDV();
|
|
assertEquals(cdv.x(), 13 /* 12 + 1 */, "x");
|
|
assertEquals(cdv.y(), 15 /* 13 + 2 */, "y");
|
|
mBean5.setCDV(new WithCDV(14, 15));
|
|
cdv = mBean5.getCDV();
|
|
assertEquals(cdv.x(), 16 /* 14 + 1*2 */, "x");
|
|
assertEquals(cdv.y(), 19 /* 15 + 2*2 */, "y");
|
|
|
|
// Test non compliant records: this one has an Object (not mappable to OpenType)
|
|
var recname4 = new ObjectName("test:type=NCR1");
|
|
var x = standard
|
|
? expectThrows(IllegalArgumentException.class,
|
|
() -> new StandardMBean(new NC1(), NC1MXBean.class, true))
|
|
: expectThrows(NotCompliantMBeanException.class,
|
|
() -> server.registerMBean(new NC1(), recname4));
|
|
reportExpected(x);
|
|
assertEquals( originalCause(x).getClass(), OpenDataException.class);
|
|
|
|
// Test non compliant records: this one has a List<? super Integer>
|
|
// (not mappable to OpenType)
|
|
var recname5 = new ObjectName("test:type=NCR2");
|
|
var x2 = standard
|
|
? expectThrows(IllegalArgumentException.class,
|
|
() -> new StandardMBean(new NC2(), NC2MXBean.class, true))
|
|
: expectThrows(NotCompliantMBeanException.class,
|
|
() -> server.registerMBean(new NC2(), recname5));
|
|
reportExpected(x2);
|
|
assertEquals( originalCause(x2).getClass(), OpenDataException.class);
|
|
|
|
// Test non compliant records: this one has no getters
|
|
// (not mappable to OpenType)
|
|
var recname6 = new ObjectName("test:type=NCR3");
|
|
var x3 = standard
|
|
? expectThrows(IllegalArgumentException.class,
|
|
() -> new StandardMBean(new NC3(), NC3MXBean.class, true))
|
|
: expectThrows(NotCompliantMBeanException.class,
|
|
() -> server.registerMBean(new NC3(), recname6));
|
|
reportExpected(x3);
|
|
assertEquals( originalCause(x3).getClass(), OpenDataException.class);
|
|
|
|
// test that a composite data that doesn't have all the records
|
|
// components prevents the record from being reconstructed.
|
|
var recname7 = new ObjectName("test:type=Records2,instance=6");
|
|
Records2 rec2 = new Records2();
|
|
var mbean7 = standard
|
|
? new StandardMBean(rec2, Records2MXBean.class, true)
|
|
: rec2;
|
|
server.registerMBean(mbean7, recname7);
|
|
var cd7 = (CompositeData) server.getAttribute(recname7, "DataPoint");
|
|
var cdt7 = cd7.getCompositeType();
|
|
var itemNames7 = List.of("x", "mixed")
|
|
.toArray(String[]::new);
|
|
var itemDesc7 = Stream.of(itemNames7)
|
|
.map(cdt7::getDescription)
|
|
.toArray(String[]::new);
|
|
var itemTypes7 = Stream.of(itemNames7)
|
|
.map(cdt7::getType)
|
|
.toArray(OpenType<?>[]::new);
|
|
var notmappable = new CompositeType(cdt7.getTypeName(),
|
|
cdt7.getDescription(),
|
|
itemNames7,
|
|
itemDesc7,
|
|
itemTypes7);
|
|
var itemValues7 = Stream.of(itemNames7)
|
|
.map(cd7::get)
|
|
.toArray();
|
|
var notmappableVal = new CompositeDataSupport(notmappable, itemNames7, itemValues7);
|
|
var attribute6 = new Attribute("DataPoint", notmappableVal);
|
|
var x4 = expectThrows(MBeanException.class,
|
|
standard ? () -> ((StandardMBean)mbean7).setAttribute(attribute6)
|
|
: () -> server.setAttribute(recname7, attribute6));
|
|
reportExpected(x4);
|
|
assertEquals(originalCause(x4).getClass(), InvalidObjectException.class);
|
|
|
|
}
|
|
|
|
static final void reportExpected(Throwable x) {
|
|
System.out.println("\nGot expected exception: " + x);
|
|
Throwable cause = x;
|
|
while ((cause = cause.getCause()) != null) {
|
|
System.out.println("\tCaused by: " + cause);
|
|
}
|
|
}
|
|
|
|
static final Throwable originalCause(Throwable t) {
|
|
while (t.getCause() != null) t = t.getCause();
|
|
return t;
|
|
}
|
|
|
|
static void println(String name, CompositeData cd) {
|
|
var cdt = cd.getCompositeType();
|
|
System.out.printf("%s: %s %s\n", name, cdt.getTypeName(),
|
|
cdt.keySet().stream()
|
|
.map(k -> k + "=" + cd.get(k))
|
|
.collect(Collectors.joining(", ", "{ ", " }")));
|
|
|
|
}
|
|
|
|
}
|