Skip to content

Commit 27e531c

Browse files
committed
#80 - Investigate use of MethodHandles instead of Constructor and Method references
1 parent 8dd7e1d commit 27e531c

11 files changed

+197
-78
lines changed

src/main/java/org/hibernate/models/internal/AnnotationHelper.java

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@
99
import java.lang.annotation.Annotation;
1010
import java.lang.annotation.Inherited;
1111
import java.lang.annotation.Target;
12-
import java.lang.reflect.InvocationTargetException;
1312
import java.util.EnumSet;
1413
import java.util.List;
15-
import java.util.Locale;
1614

17-
import org.hibernate.models.AnnotationAccessException;
1815
import org.hibernate.models.spi.AnnotationDescriptor;
1916
import org.hibernate.models.spi.AnnotationTarget;
2017
import org.hibernate.models.spi.AttributeDescriptor;
18+
import org.hibernate.models.spi.JdkValueExtractor;
2119
import org.hibernate.models.spi.RenderingCollector;
2220
import org.hibernate.models.spi.SourceModelBuildingContext;
2321

@@ -67,7 +65,7 @@ public static <A extends Annotation> void render(
6765
attributes.forEach( (attribute) -> attribute.getTypeDescriptor().render(
6866
collector,
6967
attribute.getName(),
70-
extractValue( annotation, attribute ),
68+
extractValue( annotation, attribute, context ),
7169
context
7270
) );
7371

@@ -107,7 +105,7 @@ public static <A extends Annotation> void render(
107105
attributes.forEach( (attribute) -> attribute.getTypeDescriptor().render(
108106
collector,
109107
attribute.getName(),
110-
extractValue( annotation, attribute ),
108+
extractValue( annotation, attribute, context ),
111109
context
112110
) );
113111

@@ -116,21 +114,11 @@ public static <A extends Annotation> void render(
116114
}
117115
}
118116

119-
public static <A extends Annotation, R> R extractValue(A annotationUsage, AttributeDescriptor<R> attributeDescriptor) {
120-
try {
121-
//noinspection unchecked
122-
return (R) attributeDescriptor.getAttributeMethod().invoke( annotationUsage );
123-
}
124-
catch (IllegalAccessException | InvocationTargetException e) {
125-
throw new AnnotationAccessException(
126-
String.format(
127-
Locale.ROOT,
128-
"Unable to access annotation attribute value : %s.%s",
129-
annotationUsage.annotationType().getName(),
130-
attributeDescriptor.getName()
131-
),
132-
e
133-
);
134-
}
117+
public static <A extends Annotation, R> R extractValue(
118+
A annotationUsage,
119+
AttributeDescriptor<R> attributeDescriptor,
120+
SourceModelBuildingContext modelContext) {
121+
final JdkValueExtractor<R> valueExtractor = attributeDescriptor.getTypeDescriptor().createJdkValueExtractor( modelContext );
122+
return valueExtractor.extractValue( annotationUsage, attributeDescriptor, modelContext );
135123
}
136124
}

src/main/java/org/hibernate/models/internal/AnnotationUsageHelper.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import org.hibernate.models.spi.AnnotationDescriptor;
1818
import org.hibernate.models.spi.AttributeDescriptor;
1919
import org.hibernate.models.spi.SourceModelBuildingContext;
20-
import org.hibernate.models.spi.SourceModelContext;
2120

2221
import org.jboss.jandex.AnnotationInstance;
2322

@@ -123,7 +122,7 @@ public static <A extends Annotation, C extends Annotation> void forEachRepeatedA
123122
if ( container != null ) {
124123
final AnnotationDescriptor<C> containerDescriptor = modelContext.getAnnotationDescriptorRegistry().getDescriptor( containerType );
125124
final AttributeDescriptor<A[]> attribute = containerDescriptor.getAttribute( "value" );
126-
final A[] repetitions = AnnotationHelper.extractValue( container, attribute );
125+
final A[] repetitions = AnnotationHelper.extractValue( container, attribute, modelContext );
127126
CollectionHelper.forEach( repetitions, consumer );
128127
}
129128
}
@@ -144,7 +143,7 @@ public static <A extends Annotation> void forEachRepeatedAnnotationUsages(
144143
if ( container != null ) {
145144
final AnnotationDescriptor<?> containerDescriptor = modelContext.getAnnotationDescriptorRegistry().getDescriptor( containerType );
146145
final AttributeDescriptor<A[]> attribute = containerDescriptor.getAttribute( "value" );
147-
final A[] repetitions = AnnotationHelper.extractValue( container, attribute );
146+
final A[] repetitions = AnnotationHelper.extractValue( container, attribute, modelContext );
148147
CollectionHelper.forEach( repetitions, consumer );
149148
}
150149
}
@@ -162,7 +161,7 @@ public static <A extends Annotation> A[] getRepeatedUsages(
162161

163162
if ( containerUsage != null ) {
164163
final AttributeDescriptor<A[]> attribute = type.getRepeatableContainer().getAttribute( "value" );
165-
final A[] repeatableValues = AnnotationHelper.extractValue( containerUsage, attribute );
164+
final A[] repeatableValues = AnnotationHelper.extractValue( containerUsage, attribute, modelContext );
166165

167166
if ( CollectionHelper.isNotEmpty( repeatableValues ) ) {
168167
if ( usage != null ) {
@@ -227,9 +226,9 @@ private static <A extends Annotation> boolean nameMatches(
227226
AnnotationDescriptor<A> descriptor,
228227
String matchValue,
229228
String attributeToMatch,
230-
SourceModelContext modelContext) {
229+
SourceModelBuildingContext modelContext) {
231230
final AttributeDescriptor<String> attributeDescriptor = descriptor.getAttribute( attributeToMatch );
232-
final String usageName = AnnotationHelper.extractValue( annotationUsage, attributeDescriptor );
231+
final String usageName = AnnotationHelper.extractValue( annotationUsage, attributeDescriptor, modelContext );
233232
return matchValue.equals( usageName );
234233
}
235234

src/main/java/org/hibernate/models/internal/AttributeDescriptorImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ public ValueTypeDescriptor<T> getTypeDescriptor() {
5252
return typeDescriptor;
5353
}
5454

55+
@Override
56+
public T getDefaultValue() {
57+
//noinspection unchecked
58+
return (T) method.getDefaultValue();
59+
}
60+
5561
@Override
5662
public Method getAttributeMethod() {
5763
return method;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
* Copyright: Red Hat Inc. and Hibernate Authors
6+
*/
7+
8+
package org.hibernate.models.internal;
9+
10+
import org.hibernate.models.ModelsException;
11+
12+
/**
13+
* @author Steve Ebersole
14+
*/
15+
public class MethodInvocationException extends ModelsException {
16+
public MethodInvocationException(String message) {
17+
super( message );
18+
}
19+
20+
public MethodInvocationException(String message, Throwable cause) {
21+
super( message, cause );
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
* Copyright: Red Hat Inc. and Hibernate Authors
6+
*/
7+
8+
package org.hibernate.models.internal;
9+
10+
import org.hibernate.models.ModelsException;
11+
12+
/**
13+
* @author Steve Ebersole
14+
*/
15+
public class MethodResolutionException extends ModelsException {
16+
public MethodResolutionException(String message) {
17+
super( message );
18+
}
19+
20+
public MethodResolutionException(String message, Throwable cause) {
21+
super( message, cause );
22+
}
23+
}

src/main/java/org/hibernate/models/internal/OrmAnnotationDescriptor.java

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
package org.hibernate.models.internal;
99

1010
import java.lang.annotation.Annotation;
11-
import java.lang.reflect.Constructor;
12-
import java.lang.reflect.InvocationTargetException;
11+
import java.lang.invoke.MethodHandle;
12+
import java.lang.invoke.MethodHandles;
13+
import java.lang.invoke.MethodType;
1314
import java.util.Collections;
1415
import java.util.List;
1516
import java.util.Map;
@@ -26,14 +27,17 @@
2627
* does not collect annotations from the annotation class as we never care about
2728
* meta-annotations in these cases.
2829
*
30+
* @implNote There are a few cases in Hibernate ORM e.g. where we do care about meta-annotations,
31+
* but those are handled specially there.
32+
*
2933
* @author Steve Ebersole
3034
*/
3135
public class OrmAnnotationDescriptor<A extends Annotation, C extends A> extends AbstractAnnotationDescriptor<A> {
3236
private final Class<C> concreteClass;
3337
private final List<AttributeDescriptor<?>> attributeDescriptors;
3438

35-
private DynamicCreator<A,C> dynamicCreator;
36-
private JdkCreator<A,C> jdkCreator;
39+
private final DynamicCreator<A,C> dynamicCreator;
40+
private final JdkCreator<A,C> jdkCreator;
3741
private JandexCreator<A,C> jandexCreator;
3842

3943
public OrmAnnotationDescriptor(
@@ -55,6 +59,9 @@ public OrmAnnotationDescriptor(
5559

5660
this.concreteClass = concreteClass;
5761
this.attributeDescriptors = AnnotationDescriptorBuilding.extractAttributeDescriptors( annotationType );
62+
63+
this.dynamicCreator = new DynamicCreator<>( annotationType, concreteClass );
64+
this.jdkCreator = new JdkCreator<>( annotationType, concreteClass );
5865
}
5966

6067
@Override
@@ -66,17 +73,11 @@ public OrmAnnotationDescriptor(
6673

6774
@Override
6875
public C createUsage(SourceModelBuildingContext context) {
69-
if ( dynamicCreator == null ) {
70-
dynamicCreator = new DynamicCreator<>( getAnnotationType(), concreteClass );
71-
}
7276
return dynamicCreator.createUsage( context );
7377
}
7478

7579
@Override
7680
public C createUsage(A jdkAnnotation, SourceModelBuildingContext context) {
77-
if ( jdkCreator == null ) {
78-
jdkCreator = new JdkCreator<>( getAnnotationType(), concreteClass );
79-
}
8081
return jdkCreator.createUsage( jdkAnnotation, context );
8182
}
8283

@@ -99,93 +100,105 @@ public String toString() {
99100
}
100101

101102
public static class DynamicCreator<A extends Annotation, C extends A> {
102-
private final Constructor<C> constructor;
103+
private final MethodHandle constructor;
104+
private final Class<C> concreteClass;
103105

104106
public DynamicCreator(Class<A> annotationType, Class<C> concreteClass) {
105-
this( resolveConstructor( concreteClass ) );
107+
this( resolveConstructor( concreteClass ), concreteClass );
106108
}
107109

108-
private static <A extends Annotation, C extends A> Constructor<C> resolveConstructor(Class<C> concreteClass) {
110+
private static <A extends Annotation, C extends A> MethodHandle resolveConstructor(Class<C> concreteClass) {
109111
try {
110-
return concreteClass.getDeclaredConstructor( SourceModelBuildingContext.class );
112+
final MethodType methodType = MethodType.methodType( void.class, SourceModelBuildingContext.class );
113+
return MethodHandles.publicLookup().findConstructor( concreteClass, methodType );
111114
}
112-
catch (NoSuchMethodException e) {
113-
throw new RuntimeException( e );
115+
catch (Exception e) {
116+
throw new MethodResolutionException( "Unable to locate default-variant constructor for `" + concreteClass.getName() + "`", e );
114117
}
115118
}
116119

117-
public DynamicCreator(Constructor<C> constructor) {
120+
public DynamicCreator(MethodHandle constructor, Class<C> concreteClass) {
118121
this.constructor = constructor;
122+
this.concreteClass = concreteClass;
119123
}
120124

121125
public C createUsage(SourceModelBuildingContext context) {
122126
try {
123-
return constructor.newInstance( context );
127+
//noinspection unchecked
128+
return (C) constructor.invoke( context );
124129
}
125-
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
126-
throw new RuntimeException( e );
130+
catch (Throwable e) {
131+
throw new MethodInvocationException( "Unable to invoke default-variant constructor for `" + concreteClass.getName() + "`", e );
127132
}
128133
}
129134
}
130135

131136
public static class JdkCreator<A extends Annotation, C extends A> {
132-
private final Constructor<C> constructor;
137+
private final MethodHandle constructor;
138+
private final Class<C> concreteClass;
133139

134140
public JdkCreator(Class<A> annotationType, Class<C> concreteClass) {
135-
this( resolveConstructor( annotationType, concreteClass ) );
141+
this( resolveConstructor( annotationType, concreteClass ), concreteClass );
136142
}
137143

138-
private static <A extends Annotation, C extends A> Constructor<C> resolveConstructor(
144+
private static <A extends Annotation, C extends A> MethodHandle resolveConstructor(
139145
Class<A> annotationType,
140146
Class<C> concreteClass) {
141147
try {
142-
return concreteClass.getDeclaredConstructor( annotationType, SourceModelBuildingContext.class );
148+
final MethodType methodType = MethodType.methodType( void.class, annotationType, SourceModelBuildingContext.class );
149+
return MethodHandles.publicLookup().findConstructor( concreteClass, methodType );
143150
}
144-
catch (NoSuchMethodException e) {
145-
throw new RuntimeException( e );
151+
catch (Exception e) {
152+
throw new MethodResolutionException( "Unable to locate JDK-variant constructor for `" + concreteClass.getName() + "`", e );
146153
}
147154
}
148155

149-
public JdkCreator(Constructor<C> constructor) {
156+
public JdkCreator(MethodHandle constructor, Class<C> concreteClass) {
150157
this.constructor = constructor;
158+
this.concreteClass = concreteClass;
151159
}
152160

153161
public C createUsage(A jdkAnnotation, SourceModelBuildingContext context) {
154162
try {
155-
return constructor.newInstance( jdkAnnotation, context );
163+
//noinspection unchecked
164+
return (C) constructor.invoke( jdkAnnotation, context );
156165
}
157-
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
158-
throw new RuntimeException( e );
166+
catch (Throwable e) {
167+
throw new MethodInvocationException( "Unable to invoke JDK-variant constructor for `" + concreteClass.getName() + "`", e );
159168
}
160169
}
161170
}
162171

163172
public static class JandexCreator<A extends Annotation, C extends A> {
164-
private final Constructor<C> constructor;
173+
private final MethodHandle constructor;
174+
private final Class<C> concreteClass;
165175

166176
public JandexCreator(Class<C> concreteClass) {
167-
this( resolveConstructor( concreteClass ) );
177+
this( resolveConstructor( concreteClass ), concreteClass );
168178
}
169179

170-
private static <A extends Annotation, C extends A> Constructor<C> resolveConstructor(Class<C> concreteClass) {
180+
private static <A extends Annotation, C extends A> MethodHandle resolveConstructor(Class<C> concreteClass) {
171181
try {
172-
return concreteClass.getDeclaredConstructor( AnnotationInstance.class, SourceModelBuildingContext.class );
182+
final MethodType methodType = MethodType.methodType( void.class, AnnotationInstance.class, SourceModelBuildingContext.class );
183+
return MethodHandles.publicLookup().findConstructor( concreteClass, methodType );
173184
}
174-
catch (NoSuchMethodException e) {
175-
throw new RuntimeException( e );
185+
catch (Exception e) {
186+
throw new MethodResolutionException( "Unable to locate Jandex-variant constructor for `" + concreteClass.getName() + "`", e );
176187
}
177188
}
178189

179-
public JandexCreator(Constructor<C> constructor) {
190+
public JandexCreator(MethodHandle constructor, Class<C> concreteClass) {
180191
this.constructor = constructor;
192+
this.concreteClass = concreteClass;
181193
}
182194

183195
public C createUsage(AnnotationInstance jandexAnnotation, SourceModelBuildingContext context) {
184196
try {
185-
return constructor.newInstance( jandexAnnotation, context );
197+
//noinspection unchecked
198+
return (C) constructor.invoke( jandexAnnotation, context );
186199
}
187-
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
188-
throw new RuntimeException( e );
200+
catch (Throwable e) {
201+
throw new MethodInvocationException( "Unable to invoke Jandex-variant constructor for `" + concreteClass.getName() + "`", e );
189202
}
190203
}
191204
}

src/main/java/org/hibernate/models/internal/jandex/JandexValueHelper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ public static <V> V extractOptionalValue(
6363
SourceModelBuildingContext modelContext) {
6464
final AnnotationValue value = usage.value( attributeDescriptor.getName() );
6565
if ( value == null ) {
66-
//noinspection unchecked
67-
return (V) attributeDescriptor.getAttributeMethod().getDefaultValue();
66+
return attributeDescriptor.getDefaultValue();
6867
}
6968
return attributeDescriptor.getTypeDescriptor().createJandexValueConverter( modelContext ).convert( value, modelContext );
7069
}

0 commit comments

Comments
 (0)