Skip to content

Commit 60836cd

Browse files
committed
HHH-7667 - Investigate expanding bytecode enhancement support
(cherry picked from commit 5506a48)
1 parent 889405b commit 60836cd

File tree

6 files changed

+271
-47
lines changed

6 files changed

+271
-47
lines changed

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancementContext.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,40 @@
2323
*/
2424
package org.hibernate.bytecode.enhance.spi;
2525

26-
import java.security.ProtectionDomain;
26+
import javassist.CtField;
2727

2828
/**
2929
* @author Steve Ebersole
3030
*/
3131
public interface EnhancementContext {
32+
/**
33+
* Does the given class name represent a entity class?
34+
*
35+
* @param className The name of the class to check.
36+
*
37+
* @return {@code true} if the class is an entity; {@code false} otherwise.
38+
*/
3239
public boolean isEntityClass(String className);
40+
41+
/**
42+
* Does the given class name represent an embeddable/component class?
43+
*
44+
* @param className The name of the class to check.
45+
*
46+
* @return {@code true} if the class is an embeddable/component; {@code false} otherwise.
47+
*/
3348
public boolean isCompositeClass(String className);
49+
50+
/**
51+
* Does the field represent persistent state? Persistent fields will be "enhanced".
52+
* <p/>
53+
* todo : not sure its a great idea to expose Javassist classes this way.
54+
// may be better to perform basic checks in the caller (non-static, etc) and call out with just the
55+
// Class name and field name...
56+
57+
* @param ctField The field reference.
58+
*
59+
* @return {@code true} if the field is ; {@code false} otherwise.
60+
*/
61+
public boolean isPersistentField(CtField ctField);
3462
}

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/Enhancer.java

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
import java.io.ByteArrayOutputStream;
2929
import java.io.DataOutputStream;
3030
import java.io.IOException;
31+
import java.util.IdentityHashMap;
3132

3233
import javassist.CannotCompileException;
3334
import javassist.ClassPool;
3435
import javassist.CtClass;
3536
import javassist.CtField;
37+
import javassist.CtMethod;
3638
import javassist.CtNewMethod;
3739
import javassist.Modifier;
3840
import javassist.bytecode.AnnotationsAttribute;
@@ -44,7 +46,7 @@
4446

4547
import org.hibernate.HibernateException;
4648
import org.hibernate.bytecode.enhance.EnhancementException;
47-
import org.hibernate.bytecode.internal.javassist.FieldHandled;
49+
import org.hibernate.engine.spi.Managed;
4850
import org.hibernate.engine.spi.ManagedComposite;
4951
import org.hibernate.engine.spi.ManagedEntity;
5052
import org.hibernate.engine.spi.EntityEntry;
@@ -57,6 +59,9 @@
5759
public class Enhancer {
5860
private static final CoreMessageLogger log = Logger.getMessageLogger( CoreMessageLogger.class, Enhancer.class.getName() );
5961

62+
private static final String PERSISTENT_FIELD_READER_PREFIX = "$hibernate_read_";
63+
private static final String PERSISTENT_FIELD_WRITER_PREFIX = "$hibernate_write_";
64+
6065
public static final String ENTITY_INSTANCE_GETTER_NAME = "hibernate_getEntityInstance";
6166

6267
public static final String ENTITY_ENTRY_FIELD_NAME = "$hibernate_entityEntryHolder";
@@ -168,7 +173,7 @@ private void enhance(CtClass managedCtClass) {
168173

169174
final String[] interfaceNames = managedCtClass.getClassFile2().getInterfaces();
170175
for ( String interfaceName : interfaceNames ) {
171-
if ( FieldHandled.class.getName().equals( interfaceName ) ) {
176+
if ( Managed.class.getName().equals( interfaceName ) ) {
172177
log.debug( "skipping enhancement : already enhanced" );
173178
return;
174179
}
@@ -191,13 +196,18 @@ private void enhanceAsEntity(CtClass managedCtClass) {
191196
// add the ManagedEntity interface
192197
managedCtClass.addInterface( managedEntityCtClass );
193198

194-
addEntityInstanceHandling( managedCtClass, constPool );
195-
addEntityEntryHandling( managedCtClass, constPool );
196-
addLinkedPreviousHandling( managedCtClass, constPool );
197-
addLinkedNextHandling( managedCtClass, constPool );
199+
// enhancePersistentAttributes( managedCtClass );
200+
201+
addEntityInstanceHandling( managedCtClass );
202+
addEntityEntryHandling( managedCtClass );
203+
addLinkedPreviousHandling( managedCtClass );
204+
addLinkedNextHandling( managedCtClass );
198205
}
199206

200-
private void addEntityInstanceHandling(CtClass managedCtClass, ConstPool constPool) {
207+
private void enhanceAsComposite(CtClass classFile) {
208+
}
209+
210+
private void addEntityInstanceHandling(CtClass managedCtClass) {
201211
// add the ManagedEntity#hibernate_getEntityInstance method
202212
try {
203213
managedCtClass.addMethod(
@@ -224,7 +234,9 @@ private void addEntityInstanceHandling(CtClass managedCtClass, ConstPool constPo
224234
// essentially add `return this;`
225235
}
226236

227-
private void addEntityEntryHandling(CtClass managedCtClass, ConstPool constPool) {
237+
private void addEntityEntryHandling(CtClass managedCtClass) {
238+
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
239+
228240
// add field to hold EntityEntry
229241
final CtField entityEntryField;
230242
try {
@@ -243,6 +255,7 @@ private void addEntityEntryHandling(CtClass managedCtClass, ConstPool constPool)
243255

244256
// make that new field transient and @Transient
245257
entityEntryField.setModifiers( entityEntryField.getModifiers() | Modifier.TRANSIENT );
258+
entityEntryField.setModifiers( Modifier.setPrivate( entityEntryField.getModifiers() ) );
246259
AnnotationsAttribute annotationsAttribute = getVisibleAnnotations( entityEntryField.getFieldInfo() );
247260
annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) );
248261

@@ -279,11 +292,13 @@ private void addEntityEntryHandling(CtClass managedCtClass, ConstPool constPool)
279292
}
280293
}
281294

282-
private void addLinkedPreviousHandling(CtClass managedCtClass, ConstPool constPool) {
295+
private void addLinkedPreviousHandling(CtClass managedCtClass) {
296+
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
297+
283298
// add field to hold "previous" ManagedEntity
284299
final CtField previousField;
285300
try {
286-
previousField = new CtField( managedCtClass, PREVIOUS_FIELD_NAME, managedCtClass );
301+
previousField = new CtField( managedEntityCtClass, PREVIOUS_FIELD_NAME, managedCtClass );
287302
managedCtClass.addField( previousField );
288303
}
289304
catch (CannotCompileException e) {
@@ -298,6 +313,7 @@ private void addLinkedPreviousHandling(CtClass managedCtClass, ConstPool constPo
298313

299314
// make that new field transient and @Transient
300315
previousField.setModifiers( previousField.getModifiers() | Modifier.TRANSIENT );
316+
previousField.setModifiers( Modifier.setPrivate( previousField.getModifiers() ) );
301317
AnnotationsAttribute annotationsAttribute = getVisibleAnnotations( previousField.getFieldInfo() );
302318
annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) );
303319

@@ -330,11 +346,13 @@ private void addLinkedPreviousHandling(CtClass managedCtClass, ConstPool constPo
330346
}
331347
}
332348

333-
private void addLinkedNextHandling(CtClass managedCtClass, ConstPool constPool) {
349+
private void addLinkedNextHandling(CtClass managedCtClass) {
350+
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
351+
334352
// add field to hold "next" ManagedEntity
335353
final CtField nextField;
336354
try {
337-
nextField = new CtField( managedCtClass, NEXT_FIELD_NAME, managedCtClass );
355+
nextField = new CtField( managedEntityCtClass, NEXT_FIELD_NAME, managedCtClass );
338356
managedCtClass.addField( nextField );
339357
}
340358
catch (CannotCompileException e) {
@@ -347,8 +365,9 @@ private void addLinkedNextHandling(CtClass managedCtClass, ConstPool constPool)
347365
);
348366
}
349367

350-
// make that new field transient and @Transient
368+
// make that new field (1) private, (2) transient and (3) @Transient
351369
nextField.setModifiers( nextField.getModifiers() | Modifier.TRANSIENT );
370+
nextField.setModifiers( Modifier.setPrivate( nextField.getModifiers() ) );
352371
AnnotationsAttribute annotationsAttribute = getVisibleAnnotations( nextField.getFieldInfo() );
353372
annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) );
354373

@@ -390,8 +409,84 @@ private AnnotationsAttribute getVisibleAnnotations(FieldInfo fieldInfo) {
390409
return annotationsAttribute;
391410
}
392411

393-
private void enhanceAsComposite(CtClass classFile) {
412+
private void enhancePersistentAttributes(CtClass managedCtClass) {
413+
final IdentityHashMap<CtField,FieldVirtualReadWritePair> fieldToMethodsXref = new IdentityHashMap<CtField, FieldVirtualReadWritePair>();
414+
415+
for ( CtField ctField : managedCtClass.getFields() ) {
416+
if ( ! enhancementContext.isPersistentField( ctField ) ) {
417+
continue;
418+
}
419+
420+
final FieldVirtualReadWritePair methodPair = addReadAndWriteMethod( managedCtClass, ctField );
421+
fieldToMethodsXref.put( ctField, methodPair );
422+
}
423+
424+
transformFieldAccessesIntoReadsAndWrites( managedCtClass, fieldToMethodsXref );
425+
}
426+
427+
private FieldVirtualReadWritePair addReadAndWriteMethod(CtClass managedCtClass, CtField persistentField) {
428+
// add the "reader"
429+
final CtMethod reader = generateFieldReader( managedCtClass, persistentField );
430+
431+
// add the "writer"
432+
final CtMethod writer = generateFieldWriter( managedCtClass, persistentField );
433+
434+
return new FieldVirtualReadWritePair( reader, writer );
394435
}
395436

437+
private CtMethod generateFieldReader(CtClass managedCtClass, CtField persistentField) {
438+
// todo : temporary; still need to add hooks into lazy-loading
439+
try {
440+
final String name = PERSISTENT_FIELD_READER_PREFIX + persistentField.getName();
441+
CtMethod reader = CtNewMethod.getter( name, persistentField );
442+
managedCtClass.addMethod( reader );
443+
return reader;
444+
}
445+
catch (CannotCompileException e) {
446+
throw new EnhancementException(
447+
String.format(
448+
"Could not enhance entity class [%s] to add virtual reader method for field [%s]",
449+
managedCtClass.getName(),
450+
persistentField.getName()
451+
),
452+
e
453+
);
454+
}
455+
}
456+
457+
private CtMethod generateFieldWriter(CtClass managedCtClass, CtField persistentField) {
458+
// todo : temporary; still need to add hooks into lazy-loading and dirtying
459+
try {
460+
final String name = PERSISTENT_FIELD_WRITER_PREFIX + persistentField.getName();
461+
CtMethod writer = CtNewMethod.setter( name, persistentField );
462+
managedCtClass.addMethod( writer );
463+
return writer;
464+
}
465+
catch (CannotCompileException e) {
466+
throw new EnhancementException(
467+
String.format(
468+
"Could not enhance entity class [%s] to add virtual writer method for field [%s]",
469+
managedCtClass.getName(),
470+
persistentField.getName()
471+
),
472+
e
473+
);
474+
}
475+
}
476+
477+
private void transformFieldAccessesIntoReadsAndWrites(
478+
CtClass managedCtClass,
479+
IdentityHashMap<CtField, FieldVirtualReadWritePair> fieldToMethodsXref) {
480+
}
481+
482+
private static class FieldVirtualReadWritePair {
483+
private final CtMethod readMethod;
484+
private final CtMethod writeMethod;
485+
486+
private FieldVirtualReadWritePair(CtMethod readMethod, CtMethod writeMethod) {
487+
this.readMethod = readMethod;
488+
this.writeMethod = writeMethod;
489+
}
490+
}
396491

397492
}

hibernate-core/src/main/java/org/hibernate/tool/enhance/EnhancementTask.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package org.hibernate.tool.enhance;
2525

2626
import javax.persistence.Entity;
27+
import javax.persistence.Transient;
2728
import java.io.File;
2829
import java.io.FileInputStream;
2930
import java.io.FileNotFoundException;
@@ -33,7 +34,10 @@
3334

3435
import javassist.ClassPool;
3536
import javassist.CtClass;
37+
import javassist.CtField;
3638
import javassist.NotFoundException;
39+
import javassist.bytecode.AnnotationsAttribute;
40+
import javassist.bytecode.annotation.Annotation;
3741
import org.apache.tools.ant.BuildException;
3842
import org.apache.tools.ant.DirectoryScanner;
3943
import org.apache.tools.ant.Project;
@@ -74,6 +78,12 @@ public boolean isEntityClass(String className) {
7478
public boolean isCompositeClass(String className) {
7579
return false;
7680
}
81+
82+
@Override
83+
public boolean isPersistentField(CtField ctField) {
84+
// current check is to look for @Transient
85+
return ! ctField.hasAnnotation( Transient.class );
86+
}
7787
};
7888

7989
// we use the CtClass stuff here just as a simple vehicle for obtaining low level information about

0 commit comments

Comments
 (0)