[HHH-9690] reorganization of bytecode enhancer code
Enhancer class was split into 3 logical units: EntityEnhancer for regular entities; CompositeEnhancer for emmbeded entities; PersistentAttributesEnhancer to enhance the persistent fields of both types of entities Added a few helper classes and re-worked the tests a bit as well
This commit is contained in:
parent
e9230758b4
commit
4def797408
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
||||
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import javassist.NotFoundException;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
|
||||
import javax.persistence.Id;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* utility class to generate interceptor methods
|
||||
* @see org.hibernate.engine.spi.PersistentAttributeInterceptor
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public abstract class AttributeTypeDescriptor {
|
||||
|
||||
public abstract String buildReadInterceptionBodyFragment(String fieldName);
|
||||
|
||||
public abstract String buildWriteInterceptionBodyFragment(String fieldName);
|
||||
|
||||
public String buildInLineDirtyCheckingBodyFragment(EnhancementContext context, CtField currentValue) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
try {
|
||||
// should ignore primary keys
|
||||
for ( Object o : currentValue.getType().getAnnotations() ) {
|
||||
if ( o instanceof Id) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
builder.append( String.format( "if (%s() != null", EnhancerConstants.INTERCEPTOR_GETTER_NAME ) );
|
||||
|
||||
// primitives || enums
|
||||
if ( currentValue.getType().isPrimitive() || currentValue.getType().isEnum() ) {
|
||||
builder.append( String.format( " && %s != $1)", currentValue.getName()) );
|
||||
}
|
||||
// simple data types
|
||||
else if ( currentValue.getType().getName().startsWith( "java.lang" )
|
||||
|| currentValue.getType().getName().startsWith( "java.math.Big" )
|
||||
|| currentValue.getType().getName().startsWith( "java.sql.Time" )
|
||||
|| currentValue.getType().getName().startsWith( "java.sql.Date" )
|
||||
|| currentValue.getType().getName().startsWith( "java.util.Date" )
|
||||
|| currentValue.getType().getName().startsWith( "java.util.Calendar" ) ) {
|
||||
builder.append( String.format( "&& ((%s == null) || (!%<s.equals($1))))", currentValue.getName() ) );
|
||||
}
|
||||
// all other objects
|
||||
else {
|
||||
// if the field is a collection we return since we handle that in a separate method
|
||||
for ( CtClass ctClass : currentValue.getType().getInterfaces() ) {
|
||||
if ( ctClass.getName().equals( Collection.class.getName() ) ) {
|
||||
// if the collection is not managed we should write it to the tracker
|
||||
if ( context.isMappedCollection( currentValue ) ) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: for now just call equals, should probably do something else here
|
||||
builder.append( String.format( "&& ((%s == null) || (!%<s.equals($1))))", currentValue.getName() ) );
|
||||
}
|
||||
builder.append( String.format( " { %s(\"%s\"); }", EnhancerConstants.TRACKER_CHANGER_NAME, currentValue.getName() ) );
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
/**
|
||||
* factory method to get the AttributeTypeDescriptor for a particular field type
|
||||
*/
|
||||
public static AttributeTypeDescriptor resolve(CtField persistentField) throws NotFoundException {
|
||||
if ( persistentField.getType() == CtClass.booleanType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Boolean.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.byteType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Byte.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.charType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Character.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.shortType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Short.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.intType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Integer.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.longType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Long.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.doubleType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Double.TYPE );
|
||||
}
|
||||
else if ( persistentField.getType() == CtClass.floatType ) {
|
||||
return new PrimitiveAttributeTypeDescriptor( Float.TYPE );
|
||||
}
|
||||
else {
|
||||
return new ObjectAttributeTypeDescriptor( persistentField.getType() );
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
/**
|
||||
* AttributeTypeDescriptor for non primitive types
|
||||
*/
|
||||
private static class ObjectAttributeTypeDescriptor extends AttributeTypeDescriptor {
|
||||
|
||||
private final String type;
|
||||
|
||||
private ObjectAttributeTypeDescriptor(CtClass concreteType) {
|
||||
this.type = concreteType.getName();
|
||||
}
|
||||
|
||||
public String buildReadInterceptionBodyFragment(String fieldName) {
|
||||
return String.format( "" +
|
||||
"if ( %3$s() != null ) {%n" +
|
||||
" this.%1$s = (%2$s) %3$s().readObject(this, \"%1$s\", this.%1$s);%n" +
|
||||
"}",
|
||||
fieldName,
|
||||
type,
|
||||
EnhancerConstants.INTERCEPTOR_GETTER_NAME);
|
||||
}
|
||||
|
||||
public String buildWriteInterceptionBodyFragment(String fieldName) {
|
||||
return String.format( "" +
|
||||
"%2$s localVar = $1;%n" +
|
||||
"if ( %3$s() != null ) {%n" +
|
||||
" localVar = (%2$s) %3$s().writeObject(this, \"%1$s\", this.%1$s, $1);%n" +
|
||||
"}%n" +
|
||||
"this.%1$s = localVar;",
|
||||
fieldName,
|
||||
type,
|
||||
EnhancerConstants.INTERCEPTOR_GETTER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AttributeTypeDescriptor for primitive types
|
||||
*/
|
||||
private static class PrimitiveAttributeTypeDescriptor extends AttributeTypeDescriptor {
|
||||
|
||||
private final String type;
|
||||
|
||||
private PrimitiveAttributeTypeDescriptor(Class<?> primitiveType) {
|
||||
if ( !primitiveType.isPrimitive() ) {
|
||||
throw new IllegalArgumentException( "Primitive attribute type descriptor can only be used on primitive types" );
|
||||
}
|
||||
// capitalize first letter
|
||||
this.type = primitiveType.getSimpleName().substring( 0, 1 ).toUpperCase() + primitiveType.getSimpleName().substring( 1 );
|
||||
}
|
||||
|
||||
public String buildReadInterceptionBodyFragment(String fieldName) {
|
||||
return String.format( "" +
|
||||
"if (%3$s() != null ) {%n" +
|
||||
" this.%1$s = %3$s().read%2$s(this, \"%1$s\", this.%1$s);%n" +
|
||||
"}",
|
||||
fieldName,
|
||||
type,
|
||||
EnhancerConstants.INTERCEPTOR_GETTER_NAME );
|
||||
}
|
||||
|
||||
public String buildWriteInterceptionBodyFragment(String fieldName) {
|
||||
return String.format( "" +
|
||||
"%2$s localVar = $1;%n" +
|
||||
"if ( %4$s() != null ) {%n" +
|
||||
" localVar = %4$s().write%3$s(this, \"%1$s\", this.%1$s, $1);%n" +
|
||||
"}%n" +
|
||||
"this.%1$s = localVar;",
|
||||
fieldName,
|
||||
type.toLowerCase(),
|
||||
type,
|
||||
EnhancerConstants.INTERCEPTOR_GETTER_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.CtClass;
|
||||
import javassist.NotFoundException;
|
||||
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.CompositeOwner;
|
||||
import org.hibernate.engine.spi.CompositeTracker;
|
||||
|
||||
/**
|
||||
* enhancer for composite (embeddable) entities
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public class CompositeEnhancer extends Enhancer {
|
||||
|
||||
public CompositeEnhancer(EnhancementContext context) {
|
||||
super( context );
|
||||
}
|
||||
|
||||
public void enhance(CtClass managedCtClass) {
|
||||
addInterceptorHandling( managedCtClass );
|
||||
|
||||
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||
addInLineDirtyHandling( managedCtClass );
|
||||
}
|
||||
|
||||
new PersistentAttributesEnhancer( enhancementContext ).enhance( managedCtClass );
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
private void addInLineDirtyHandling(CtClass managedCtClass) {
|
||||
try {
|
||||
managedCtClass.addInterface( classPool.get( CompositeTracker.class.getName() ) );
|
||||
|
||||
final CtClass compositeCtType = classPool.get( CompositeOwnerTracker.class.getName() );
|
||||
FieldWriter.addField( managedCtClass, compositeCtType, EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
|
||||
|
||||
createCompositeTrackerMethod( managedCtClass );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
nfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createCompositeTrackerMethod(CtClass managedCtClass) {
|
||||
try {
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public void %1$s(String name, %3$s tracker) {%n" +
|
||||
" if (%2$s == null) { %2$s = new %4$s(); }%n" +
|
||||
" %2$s.add(name, tracker);%n" +
|
||||
"}",
|
||||
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
||||
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
|
||||
CompositeOwner.class.getName(),
|
||||
CompositeOwnerTracker.class.getName() );
|
||||
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public void %1$s(String name) {%n" +
|
||||
" if (%2$s != null) { %2$s.removeOwner(name); }%n" +
|
||||
"}",
|
||||
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER,
|
||||
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
cce.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import javassist.Modifier;
|
||||
import javassist.NotFoundException;
|
||||
import org.hibernate.bytecode.enhance.internal.tracker.CollectionTracker;
|
||||
import org.hibernate.bytecode.enhance.internal.tracker.SimpleDirtyTracker;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* enhancer for regular entities
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public class EntityEnhancer extends Enhancer {
|
||||
|
||||
public EntityEnhancer(EnhancementContext context) {
|
||||
super( context );
|
||||
}
|
||||
|
||||
// for very small sizes SimpleDirtyTracker implementation ends up being faster
|
||||
private static final String TRACKER_IMPL = SimpleDirtyTracker.class.getName();
|
||||
|
||||
public void enhance(CtClass managedCtClass) {
|
||||
// add the ManagedEntity interface
|
||||
managedCtClass.addInterface( managedEntityCtClass );
|
||||
|
||||
addEntityInstanceHandling( managedCtClass );
|
||||
addEntityEntryHandling( managedCtClass );
|
||||
addLinkedPreviousHandling( managedCtClass );
|
||||
addLinkedNextHandling( managedCtClass );
|
||||
addInterceptorHandling( managedCtClass );
|
||||
|
||||
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||
addInLineDirtyHandling( managedCtClass );
|
||||
}
|
||||
|
||||
new PersistentAttributesEnhancer( enhancementContext ).enhance( managedCtClass );
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
private void addEntityInstanceHandling(CtClass managedCtClass) {
|
||||
try {
|
||||
MethodWriter.write( managedCtClass, "public Object %s() { return this; }", EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
final String msg = String.format( "Could not enhance entity class [%s] to add EntityEntry getter", managedCtClass.getName() );
|
||||
throw new EnhancementException(msg, cce);
|
||||
}
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
private void addEntityEntryHandling(CtClass managedCtClass) {
|
||||
FieldWriter.addFieldWithGetterAndSetter( managedCtClass, entityEntryCtClass,
|
||||
EnhancerConstants.ENTITY_ENTRY_FIELD_NAME,
|
||||
EnhancerConstants.ENTITY_ENTRY_GETTER_NAME,
|
||||
EnhancerConstants.ENTITY_ENTRY_SETTER_NAME );
|
||||
}
|
||||
|
||||
private void addLinkedPreviousHandling(CtClass managedCtClass) {
|
||||
FieldWriter.addFieldWithGetterAndSetter( managedCtClass, managedEntityCtClass,
|
||||
EnhancerConstants.PREVIOUS_FIELD_NAME,
|
||||
EnhancerConstants.PREVIOUS_GETTER_NAME,
|
||||
EnhancerConstants.PREVIOUS_SETTER_NAME );
|
||||
}
|
||||
|
||||
private void addLinkedNextHandling(CtClass managedCtClass) {
|
||||
FieldWriter.addFieldWithGetterAndSetter( managedCtClass, managedEntityCtClass,
|
||||
EnhancerConstants.NEXT_FIELD_NAME,
|
||||
EnhancerConstants.NEXT_GETTER_NAME,
|
||||
EnhancerConstants.NEXT_SETTER_NAME );
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
private void addInLineDirtyHandling(CtClass managedCtClass) {
|
||||
try {
|
||||
managedCtClass.addInterface( classPool.get( SelfDirtinessTracker.class.getName() ) );
|
||||
|
||||
FieldWriter.addField( managedCtClass, classPool.get( TRACKER_IMPL ), EnhancerConstants.TRACKER_FIELD_NAME );
|
||||
FieldWriter.addField( managedCtClass, classPool.get( CollectionTracker.class.getName() ), EnhancerConstants.TRACKER_COLLECTION_NAME );
|
||||
|
||||
createDirtyTrackerMethods( managedCtClass );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
nfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createDirtyTrackerMethods(CtClass managedCtClass) {
|
||||
try {
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public void %1$s(String name) {%n" +
|
||||
" if (%2$s == null) { %2$s = new %3$s(); }%n" +
|
||||
" %2$s.add(name);%n" +
|
||||
"}",
|
||||
EnhancerConstants.TRACKER_CHANGER_NAME,
|
||||
EnhancerConstants.TRACKER_FIELD_NAME,
|
||||
TRACKER_IMPL );
|
||||
|
||||
/* --- */
|
||||
|
||||
createCollectionDirtyCheckMethod( managedCtClass );
|
||||
createCollectionDirtyCheckGetFieldsMethod( managedCtClass );
|
||||
createClearDirtyCollectionMethod( managedCtClass );
|
||||
|
||||
/* --- */
|
||||
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public java.util.Set %1$s() {%n" +
|
||||
" if (%2$s == null) { %2$s = new %4$s(); }%n" +
|
||||
" %3$s(%2$s);%n" +
|
||||
" return %2$s.asSet();%n" +
|
||||
"}",
|
||||
EnhancerConstants.TRACKER_GET_NAME,
|
||||
EnhancerConstants.TRACKER_FIELD_NAME,
|
||||
EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME,
|
||||
TRACKER_IMPL );
|
||||
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public boolean %1$s() {%n" +
|
||||
" return (%2$s != null && !%2$s.isEmpty()) || %3$s();%n" +
|
||||
"}",
|
||||
EnhancerConstants.TRACKER_HAS_CHANGED_NAME,
|
||||
EnhancerConstants.TRACKER_FIELD_NAME,
|
||||
EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME );
|
||||
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public void %1$s() {%n" +
|
||||
" if (%2$s != null) { %2$s.clear(); }%n" +
|
||||
" %3$s();%n" +
|
||||
"}",
|
||||
EnhancerConstants.TRACKER_CLEAR_NAME,
|
||||
EnhancerConstants.TRACKER_FIELD_NAME,
|
||||
EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
cce.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
private List<CtField> collectCollectionFields(CtClass managedCtClass) {
|
||||
final List<CtField> collectionList = new LinkedList<CtField>();
|
||||
try {
|
||||
for ( CtField ctField : managedCtClass.getDeclaredFields() ) {
|
||||
// skip static fields and skip fields added by enhancement
|
||||
if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( enhancementContext.isPersistentField( ctField ) ) {
|
||||
for ( CtClass ctClass : ctField.getType().getInterfaces() ) {
|
||||
if ( ctClass.getName().equals( Collection.class.getName() ) ) {
|
||||
collectionList.add( ctField );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NotFoundException ignored) {
|
||||
}
|
||||
return collectionList;
|
||||
}
|
||||
|
||||
private void createCollectionDirtyCheckMethod(CtClass managedCtClass) {
|
||||
try {
|
||||
final StringBuilder body = new StringBuilder();
|
||||
|
||||
body.append( String.format( "" +
|
||||
"private boolean %1$s() {%n" +
|
||||
" if (%2$s() == null || %3$s == null) { return false; }%n",
|
||||
EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME,
|
||||
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
|
||||
EnhancerConstants.TRACKER_COLLECTION_NAME ) );
|
||||
|
||||
for ( CtField ctField : collectCollectionFields( managedCtClass ) ) {
|
||||
if ( !enhancementContext.isMappedCollection( ctField )) {
|
||||
body.append( String.format( "" +
|
||||
" // collection field [%1$s]%n" +
|
||||
" if (%1$s == null && %2$s.getSize(\"%1$s\") != -1) { return true; }%n"+
|
||||
" if (%1$s != null && %2$s.getSize(\"%1$s\") != %1$s.size()) { return true; }%n",
|
||||
ctField.getName(),
|
||||
EnhancerConstants.TRACKER_COLLECTION_NAME ) );
|
||||
}
|
||||
}
|
||||
body.append( " return false;%n}" );
|
||||
|
||||
MethodWriter.write( managedCtClass, body.toString() );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
cce.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createCollectionDirtyCheckGetFieldsMethod(CtClass managedCtClass) {
|
||||
try {
|
||||
final StringBuilder body = new StringBuilder();
|
||||
|
||||
body.append( String.format( "" +
|
||||
"private void %1$s(%3$s tracker) {%n" +
|
||||
" if (%2$s == null) { return; }%n",
|
||||
EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME,
|
||||
EnhancerConstants.TRACKER_COLLECTION_NAME,
|
||||
TRACKER_IMPL ) );
|
||||
|
||||
for ( CtField ctField : collectCollectionFields( managedCtClass ) ) {
|
||||
if ( !enhancementContext.isMappedCollection( ctField )) {
|
||||
body.append( String.format( "" +
|
||||
" // Collection field [%1$s]%n" +
|
||||
" if (%1$s == null && %2$s.getSize(\"%1$s\") != -1) { tracker.add(\"%1$s\"); }%n"+
|
||||
" if (%1$s != null && %2$s.getSize(\"%1$s\") != %1$s.size()) { tracker.add(\"%1$s\"); }%n",
|
||||
ctField.getName(),
|
||||
EnhancerConstants.TRACKER_COLLECTION_NAME ) );
|
||||
}
|
||||
}
|
||||
body.append( "}" );
|
||||
|
||||
MethodWriter.write( managedCtClass, body.toString() );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
cce.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createClearDirtyCollectionMethod(CtClass managedCtClass) throws CannotCompileException {
|
||||
try {
|
||||
final StringBuilder body = new StringBuilder();
|
||||
|
||||
body.append( String.format( "" +
|
||||
"private void %1$s() {%n" +
|
||||
" if (%2$s == null) { %2$s = new %3$s(); }%n",
|
||||
EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME,
|
||||
EnhancerConstants.TRACKER_COLLECTION_NAME,
|
||||
CollectionTracker.class.getName()) );
|
||||
|
||||
for ( CtField ctField : collectCollectionFields( managedCtClass ) ) {
|
||||
if ( !enhancementContext.isMappedCollection( ctField ) ) {
|
||||
body.append( String.format( "" +
|
||||
" // Collection field [%1$s]%n" +
|
||||
" if (%1$s == null) { %2$s.add(\"%1$s\", -1); }%n"+
|
||||
" else { %2$s.add(\"%1$s\", %1$s.size()); }%n",
|
||||
ctField.getName(),
|
||||
EnhancerConstants.TRACKER_COLLECTION_NAME) );
|
||||
}
|
||||
}
|
||||
body.append( "}" );
|
||||
|
||||
MethodWriter.write( managedCtClass, body.toString() );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
cce.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import javassist.Modifier;
|
||||
import javassist.bytecode.AnnotationsAttribute;
|
||||
import javassist.bytecode.FieldInfo;
|
||||
import javassist.bytecode.annotation.Annotation;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
||||
import javax.persistence.Transient;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public class FieldWriter {
|
||||
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( FieldWriter.class );
|
||||
|
||||
private FieldWriter() { }
|
||||
|
||||
/* --- */
|
||||
|
||||
/**
|
||||
* Add enhancement field
|
||||
*/
|
||||
public static void addField(CtClass target, CtClass type, String field) {
|
||||
addPrivateTransient( target, type, field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add enhancement field with getter and setter
|
||||
*/
|
||||
public static void addFieldWithGetterAndSetter(CtClass target, CtClass type, String field, String getter, String setter) {
|
||||
addPrivateTransient( target, type, field );
|
||||
MethodWriter.addGetter( target, field, getter );
|
||||
MethodWriter.addSetter( target, field, setter );
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
private static void addPrivateTransient(CtClass target, CtClass type, String name) {
|
||||
addWithModifiers( target, type, name, Modifier.PRIVATE | Modifier.TRANSIENT, Transient.class );
|
||||
log.debugf( "Wrote field into [%s]: @Transient private transient %s %s() %s;%n", target.getName(), type.getName(), name );
|
||||
}
|
||||
|
||||
private static void addWithModifiers(CtClass target, CtClass type, String name, int modifiers, Class<?> ... annotations ) {
|
||||
try {
|
||||
final CtField f = new CtField( type, name, target );
|
||||
f.setModifiers( f.getModifiers() | modifiers );
|
||||
addAnnotations( f.getFieldInfo(), annotations );
|
||||
target.addField( f );
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
final String msg = String.format( "Could not enhance class [%s] to add field [%s]", target.getName(), name );
|
||||
throw new EnhancementException( msg, cce );
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
private static void addAnnotations(FieldInfo fieldInfo, Class<?>[] annotations) {
|
||||
AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute) fieldInfo.getAttribute( AnnotationsAttribute.visibleTag );
|
||||
if ( annotationsAttribute == null ) {
|
||||
annotationsAttribute = new AnnotationsAttribute( fieldInfo.getConstPool(), AnnotationsAttribute.visibleTag );
|
||||
fieldInfo.addAttribute( annotationsAttribute );
|
||||
}
|
||||
for (Class<?> annotation : annotations) {
|
||||
annotationsAttribute.addAnnotation( new Annotation( annotation.getName(), fieldInfo.getConstPool() ) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
import javassist.CtNewMethod;
|
||||
import javassist.NotFoundException;
|
||||
import javassist.bytecode.ConstPool;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
||||
/**
|
||||
* utility class to compile methods and add the to class files
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public class MethodWriter {
|
||||
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( MethodWriter.class );
|
||||
|
||||
private MethodWriter() { }
|
||||
|
||||
/* --- */
|
||||
|
||||
/**
|
||||
* convenience method that builds a method from a format string. {@see String.format} for more details
|
||||
*
|
||||
* @throws CannotCompileException
|
||||
*/
|
||||
public static CtMethod write(CtClass target, String format, Object ... args) throws CannotCompileException {
|
||||
final String body = String.format( format, args );
|
||||
// System.out.printf( "writing method into [%s]:%n%s%n", target.getName(), body );
|
||||
log.debugf( "writing method into [%s]:%n%s%n", target.getName(), body );
|
||||
final CtMethod method = CtNewMethod.make( body, target );
|
||||
target.addMethod( method );
|
||||
return method;
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
public static CtMethod addGetter(CtClass target, String field, String name) {
|
||||
try {
|
||||
log.debugf( "Writing getter method [%s] into [%s] for field [%s]%n", name, target.getName(), field );
|
||||
final CtMethod method = CtNewMethod.getter( name, target.getField( field ) );
|
||||
target.addMethod( method );
|
||||
return method;
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
final String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
|
||||
throw new EnhancementException( msg, cce );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
final String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
|
||||
throw new EnhancementException( msg, nfe );
|
||||
}
|
||||
}
|
||||
|
||||
public static CtMethod addSetter(CtClass target, String field, String name) {
|
||||
try {
|
||||
log.debugf( "Writing setter method [%s] into [%s] for field [%s]%n", name, target.getName(), field );
|
||||
final CtMethod method = CtNewMethod.setter( name, target.getField( field ) );
|
||||
target.addMethod( method );
|
||||
return method;
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
final String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
|
||||
throw new EnhancementException( msg, cce );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
final String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
|
||||
throw new EnhancementException( msg, nfe );
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
public static int addMethod(ConstPool cPool, CtMethod method) {
|
||||
return cPool.addMethodrefInfo( cPool.getThisClassInfo(), method.getName(), method.getSignature() );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import javassist.CtMethod;
|
||||
import javassist.Modifier;
|
||||
import javassist.NotFoundException;
|
||||
import javassist.bytecode.BadBytecode;
|
||||
import javassist.bytecode.CodeIterator;
|
||||
import javassist.bytecode.ConstPool;
|
||||
import javassist.bytecode.MethodInfo;
|
||||
import javassist.bytecode.Opcode;
|
||||
import javassist.bytecode.stackmap.MapMaker;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.CompositeOwner;
|
||||
import org.hibernate.engine.spi.CompositeTracker;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
||||
import javax.persistence.Embedded;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* enhancer for persistent attributes of any type of entity
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public class PersistentAttributesEnhancer extends Enhancer {
|
||||
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesEnhancer.class );
|
||||
|
||||
public PersistentAttributesEnhancer(EnhancementContext context) {
|
||||
super( context );
|
||||
}
|
||||
|
||||
public void enhance(CtClass managedCtClass) {
|
||||
final IdentityHashMap<String, PersistentAttributeAccessMethods> attrDescriptorMap = new IdentityHashMap<String, PersistentAttributeAccessMethods>();
|
||||
|
||||
for ( CtField persistentField : collectPersistentFields( managedCtClass ) ) {
|
||||
attrDescriptorMap.put( persistentField.getName(), enhancePersistentAttribute( managedCtClass, persistentField ) );
|
||||
}
|
||||
|
||||
// lastly, find all references to the transformed fields and replace with calls to the added reader/writer methods
|
||||
enhanceAttributesAccess( managedCtClass, attrDescriptorMap );
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
// TODO: drive this from the Hibernate metamodel instance...
|
||||
private CtField[] collectPersistentFields(CtClass managedCtClass) {
|
||||
final List<CtField> persistentFieldList = new LinkedList<CtField>();
|
||||
for ( CtField ctField : managedCtClass.getDeclaredFields() ) {
|
||||
// skip static fields and skip fields added by enhancement
|
||||
if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( enhancementContext.isPersistentField( ctField ) ) {
|
||||
persistentFieldList.add( ctField );
|
||||
}
|
||||
}
|
||||
return enhancementContext.order( persistentFieldList.toArray( new CtField[ persistentFieldList.size() ] ) );
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
private PersistentAttributeAccessMethods enhancePersistentAttribute(CtClass managedCtClass, CtField persistentField) {
|
||||
try {
|
||||
final AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( persistentField );
|
||||
return new PersistentAttributeAccessMethods(
|
||||
generateFieldReader( managedCtClass, persistentField, typeDescriptor ),
|
||||
generateFieldWriter( managedCtClass, persistentField, typeDescriptor ) );
|
||||
}
|
||||
catch (Exception e) {
|
||||
final String msg = String.format( "Unable to enhance persistent attribute [%s:%s]", managedCtClass.getName(), persistentField.getName() );
|
||||
throw new EnhancementException( msg, e );
|
||||
}
|
||||
}
|
||||
|
||||
private CtMethod generateFieldReader(CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) {
|
||||
final String fieldName = persistentField.getName();
|
||||
final String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;
|
||||
|
||||
// read attempts only have to deal lazy-loading support, not dirty checking;
|
||||
// so if the field is not enabled as lazy-loadable return a plain simple getter as the reader
|
||||
if ( !enhancementContext.isLazyLoadable( persistentField ) ) {
|
||||
return MethodWriter.addGetter( managedCtClass, fieldName, readerName );
|
||||
}
|
||||
|
||||
// TODO: temporary solution...
|
||||
try {
|
||||
return MethodWriter.write( managedCtClass, "private %s %s() {%n %s%n return this.%s;%n}",
|
||||
persistentField.getType().getName(),
|
||||
readerName,
|
||||
typeDescriptor.buildReadInterceptionBodyFragment( fieldName ),
|
||||
fieldName);
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
final String msg = String.format( "Could not enhance entity class [%s] to add field reader method [%s]", managedCtClass.getName(), readerName );
|
||||
throw new EnhancementException( msg, cce );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
final String msg = String.format( "Could not enhance entity class [%s] to add field reader method [%s]", managedCtClass.getName(), readerName );
|
||||
throw new EnhancementException( msg, nfe );
|
||||
}
|
||||
}
|
||||
|
||||
private CtMethod generateFieldWriter(CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) {
|
||||
final String fieldName = persistentField.getName();
|
||||
final String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
|
||||
|
||||
try {
|
||||
final CtMethod writer;
|
||||
|
||||
if ( !enhancementContext.isLazyLoadable( persistentField ) ) {
|
||||
writer = MethodWriter.addSetter( managedCtClass, fieldName, writerName );
|
||||
}
|
||||
else {
|
||||
writer = MethodWriter.write( managedCtClass, "private void %s(%s %s) {%n %s%n}",
|
||||
writerName,
|
||||
persistentField.getType().getName(),
|
||||
fieldName,
|
||||
typeDescriptor.buildWriteInterceptionBodyFragment( fieldName ) );
|
||||
}
|
||||
|
||||
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||
writer.insertBefore( String.format( "if (%s != null) { %<s.callOwner(\".%s\"); }%n",
|
||||
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
|
||||
fieldName ) );
|
||||
}
|
||||
else if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||
writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( enhancementContext, persistentField ) );
|
||||
}
|
||||
|
||||
// composite fields
|
||||
if ( persistentField.hasAnnotation( Embedded.class ) ) {
|
||||
// make sure to add the CompositeOwner interface
|
||||
managedCtClass.addInterface( classPool.get( CompositeOwner.class.getName() ) );
|
||||
|
||||
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||
// if a composite have a embedded field we need to implement the TRACKER_CHANGER_NAME method as well
|
||||
MethodWriter.write( managedCtClass, "" +
|
||||
"public void %1$s(String name) {%n" +
|
||||
" if (%2$s != null) { %2$s.callOwner(\".\" + name) ; }%n}",
|
||||
EnhancerConstants.TRACKER_CHANGER_NAME,
|
||||
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
|
||||
}
|
||||
|
||||
// cleanup previous owner
|
||||
writer.insertBefore( String.format( "" +
|
||||
"if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
|
||||
fieldName,
|
||||
CompositeTracker.class.getName(),
|
||||
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER ) );
|
||||
|
||||
// trigger track changes
|
||||
writer.insertAfter( String.format( "" +
|
||||
"((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this);%n" +
|
||||
"%5$s(\"%1$s\");",
|
||||
fieldName,
|
||||
CompositeTracker.class.getName(),
|
||||
CompositeOwner.class.getName(),
|
||||
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
||||
EnhancerConstants.TRACKER_CHANGER_NAME ) );
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
catch (CannotCompileException cce) {
|
||||
final String msg = String.format( "Could not enhance entity class [%s] to add field writer method [%s]", managedCtClass.getName(), writerName );
|
||||
throw new EnhancementException( msg, cce );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
final String msg = String.format( "Could not enhance entity class [%s] to add field writer method [%s]", managedCtClass.getName(), writerName );
|
||||
throw new EnhancementException( msg, nfe );
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
protected void enhanceAttributesAccess(CtClass managedCtClass, IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) {
|
||||
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
|
||||
|
||||
for ( Object oMethod : managedCtClass.getClassFile().getMethods() ) {
|
||||
final MethodInfo methodInfo = (MethodInfo) oMethod;
|
||||
final String methodName = methodInfo.getName();
|
||||
|
||||
// skip methods added by enhancement and abstract methods (methods without any code)
|
||||
if ( methodName.startsWith( "$$_hibernate_" ) || methodInfo.getCodeAttribute() == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final int index = itr.next();
|
||||
final int op = itr.byteAt( index );
|
||||
if ( op != Opcode.PUTFIELD && op != Opcode.GETFIELD ) {
|
||||
continue;
|
||||
}
|
||||
final String fieldName = constPool.getFieldrefName( itr.u16bitAt( index + 1 ) );
|
||||
final PersistentAttributeAccessMethods attributeMethods = attributeDescriptorMap.get( fieldName );
|
||||
|
||||
// its not a field we have enhanced for interception, so skip it
|
||||
if ( attributeMethods == null ) {
|
||||
continue;
|
||||
}
|
||||
//System.out.printf( "Transforming access to field [%s] from method [%s]%n", fieldName, methodName );
|
||||
log.debugf( "Transforming access to field [%s] from method [%s]", fieldName, methodName );
|
||||
|
||||
if ( op == Opcode.GETFIELD ) {
|
||||
final int methodIndex = MethodWriter.addMethod( constPool, attributeMethods.getReader() );
|
||||
itr.writeByte( Opcode.INVOKESPECIAL, index );
|
||||
itr.write16bit( methodIndex, index + 1 );
|
||||
}
|
||||
else {
|
||||
final int methodIndex = MethodWriter.addMethod( constPool, attributeMethods.getWriter() );
|
||||
itr.writeByte( Opcode.INVOKESPECIAL, index );
|
||||
itr.write16bit( methodIndex, index + 1 );
|
||||
}
|
||||
}
|
||||
methodInfo.getCodeAttribute().setAttribute( MapMaker.make( classPool, methodInfo ) );
|
||||
}
|
||||
catch (BadBytecode bb) {
|
||||
final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName );
|
||||
throw new EnhancementException( msg, bb );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
private static class PersistentAttributeAccessMethods {
|
||||
|
||||
private final CtMethod reader;
|
||||
private final CtMethod writer;
|
||||
|
||||
private PersistentAttributeAccessMethods(CtMethod reader, CtMethod writer) {
|
||||
this.reader = reader;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
private CtMethod getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
private CtMethod getWriter() {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* package containing bytecode enhancement code (internals)
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal;
|
|
@ -21,32 +21,45 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
package org.hibernate.bytecode.enhance.internal.tracker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* small low memory class to keep track of the number of elements in a collection
|
||||
*
|
||||
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||
*/
|
||||
public class CollectionTracker {
|
||||
private Map<String, Integer> tracker;
|
||||
public final class CollectionTracker {
|
||||
|
||||
private String[] names;
|
||||
private int[] sizes;
|
||||
|
||||
public CollectionTracker() {
|
||||
tracker = new HashMap<String, Integer>();
|
||||
names = new String[0];
|
||||
sizes = new int[0];
|
||||
}
|
||||
|
||||
public void add(String name, int size) {
|
||||
tracker.put( name, size );
|
||||
for ( int i = 0; i < names.length; i++ ) {
|
||||
if ( names[i].equals( name ) ) {
|
||||
sizes[i] = size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
names = Arrays.copyOf( names, names.length + 1 );
|
||||
names[names.length - 1] = name;
|
||||
sizes = Arrays.copyOf( sizes, sizes.length + 1 );
|
||||
sizes[sizes.length - 1] = size;
|
||||
}
|
||||
|
||||
public int getSize(String name) {
|
||||
Integer size = tracker.get( name );
|
||||
if ( size == null ) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return size;
|
||||
for ( int i = 0; i < names.length; i++ ) {
|
||||
if ( name.equals( names[i] ) ) {
|
||||
return sizes[i];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
|
@ -21,64 +21,63 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
package org.hibernate.bytecode.enhance.internal.tracker;
|
||||
|
||||
import org.hibernate.engine.spi.CompositeOwner;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* small low memory class to keep references to composite owners
|
||||
*
|
||||
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||
*/
|
||||
public class CompositeOwnerTracker {
|
||||
public final class CompositeOwnerTracker {
|
||||
|
||||
private String[] names;
|
||||
private CompositeOwner[] owners;
|
||||
private int size;
|
||||
|
||||
public CompositeOwnerTracker() {
|
||||
names = new String[1];
|
||||
owners = new CompositeOwner[1];
|
||||
names = new String[0];
|
||||
owners = new CompositeOwner[0];
|
||||
}
|
||||
|
||||
public void add(String name, CompositeOwner owner) {
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
for ( int i = 0; i < names.length; i++ ) {
|
||||
if ( names[i].equals( name ) ) {
|
||||
owners[i] = owner;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( size >= names.length ) {
|
||||
String[] tmpNames = new String[size + 1];
|
||||
System.arraycopy( names, 0, tmpNames, 0, size );
|
||||
names = tmpNames;
|
||||
CompositeOwner[] tmpOwners = new CompositeOwner[size + 1];
|
||||
System.arraycopy( owners, 0, tmpOwners, 0, size );
|
||||
owners = tmpOwners;
|
||||
}
|
||||
names[size] = name;
|
||||
owners[size] = owner;
|
||||
size++;
|
||||
names = Arrays.copyOf( names, names.length + 1 );
|
||||
names[names.length - 1] = name;
|
||||
owners = Arrays.copyOf( owners, owners.length + 1 );
|
||||
owners[owners.length - 1] = owner;
|
||||
}
|
||||
|
||||
public void callOwner(String fieldName) {
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
owners[i].$$_hibernate_trackChange( names[i] + fieldName );
|
||||
for ( int i = 0; i < owners.length ; i++ ) {
|
||||
if ( owners[i] != null ) {
|
||||
owners[i].$$_hibernate_trackChange( names[i] + fieldName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeOwner(String name) {
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
if ( names[i].equals( name ) ) {
|
||||
if ( i < size ) {
|
||||
for ( int j = i; j < size - 1; j++ ) {
|
||||
names[j] = names[j + 1];
|
||||
owners[j] = owners[j + 1];
|
||||
}
|
||||
names[size - 1] = null;
|
||||
owners[size - 1] = null;
|
||||
size--;
|
||||
}
|
||||
for ( int i = 0; i < names.length; i++ ) {
|
||||
if ( name.equals( names[i] ) ) {
|
||||
|
||||
final String[] newNames = Arrays.copyOf( names, names.length - 1 );
|
||||
System.arraycopy( names, i + 1, newNames, i, newNames.length - i);
|
||||
names = newNames;
|
||||
|
||||
final CompositeOwner[] newOwners = Arrays.copyOf( owners, owners.length - 1 );
|
||||
System.arraycopy( owners, i + 1, newOwners, i, newOwners.length - i);
|
||||
owners = newOwners;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal.tracker;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* small low memory class to keep track of changed fields
|
||||
*
|
||||
* uses an array as a set (under the assumption that the number of elements will be low) to avoid having to instantiate an HashSet.
|
||||
* if the assumption does not, hold the array can be kept ordered to reduce the cost of verifying duplicates
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public final class SimpleDirtyTracker {
|
||||
|
||||
private String[] names;
|
||||
|
||||
public SimpleDirtyTracker() {
|
||||
names = new String[0];
|
||||
}
|
||||
|
||||
public void add(String name) {
|
||||
for (String existing : names) {
|
||||
if ( existing.equals( name ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
names = Arrays.copyOf( names, names.length + 1 );
|
||||
names[names.length - 1] = name;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
names = new String[0];
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return names.length == 0;
|
||||
}
|
||||
|
||||
public Set<String> asSet() {
|
||||
return new CopyOnWriteArraySet<String>( Arrays.asList( names ) );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal.tracker;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* small low memory class to keep track of changed fields
|
||||
*
|
||||
* similar to BasicTracker but where the array is kept ordered to reduce the cost of verifying duplicates
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public final class SortedDirtyTracker {
|
||||
|
||||
private String[] names;
|
||||
|
||||
public SortedDirtyTracker() {
|
||||
names = new String[0];
|
||||
}
|
||||
|
||||
public void add(String name) {
|
||||
// we do a binary search: even if we don't find the name at least we get the position to insert into the array
|
||||
int insert = 0;
|
||||
for ( int low = 0, high = names.length - 1; low <= high; ) {
|
||||
final int middle = low + ( ( high - low ) / 2 );
|
||||
if ( names[middle].compareTo( name ) > 0 ) {
|
||||
// bottom half: higher bound in (middle - 1) and insert position in middle
|
||||
high = middle - 1;
|
||||
insert = middle;
|
||||
}
|
||||
else if( names[middle].compareTo( name ) < 0 ) {
|
||||
// top half: lower bound in (middle + 1) and insert position after middle
|
||||
insert = low = middle + 1;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final String[] newNames = new String[names.length + 1];
|
||||
System.arraycopy( names, 0, newNames, 0, insert);
|
||||
System.arraycopy( names, insert, newNames, insert + 1, names.length - insert);
|
||||
newNames[insert] = name;
|
||||
names = newNames;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
names = new String[0];
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return names.length == 0;
|
||||
}
|
||||
|
||||
public Set<String> asSet() {
|
||||
return new CopyOnWriteArraySet<String>( Arrays.asList( names ) );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* specialized classes to keep track of changes
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal.tracker;
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* Package defining bytecode code enhancement (instrumentation) support.
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance;
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
/**
|
||||
* default implementation of EnhancementContext. May be sub-classed as needed.
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public class DefaultEnhancementContext implements EnhancementContext {
|
||||
|
||||
/**
|
||||
* @return the classloader for this class
|
||||
*/
|
||||
public ClassLoader getLoadingClassLoader() {
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* look for @Entity annotation
|
||||
*/
|
||||
public boolean isEntityClass(CtClass classDescriptor) {
|
||||
return classDescriptor.hasAnnotation( Entity.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* look for @Embeddable annotation
|
||||
*/
|
||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
||||
return classDescriptor.hasAnnotation( Embeddable.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true
|
||||
*/
|
||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true
|
||||
*/
|
||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true
|
||||
*/
|
||||
public boolean isLazyLoadable(CtField field) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* look for @Transient annotation
|
||||
*/
|
||||
public boolean isPersistentField(CtField ctField) {
|
||||
return ! ctField.hasAnnotation( Transient.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* look for @OneToMany, @ManyToMany and @ElementCollection annotations
|
||||
*/
|
||||
public boolean isMappedCollection(CtField field) {
|
||||
return field.hasAnnotation( OneToMany.class ) || field.hasAnnotation( ManyToMany.class ) || field.hasAnnotation( ElementCollection.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* keep the same order.
|
||||
*/
|
||||
public CtField[] order(CtField[] persistentFields) {
|
||||
return persistentFields;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance;
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
* Package defining bytecode code enhancement (instrumentation) support.
|
||||
* package defining bytecode code enhancement (instrumentation) support.
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.customer.Address;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.customer.Customer;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.customer.CustomerInventory;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.customer.SupplierComponentPK;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CustomerEnhancerTest extends BaseUnitTestCase {
|
||||
|
||||
@Test
|
||||
public void testEnhancement() throws Exception {
|
||||
testFor(Customer.class);
|
||||
}
|
||||
|
||||
private void testFor(Class entityClassToEnhance) throws Exception {
|
||||
ClassLoader cl = new ClassLoader() {};
|
||||
|
||||
// just for debugging
|
||||
Class<?> addressClass = EnhancerTestUtils.enhanceAndDecompile(Address.class, cl);
|
||||
Class<?> customerInventoryClass = EnhancerTestUtils.enhanceAndDecompile(CustomerInventory.class, cl);
|
||||
Class<?> supplierComponentPKCtClass = EnhancerTestUtils.enhanceAndDecompile(SupplierComponentPK.class, cl);
|
||||
|
||||
Class<?> entityClass = EnhancerTestUtils.enhanceAndDecompile(entityClassToEnhance, cl);
|
||||
Object entityInstance = entityClass.newInstance();
|
||||
assertTyping(ManagedEntity.class, entityInstance);
|
||||
|
||||
// call the new methods
|
||||
Method setter = entityClass.getMethod(EnhancerConstants.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class);
|
||||
Method getter = entityClass.getMethod(EnhancerConstants.ENTITY_ENTRY_GETTER_NAME);
|
||||
assertNull(getter.invoke(entityInstance));
|
||||
setter.invoke(entityInstance, EnhancerTestUtils.makeEntityEntry());
|
||||
assertNotNull(getter.invoke(entityInstance));
|
||||
setter.invoke(entityInstance, new Object[] {null});
|
||||
assertNull(getter.invoke(entityInstance));
|
||||
|
||||
Method entityInstanceGetter = entityClass.getMethod(EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME);
|
||||
assertSame(entityInstance, entityInstanceGetter.invoke(entityInstance));
|
||||
|
||||
Method previousGetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_GETTER_NAME);
|
||||
Method previousSetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class);
|
||||
previousSetter.invoke(entityInstance, entityInstance);
|
||||
assertSame(entityInstance, previousGetter.invoke(entityInstance));
|
||||
|
||||
Method nextGetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_GETTER_NAME);
|
||||
Method nextSetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class);
|
||||
nextSetter.invoke(entityInstance, entityInstance);
|
||||
assertSame( entityInstance, nextGetter.invoke(entityInstance));
|
||||
|
||||
// add an attribute interceptor...
|
||||
assertNull(entityClass.getMethod(EnhancerConstants.INTERCEPTOR_GETTER_NAME).invoke(entityInstance));
|
||||
entityClass.getMethod("getId").invoke(entityInstance);
|
||||
|
||||
Method interceptorSetter = entityClass.getMethod(EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class);
|
||||
interceptorSetter.invoke(entityInstance, new EnhancerTestUtils.LocalPersistentAttributeInterceptor());
|
||||
assertNotNull(entityClass.getMethod(EnhancerConstants.INTERCEPTOR_GETTER_NAME).invoke(entityInstance));
|
||||
|
||||
// dirty checking is unfortunately just printlns for now... just verify the test output
|
||||
entityClass.getMethod("getId").invoke(entityInstance);
|
||||
entityClass.getMethod("setId", Integer.class).invoke(entityInstance, entityClass.getMethod("getId").invoke(entityInstance));
|
||||
entityClass.getMethod("setId", Integer.class).invoke(entityInstance, 1);
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "id");
|
||||
|
||||
entityClass.getMethod("setFirstName", String.class).invoke(entityInstance, "Erik");
|
||||
entityClass.getMethod("setLastName", String.class).invoke(entityInstance, "Mykland");
|
||||
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "id", "firstName", "lastName");
|
||||
EnhancerTestUtils.clearDirtyTracking(entityInstance);
|
||||
|
||||
// testing composite object
|
||||
Object address = addressClass.newInstance();
|
||||
|
||||
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address);
|
||||
addressClass.getMethod("setCity", String.class).invoke(address, "Arendal");
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address", "address.city");
|
||||
EnhancerTestUtils.clearDirtyTracking(entityInstance);
|
||||
|
||||
//make sure that new composite instances are cleared
|
||||
Object address2 = addressClass.newInstance();
|
||||
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address2);
|
||||
addressClass.getMethod("setStreet1", String.class).invoke(address, "Heggedalveien");
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address");
|
||||
}
|
||||
|
||||
}
|
|
@ -23,401 +23,148 @@
|
|||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import javassist.LoaderClassPath;
|
||||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
|
||||
import org.hibernate.test.bytecode.enhancement.entity.Address;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.Country;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.SimpleEntity;
|
||||
import org.hibernate.test.bytecode.enhancement.entity.SubEntity;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EnhancerTest extends BaseUnitTestCase {
|
||||
private static EnhancementContext enhancementContext = new EnhancementContext() {
|
||||
@Override
|
||||
public ClassLoader getLoadingClassLoader() {
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntityClass(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
@Test
|
||||
public void testEnhancement() throws Exception {
|
||||
testFor(SimpleEntity.class);
|
||||
testFor(SubEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
||||
return false;
|
||||
}
|
||||
private void testFor(Class<?> entityClassToEnhance) throws Exception {
|
||||
ClassLoader cl = new ClassLoader() {};
|
||||
Class<?> entityClass = EnhancerTestUtils.enhanceAndDecompile(entityClassToEnhance, cl);
|
||||
Object entityInstance = entityClass.newInstance();
|
||||
|
||||
@Override
|
||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
assertTyping(ManagedEntity.class, entityInstance);
|
||||
|
||||
@Override
|
||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
// call the new methods
|
||||
Method setter = entityClass.getMethod(EnhancerConstants.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class);
|
||||
Method getter = entityClass.getMethod(EnhancerConstants.ENTITY_ENTRY_GETTER_NAME);
|
||||
|
||||
@Override
|
||||
public boolean isLazyLoadable(CtField field) {
|
||||
return true;
|
||||
}
|
||||
assertNull(getter.invoke(entityInstance));
|
||||
setter.invoke(entityInstance, EnhancerTestUtils.makeEntityEntry());
|
||||
assertNotNull(getter.invoke(entityInstance));
|
||||
setter.invoke(entityInstance, new Object[] { null } );
|
||||
assertNull(getter.invoke(entityInstance));
|
||||
|
||||
@Override
|
||||
public boolean isMappedCollection(CtField field) {
|
||||
try {
|
||||
return (field.getAnnotation(OneToMany.class) != null ||
|
||||
field.getAnnotation(ManyToMany.class) != null ||
|
||||
field.getAnnotation(ElementCollection.class) != null);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Method entityInstanceGetter = entityClass.getMethod(EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME);
|
||||
assertSame(entityInstance, entityInstanceGetter.invoke(entityInstance));
|
||||
|
||||
@Override
|
||||
public boolean isPersistentField(CtField ctField) {
|
||||
return true;
|
||||
}
|
||||
Method previousGetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_GETTER_NAME);
|
||||
Method previousSetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class);
|
||||
previousSetter.invoke(entityInstance, entityInstance);
|
||||
assertSame(entityInstance, previousGetter.invoke(entityInstance));
|
||||
|
||||
@Override
|
||||
public CtField[] order(CtField[] persistentFields) {
|
||||
return persistentFields;
|
||||
}
|
||||
};
|
||||
Method nextGetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_GETTER_NAME);
|
||||
Method nextSetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class);
|
||||
nextSetter.invoke(entityInstance, entityInstance);
|
||||
assertSame(entityInstance, nextGetter.invoke(entityInstance));
|
||||
|
||||
@Test
|
||||
public void testEnhancement() throws Exception {
|
||||
testFor( SimpleEntity.class );
|
||||
testFor( SubEntity.class );
|
||||
}
|
||||
// add an attribute interceptor...
|
||||
Method interceptorGetter = entityClass.getMethod(EnhancerConstants.INTERCEPTOR_GETTER_NAME);
|
||||
assertNull(interceptorGetter.invoke(entityInstance));
|
||||
entityClass.getMethod("getId").invoke(entityInstance);
|
||||
|
||||
private void testFor(Class entityClassToEnhance) throws Exception {
|
||||
Enhancer enhancer = new Enhancer( enhancementContext );
|
||||
CtClass entityCtClass = generateCtClassForAnEntity( entityClassToEnhance );
|
||||
byte[] original = entityCtClass.toBytecode();
|
||||
//byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
|
||||
byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
|
||||
assertFalse( "entity was not enhanced", Arrays.equals( original, enhanced ) );
|
||||
Method interceptorSetter = entityClass.getMethod(EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class);
|
||||
interceptorSetter.invoke(entityInstance, new EnhancerTestUtils.LocalPersistentAttributeInterceptor());
|
||||
assertNotNull(interceptorGetter.invoke(entityInstance));
|
||||
|
||||
ClassLoader cl = new ClassLoader() { };
|
||||
ClassPool cp = new ClassPool( false );
|
||||
cp.appendClassPath( new LoaderClassPath( cl ) );
|
||||
CtClass enhancedCtClass = cp.makeClass( new ByteArrayInputStream( enhanced ) );
|
||||
enhancedCtClass.debugWriteFile("/tmp");
|
||||
//just for debugging
|
||||
Class addressClass = null;
|
||||
Class countryClass = null;
|
||||
if(entityClassToEnhance.getName().endsWith("SimpleEntity")) {
|
||||
CtClass addressCtClass = generateCtClassForAnEntity( Address.class );
|
||||
byte[] enhancedAddress = enhancer.enhanceComposite(Address.class.getName(), addressCtClass.toBytecode());
|
||||
CtClass enhancedCtClassAddress = cp.makeClass( new ByteArrayInputStream( enhancedAddress ) );
|
||||
enhancedCtClassAddress.debugWriteFile("/tmp");
|
||||
addressClass = enhancedCtClassAddress.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
// dirty checking is unfortunately just printlns for now... just verify the test output
|
||||
entityClass.getMethod("getId").invoke(entityInstance);
|
||||
entityClass.getMethod("setId", Long.class).invoke(entityInstance, entityClass.getMethod("getId").invoke(entityInstance));
|
||||
entityClass.getMethod("setId", Long.class).invoke(entityInstance, 1L);
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "id");
|
||||
|
||||
CtClass countryCtClass = generateCtClassForAnEntity( Country.class );
|
||||
byte[] enhancedCountry = enhancer.enhanceComposite(Country.class.getName(), countryCtClass.toBytecode());
|
||||
CtClass enhancedCtClassCountry = cp.makeClass( new ByteArrayInputStream( enhancedCountry ) );
|
||||
enhancedCtClassCountry.debugWriteFile("/tmp");
|
||||
countryClass = enhancedCtClassCountry.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
entityClass.getMethod("isActive").invoke(entityInstance);
|
||||
entityClass.getMethod("setActive", boolean.class).invoke(entityInstance, entityClass.getMethod("isActive").invoke(entityInstance));
|
||||
entityClass.getMethod("setActive", boolean.class).invoke(entityInstance, true);
|
||||
|
||||
}
|
||||
Class entityClass = enhancedCtClass.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
Object entityInstance = entityClass.newInstance();
|
||||
entityClass.getMethod("getSomeNumber").invoke(entityInstance);
|
||||
entityClass.getMethod("setSomeNumber", long.class).invoke(entityInstance, entityClass.getMethod("getSomeNumber").invoke(entityInstance));
|
||||
entityClass.getMethod("setSomeNumber", long.class).invoke(entityInstance, 1L);
|
||||
|
||||
assertTyping( ManagedEntity.class, entityInstance );
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "id", "active", "someNumber");
|
||||
EnhancerTestUtils.clearDirtyTracking(entityInstance);
|
||||
|
||||
// call the new methods
|
||||
//
|
||||
Method setter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class );
|
||||
Method getter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_GETTER_NAME );
|
||||
assertNull( getter.invoke( entityInstance ) );
|
||||
setter.invoke( entityInstance, makeEntityEntry() );
|
||||
assertNotNull( getter.invoke( entityInstance ) );
|
||||
setter.invoke( entityInstance, new Object[] {null} );
|
||||
assertNull( getter.invoke( entityInstance ) );
|
||||
// setting the same value should not make it dirty
|
||||
entityClass.getMethod("setSomeNumber", long.class).invoke(entityInstance, 1L);
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance);
|
||||
|
||||
Method entityInstanceGetter = entityClass.getMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME );
|
||||
assertSame( entityInstance, entityInstanceGetter.invoke( entityInstance ) );
|
||||
if(entityClassToEnhance.getName().endsWith(SimpleEntity.class.getSimpleName())) {
|
||||
cl = new ClassLoader() {};
|
||||
|
||||
Method previousGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
|
||||
Method previousSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
|
||||
previousSetter.invoke( entityInstance, entityInstance );
|
||||
assertSame( entityInstance, previousGetter.invoke( entityInstance ) );
|
||||
Class<?> addressClass = EnhancerTestUtils.enhanceAndDecompile(Address.class, cl);
|
||||
Class<?> countryClass = EnhancerTestUtils.enhanceAndDecompile(Country.class, cl);
|
||||
|
||||
Method nextGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
|
||||
Method nextSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
|
||||
nextSetter.invoke( entityInstance, entityInstance );
|
||||
assertSame( entityInstance, nextGetter.invoke( entityInstance ) );
|
||||
entityClass = EnhancerTestUtils.enhanceAndDecompile(entityClassToEnhance, cl);
|
||||
entityInstance = entityClass.newInstance();
|
||||
|
||||
// add an attribute interceptor...
|
||||
Method interceptorGetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
|
||||
Method interceptorSetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
|
||||
|
||||
assertNull( interceptorGetter.invoke( entityInstance ) );
|
||||
entityClass.getMethod( "getId" ).invoke( entityInstance );
|
||||
|
||||
interceptorSetter.invoke( entityInstance, new LocalPersistentAttributeInterceptor() );
|
||||
assertNotNull(interceptorGetter.invoke(entityInstance));
|
||||
|
||||
// dirty checking is unfortunately just printlns for now... just verify the test output
|
||||
entityClass.getMethod( "getId" ).invoke( entityInstance );
|
||||
entityClass.getMethod( "setId", Long.class ).invoke( entityInstance, entityClass.getMethod( "getId" ).invoke( entityInstance ) );
|
||||
entityClass.getMethod( "setId", Long.class ).invoke( entityInstance, 1L );
|
||||
assertTrue((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
|
||||
|
||||
entityClass.getMethod( "isActive" ).invoke( entityInstance );
|
||||
entityClass.getMethod( "setActive", boolean.class ).invoke( entityInstance, entityClass.getMethod( "isActive" ).invoke( entityInstance ) );
|
||||
entityClass.getMethod( "setActive", boolean.class ).invoke(entityInstance, true);
|
||||
|
||||
entityClass.getMethod( "getSomeNumber" ).invoke( entityInstance );
|
||||
entityClass.getMethod( "setSomeNumber", long.class ).invoke( entityInstance, entityClass.getMethod( "getSomeNumber" ).invoke( entityInstance ) );
|
||||
entityClass.getMethod( "setSomeNumber", long.class ).invoke(entityInstance, 1L);
|
||||
assertEquals(3, ((Set) entityClass.getMethod("$$_hibernate_getDirtyAttributes").invoke(entityInstance)).size());
|
||||
|
||||
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
|
||||
//setting the same value should not make it dirty
|
||||
entityClass.getMethod( "setSomeNumber", long.class ).invoke(entityInstance, 1L);
|
||||
//assertEquals(0, ((Set) entityClass.getMethod("$$_hibernate_getDirtyAttributes").invoke(entityInstance)).size());
|
||||
//assertFalse((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
|
||||
|
||||
if(entityClass.getName().endsWith("SimpleEntity")) {
|
||||
interceptorSetter = entityClass.getMethod(EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class);
|
||||
interceptorSetter.invoke(entityInstance, new EnhancerTestUtils.LocalPersistentAttributeInterceptor());
|
||||
|
||||
List<String> strings = new ArrayList<String>();
|
||||
strings.add("FooBar");
|
||||
entityClass.getMethod("setSomeStrings", List.class).invoke(entityInstance, strings);
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "someStrings");
|
||||
EnhancerTestUtils.clearDirtyTracking(entityInstance);
|
||||
|
||||
entityClass.getMethod( "setSomeStrings", java.util.List.class ).invoke(entityInstance, strings);
|
||||
strings.add("JADA!");
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "someStrings");
|
||||
EnhancerTestUtils.clearDirtyTracking(entityInstance);
|
||||
|
||||
assertTrue((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
|
||||
// this should not set the entity to dirty
|
||||
Set<Integer> intSet = new HashSet<Integer>();
|
||||
intSet.add(42);
|
||||
entityClass.getMethod("setSomeInts", Set.class).invoke(entityInstance, intSet);
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance);
|
||||
|
||||
Set tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(1, tracked.size());
|
||||
assertEquals("someStrings", tracked.iterator().next());
|
||||
|
||||
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
|
||||
|
||||
((List) entityClass.getMethod( "getSomeStrings").invoke(entityInstance)).add("JADA!");
|
||||
Boolean isDirty = (Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance);
|
||||
assertTrue(isDirty);
|
||||
assertEquals("someStrings",
|
||||
((Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance)).iterator().next());
|
||||
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
|
||||
|
||||
//this should not set the entity to dirty
|
||||
Set<Integer> ints = new HashSet<Integer>();
|
||||
ints.add(42);
|
||||
entityClass.getMethod( "setSomeInts", java.util.Set.class ).invoke(entityInstance, ints);
|
||||
isDirty = (Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance);
|
||||
assertFalse(isDirty);
|
||||
|
||||
//testing composite object
|
||||
assert addressClass != null;
|
||||
Object address = addressClass.newInstance();
|
||||
|
||||
assert countryClass != null;
|
||||
Object country = countryClass.newInstance();
|
||||
|
||||
//Method adrInterceptorGetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
|
||||
//Method adrInterceptorSetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
|
||||
//adrInterceptorSetter.invoke( address, new LocalPersistentAttributeInterceptor() );
|
||||
// testing composite object
|
||||
Object address = addressClass.newInstance();
|
||||
Object country = countryClass.newInstance();
|
||||
|
||||
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address);
|
||||
|
||||
addressClass.getMethod("setCity", String.class).invoke(address, "Arendal");
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address", "address.city");
|
||||
|
||||
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(2, tracked.size());
|
||||
Iterator iter = tracked.iterator();
|
||||
assertEquals("address", iter.next());
|
||||
assertEquals("address.city", iter.next());
|
||||
entityClass.getMethod(EnhancerConstants.TRACKER_CLEAR_NAME).invoke(entityInstance);
|
||||
|
||||
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
|
||||
|
||||
//make sure that new composite instances are cleared
|
||||
Object address2 = addressClass.newInstance();
|
||||
// make sure that new composite instances are cleared
|
||||
Object address2 = addressClass.newInstance();
|
||||
|
||||
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address2);
|
||||
addressClass.getMethod("setStreet1", String.class).invoke(address, "Heggedalveien");
|
||||
|
||||
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(1, tracked.size());
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address");
|
||||
|
||||
addressClass.getMethod("setCountry", countryClass).invoke(address2, country);
|
||||
countryClass.getMethod("setName", String.class).invoke(country, "Norway");
|
||||
|
||||
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(3, tracked.size());
|
||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address", "address.country", "address.country.name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CtClass generateCtClassForAnEntity(Class entityClassToEnhance) throws Exception {
|
||||
ClassPool cp = new ClassPool( false );
|
||||
return cp.makeClass(
|
||||
getClass().getClassLoader().getResourceAsStream(
|
||||
entityClassToEnhance.getName().replace( '.', '/' ) + ".class"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private EntityEntry makeEntityEntry() {
|
||||
return new EntityEntry(
|
||||
Status.MANAGED,
|
||||
null,
|
||||
null,
|
||||
new Long(1),
|
||||
null,
|
||||
LockMode.NONE,
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private class LocalPersistentAttributeInterceptor implements PersistentAttributeInterceptor {
|
||||
@Override
|
||||
public boolean readBoolean(Object obj, String name, boolean oldValue) {
|
||||
System.out.println( "Reading boolean [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
|
||||
System.out.println( "Writing boolean [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte(Object obj, String name, byte oldValue) {
|
||||
System.out.println( "Reading byte [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
|
||||
System.out.println( "Writing byte [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar(Object obj, String name, char oldValue) {
|
||||
System.out.println( "Reading char [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char writeChar(Object obj, String name, char oldValue, char newValue) {
|
||||
System.out.println( "Writing char [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort(Object obj, String name, short oldValue) {
|
||||
System.out.println( "Reading short [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short writeShort(Object obj, String name, short oldValue, short newValue) {
|
||||
System.out.println( "Writing short [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt(Object obj, String name, int oldValue) {
|
||||
System.out.println( "Reading int [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeInt(Object obj, String name, int oldValue, int newValue) {
|
||||
System.out.println( "Writing int [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat(Object obj, String name, float oldValue) {
|
||||
System.out.println( "Reading float [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float writeFloat(Object obj, String name, float oldValue, float newValue) {
|
||||
System.out.println( "Writing float [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble(Object obj, String name, double oldValue) {
|
||||
System.out.println( "Reading double [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double writeDouble(Object obj, String name, double oldValue, double newValue) {
|
||||
System.out.println( "Writing double [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong(Object obj, String name, long oldValue) {
|
||||
System.out.println( "Reading long [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeLong(Object obj, String name, long oldValue, long newValue) {
|
||||
System.out.println( "Writing long [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readObject(Object obj, String name, Object oldValue) {
|
||||
System.out.println( "Reading Object [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
|
||||
System.out.println( "Writing Object [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.javap.JavapTask;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.LoaderClassPath;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.CompositeOwner;
|
||||
import org.hibernate.engine.spi.CompositeTracker;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* utility class to use in bytecode enhancement tests
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class EnhancerTestUtils extends BaseUnitTestCase {
|
||||
|
||||
private static EnhancementContext enhancementContext = new DefaultEnhancementContext();
|
||||
|
||||
private static String workingDir = System.getProperty("java.io.tmpdir");
|
||||
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger(EnhancerTestUtils.class);
|
||||
|
||||
/**
|
||||
* method that performs the enhancement of a class
|
||||
* also checks the signature of enhanced entities methods using 'javap' decompiler
|
||||
*/
|
||||
static Class<?> enhanceAndDecompile(Class<?> classToEnhance, ClassLoader cl) throws Exception {
|
||||
CtClass entityCtClass = generateCtClassForAnEntity(classToEnhance);
|
||||
|
||||
byte[] original = entityCtClass.toBytecode();
|
||||
byte[] enhanced = new Enhancer(enhancementContext).enhance(entityCtClass.getName(), original);
|
||||
assertFalse("entity was not enhanced", Arrays.equals(original, enhanced));
|
||||
log.infof("enhanced entity [%s]", entityCtClass.getName());
|
||||
|
||||
ClassPool cp = new ClassPool(false);
|
||||
cp.appendClassPath(new LoaderClassPath(cl));
|
||||
CtClass enhancedCtClass = cp.makeClass(new ByteArrayInputStream(enhanced));
|
||||
|
||||
enhancedCtClass.debugWriteFile(workingDir);
|
||||
decompileDumpedClass(classToEnhance.getName());
|
||||
|
||||
Class<?> enhancedClass = enhancedCtClass.toClass(cl, EnhancerTestUtils.class.getProtectionDomain());
|
||||
assertNotNull(enhancedClass);
|
||||
return enhancedClass;
|
||||
}
|
||||
|
||||
private static void decompileDumpedClass(String className) {
|
||||
try {
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
|
||||
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singletonList(new File(workingDir)));
|
||||
|
||||
JavapTask javapTask = new JavapTask();
|
||||
for (JavaFileObject jfo : fileManager.getJavaFileObjects(workingDir + File.separator + getFilenameForClassName(className))) {
|
||||
try {
|
||||
Set<String> interfaceNames = new HashSet<String>();
|
||||
Set<String> fieldNames = new HashSet<String>();
|
||||
Set<String> methodNames = new HashSet<String>();
|
||||
|
||||
JavapTask.ClassFileInfo info = javapTask.read(jfo);
|
||||
|
||||
log.infof("decompiled class [%s]", info.cf.getName());
|
||||
|
||||
for (int i : info.cf.interfaces) {
|
||||
interfaceNames.add(info.cf.constant_pool.getClassInfo(i).getName());
|
||||
log.debugf("declared iFace = ", info.cf.constant_pool.getClassInfo(i).getName());
|
||||
}
|
||||
for (com.sun.tools.classfile.Field f : info.cf.fields) {
|
||||
fieldNames.add(f.getName(info.cf.constant_pool));
|
||||
log.debugf("declared field = ", f.getName(info.cf.constant_pool));
|
||||
}
|
||||
for (com.sun.tools.classfile.Method m : info.cf.methods) {
|
||||
methodNames.add(m.getName(info.cf.constant_pool));
|
||||
log.debugf("declared method = ", m.getName(info.cf.constant_pool));
|
||||
}
|
||||
|
||||
// checks signature against known interfaces
|
||||
if (interfaceNames.contains(PersistentAttributeInterceptor.class.getName())) {
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.INTERCEPTOR_FIELD_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.INTERCEPTOR_GETTER_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.INTERCEPTOR_SETTER_NAME));
|
||||
}
|
||||
if (interfaceNames.contains(ManagedEntity.class.getName())) {
|
||||
assertTrue(methodNames.contains(EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME));
|
||||
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.ENTITY_ENTRY_FIELD_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.ENTITY_ENTRY_GETTER_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.ENTITY_ENTRY_SETTER_NAME));
|
||||
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.PREVIOUS_FIELD_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.PREVIOUS_GETTER_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.PREVIOUS_SETTER_NAME));
|
||||
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.NEXT_FIELD_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.NEXT_GETTER_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.NEXT_SETTER_NAME));
|
||||
}
|
||||
if (interfaceNames.contains(SelfDirtinessTracker.class.getName())) {
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.TRACKER_FIELD_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.TRACKER_GET_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.TRACKER_CLEAR_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.TRACKER_HAS_CHANGED_NAME));
|
||||
}
|
||||
if (interfaceNames.contains(CompositeTracker.class.getName())) {
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER));
|
||||
}
|
||||
if (interfaceNames.contains(CompositeOwner.class.getName())) {
|
||||
assertTrue(fieldNames.contains(EnhancerConstants.TRACKER_CHANGER_NAME));
|
||||
assertTrue(methodNames.contains(EnhancerConstants.TRACKER_CHANGER_NAME));
|
||||
}
|
||||
} catch (ConstantPoolException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
assertNull("Failed to open class file", ioe);
|
||||
} catch (RuntimeException re) {
|
||||
log.warnf(re, "WARNING: UNABLE DECOMPILE DUE TO %s", re.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static CtClass generateCtClassForAnEntity(Class<?> entityClassToEnhance) throws Exception {
|
||||
ClassPool cp = new ClassPool(false);
|
||||
return cp.makeClass(EnhancerTestUtils.class.getClassLoader().getResourceAsStream(getFilenameForClassName(entityClassToEnhance.getName())));
|
||||
}
|
||||
|
||||
private static String getFilenameForClassName(String className) {
|
||||
return className.replace('.', File.separatorChar) + JavaFileObject.Kind.CLASS.extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* clears the dirty set for an entity
|
||||
*/
|
||||
public static void clearDirtyTracking (Object entityInstance) {
|
||||
try {
|
||||
entityInstance.getClass().getMethod(EnhancerConstants.TRACKER_CLEAR_NAME).invoke(entityInstance);
|
||||
checkDirtyTracking(entityInstance);
|
||||
} catch (InvocationTargetException e) {
|
||||
assertNull("Exception in clear dirty tracking", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
assertNull("Exception in clear dirty tracking", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
assertNull("Exception in clear dirty tracking", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* compares the dirty fields of an entity with a set of expected values
|
||||
*/
|
||||
public static void checkDirtyTracking (Object entityInstance, String ... dirtyFields) {
|
||||
try {
|
||||
assertTrue((dirtyFields.length == 0) != (Boolean) entityInstance.getClass().getMethod(EnhancerConstants.TRACKER_HAS_CHANGED_NAME).invoke(entityInstance));
|
||||
Set<?> tracked = (Set<?>) entityInstance.getClass().getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(dirtyFields.length, tracked.size());
|
||||
assertTrue(tracked.containsAll(Arrays.asList(dirtyFields)));
|
||||
} catch (InvocationTargetException e) {
|
||||
assertNull("Exception while checking dirty tracking", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
assertNull("Exception while checking dirty tracking", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
assertNull("Exception while checking dirty tracking", e);
|
||||
}
|
||||
}
|
||||
|
||||
static EntityEntry makeEntityEntry() {
|
||||
return new EntityEntry(
|
||||
Status.MANAGED,
|
||||
null,
|
||||
null,
|
||||
1,
|
||||
null,
|
||||
LockMode.NONE,
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public static class LocalPersistentAttributeInterceptor implements PersistentAttributeInterceptor {
|
||||
|
||||
@Override public boolean readBoolean(Object obj, String name, boolean oldValue) {
|
||||
log.infof( "Reading boolean [%s]" , name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
|
||||
log.infof( "Writing boolean []", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public byte readByte(Object obj, String name, byte oldValue) {
|
||||
log.infof( "Reading byte [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
|
||||
log.infof( "Writing byte [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public char readChar(Object obj, String name, char oldValue) {
|
||||
log.infof( "Reading char [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public char writeChar(Object obj, String name, char oldValue, char newValue) {
|
||||
log.infof( "Writing char [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public short readShort(Object obj, String name, short oldValue) {
|
||||
log.infof( "Reading short [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public short writeShort(Object obj, String name, short oldValue, short newValue) {
|
||||
log.infof( "Writing short [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public int readInt(Object obj, String name, int oldValue) {
|
||||
log.infof( "Reading int [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public int writeInt(Object obj, String name, int oldValue, int newValue) {
|
||||
log.infof( "Writing int [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public float readFloat(Object obj, String name, float oldValue) {
|
||||
log.infof( "Reading float [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public float writeFloat(Object obj, String name, float oldValue, float newValue) {
|
||||
log.infof( "Writing float [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public double readDouble(Object obj, String name, double oldValue) {
|
||||
log.infof( "Reading double [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public double writeDouble(Object obj, String name, double oldValue, double newValue) {
|
||||
log.infof( "Writing double [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public long readLong(Object obj, String name, long oldValue) {
|
||||
log.infof( "Reading long [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public long writeLong(Object obj, String name, long oldValue, long newValue) {
|
||||
log.infof( "Writing long [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override public Object readObject(Object obj, String name, Object oldValue) {
|
||||
log.infof( "Reading Object [%s]", name );
|
||||
return oldValue;
|
||||
}
|
||||
@Override public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
|
||||
log.infof( "Writing Object [%s]", name );
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -25,6 +25,7 @@ package org.hibernate.test.bytecode.enhancement;
|
|||
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.hibernate.test.bytecode.enhancement.entity.MyEntity;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
@ -53,14 +54,14 @@ public class MostBasicEnhancementTest extends BaseCoreFunctionalTestCase {
|
|||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
MyEntity myEntity = (MyEntity) s.get( MyEntity.class, 1L );
|
||||
MyEntity myEntity1 = (MyEntity) s.get( MyEntity.class, 1L );
|
||||
MyEntity myEntity2 = (MyEntity) s.get( MyEntity.class, 2L );
|
||||
|
||||
assertNotNull( myEntity.$$_hibernate_getEntityInstance() );
|
||||
assertSame( myEntity, myEntity.$$_hibernate_getEntityInstance() );
|
||||
assertNotNull( myEntity.$$_hibernate_getEntityEntry() );
|
||||
assertNull( myEntity.$$_hibernate_getPreviousManagedEntity() );
|
||||
assertNotNull( myEntity.$$_hibernate_getNextManagedEntity() );
|
||||
assertNotNull( myEntity1.$$_hibernate_getEntityInstance() );
|
||||
assertSame( myEntity1, myEntity1.$$_hibernate_getEntityInstance() );
|
||||
assertNotNull( myEntity1.$$_hibernate_getEntityEntry() );
|
||||
assertNull( myEntity1.$$_hibernate_getPreviousManagedEntity() );
|
||||
assertNotNull( myEntity1.$$_hibernate_getNextManagedEntity() );
|
||||
|
||||
assertNotNull( myEntity2.$$_hibernate_getEntityInstance() );
|
||||
assertSame( myEntity2, myEntity2.$$_hibernate_getEntityInstance() );
|
||||
|
@ -72,7 +73,7 @@ public class MostBasicEnhancementTest extends BaseCoreFunctionalTestCase {
|
|||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
assertNull( myEntity.$$_hibernate_getEntityEntry() );
|
||||
assertNull( myEntity1.$$_hibernate_getEntityEntry() );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,382 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.customer;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import javassist.LoaderClassPath;
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CustomerEnhancerTest extends BaseUnitTestCase {
|
||||
private static EnhancementContext enhancementContext = new EnhancementContext() {
|
||||
@Override
|
||||
public ClassLoader getLoadingClassLoader() {
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntityClass(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLazyLoadable(CtField field) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMappedCollection(CtField field) {
|
||||
try {
|
||||
return (field.getAnnotation(OneToMany.class) != null ||
|
||||
field.getAnnotation(ManyToMany.class) != null ||
|
||||
field.getAnnotation(ElementCollection.class) != null);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPersistentField(CtField ctField) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CtField[] order(CtField[] persistentFields) {
|
||||
return persistentFields;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testEnhancement() throws Exception {
|
||||
testFor( Customer.class );
|
||||
}
|
||||
|
||||
private void testFor(Class entityClassToEnhance) throws Exception {
|
||||
Enhancer enhancer = new Enhancer( enhancementContext );
|
||||
CtClass entityCtClass = generateCtClassForAnEntity( entityClassToEnhance );
|
||||
byte[] original = entityCtClass.toBytecode();
|
||||
//byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
|
||||
byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
|
||||
assertFalse( "entity was not enhanced", Arrays.equals( original, enhanced ) );
|
||||
|
||||
ClassLoader cl = new ClassLoader() { };
|
||||
ClassPool cp = new ClassPool( false );
|
||||
cp.appendClassPath( new LoaderClassPath( cl ) );
|
||||
CtClass enhancedCtClass = cp.makeClass( new ByteArrayInputStream( enhanced ) );
|
||||
enhancedCtClass.debugWriteFile("/tmp");
|
||||
//just for debugging
|
||||
Class addressClass = null;
|
||||
Class customerInventoryClass = null;
|
||||
Class supplierComponentPKClass = null;
|
||||
|
||||
CtClass addressCtClass = generateCtClassForAnEntity( org.hibernate.test.bytecode.enhancement.customer.Address.class );
|
||||
byte[] enhancedAddress = enhancer.enhanceComposite(Address.class.getName(), addressCtClass.toBytecode());
|
||||
CtClass enhancedCtClassAddress = cp.makeClass( new ByteArrayInputStream( enhancedAddress ) );
|
||||
enhancedCtClassAddress.debugWriteFile("/tmp");
|
||||
addressClass = enhancedCtClassAddress.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
|
||||
CtClass customerInventoryCtClass = generateCtClassForAnEntity( CustomerInventory.class );
|
||||
byte[] enhancedCustomerInventory = enhancer.enhance(CustomerInventory.class.getName(), customerInventoryCtClass.toBytecode());
|
||||
CtClass enhancedCtClassCustomerInventory = cp.makeClass( new ByteArrayInputStream( enhancedCustomerInventory ) );
|
||||
enhancedCtClassCustomerInventory.debugWriteFile("/tmp");
|
||||
customerInventoryClass = enhancedCtClassCustomerInventory.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
|
||||
|
||||
CtClass supplierComponentPKCtClass = generateCtClassForAnEntity( SupplierComponentPK.class );
|
||||
byte[] enhancedSupplierComponentPK = enhancer.enhanceComposite(SupplierComponentPK.class.getName(), supplierComponentPKCtClass.toBytecode());
|
||||
CtClass enhancedCtClassSupplierComponentPK = cp.makeClass( new ByteArrayInputStream( enhancedSupplierComponentPK ) );
|
||||
enhancedCtClassSupplierComponentPK.debugWriteFile("/tmp");
|
||||
supplierComponentPKClass = enhancedCtClassSupplierComponentPK.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
|
||||
Class entityClass = enhancedCtClass.toClass( cl, this.getClass().getProtectionDomain() );
|
||||
Object entityInstance = entityClass.newInstance();
|
||||
|
||||
assertTyping( ManagedEntity.class, entityInstance );
|
||||
|
||||
// call the new methods
|
||||
//
|
||||
Method setter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class );
|
||||
Method getter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_GETTER_NAME );
|
||||
assertNull( getter.invoke( entityInstance ) );
|
||||
setter.invoke( entityInstance, makeEntityEntry() );
|
||||
assertNotNull( getter.invoke( entityInstance ) );
|
||||
setter.invoke( entityInstance, new Object[] {null} );
|
||||
assertNull( getter.invoke( entityInstance ) );
|
||||
|
||||
Method entityInstanceGetter = entityClass.getMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME );
|
||||
assertSame( entityInstance, entityInstanceGetter.invoke( entityInstance ) );
|
||||
|
||||
Method previousGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
|
||||
Method previousSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
|
||||
previousSetter.invoke( entityInstance, entityInstance );
|
||||
assertSame( entityInstance, previousGetter.invoke( entityInstance ) );
|
||||
|
||||
Method nextGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
|
||||
Method nextSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
|
||||
nextSetter.invoke( entityInstance, entityInstance );
|
||||
assertSame( entityInstance, nextGetter.invoke( entityInstance ) );
|
||||
|
||||
// add an attribute interceptor...
|
||||
Method interceptorGetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
|
||||
Method interceptorSetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
|
||||
|
||||
assertNull( interceptorGetter.invoke( entityInstance ) );
|
||||
entityClass.getMethod( "getId" ).invoke( entityInstance );
|
||||
|
||||
interceptorSetter.invoke( entityInstance, new LocalPersistentAttributeInterceptor() );
|
||||
assertNotNull(interceptorGetter.invoke(entityInstance));
|
||||
|
||||
// dirty checking is unfortunately just printlns for now... just verify the test output
|
||||
entityClass.getMethod( "getId" ).invoke( entityInstance );
|
||||
entityClass.getMethod( "setId", Integer.class ).invoke( entityInstance, entityClass.getMethod( "getId" ).invoke( entityInstance ) );
|
||||
entityClass.getMethod( "setId", Integer.class ).invoke( entityInstance, 1 );
|
||||
assertTrue((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
|
||||
|
||||
entityClass.getMethod( "setFirstName", String.class ).invoke(entityInstance, "Erik");
|
||||
|
||||
entityClass.getMethod( "setLastName", String.class ).invoke(entityInstance, "Mykland");
|
||||
assertEquals(3, ((Set) entityClass.getMethod("$$_hibernate_getDirtyAttributes").invoke(entityInstance)).size());
|
||||
|
||||
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
|
||||
|
||||
//testing composite object
|
||||
assert addressClass != null;
|
||||
Object address = addressClass.newInstance();
|
||||
|
||||
assert customerInventoryClass != null;
|
||||
Object customerInventory = customerInventoryClass.newInstance();
|
||||
|
||||
//Method adrInterceptorGetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
|
||||
//Method adrInterceptorSetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
|
||||
//adrInterceptorSetter.invoke( address, new LocalPersistentAttributeInterceptor() );
|
||||
|
||||
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address);
|
||||
|
||||
addressClass.getMethod("setCity", String.class).invoke(address, "Arendal");
|
||||
|
||||
Set tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(2, tracked.size());
|
||||
Iterator iter = tracked.iterator();
|
||||
assertEquals("address", iter.next());
|
||||
assertEquals("address.city", iter.next());
|
||||
|
||||
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
|
||||
|
||||
//make sure that new composite instances are cleared
|
||||
Object address2 = addressClass.newInstance();
|
||||
|
||||
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address2);
|
||||
addressClass.getMethod("setStreet1", String.class).invoke(address, "Heggedalveien");
|
||||
|
||||
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
|
||||
assertEquals(1, tracked.size());
|
||||
|
||||
}
|
||||
|
||||
private CtClass generateCtClassForAnEntity(Class entityClassToEnhance) throws Exception {
|
||||
ClassPool cp = new ClassPool( false );
|
||||
return cp.makeClass(
|
||||
getClass().getClassLoader().getResourceAsStream(
|
||||
entityClassToEnhance.getName().replace( '.', '/' ) + ".class"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private EntityEntry makeEntityEntry() {
|
||||
return new EntityEntry(
|
||||
Status.MANAGED,
|
||||
null,
|
||||
null,
|
||||
new Long(1),
|
||||
null,
|
||||
LockMode.NONE,
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private class LocalPersistentAttributeInterceptor implements PersistentAttributeInterceptor {
|
||||
@Override
|
||||
public boolean readBoolean(Object obj, String name, boolean oldValue) {
|
||||
System.out.println( "Reading boolean [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
|
||||
System.out.println( "Writing boolean [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte(Object obj, String name, byte oldValue) {
|
||||
System.out.println( "Reading byte [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
|
||||
System.out.println( "Writing byte [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar(Object obj, String name, char oldValue) {
|
||||
System.out.println( "Reading char [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char writeChar(Object obj, String name, char oldValue, char newValue) {
|
||||
System.out.println( "Writing char [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort(Object obj, String name, short oldValue) {
|
||||
System.out.println( "Reading short [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short writeShort(Object obj, String name, short oldValue, short newValue) {
|
||||
System.out.println( "Writing short [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt(Object obj, String name, int oldValue) {
|
||||
System.out.println( "Reading int [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeInt(Object obj, String name, int oldValue, int newValue) {
|
||||
System.out.println( "Writing int [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat(Object obj, String name, float oldValue) {
|
||||
System.out.println( "Reading float [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float writeFloat(Object obj, String name, float oldValue, float newValue) {
|
||||
System.out.println( "Writing float [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble(Object obj, String name, double oldValue) {
|
||||
System.out.println( "Reading double [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double writeDouble(Object obj, String name, double oldValue, double newValue) {
|
||||
System.out.println( "Writing double [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong(Object obj, String name, long oldValue) {
|
||||
System.out.println( "Reading long [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeLong(Object obj, String name, long oldValue, long newValue) {
|
||||
System.out.println( "Writing long [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readObject(Object obj, String name, Object oldValue) {
|
||||
System.out.println( "Reading Object [" + name + "]" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
|
||||
System.out.println( "Writing Object [" + name + "]" );
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* Licensed under the Eclipse Public License version 1.0, available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
|
@ -4,7 +4,7 @@
|
|||
* Licensed under the Eclipse Public License version 1.0, available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
|
@ -21,15 +21,15 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
|
@ -21,16 +21,16 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.entity;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
|
@ -20,13 +20,15 @@
|
|||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/package org.hibernate.test.bytecode.enhancement.customer;
|
||||
*/package org.hibernate.test.bytecode.enhancement.entity.customer;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||
*/
|
||||
@Embeddable
|
||||
public class Address implements Serializable {
|
||||
private String street1;
|
||||
private String street2;
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.customer;
|
||||
package org.hibernate.test.bytecode.enhancement.entity.customer;
|
||||
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.AttributeOverrides;
|
|
@ -20,7 +20,8 @@
|
|||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/package org.hibernate.test.bytecode.enhancement.customer;
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.entity.customer;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
|
@ -4,7 +4,7 @@
|
|||
* Licensed under the Eclipse Public License version 1.0, available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.customer;
|
||||
package org.hibernate.test.bytecode.enhancement.entity.customer;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.customer;
|
||||
package org.hibernate.test.bytecode.enhancement.entity.customer;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
|
@ -21,9 +21,9 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement;
|
||||
package org.hibernate.test.bytecode.enhancement.tracker;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.CompositeOwnerTracker;
|
||||
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
||||
import org.hibernate.engine.spi.CompositeOwner;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -32,7 +32,6 @@ import static org.junit.Assert.assertEquals;
|
|||
/**
|
||||
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||
*/
|
||||
|
||||
public class CompositeOwnerTrackerTest {
|
||||
|
||||
private int counter = 0;
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.tracker;
|
||||
|
||||
import org.hibernate.bytecode.enhance.internal.tracker.SortedDirtyTracker;
|
||||
import org.hibernate.bytecode.enhance.internal.tracker.SimpleDirtyTracker;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||
*/
|
||||
public class DirtyTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testSimpleTracker() {
|
||||
SimpleDirtyTracker tracker = new SimpleDirtyTracker();
|
||||
assertTrue(tracker.isEmpty());
|
||||
assertTrue(tracker.asSet().isEmpty());
|
||||
|
||||
tracker.add("foo");
|
||||
assertFalse(tracker.isEmpty());
|
||||
assertArrayEquals(tracker.asSet().toArray(), new String[]{"foo"});
|
||||
|
||||
tracker.clear();
|
||||
assertTrue(tracker.isEmpty());
|
||||
assertTrue(tracker.asSet().isEmpty());
|
||||
|
||||
tracker.add("foo");
|
||||
tracker.add("bar");
|
||||
tracker.add("another.bar");
|
||||
tracker.add("foo");
|
||||
tracker.add("another.foo");
|
||||
tracker.add("another.bar");
|
||||
assertTrue(tracker.asSet().size() == 4);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedTracker() {
|
||||
SortedDirtyTracker tracker = new SortedDirtyTracker();
|
||||
assertTrue(tracker.isEmpty());
|
||||
assertTrue(tracker.asSet().isEmpty());
|
||||
|
||||
tracker.add("foo");
|
||||
assertFalse(tracker.isEmpty());
|
||||
assertArrayEquals(tracker.asSet().toArray(), new String[]{"foo"});
|
||||
|
||||
tracker.clear();
|
||||
assertTrue(tracker.isEmpty());
|
||||
assertTrue(tracker.asSet().isEmpty());
|
||||
|
||||
tracker.add("foo");
|
||||
tracker.add("bar");
|
||||
tracker.add("another.bar");
|
||||
tracker.add("foo");
|
||||
tracker.add("another.foo");
|
||||
tracker.add("another.bar");
|
||||
assertTrue(tracker.asSet().size() == 4);
|
||||
|
||||
// we the algorithm for this implementation relies on the fact that the array is kept sorted, so let's check it really is
|
||||
assertTrue(isSorted(tracker.asSet()));
|
||||
}
|
||||
|
||||
private boolean isSorted(Set<String> set) {
|
||||
String[] arr = new String[set.size()];
|
||||
arr = set.toArray(arr);
|
||||
for (int i = 1; i < arr.length; i++) {
|
||||
if (arr[i - 1].compareTo(arr[i]) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue