HHH-12327 - Remove the Envers dependency on Javassist.
This commit is contained in:
parent
493c968141
commit
4a3f7c19c0
|
@ -12,8 +12,6 @@ description = 'Hibernate\'s entity version (audit/history) support'
|
|||
|
||||
dependencies {
|
||||
compile( project( ':hibernate-core' ) )
|
||||
//Ideally javassist should be only an optional dependency but it's currently required by Envers: see HHH-12327
|
||||
compile( libraries.javassist )
|
||||
|
||||
provided( [group: 'org.hibernate', name: 'hibernate-tools', version: '3.2.0.ga'] )
|
||||
provided( libraries.ant )
|
||||
|
@ -21,7 +19,6 @@ dependencies {
|
|||
|
||||
testCompile( project( ':hibernate-testing' ) )
|
||||
testCompile( project( path: ':hibernate-core', configuration: 'tests' ) )
|
||||
testRuntime( libraries.byteBuddy )
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.envers.internal.entities.mapper;
|
||||
|
||||
/**
|
||||
* Abstract implementation of a {@link PropertyMapper}.
|
||||
*
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public abstract class AbstractPropertyMapper implements PropertyMapper {
|
||||
private boolean map;
|
||||
|
||||
@Override
|
||||
public void markAsDynamicComponentMap() {
|
||||
this.map = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamicComponentMap() {
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ import org.hibernate.property.access.spi.Setter;
|
|||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class ComponentPropertyMapper implements PropertyMapper, CompositeMapperBuilder {
|
||||
public class ComponentPropertyMapper extends AbstractPropertyMapper implements CompositeMapperBuilder {
|
||||
private final PropertyData propertyData;
|
||||
private final MultiPropertyMapper delegate;
|
||||
private final Class componentClass;
|
||||
|
@ -122,28 +122,34 @@ public class ComponentPropertyMapper implements PropertyMapper, CompositeMapperB
|
|||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
propertyData,
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
|
||||
if ( isAllPropertiesNull( data ) ) {
|
||||
// single property, but default value need not be null, so we'll set it to null anyway
|
||||
setter.set( obj, null, null );
|
||||
}
|
||||
else {
|
||||
// set the component
|
||||
try {
|
||||
final Object subObj = ReflectHelper.getDefaultConstructor( componentClass ).newInstance();
|
||||
setter.set( obj, subObj, null );
|
||||
try {
|
||||
final Object subObj = ReflectHelper.getDefaultConstructor( componentClass ).newInstance();
|
||||
|
||||
if ( isDynamicComponentMap() ) {
|
||||
( (Map) obj ).put( propertyData.getBeanName(), subObj );
|
||||
delegate.mapToEntityFromMap( enversService, subObj, data, primaryKey, versionsReader, revision );
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new AuditException( e );
|
||||
else {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
propertyData,
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
|
||||
if ( isAllPropertiesNull( data ) ) {
|
||||
// single property, but default value need not be null, so we'll set it to null anyway
|
||||
setter.set( obj, null, null );
|
||||
}
|
||||
else {
|
||||
// set the component
|
||||
setter.set( obj, subObj, null );
|
||||
delegate.mapToEntityFromMap( enversService, subObj, data, primaryKey, versionsReader, revision );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.envers.internal.entities.mapper;
|
||||
|
||||
/**
|
||||
* Contract for {@link PropertyMapper} implementations to expose whether they should be included
|
||||
* as a wrapper for a {@code <dynamic-component/>} mapping.
|
||||
*
|
||||
* In this mapping, values are actually stored as a key-value pair in a HashMap rather than
|
||||
* them being treated as java-bean values using a setter method.
|
||||
*
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public interface DynamicComponentMapperSupport {
|
||||
/**
|
||||
* Mark the property mapper that it wraps a dynamic-component.
|
||||
*/
|
||||
void markAsDynamicComponentMap();
|
||||
|
||||
/**
|
||||
* Returns whether the property mapper wraps a dynamic-component.
|
||||
*/
|
||||
boolean isDynamicComponentMap();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
|
@ -12,13 +12,12 @@ import org.hibernate.engine.spi.SessionImplementor;
|
|||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.internal.entities.PropertyData;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.MapProxyTool;
|
||||
import org.hibernate.envers.internal.tools.StringTools;
|
||||
|
||||
/**
|
||||
* Multi mapper for dynamic components (it knows that component is a map, not a class)
|
||||
*
|
||||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
||||
|
||||
|
@ -28,6 +27,22 @@ public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
|||
this.dynamicComponentData = dynamicComponentData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addComposite(PropertyData propertyData, PropertyMapper propertyMapper) {
|
||||
// delegate to the super implementation and then mark the property mapper as a dynamic-component map.
|
||||
super.addComposite( propertyData, propertyMapper );
|
||||
propertyMapper.markAsDynamicComponentMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(PropertyData propertyData) {
|
||||
final SinglePropertyMapper single = new SinglePropertyMapper();
|
||||
single.add( propertyData );
|
||||
|
||||
// delegate to our implementation of #addComposite for specialized behavior.
|
||||
addComposite( propertyData, single );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mapToMapFromEntity(
|
||||
SessionImplementor session,
|
||||
|
@ -96,7 +111,6 @@ public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mapToEntityFromMap(
|
||||
EnversService enversService,
|
||||
Object obj,
|
||||
|
@ -104,22 +118,8 @@ public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
|||
Object primaryKey,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Number revision) {
|
||||
Object mapProxy = MapProxyTool.newInstanceOfBeanProxyForMap(
|
||||
generateClassName( data, dynamicComponentData.getBeanName() ),
|
||||
(Map) obj,
|
||||
properties.keySet(),
|
||||
enversService.getClassLoaderService()
|
||||
);
|
||||
for ( PropertyMapper mapper : properties.values() ) {
|
||||
mapper.mapToEntityFromMap( enversService, mapProxy, data, primaryKey, versionsReader, revision );
|
||||
mapper.mapToEntityFromMap( enversService, obj, data, primaryKey, versionsReader, revision );
|
||||
}
|
||||
}
|
||||
|
||||
private String generateClassName(Map data, String dynamicComponentPropertyName) {
|
||||
return ( data.get( "$type$" ) + StringTools.capitalizeFirst( dynamicComponentPropertyName ) ).replaceAll(
|
||||
"_",
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.hibernate.property.access.spi.Getter;
|
|||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class MultiPropertyMapper implements ExtendedPropertyMapper {
|
||||
public class MultiPropertyMapper extends AbstractPropertyMapper implements ExtendedPropertyMapper {
|
||||
protected final Map<PropertyData, PropertyMapper> properties;
|
||||
private final Map<String, PropertyData> propertyDatas;
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
|||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public interface PropertyMapper extends ModifiedFlagMapperSupport {
|
||||
public interface PropertyMapper extends ModifiedFlagMapperSupport, DynamicComponentMapperSupport {
|
||||
/**
|
||||
* Maps properties to the given map, basing on differences between properties of new and old objects.
|
||||
*
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.hibernate.property.access.spi.SetterFieldImpl;
|
|||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder {
|
||||
public class SinglePropertyMapper extends AbstractPropertyMapper implements SimpleMapperBuilder {
|
||||
private PropertyData propertyData;
|
||||
|
||||
public SinglePropertyMapper(PropertyData propertyData) {
|
||||
|
@ -97,27 +97,34 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
|
|||
return;
|
||||
}
|
||||
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
propertyData,
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
final Object value = data.get( propertyData.getName() );
|
||||
|
||||
final Object value = data.get( propertyData.getName() );
|
||||
if ( isDynamicComponentMap() ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> map = (Map<String, Object>) obj;
|
||||
map.put( propertyData.getBeanName(), value );
|
||||
}
|
||||
else {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
propertyData,
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
|
||||
// We only set a null value if the field is not primitive. Otherwise, we leave it intact.
|
||||
if ( value != null || !isPrimitive( setter, propertyData, obj.getClass() ) ) {
|
||||
setter.set( obj, value, null );
|
||||
// We only set a null value if the field is not primitive. Otherwise, we leave it intact.
|
||||
if ( value != null || !isPrimitive( setter, propertyData, obj.getClass() ) ) {
|
||||
setter.set( obj, value, null );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPrimitive(Setter setter, PropertyData propertyData, Class<?> cls) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
|||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class SubclassPropertyMapper implements ExtendedPropertyMapper {
|
||||
public class SubclassPropertyMapper extends AbstractPropertyMapper implements ExtendedPropertyMapper {
|
||||
private ExtendedPropertyMapper main;
|
||||
private ExtendedPropertyMapper parentMapper;
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ import org.hibernate.envers.RevisionType;
|
|||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.PropertyData;
|
||||
import org.hibernate.envers.internal.entities.mapper.AbstractPropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.Initializor;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.ReflectionTools;
|
||||
|
@ -42,7 +42,7 @@ import org.hibernate.property.access.spi.Setter;
|
|||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
|
||||
public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper {
|
||||
protected final CommonCollectionMapperData commonCollectionMapperData;
|
||||
protected final Class<? extends T> collectionClass;
|
||||
protected final boolean ordinalInId;
|
||||
|
@ -256,45 +256,68 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
|
|||
final AuditReaderImplementor versionsReader,
|
||||
final Number revision) {
|
||||
final String revisionTypePropertyName = enversService.getAuditEntitiesConfiguration().getRevisionTypePropName();
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
commonCollectionMapperData.getCollectionReferencingPropertyData(),
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
|
||||
try {
|
||||
setter.set(
|
||||
obj,
|
||||
proxyConstructor.newInstance(
|
||||
getInitializor(
|
||||
enversService,
|
||||
versionsReader,
|
||||
primaryKey,
|
||||
revision,
|
||||
RevisionType.DEL.equals( data.get( revisionTypePropertyName ) )
|
||||
)
|
||||
),
|
||||
null
|
||||
if ( isDynamicComponentMap() ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> map = (Map<String, Object>) obj;
|
||||
try {
|
||||
map.put(
|
||||
commonCollectionMapperData.getCollectionReferencingPropertyData().getBeanName(),
|
||||
proxyConstructor.newInstance(
|
||||
getInitializor(
|
||||
enversService,
|
||||
versionsReader,
|
||||
primaryKey,
|
||||
revision,
|
||||
RevisionType.DEL.equals( data.get( revisionTypePropertyName ) )
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
}
|
||||
else {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
commonCollectionMapperData.getCollectionReferencingPropertyData(),
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
|
||||
return null;
|
||||
try {
|
||||
setter.set(
|
||||
obj,
|
||||
proxyConstructor.newInstance(
|
||||
getInitializor(
|
||||
enversService,
|
||||
versionsReader,
|
||||
primaryKey,
|
||||
revision,
|
||||
RevisionType.DEL.equals( data.get( revisionTypePropertyName ) )
|
||||
)
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
catch ( InstantiationException e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
catch ( IllegalAccessException e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
catch ( InvocationTargetException e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,8 +17,8 @@ import org.hibernate.engine.spi.SessionImplementor;
|
|||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.internal.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.internal.entities.PropertyData;
|
||||
import org.hibernate.envers.internal.entities.mapper.AbstractPropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.ReflectionTools;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
|
@ -30,7 +30,7 @@ import org.hibernate.service.ServiceRegistry;
|
|||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public abstract class AbstractToOneMapper implements PropertyMapper {
|
||||
public abstract class AbstractToOneMapper extends AbstractPropertyMapper {
|
||||
private final ServiceRegistry serviceRegistry;
|
||||
private final PropertyData propertyData;
|
||||
|
||||
|
@ -90,21 +90,28 @@ public abstract class AbstractToOneMapper implements PropertyMapper {
|
|||
}
|
||||
|
||||
protected void setPropertyValue(Object targetObject, Object value) {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
targetObject.getClass(),
|
||||
propertyData,
|
||||
serviceRegistry
|
||||
);
|
||||
setter.set( targetObject, value, null );
|
||||
if ( isDynamicComponentMap() ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> map = (Map<String, Object>) targetObject;
|
||||
map.put( propertyData.getBeanName(), value );
|
||||
}
|
||||
else {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
targetObject.getClass(),
|
||||
propertyData,
|
||||
serviceRegistry
|
||||
);
|
||||
setter.set( targetObject, value, null );
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.envers.internal.tools;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtConstructor;
|
||||
import javassist.CtField;
|
||||
import javassist.CtMethod;
|
||||
import javassist.CtNewConstructor;
|
||||
import javassist.NotFoundException;
|
||||
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||
import org.hibernate.envers.internal.entities.PropertyData;
|
||||
|
||||
import static org.hibernate.envers.internal.tools.StringTools.capitalizeFirst;
|
||||
import static org.hibernate.envers.internal.tools.StringTools.getLastComponent;
|
||||
|
||||
/**
|
||||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||
*/
|
||||
public final class MapProxyTool {
|
||||
private MapProxyTool() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance of map proxy class. This proxy class will be a java bean with properties from <code>propertyDatas</code>.
|
||||
* Instance will proxy calls to instance of the map passed as parameter.
|
||||
*
|
||||
* @param className Name of the class to construct (should be unique within class loader)
|
||||
* @param map instance that will be proxied by java bean
|
||||
* @param propertyDatas properties that should java bean declare
|
||||
* @param classLoaderService class loader service
|
||||
*
|
||||
* @return new instance of proxy
|
||||
*/
|
||||
public static Object newInstanceOfBeanProxyForMap(
|
||||
String className,
|
||||
Map<String, Object> map,
|
||||
Set<PropertyData> propertyDatas,
|
||||
ClassLoaderService classLoaderService) {
|
||||
Map<String, Class<?>> properties = prepareProperties( propertyDatas );
|
||||
return createNewInstance( map, classForName( className, properties, classLoaderService ) );
|
||||
}
|
||||
|
||||
private static Object createNewInstance(Map<String, Object> map, Class aClass) {
|
||||
try {
|
||||
return aClass.getConstructor( Map.class ).newInstance( map );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Class<?>> prepareProperties(Set<PropertyData> propertyDatas) {
|
||||
Map<String, Class<?>> properties = new HashMap<>();
|
||||
for ( PropertyData propertyData : propertyDatas ) {
|
||||
properties.put( propertyData.getBeanName(), Object.class );
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static Class loadClass(String className, ClassLoaderService classLoaderService) {
|
||||
try {
|
||||
return ReflectionTools.loadClass( className, classLoaderService );
|
||||
}
|
||||
catch (ClassLoadingException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates/loads proxy class for given name with properties for map.
|
||||
*
|
||||
* @param className name of the class that will be generated/loaded
|
||||
* @param properties list of properties that should be exposed via java bean
|
||||
* @param classLoaderService class loader service
|
||||
*
|
||||
* @return proxy class that wraps map into java bean
|
||||
*/
|
||||
public static Class classForName(
|
||||
String className,
|
||||
Map<String, Class<?>> properties,
|
||||
ClassLoaderService classLoaderService) {
|
||||
Class aClass = loadClass( className, classLoaderService );
|
||||
if ( aClass == null ) {
|
||||
aClass = generate( className, properties );
|
||||
}
|
||||
return aClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected for test only
|
||||
*/
|
||||
protected static Class generate(String className, Map<String, Class<?>> properties) {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
CtClass cc = pool.makeClass( className );
|
||||
|
||||
cc.addInterface( resolveCtClass( Serializable.class ) );
|
||||
cc.addField( new CtField( resolveCtClass( Map.class ), "theMap", cc ) );
|
||||
cc.addConstructor( generateConstructor( className, cc ) );
|
||||
|
||||
for ( Entry<String, Class<?>> entry : properties.entrySet() ) {
|
||||
|
||||
// add getter
|
||||
cc.addMethod( generateGetter( cc, entry.getKey(), entry.getValue() ) );
|
||||
|
||||
// add setter
|
||||
cc.addMethod( generateSetter( cc, entry.getKey(), entry.getValue() ) );
|
||||
}
|
||||
return cc.toClass();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private static CtConstructor generateConstructor(String className, CtClass cc)
|
||||
throws NotFoundException, CannotCompileException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append( "public " )
|
||||
.append( getLastComponent( className ) )
|
||||
.append( "(" )
|
||||
.append( Map.class.getName() )
|
||||
.append( " map)" )
|
||||
.append( "{" )
|
||||
.append( "this.theMap = map;" )
|
||||
.append( "}" );
|
||||
return CtNewConstructor.make( sb.toString(), cc );
|
||||
}
|
||||
|
||||
private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
|
||||
throws CannotCompileException {
|
||||
|
||||
String getterName = "get" + capitalizeFirst( fieldName );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( "public " ).append( fieldClass.getName() ).append( " " )
|
||||
.append( getterName ).append( "(){" ).append( "return (" ).append( fieldClass.getName() ).append(
|
||||
")this.theMap.get(\""
|
||||
)
|
||||
.append( fieldName ).append( "\")" ).append( ";" ).append( "}" );
|
||||
return CtMethod.make( sb.toString(), declaringClass );
|
||||
}
|
||||
|
||||
private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
|
||||
throws CannotCompileException {
|
||||
|
||||
String setterName = "set" + capitalizeFirst( fieldName );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( "public void " ).append( setterName ).append( "(" )
|
||||
.append( fieldClass.getName() ).append( " " ).append( fieldName )
|
||||
.append( ")" ).append( "{" ).append( "this.theMap.put(\"" ).append( fieldName )
|
||||
.append( "\"," ).append( fieldName ).append( ")" ).append( ";" ).append( "}" );
|
||||
return CtMethod.make( sb.toString(), declaringClass );
|
||||
}
|
||||
|
||||
private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
|
||||
return resolveCtClass( clazz.getName() );
|
||||
}
|
||||
|
||||
|
||||
private static CtClass resolveCtClass(String clazz) throws NotFoundException {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
return pool.get( clazz );
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.envers.internal.tools;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class MapProxyTest {
|
||||
|
||||
private StandardServiceRegistry serviceRegistry;
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
serviceRegistry = new StandardServiceRegistryBuilder().build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() {
|
||||
StandardServiceRegistryBuilder.destroy( serviceRegistry );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGenerateClassWithAppropriateGetter() throws Exception {
|
||||
//given
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
int ageExpected = 14;
|
||||
map.put("age", ageExpected);
|
||||
Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
|
||||
properties.put("age", Integer.class);
|
||||
//when
|
||||
Class testClass = MapProxyTool.classForName("TestClass1", properties, new ClassLoaderServiceImpl());
|
||||
Object testClassInstance = testClass.getConstructor(Map.class).newInstance(map);
|
||||
|
||||
//then
|
||||
Getter getter = ReflectionTools.getGetter( testClass, "age", "property", serviceRegistry );
|
||||
int age = (Integer) getter.get(testClassInstance);
|
||||
Assert.assertEquals(ageExpected, age);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGenerateClassWithAppropriateSetter() throws Exception {
|
||||
//given
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
|
||||
properties.put("age", Integer.class);
|
||||
|
||||
//when
|
||||
Class testClass = MapProxyTool.classForName("TestClass2", properties, new ClassLoaderServiceImpl());
|
||||
Object testClassInstance = testClass.getConstructor(Map.class).newInstance(map);
|
||||
|
||||
//then
|
||||
Setter setter = ReflectionTools.getSetter(testClass, "age", "property", serviceRegistry);
|
||||
int ageExpected = 14;
|
||||
setter.set(testClassInstance, ageExpected, null);
|
||||
Object age = map.get("age");
|
||||
Assert.assertEquals(ageExpected, age);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGenerateClassWithAppropriateAccessorsForBoolean() throws Exception {
|
||||
//given
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("checkbox",true);
|
||||
Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
|
||||
properties.put("checkbox", Boolean.class);
|
||||
|
||||
//when
|
||||
Class testClass = MapProxyTool.classForName("TestClass3", properties, new ClassLoaderServiceImpl());
|
||||
Object testClassInstance = testClass.getConstructor(Map.class).newInstance(map);
|
||||
|
||||
//then
|
||||
Getter getter = ReflectionTools.getGetter(testClass, "checkbox", "property", serviceRegistry);
|
||||
Assert.assertTrue((Boolean) getter.get(testClassInstance));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue