Skip to content

Commit

Permalink
8264124: Update MXBean specification and implementation to extend map…
Browse files Browse the repository at this point in the history
…ping of CompositeType to records
  • Loading branch information
dfuch committed Mar 25, 2021
1 parent bc91596 commit bf8b437
Show file tree
Hide file tree
Showing 3 changed files with 667 additions and 32 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
Expand Down Expand Up @@ -443,9 +443,11 @@ private MXBeanMapping makeCompositeMapping(Class<?> c,
if (gcInfoHack && propertyName.equals("CompositeType"))
continue;

Method old =
getterMap.put(decapitalize(propertyName),
method);
// Don't decapitalize if this is a record component name.
// We only decapitalize for getXxxx(), isXxxx(), and setXxxx()
String name = c.isRecord() && method.getName().equals(propertyName)
? propertyName : decapitalize(propertyName);
Method old = getterMap.put(name, method);
if (old != null) {
final String msg =
"Class " + c.getName() + " has method name clash: " +
Expand Down Expand Up @@ -862,6 +864,9 @@ private synchronized void makeCompositeBuilder()
{
new CompositeBuilderViaFrom(targetClass, itemNames),
},
{
new RecordCompositeBuilder(targetClass, itemNames),
},
{
new CompositeBuilderViaConstructor(targetClass, itemNames),
},
Expand Down Expand Up @@ -1139,14 +1144,14 @@ Object fromCompositeData(CompositeData cd,
/** Builder for when the target class has a constructor that is
annotated with {@linkplain ConstructorParameters &#64;ConstructorParameters}
or {@code @ConstructorProperties} so we can see the correspondence to getters. */
private static final class CompositeBuilderViaConstructor
private static class CompositeBuilderViaConstructor
extends CompositeBuilder {

CompositeBuilderViaConstructor(Class<?> targetClass, String[] itemNames) {
super(targetClass, itemNames);
}

private String[] getConstPropValues(Constructor<?> ctr) {
String[] getConstPropValues(Constructor<?> ctr) {
// is constructor annotated by javax.management.ConstructorParameters ?
ConstructorParameters ctrProps = ctr.getAnnotation(ConstructorParameters.class);
if (ctrProps != null) {
Expand All @@ -1171,8 +1176,7 @@ && getConstPropValues(constr) != null)
}

if (annotatedConstrList.isEmpty())
return "no constructor has either @ConstructorParameters " +
"or @ConstructorProperties annotation";
return reportNoConstructor();

annotatedConstructors = newList();

Expand All @@ -1196,17 +1200,15 @@ && getConstPropValues(constr) != null)
// so we can test unambiguity.
Set<BitSet> getterIndexSets = newSet();
for (Constructor<?> constr : annotatedConstrList) {
String annotationName =
constr.isAnnotationPresent(ConstructorParameters.class) ?
"@ConstructorParameters" : "@ConstructorProperties";
String matchingMechanism = matchingMechanism(constr);

String[] propertyNames = getConstPropValues(constr);

Type[] paramTypes = constr.getGenericParameterTypes();
if (paramTypes.length != propertyNames.length) {
final String msg =
"Number of constructor params does not match " +
annotationName + " annotation: " + constr;
referenceMechannism(matchingMechanism) +": " + constr;
throw new InvalidObjectException(msg);
}

Expand All @@ -1219,7 +1221,7 @@ && getConstPropValues(constr) != null)
String propertyName = propertyNames[i];
if (!getterMap.containsKey(propertyName)) {
String msg =
annotationName + " includes name " + propertyName +
matchingMechanism + " includes name " + propertyName +
" which does not correspond to a property";
for (String getterName : getterMap.keySet()) {
if (getterName.equalsIgnoreCase(propertyName)) {
Expand All @@ -1234,7 +1236,7 @@ && getConstPropValues(constr) != null)
paramIndexes[getterIndex] = i;
if (present.get(getterIndex)) {
final String msg =
annotationName + " contains property " +
matchingMechanism + " contains property " +
propertyName + " more than once: " + constr;
throw new InvalidObjectException(msg);
}
Expand All @@ -1243,7 +1245,7 @@ && getConstPropValues(constr) != null)
Type propertyType = getter.getGenericReturnType();
if (!propertyType.equals(paramTypes[i])) {
final String msg =
annotationName + " gives property " + propertyName +
matchingMechanism + " gives property " + propertyName +
" of type " + propertyType + " for parameter " +
" of type " + paramTypes[i] + ": " + constr;
throw new InvalidObjectException(msg);
Expand All @@ -1252,10 +1254,7 @@ && getConstPropValues(constr) != null)

if (!getterIndexSets.add(present)) {
final String msg =
"More than one constructor has " +
"@ConstructorParameters or @ConstructorProperties " +
"annotation with this set of names: " +
Arrays.toString(propertyNames);
reportMultipleConstructorsFoundFor(propertyNames);
throw new InvalidObjectException(msg);
}

Expand Down Expand Up @@ -1292,10 +1291,7 @@ else if (seen) {
i = u.nextSetBit(i+1))
names.add(itemNames[i]);
final String msg =
"Constructors with @ConstructorParameters or " +
"@ConstructorProperties annotation " +
"would be ambiguous for these items: " +
names;
reportConstructorsAmbiguousFor(names);
throw new InvalidObjectException(msg);
}
}
Expand All @@ -1305,7 +1301,41 @@ else if (seen) {
return null; // success!
}

final Object fromCompositeData(CompositeData cd,
String reportNoConstructor() {
return "no constructor has either @ConstructorParameters " +
"or @ConstructorProperties annotation";
}

String matchingMechanism(Constructor<?> constr) {
return constr.isAnnotationPresent(ConstructorParameters.class) ?
"@ConstructorParameters" : "@ConstructorProperties";
}

String referenceMechannism(String matchingMechanism) {
return matchingMechanism + " annotation";
}

String reportMultipleConstructorsFoundFor(String... propertyNames) {
return "More than one constructor has " +
"@ConstructorParameters or @ConstructorProperties " +
"annotation with this set of names: " +
Arrays.toString(propertyNames);
}

String reportConstructorsAmbiguousFor(Set<String> names) {
return "Constructors with @ConstructorParameters or " +
"@ConstructorProperties annotation " +
"would be ambiguous for these items: " +
names;
}

String reportNoConstructorFoundFor(Set<String> names) {
return "No constructor has either @ConstructorParameters " +
"or @ConstructorProperties annotation for this set of " +
"items: " + names;
}

Object fromCompositeData(CompositeData cd,
String[] itemNames,
MXBeanMapping[] mappings)
throws InvalidObjectException {
Expand All @@ -1330,10 +1360,7 @@ final Object fromCompositeData(CompositeData cd,
}

if (max == null) {
final String msg =
"No constructor has either @ConstructorParameters " +
"or @ConstructorProperties annotation for this set of " +
"items: " + ct.keySet();
final String msg = reportNoConstructorFoundFor(ct.keySet());
throw new InvalidObjectException(msg);
}

Expand Down Expand Up @@ -1379,6 +1406,73 @@ private static class Constr {
private List<Constr> annotatedConstructors;
}

/** Builder for when the target class is a record */
private static final class RecordCompositeBuilder
extends CompositeBuilderViaConstructor {

RecordCompositeBuilder(Class<?> targetClass, String[] itemNames) {
super(targetClass, itemNames);
}

String[] getConstPropValues(Constructor<?> ctor) {
var components = getTargetClass().getRecordComponents();
var ptypes = ctor.getGenericParameterTypes();
if (components.length != ptypes.length) {
return super.getConstPropValues(ctor);
}
var len = components.length;
String[] res = new String[len];
for (int i=0; i < len ; i++) {
if (!ptypes[i].equals(components[i].getGenericType())) {
return super.getConstPropValues(ctor);
}
res[i] = components[i].getName();
}
return res;
}

String applicable(Method[] getters) throws InvalidObjectException {
Class<?> targetClass = getTargetClass();
if (!targetClass.isRecord())
return "class is not a record";

return super.applicable(getters);
}

@Override
Object fromCompositeData(CompositeData cd, String[] itemNames, MXBeanMapping[] mappings)
throws InvalidObjectException {
return super.fromCompositeData(cd, itemNames, mappings);
}

String reportNoConstructor() {
return "canonical constructor for record not found";
}

String matchingMechanism(Constructor<?> constr) {
return "canonical constructor";
}

String referenceMechannism(String matchingMechanism) {
return matchingMechanism;
}

String reportMultipleConstructorsFoundFor(String... propertyNames) {
return "More than one constructor has this set of names: " +
Arrays.toString(propertyNames);
}

String reportConstructorsAmbiguousFor(Set<String> names) {
return "Constructors would be ambiguous for these items: " +
names;
}

String reportNoConstructorFoundFor(Set<String> names) {
return "No constructor has this set of " +
"items: " + names;
}
}

/** Builder for when the target class is an interface and contains
no methods other than getters. Then we can make an instance
using a dynamic proxy that forwards the getters to the source
Expand Down Expand Up @@ -1504,7 +1598,16 @@ static String capitalize(String name) {
public static String propertyName(Method m) {
String rest = null;
String name = m.getName();
if (name.startsWith("get"))
var c = m.getDeclaringClass();
if (c.isRecord()) {
for (var rc : c.getRecordComponents()) {
if (name.equals(rc.getName())
&& m.getReturnType() == rc.getType()) {
rest = name;
break;
}
}
} else if (name.startsWith("get"))
rest = name.substring(3);
else if (name.startsWith("is") && m.getReturnType() == boolean.class)
rest = name.substring(2);
Expand Down
34 changes: 31 additions & 3 deletions src/java.management/share/classes/javax/management/MXBean.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
Expand Down Expand Up @@ -753,7 +753,9 @@ another proxy (like {@code ModuleMXBean.getProduct()} which
{@code CompositeType} is determined by the <a href="#type-names">
type name rules</a> below.</p>
<p>The class is examined for getters using the conventions
<p>If the class is a {@link Record}, its getters are the
accessors for the record components. Otherwise, the
class is examined for getters using the conventions
<a href="#naming-conv">above</a>. (Getters must be public
instance methods.) If there are no getters, or if
any getter has a type that is not convertible, then <em>J</em> is
Expand Down Expand Up @@ -784,7 +786,13 @@ <code>String getOwner()</code>
then the item in the {@code CompositeType} is called {@code name}
and has type {@code SimpleType.BOOLEAN}.
<p>Notice that the first character (or code point) is converted to
<p>If the class is a {@link Record} and the getter is a component
accessor for a record component {@code name} of type <em>T</em>,
then the item in the {@code CompositeType} has the same name
than the record component, and has type <em>opentype(T)</em>.</p>
<p>Notice that unless the class is a {@code Record}, the
first character (or code point) is converted to
lower case. This follows the Java Beans convention, which for
historical reasons is different from the Standard MBean
convention. In a Standard MBean or MXBean interface, a method
Expand Down Expand Up @@ -846,6 +854,17 @@ implements the interface {@link CompositeDataView}, then that
then that method is called to reconstruct an instance of
<em>J</em>.</p></li>
<li><p>Otherwise, if <em>J</em> is a {@link Record} class,
and the record canonical constructor is applicable,
an instance of <em>J</em> is reconstructed by calling
the record canonical constructor.
The canonical constructor, if applicable, is called
with the appropriate reconstructed items from the
{@code CompositeData}. The canonical constructor
is <em>applicable</em> if all the properties named
by the record components are present in the
{@code CompositeData}.</p></li>
<li><p>Otherwise, if <em>J</em> has at least one public
constructor with either {@link javax.management.ConstructorParameters
&#64;javax.management.ConstructorParameters} or
Expand Down Expand Up @@ -940,6 +959,15 @@ is not visible (e.g. when the java.desktop module is not readable or when
</blockquote>
<ol>
<li>Record:
<blockquote>
<pre>
public record NamedNumber(int number, String name) {}
</pre>
</blockquote>
</li>
<li>Static {@code from} method:
<blockquote>
Expand Down

0 comments on commit bf8b437

Please sign in to comment.