Skip to content

Commit ba3359f

Browse files
raphwsebersole
authored andcommitted
HHH-11152: Added BytecodeProvider based on Byte Buddy
1 parent 31a60b8 commit ba3359f

File tree

77 files changed

+4249
-624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+4249
-624
lines changed

build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ subprojects { subProject ->
154154

155155
testRuntime( libraries.log4j )
156156
testRuntime( libraries.javassist )
157+
testRuntime( libraries.byteBuddy )
157158
testRuntime( libraries.woodstox )
158159

159160
//Databases

documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
org.hibernate.cfg,
7777
org.hibernate.service,
7878
javax.persistence;version="[1.0.0,2.1.0]",
79-
<!-- Needed for proxying's Javassist enhancement during runtime -->
79+
<!-- Needed for proxy enhancement during runtime -->
8080
org.hibernate.proxy,
8181
javassist.util.proxy,
8282
*

hibernate-core/hibernate-core.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ configurations {
2323
dependencies {
2424
compile( libraries.jpa )
2525
compile( libraries.javassist )
26+
compile( libraries.byteBuddy )
2627
compile( libraries.antlr )
2728
compile( libraries.jta )
2829
compile( libraries.jandex )
@@ -95,6 +96,7 @@ dependencies {
9596
testRuntime( libraries.expression_language_impl )
9697
testRuntime( 'jaxen:jaxen:1.1' )
9798
testRuntime( libraries.javassist )
99+
testRuntime( libraries.byteBuddy )
98100

99101
testCompile( project( ':hibernate-jpamodelgen' ) )
100102

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
8+
9+
import java.util.Collection;
10+
import java.util.Map;
11+
import javax.persistence.Access;
12+
import javax.persistence.AccessType;
13+
import javax.persistence.ManyToMany;
14+
import javax.persistence.ManyToOne;
15+
import javax.persistence.OneToMany;
16+
import javax.persistence.OneToOne;
17+
18+
import org.hibernate.bytecode.enhance.spi.EnhancementException;
19+
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
20+
import org.hibernate.internal.CoreLogging;
21+
import org.hibernate.internal.CoreMessageLogger;
22+
23+
import net.bytebuddy.asm.Advice;
24+
import net.bytebuddy.description.annotation.AnnotationDescription;
25+
import net.bytebuddy.description.annotation.AnnotationValue;
26+
import net.bytebuddy.description.field.FieldDescription;
27+
import net.bytebuddy.description.method.MethodDescription;
28+
import net.bytebuddy.description.type.TypeDescription;
29+
import net.bytebuddy.dynamic.scaffold.FieldLocator;
30+
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
31+
import net.bytebuddy.implementation.Implementation;
32+
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
33+
import net.bytebuddy.jar.asm.MethodVisitor;
34+
import net.bytebuddy.jar.asm.Opcodes;
35+
import net.bytebuddy.jar.asm.Type;
36+
37+
class BiDirectionalAssociationHandler implements Implementation {
38+
39+
private static final CoreMessageLogger log = CoreLogging.messageLogger( BiDirectionalAssociationHandler.class );
40+
41+
static Implementation wrap(
42+
TypeDescription managedCtClass,
43+
ByteBuddyEnhancementContext enhancementContext,
44+
FieldDescription persistentField,
45+
Implementation implementation) {
46+
if ( !enhancementContext.doBiDirectionalAssociationManagement( persistentField ) ) {
47+
return implementation;
48+
}
49+
50+
TypeDescription targetEntity = getTargetEntityClass( managedCtClass, persistentField );
51+
if ( targetEntity == null ) {
52+
return implementation;
53+
}
54+
String mappedBy = getMappedBy( persistentField, targetEntity, enhancementContext );
55+
if ( mappedBy == null || mappedBy.isEmpty() ) {
56+
log.infof(
57+
"Could not find bi-directional association for field [%s#%s]",
58+
managedCtClass.getName(),
59+
persistentField.getName()
60+
);
61+
return implementation;
62+
}
63+
64+
TypeDescription targetType = FieldLocator.ForClassHierarchy.Factory.INSTANCE.make( targetEntity )
65+
.locate( mappedBy )
66+
.getField()
67+
.getType()
68+
.asErasure();
69+
70+
if ( EnhancerImpl.isAnnotationPresent( persistentField, OneToOne.class ) ) {
71+
implementation = Advice.withCustomMapping()
72+
.bind( CodeTemplates.FieldValue.class, persistentField )
73+
.bind( CodeTemplates.MappedBy.class, mappedBy )
74+
.to( CodeTemplates.OneToOneHandler.class )
75+
.wrap( implementation );
76+
}
77+
78+
if ( EnhancerImpl.isAnnotationPresent( persistentField, OneToMany.class ) ) {
79+
implementation = Advice.withCustomMapping()
80+
.bind( CodeTemplates.FieldValue.class, persistentField )
81+
.bind( CodeTemplates.MappedBy.class, mappedBy )
82+
.to( persistentField.getType().asErasure().isAssignableTo( Map.class )
83+
? CodeTemplates.OneToManyOnMapHandler.class
84+
: CodeTemplates.OneToManyOnCollectionHandler.class )
85+
.wrap( implementation );
86+
}
87+
88+
if ( EnhancerImpl.isAnnotationPresent( persistentField, ManyToOne.class ) ) {
89+
implementation = Advice.withCustomMapping()
90+
.bind( CodeTemplates.FieldValue.class, persistentField )
91+
.bind( CodeTemplates.MappedBy.class, mappedBy )
92+
.to( CodeTemplates.ManyToOneHandler.class )
93+
.wrap( implementation );
94+
}
95+
96+
if ( EnhancerImpl.isAnnotationPresent( persistentField, ManyToMany.class ) ) {
97+
98+
if ( persistentField.getType().asErasure().isAssignableTo( Map.class ) || targetType.isAssignableTo( Map.class ) ) {
99+
log.infof(
100+
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
101+
managedCtClass.getName(),
102+
persistentField.getName()
103+
);
104+
return implementation;
105+
}
106+
107+
implementation = Advice.withCustomMapping()
108+
.bind( CodeTemplates.FieldValue.class, persistentField )
109+
.bind( CodeTemplates.MappedBy.class, mappedBy )
110+
.to( CodeTemplates.ManyToManyHandler.class )
111+
.wrap( implementation );
112+
}
113+
114+
return new BiDirectionalAssociationHandler( implementation, targetEntity, targetType, mappedBy );
115+
}
116+
117+
public static TypeDescription getTargetEntityClass(TypeDescription managedCtClass, FieldDescription persistentField) {
118+
try {
119+
AnnotationDescription.Loadable<OneToOne> oto = EnhancerImpl.getAnnotation( persistentField, OneToOne.class );
120+
AnnotationDescription.Loadable<OneToMany> otm = EnhancerImpl.getAnnotation( persistentField, OneToMany.class );
121+
AnnotationDescription.Loadable<ManyToOne> mto = EnhancerImpl.getAnnotation( persistentField, ManyToOne.class );
122+
AnnotationDescription.Loadable<ManyToMany> mtm = EnhancerImpl.getAnnotation( persistentField, ManyToMany.class );
123+
124+
if ( oto == null && otm == null && mto == null && mtm == null ) {
125+
return null;
126+
}
127+
128+
AnnotationValue<?, ?> targetClass = null;
129+
if ( oto != null ) {
130+
targetClass = oto.getValue( new MethodDescription.ForLoadedMethod( OneToOne.class.getDeclaredMethod( "targetEntity" ) ) );
131+
}
132+
if ( otm != null ) {
133+
targetClass = otm.getValue( new MethodDescription.ForLoadedMethod( OneToMany.class.getDeclaredMethod( "targetEntity" ) ) );
134+
}
135+
if ( mto != null ) {
136+
targetClass = mto.getValue( new MethodDescription.ForLoadedMethod( ManyToOne.class.getDeclaredMethod( "targetEntity" ) ) );
137+
}
138+
if ( mtm != null ) {
139+
targetClass = mtm.getValue( new MethodDescription.ForLoadedMethod( ManyToMany.class.getDeclaredMethod( "targetEntity" ) ) );
140+
}
141+
142+
if ( targetClass == null ) {
143+
log.infof(
144+
"Could not find type of bi-directional association for field [%s#%s]",
145+
managedCtClass.getName(),
146+
persistentField.getName()
147+
);
148+
return null;
149+
}
150+
else if ( !targetClass.resolve( TypeDescription.class ).represents( void.class ) ) {
151+
return targetClass.resolve( TypeDescription.class );
152+
}
153+
}
154+
catch (NoSuchMethodException ignored) {
155+
}
156+
157+
return entityType( target( persistentField ) );
158+
}
159+
160+
private static TypeDescription.Generic target(FieldDescription persistentField) {
161+
AnnotationDescription.Loadable<Access> access = persistentField.getDeclaringType().asErasure().getDeclaredAnnotations().ofType( Access.class );
162+
if ( access != null && access.loadSilent().value() == AccessType.FIELD ) {
163+
return persistentField.getType();
164+
}
165+
else {
166+
MethodDescription getter = EnhancerImpl.getterOf( persistentField );
167+
if ( getter == null ) {
168+
return persistentField.getType();
169+
}
170+
else {
171+
return getter.getReturnType();
172+
}
173+
}
174+
}
175+
176+
private static String getMappedBy(FieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
177+
String mappedBy = getMappedByNotManyToMany( target );
178+
if ( mappedBy == null || mappedBy.isEmpty() ) {
179+
return getMappedByManyToMany( target, targetEntity, context );
180+
}
181+
else {
182+
return mappedBy;
183+
}
184+
}
185+
186+
private static String getMappedByNotManyToMany(FieldDescription target) {
187+
try {
188+
AnnotationDescription.Loadable<OneToOne> oto = EnhancerImpl.getAnnotation( target, OneToOne.class );
189+
if ( oto != null ) {
190+
return oto.getValue( new MethodDescription.ForLoadedMethod( OneToOne.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
191+
}
192+
193+
AnnotationDescription.Loadable<OneToMany> otm = EnhancerImpl.getAnnotation( target, OneToMany.class );
194+
if ( otm != null ) {
195+
return otm.getValue( new MethodDescription.ForLoadedMethod( OneToMany.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
196+
}
197+
198+
AnnotationDescription.Loadable<ManyToMany> mtm = EnhancerImpl.getAnnotation( target, ManyToMany.class );
199+
if ( mtm != null ) {
200+
return mtm.getValue( new MethodDescription.ForLoadedMethod( ManyToMany.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
201+
}
202+
}
203+
catch (NoSuchMethodException ignored) {
204+
}
205+
206+
return null;
207+
}
208+
209+
private static String getMappedByManyToMany(FieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
210+
for ( FieldDescription f : targetEntity.getDeclaredFields() ) {
211+
if ( context.isPersistentField( f )
212+
&& target.getName().equals( getMappedByNotManyToMany( f ) )
213+
&& target.getDeclaringType().asErasure().isAssignableTo( entityType( f.getType() ) ) ) {
214+
log.debugf(
215+
"mappedBy association for field [%s#%s] is [%s#%s]",
216+
target.getDeclaringType().asErasure().getName(),
217+
target.getName(),
218+
targetEntity.getName(),
219+
f.getName()
220+
);
221+
return f.getName();
222+
}
223+
}
224+
return null;
225+
}
226+
227+
private static TypeDescription entityType(TypeDescription.Generic type) {
228+
if ( type.getSort().isParameterized() ) {
229+
if ( type.asErasure().isAssignableTo( Collection.class ) ) {
230+
return type.getTypeArguments().get( 0 ).asErasure();
231+
}
232+
if ( type.asErasure().isAssignableTo( Map.class ) ) {
233+
return type.getTypeArguments().get( 1 ).asErasure();
234+
}
235+
}
236+
237+
return type.asErasure();
238+
}
239+
240+
private final Implementation delegate;
241+
242+
private final TypeDescription targetEntity;
243+
244+
private final TypeDescription targetType;
245+
246+
private final String mappedBy;
247+
248+
private BiDirectionalAssociationHandler(
249+
Implementation delegate,
250+
TypeDescription targetEntity,
251+
TypeDescription targetType,
252+
String mappedBy) {
253+
this.delegate = delegate;
254+
this.targetEntity = targetEntity;
255+
this.targetType = targetType;
256+
this.mappedBy = mappedBy;
257+
}
258+
259+
@Override
260+
public ByteCodeAppender appender(Target implementationTarget) {
261+
return new WrappingAppender( delegate.appender( implementationTarget ) );
262+
}
263+
264+
@Override
265+
public InstrumentedType prepare(InstrumentedType instrumentedType) {
266+
return delegate.prepare( instrumentedType );
267+
}
268+
269+
private class WrappingAppender implements ByteCodeAppender {
270+
271+
private final ByteCodeAppender delegate;
272+
273+
private WrappingAppender(ByteCodeAppender delegate) {
274+
this.delegate = delegate;
275+
}
276+
277+
@Override
278+
public Size apply(
279+
MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
280+
return delegate.apply( new MethodVisitor( Opcodes.ASM5, methodVisitor ) {
281+
282+
@Override
283+
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
284+
if ( owner.startsWith( Type.getInternalName( CodeTemplates.class ) ) ) {
285+
if ( name.equals( "getter" ) ) {
286+
super.visitTypeInsn( Opcodes.CHECKCAST, targetEntity.getInternalName() );
287+
super.visitMethodInsn(
288+
Opcodes.INVOKEVIRTUAL,
289+
targetEntity.getInternalName(),
290+
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + mappedBy,
291+
Type.getMethodDescriptor( Type.getType( targetType.getDescriptor() ) ),
292+
false
293+
);
294+
}
295+
else if ( name.equals( "setterSelf" ) ) {
296+
super.visitInsn( Opcodes.POP );
297+
super.visitTypeInsn( Opcodes.CHECKCAST, targetEntity.getInternalName() );
298+
super.visitVarInsn( Opcodes.ALOAD, 0 );
299+
super.visitMethodInsn(
300+
Opcodes.INVOKEVIRTUAL,
301+
targetEntity.getInternalName(),
302+
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy,
303+
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( targetType.getDescriptor() ) ),
304+
false
305+
);
306+
}
307+
else if ( name.equals( "setterNull" ) ) {
308+
super.visitInsn( Opcodes.POP );
309+
super.visitTypeInsn( Opcodes.CHECKCAST, targetEntity.getInternalName() );
310+
super.visitInsn( Opcodes.ACONST_NULL );
311+
super.visitMethodInsn(
312+
Opcodes.INVOKEVIRTUAL,
313+
targetEntity.getInternalName(),
314+
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy,
315+
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( targetType.getDescriptor() ) ),
316+
false
317+
);
318+
}
319+
else {
320+
throw new EnhancementException( "Unknown template method: " + name );
321+
}
322+
}
323+
else {
324+
super.visitMethodInsn( opcode, owner, name, desc, itf );
325+
}
326+
}
327+
}, implementationContext, instrumentedMethod );
328+
}
329+
}
330+
}

0 commit comments

Comments
 (0)