Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/javax/management/mxbean/RecordsMXBeanTest.java
41149 views
1
/*
2
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
import java.io.InvalidObjectException;
25
import java.util.Collection;
26
import java.util.List;
27
import java.util.Map;
28
import java.util.Set;
29
import java.util.stream.Collectors;
30
import java.util.stream.Stream;
31
import javax.management.Attribute;
32
import javax.management.ConstructorParameters;
33
import javax.management.JMX;
34
import javax.management.MBeanException;
35
import javax.management.MBeanServer;
36
import javax.management.MBeanServerConnection;
37
import javax.management.MBeanServerFactory;
38
import javax.management.NotCompliantMBeanException;
39
import javax.management.ObjectName;
40
import javax.management.StandardMBean;
41
import javax.management.openmbean.CompositeData;
42
import javax.management.openmbean.CompositeDataSupport;
43
import javax.management.openmbean.CompositeDataView;
44
import javax.management.openmbean.CompositeType;
45
import javax.management.openmbean.OpenDataException;
46
import javax.management.openmbean.OpenType;
47
import javax.management.remote.JMXConnector;
48
import javax.management.remote.JMXConnectorServer;
49
import javax.management.remote.JMXConnectorServerFactory;
50
import javax.management.remote.JMXServiceURL;
51
52
import org.testng.annotations.DataProvider;
53
import org.testng.annotations.Test;
54
import static org.testng.Assert.*;
55
56
/**
57
* @test
58
* @bug 8264124
59
* @run testng RecordsMXBeanTest
60
*/
61
public class RecordsMXBeanTest {
62
// Simple record with open types
63
public record Data(List<Integer> ints, Map<String, List<String>> map) {}
64
// Used to test case in component names
65
public record MixedCases(int Foo, int BarBar, int foo) {}
66
// Used to test nested records
67
public record DataPoint(Data x, Data y, MixedCases mixed) {}
68
// Used to test reconstruction using a non-canonical constructor
69
public record Annotated(int x, int y, int z) {
70
@ConstructorParameters(value = {"y", "x"})
71
public Annotated(int y, int x) {
72
this(x,y,-1);
73
}
74
}
75
// Used to test reconstruction using a static `from` method
76
public record FromMethod(int x, int y, int z) {
77
public static FromMethod from(CompositeData cd) {
78
int x = (int) cd.get("x");
79
int y = (int) cd.get("y");
80
int z = -x -y;
81
return new FromMethod(x, y, z);
82
}
83
}
84
// A record that exposes methods that look like
85
// getters... These should be ignored - only the
86
// record components should be considered.
87
public record Trickster(int x, int y) {
88
public int getZ() { return -x() -y(); }
89
public boolean isTricky() { return true; }
90
}
91
// A regular class similar to the Trickster,
92
// but this time z and tricky should appear
93
// in the composite data
94
public static class TricksterToo {
95
final int x;
96
final int y;
97
@ConstructorParameters({"x", "y"})
98
public TricksterToo(int x, int y) {
99
this.x = x; this.y = y;
100
}
101
public int getX() { return x; }
102
public int getY() { return y; }
103
public int getZ() { return -x -y; }
104
public boolean isTricky() { return true; }
105
}
106
// A record with a conflicting name getX/x which
107
// should ensure that non component getters are ignored
108
public record RWithGetter(int x, int y) {
109
public int getX() { return x;}
110
}
111
// A record with an annotated cannonical constructor.
112
// Annotation should be ignored
113
public record WithAnno(int x, int y) {
114
@ConstructorParameters({"y", "x"})
115
public WithAnno(int x, int y) {
116
this.x = x;
117
this.y = y;
118
}
119
}
120
// A record that implements CompositeDataView
121
public record WithCDV(int x, int y) implements CompositeDataView {
122
@Override
123
public CompositeData toCompositeData(CompositeType ct) {
124
if (ct == null) return null;
125
try {
126
return new CompositeDataSupport(ct, new String[]{"x", "y"}, new Object[]{x() + 1, y() + 2});
127
} catch (OpenDataException x) {
128
throw new IllegalArgumentException(ct.getTypeName(), x);
129
}
130
}
131
}
132
133
// A read only MXBean interface
134
public interface RecordsMXBean {
135
public Data getData();
136
public DataPoint getDataPoint();
137
public default Map<String, DataPoint> allPoints() {
138
return Map.of("allpoints", getDataPoint());
139
}
140
}
141
142
// A read-write MXBean interface
143
public interface Records2MXBean extends RecordsMXBean {
144
public void setDataPoint(DataPoint point);
145
}
146
147
// An implementation of the read-only MXBean interface which is
148
// itself a record (this is already supported)
149
public record Records(DataPoint point) implements RecordsMXBean {
150
@Override
151
public Data getData() {
152
return point().x();
153
}
154
155
@Override
156
public DataPoint getDataPoint() {
157
return point();
158
}
159
160
@Override
161
public Map<String, DataPoint> allPoints() {
162
return Map.of("point", point());
163
}
164
}
165
166
// An implementation of the read-write MXBean interface
167
public static class Records2 implements Records2MXBean {
168
private volatile DataPoint point = new DataPoint(
169
new Data(List.of(1, 2), Map.of("foo", List.of("bar"))),
170
new Data(List.of(3, 4), Map.of("bar", List.of("foo"))),
171
new MixedCases(5, 6, 7)
172
);
173
174
@Override
175
public Data getData() {
176
return point.x;
177
}
178
179
@Override
180
public DataPoint getDataPoint() {
181
return point;
182
}
183
184
@Override
185
public void setDataPoint(DataPoint point) {
186
this.point = point;
187
}
188
189
@Override
190
public Map<String, DataPoint> allPoints() {
191
return Map.of("point", point);
192
}
193
}
194
195
// A complex MXBean interface used to test reconstruction
196
// of records through non-canonical annotated constructors
197
// and static `from` method
198
public interface ComplexMXBean {
199
Annotated getAnnotated();
200
void setAnnotated(Annotated annotated);
201
FromMethod getFromMethod();
202
void setFromMethod(FromMethod fromMethod);
203
Trickster getTrickster();
204
void setTrickster(Trickster trick);
205
TricksterToo getTricksterToo();
206
void setTricksterToo(TricksterToo trick);
207
RWithGetter getR();
208
void setR(RWithGetter r);
209
WithAnno getWithAnno();
210
void setWithAnno(WithAnno r);
211
WithCDV getCDV();
212
void setCDV(WithCDV cdv);
213
}
214
215
// An implementation of the complex MXBean interface
216
public static class Complex implements ComplexMXBean {
217
private volatile Annotated annotated = new Annotated(1, 2, 3);
218
private volatile FromMethod fromMethod = new FromMethod(1, 2, 3);
219
private volatile Trickster trickster = new Trickster(4, 5);
220
private volatile TricksterToo too = new TricksterToo(6, 7);
221
private volatile RWithGetter r = new RWithGetter(8, 9);
222
private volatile WithAnno withAnno = new WithAnno(10, 11);
223
private volatile WithCDV withCDV = new WithCDV(12, 13);
224
225
@Override
226
public Annotated getAnnotated() {
227
return annotated;
228
}
229
230
@Override
231
public void setAnnotated(Annotated annotated) {
232
this.annotated = annotated;
233
}
234
235
@Override
236
public FromMethod getFromMethod() {
237
return fromMethod;
238
}
239
240
@Override
241
public void setFromMethod(FromMethod fromMethod) {
242
this.fromMethod = fromMethod;
243
}
244
245
@Override
246
public Trickster getTrickster() {
247
return trickster;
248
}
249
250
@Override
251
public void setTrickster(Trickster trickster) {
252
this.trickster = trickster;
253
}
254
255
@Override
256
public TricksterToo getTricksterToo() {
257
return too;
258
}
259
260
@Override
261
public void setTricksterToo(TricksterToo trick) {
262
too = trick;
263
}
264
265
@Override
266
public RWithGetter getR() {
267
return r;
268
}
269
270
@Override
271
public void setR(RWithGetter r) {
272
this.r = r;
273
}
274
275
@Override
276
public WithAnno getWithAnno() {
277
return withAnno;
278
}
279
280
@Override
281
public void setWithAnno(WithAnno r) {
282
this.withAnno = r;
283
}
284
285
@Override
286
public WithCDV getCDV() {
287
return withCDV;
288
}
289
290
@Override
291
public void setCDV(WithCDV cdv) {
292
withCDV = cdv;
293
}
294
}
295
296
public record NonCompliantR1(int x, Object y) {
297
public int getX() { return x;}
298
}
299
public interface NC1MXBean {
300
public NonCompliantR1 getNCR1();
301
}
302
public class NC1 implements NC1MXBean {
303
private volatile NonCompliantR1 ncr1 = new NonCompliantR1(1,2);
304
305
@Override
306
public NonCompliantR1 getNCR1() {
307
return ncr1;
308
}
309
}
310
311
public record NonCompliantR2(int x, List<? super Integer> y) {
312
}
313
public interface NC2MXBean {
314
public NonCompliantR2 getNCR2();
315
}
316
public class NC2 implements NC2MXBean {
317
private volatile NonCompliantR2 ncr2 = new NonCompliantR2(1,List.of(2));
318
319
@Override
320
public NonCompliantR2 getNCR2() {
321
return ncr2;
322
}
323
}
324
325
public record NonCompliantR3() {
326
}
327
public interface NC3MXBean {
328
public NonCompliantR3 getNCR3();
329
}
330
public class NC3 implements NC3MXBean {
331
private volatile NonCompliantR3 ncr3 = new NonCompliantR3();
332
333
@Override
334
public NonCompliantR3 getNCR3() {
335
return ncr3;
336
}
337
}
338
339
@DataProvider(name = "wrapInStandardMBean")
340
Object[][] wrapInStandardMBean() {
341
return new Object[][] {
342
new Object[] {"wrapped in StandardMBean", true},
343
new Object[] {"not wrapped in StandardMBean", false}
344
};
345
}
346
347
@Test(dataProvider = "wrapInStandardMBean")
348
public void testLocal(String desc, boolean standard) throws Exception {
349
// test local
350
System.out.println("\nTest local " + desc);
351
MBeanServer mbs = MBeanServerFactory.newMBeanServer("test");
352
test(mbs, mbs, standard);
353
}
354
355
@Test(dataProvider = "wrapInStandardMBean")
356
public void testRemote(String desc, boolean standard) throws Exception {
357
// test remote
358
System.out.println("\nTest remote " + desc);
359
MBeanServer mbs = MBeanServerFactory.newMBeanServer("test");
360
final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
361
JMXConnectorServer server =
362
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
363
server.start();
364
try {
365
JMXConnector ctor = server.toJMXConnector(null);
366
ctor.connect();
367
try {
368
test(mbs, ctor.getMBeanServerConnection(), standard);
369
} finally {
370
ctor.close();
371
}
372
} finally {
373
server.stop();
374
}
375
}
376
377
private void test(MBeanServer server, MBeanServerConnection connection, boolean standard)
378
throws Exception {
379
380
// test RecordsMXBean via MBeanServerConnection
381
assertTrue(JMX.isMXBeanInterface(RecordsMXBean.class));
382
Records records = new Records(new DataPoint(
383
new Data(List.of(1, 2), Map.of("foo", List.of("bar"))),
384
new Data(List.of(3, 4), Map.of("bar", List.of("foo"))),
385
new MixedCases(5, 6, 7)
386
));
387
ObjectName recname = new ObjectName("test:type=Records");
388
var mbean = standard
389
? new StandardMBean(records, RecordsMXBean.class, true)
390
: records;
391
server.registerMBean(mbean, recname);
392
RecordsMXBean mxBean = JMX.newMXBeanProxy(connection, recname, RecordsMXBean.class);
393
Records retrieved = new Records(mxBean.getDataPoint());
394
assertEquals(retrieved, records);
395
assertEquals(mxBean.allPoints(), records.allPoints());
396
397
// test Records2MXBean via MBeanServerConnection
398
assertTrue(JMX.isMXBeanInterface(Records2MXBean.class));
399
Records2 records2 = new Records2();
400
assertEquals(records2.allPoints(), records.allPoints());
401
ObjectName recname2 = new ObjectName("test:type=Records2");
402
var mbean2 = standard
403
? new StandardMBean(records2, Records2MXBean.class, true)
404
: records2;
405
server.registerMBean(mbean2, recname2);
406
Records2MXBean mxBean2 = JMX.newMXBeanProxy(connection, recname2, Records2MXBean.class);
407
Records retrieved2 = new Records(mxBean2.getDataPoint());
408
assertEquals(retrieved2, records);
409
assertEquals(mxBean2.allPoints(), records.allPoints());
410
411
// mutate Records2MXBean via MBeanServerConnection
412
DataPoint point2 = new DataPoint(records.point().y(), records.point().x(), records.point().mixed());
413
mxBean2.setDataPoint(point2);
414
assertEquals(mxBean2.getDataPoint(), point2);
415
assertEquals(mxBean2.allPoints(), Map.of("point", point2));
416
417
// test reconstruction through non-canonical constructor and from method
418
Complex complex = new Complex();
419
var complexMBean = new StandardMBean(complex, ComplexMXBean.class, true);
420
ObjectName recname3 = new ObjectName("test:type=Complex");
421
var mbean3 = standard ? complexMBean : complex;
422
server.registerMBean(complexMBean, recname3);
423
ComplexMXBean mBean5 = JMX.newMXBeanProxy(connection, recname3, ComplexMXBean.class);
424
var annotated = mBean5.getAnnotated();
425
assertEquals(annotated, complex.getAnnotated());
426
// Obtain the CompositeData that corresponds to the Annotated record
427
var cd = (CompositeData) complexMBean.getAttribute("Annotated");
428
var ct = cd.getCompositeType();
429
// Construct a version of the "Annotated" composite data where z is missing
430
var nct = new CompositeType(ct.getTypeName(), ct.getDescription(), new String[] {"x", "y"},
431
new String[] {ct.getDescription("x"), ct.getDescription("y")},
432
new OpenType<?>[] {ct.getType("x"), ct.getType("y")});
433
var ncd = new CompositeDataSupport(nct, new String[] {"x", "y"},
434
new Object[] {cd.get("x"), cd.get("y")});
435
// send the modified composite data to remote, and check
436
// that the non-canonical constructor was called (this constructor
437
// sets z = -1)
438
connection.setAttribute(recname3, new Attribute("Annotated", ncd));
439
var annotated2 = mBean5.getAnnotated();
440
assertEquals(annotated2.x(), annotated.x());
441
assertEquals(annotated2.y(), annotated2.y());
442
assertEquals(annotated2.z(), -1);
443
// gets the FromMethod record, and check that the `from` method
444
// we defined was called. When reconstructed from our `from` method,
445
// z will be set to z = -x -y;
446
var from = mBean5.getFromMethod();
447
assertEquals(from.x(), 1);
448
assertEquals(from.y(), 2);
449
assertEquals(from.z(), -3);
450
mBean5.setFromMethod(new FromMethod(2, 1, 3));
451
from = mBean5.getFromMethod();
452
assertEquals(from.x(), 2);
453
assertEquals(from.y(), 1);
454
assertEquals(from.z(), -3);
455
// checks that the presence of getter-like methods doesn't
456
// prevent the record from being reconstructed.
457
var cdtrick = (CompositeData) connection.getAttribute(recname3, "Trickster");
458
println("tricky", cdtrick);
459
assertEquals(cdtrick.getCompositeType().keySet(), Set.of("x", "y"));
460
var trick = mBean5.getTrickster();
461
assertEquals(trick.x(), 4);
462
assertEquals(trick.y(), 5);
463
assertEquals(trick.getZ(), -9);
464
assertTrue(trick.isTricky());
465
mBean5.setTrickster(new Trickster(5, 4));
466
trick = mBean5.getTrickster();
467
assertEquals(trick.x(), 5);
468
assertEquals(trick.y(), 4);
469
assertEquals(trick.getZ(), -9);
470
assertTrue(trick.isTricky());
471
// get the "TricksterToo" composite data
472
var cdtoo = (CompositeData) connection.getAttribute(recname3, "TricksterToo");
473
println("tricky too", cdtoo);
474
assertEquals(cdtoo.getCompositeType().keySet(), Set.of("x", "y", "tricky", "z"));
475
var too = mBean5.getTricksterToo();
476
assertEquals(too.getX(), 6);
477
assertEquals(too.getY(), 7);
478
assertEquals(too.getZ(), -13);
479
assertTrue(too.isTricky());
480
mBean5.setTricksterToo(new TricksterToo(7, 6));
481
too = mBean5.getTricksterToo();
482
assertEquals(too.getX(), 7);
483
assertEquals(too.getY(), 6);
484
assertEquals(too.getZ(), -13);
485
assertTrue(too.isTricky());
486
487
// builds a composite data that contains more fields than
488
// the record...
489
var cdtype = cdtrick.getCompositeType();
490
var itemNames = List.of("x", "y", "z", "tricky").toArray(new String[0]);
491
var itemDesc = Stream.of(itemNames)
492
.map(cdtoo.getCompositeType()::getDescription)
493
.toArray(String[]::new);
494
var itemTypes = Stream.of(itemNames)
495
.map(cdtoo.getCompositeType()::getType)
496
.toArray(OpenType<?>[]::new);
497
var cdtype2 = new CompositeType(cdtype.getTypeName(),
498
cdtype.getDescription(), itemNames, itemDesc, itemTypes);
499
var values = Stream.of(itemNames).map(cdtoo::get).toArray();
500
var cdtrick2 = new CompositeDataSupport(cdtype2, itemNames, values);
501
// sets the composite data with more fields - the superfluous fields
502
// should be ignored...
503
connection.setAttribute(recname3, new Attribute("Trickster", cdtrick2));
504
// get the composite data we just set
505
var cdtrick3 = (CompositeData) connection.getAttribute(recname3, "Trickster");
506
assertEquals(cdtrick3.getCompositeType().keySet(), Set.of("x", "y"));
507
// get the "Trickster" through the MXBean proxy
508
var trick3 = mBean5.getTrickster();
509
assertEquals(trick3.x(), 6);
510
assertEquals(trick3.y(), 7);
511
assertEquals(trick3.getZ(), -13);
512
assertEquals(trick3.isTricky(), true);
513
// get record that has both x() and getX()
514
var rWithGetter = mBean5.getR();
515
assertEquals(rWithGetter.x(), rWithGetter.getX());
516
assertEquals(rWithGetter.x(), 8);
517
assertEquals(rWithGetter.y(), 9);
518
mBean5.setR(new RWithGetter(rWithGetter.y(), rWithGetter.x()));
519
rWithGetter = mBean5.getR();
520
assertEquals(rWithGetter.x(), rWithGetter.getX());
521
assertEquals(rWithGetter.x(), 9);
522
assertEquals(rWithGetter.y(), 8);
523
524
var withAnno = mBean5.getWithAnno();
525
assertEquals(withAnno.x(), 10);
526
assertEquals(withAnno.y(), 11);
527
withAnno = new WithAnno(12, 13);
528
mBean5.setWithAnno(withAnno);
529
withAnno = mBean5.getWithAnno();
530
assertEquals(withAnno.x(), 12);
531
assertEquals(withAnno.y(), 13);
532
533
// WithCDV.toCompositeData adds 1 to x and 2 to y,
534
// we can check how many time it's been called
535
// by looking at the values for x and y.
536
var cdv = mBean5.getCDV();
537
assertEquals(cdv.x(), 13 /* 12 + 1 */, "x");
538
assertEquals(cdv.y(), 15 /* 13 + 2 */, "y");
539
mBean5.setCDV(new WithCDV(14, 15));
540
cdv = mBean5.getCDV();
541
assertEquals(cdv.x(), 16 /* 14 + 1*2 */, "x");
542
assertEquals(cdv.y(), 19 /* 15 + 2*2 */, "y");
543
544
// Test non compliant records: this one has an Object (not mappable to OpenType)
545
var recname4 = new ObjectName("test:type=NCR1");
546
var x = standard
547
? expectThrows(IllegalArgumentException.class,
548
() -> new StandardMBean(new NC1(), NC1MXBean.class, true))
549
: expectThrows(NotCompliantMBeanException.class,
550
() -> server.registerMBean(new NC1(), recname4));
551
reportExpected(x);
552
assertEquals( originalCause(x).getClass(), OpenDataException.class);
553
554
// Test non compliant records: this one has a List<? super Integer>
555
// (not mappable to OpenType)
556
var recname5 = new ObjectName("test:type=NCR2");
557
var x2 = standard
558
? expectThrows(IllegalArgumentException.class,
559
() -> new StandardMBean(new NC2(), NC2MXBean.class, true))
560
: expectThrows(NotCompliantMBeanException.class,
561
() -> server.registerMBean(new NC2(), recname5));
562
reportExpected(x2);
563
assertEquals( originalCause(x2).getClass(), OpenDataException.class);
564
565
// Test non compliant records: this one has no getters
566
// (not mappable to OpenType)
567
var recname6 = new ObjectName("test:type=NCR3");
568
var x3 = standard
569
? expectThrows(IllegalArgumentException.class,
570
() -> new StandardMBean(new NC3(), NC3MXBean.class, true))
571
: expectThrows(NotCompliantMBeanException.class,
572
() -> server.registerMBean(new NC3(), recname6));
573
reportExpected(x3);
574
assertEquals( originalCause(x3).getClass(), OpenDataException.class);
575
576
// test that a composite data that doesn't have all the records
577
// components prevents the record from being reconstructed.
578
var recname7 = new ObjectName("test:type=Records2,instance=6");
579
Records2 rec2 = new Records2();
580
var mbean7 = standard
581
? new StandardMBean(rec2, Records2MXBean.class, true)
582
: rec2;
583
server.registerMBean(mbean7, recname7);
584
var cd7 = (CompositeData) server.getAttribute(recname7, "DataPoint");
585
var cdt7 = cd7.getCompositeType();
586
var itemNames7 = List.of("x", "mixed")
587
.toArray(String[]::new);
588
var itemDesc7 = Stream.of(itemNames7)
589
.map(cdt7::getDescription)
590
.toArray(String[]::new);
591
var itemTypes7 = Stream.of(itemNames7)
592
.map(cdt7::getType)
593
.toArray(OpenType<?>[]::new);
594
var notmappable = new CompositeType(cdt7.getTypeName(),
595
cdt7.getDescription(),
596
itemNames7,
597
itemDesc7,
598
itemTypes7);
599
var itemValues7 = Stream.of(itemNames7)
600
.map(cd7::get)
601
.toArray();
602
var notmappableVal = new CompositeDataSupport(notmappable, itemNames7, itemValues7);
603
var attribute6 = new Attribute("DataPoint", notmappableVal);
604
var x4 = expectThrows(MBeanException.class,
605
standard ? () -> ((StandardMBean)mbean7).setAttribute(attribute6)
606
: () -> server.setAttribute(recname7, attribute6));
607
reportExpected(x4);
608
assertEquals(originalCause(x4).getClass(), InvalidObjectException.class);
609
610
}
611
612
static final void reportExpected(Throwable x) {
613
System.out.println("\nGot expected exception: " + x);
614
Throwable cause = x;
615
while ((cause = cause.getCause()) != null) {
616
System.out.println("\tCaused by: " + cause);
617
}
618
}
619
620
static final Throwable originalCause(Throwable t) {
621
while (t.getCause() != null) t = t.getCause();
622
return t;
623
}
624
625
static void println(String name, CompositeData cd) {
626
var cdt = cd.getCompositeType();
627
System.out.printf("%s: %s %s\n", name, cdt.getTypeName(),
628
cdt.keySet().stream()
629
.map(k -> k + "=" + cd.get(k))
630
.collect(Collectors.joining(", ", "{ ", " }")));
631
632
}
633
634
}
635
636