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 {
|
dependencies {
|
||||||
compile( project( ':hibernate-core' ) )
|
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( [group: 'org.hibernate', name: 'hibernate-tools', version: '3.2.0.ga'] )
|
||||||
provided( libraries.ant )
|
provided( libraries.ant )
|
||||||
|
@ -21,7 +19,6 @@ dependencies {
|
||||||
|
|
||||||
testCompile( project( ':hibernate-testing' ) )
|
testCompile( project( ':hibernate-testing' ) )
|
||||||
testCompile( project( path: ':hibernate-core', configuration: 'tests' ) )
|
testCompile( project( path: ':hibernate-core', configuration: 'tests' ) )
|
||||||
testRuntime( libraries.byteBuddy )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
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 Lukasz Zuchowski (author at zuchos dot com)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class ComponentPropertyMapper implements PropertyMapper, CompositeMapperBuilder {
|
public class ComponentPropertyMapper extends AbstractPropertyMapper implements CompositeMapperBuilder {
|
||||||
private final PropertyData propertyData;
|
private final PropertyData propertyData;
|
||||||
private final MultiPropertyMapper delegate;
|
private final MultiPropertyMapper delegate;
|
||||||
private final Class componentClass;
|
private final Class componentClass;
|
||||||
|
@ -122,6 +122,14 @@ public class ComponentPropertyMapper implements PropertyMapper, CompositeMapperB
|
||||||
new PrivilegedAction<Object>() {
|
new PrivilegedAction<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object run() {
|
public Object run() {
|
||||||
|
try {
|
||||||
|
final Object subObj = ReflectHelper.getDefaultConstructor( componentClass ).newInstance();
|
||||||
|
|
||||||
|
if ( isDynamicComponentMap() ) {
|
||||||
|
( (Map) obj ).put( propertyData.getBeanName(), subObj );
|
||||||
|
delegate.mapToEntityFromMap( enversService, subObj, data, primaryKey, versionsReader, revision );
|
||||||
|
}
|
||||||
|
else {
|
||||||
final Setter setter = ReflectionTools.getSetter(
|
final Setter setter = ReflectionTools.getSetter(
|
||||||
obj.getClass(),
|
obj.getClass(),
|
||||||
propertyData,
|
propertyData,
|
||||||
|
@ -134,16 +142,14 @@ public class ComponentPropertyMapper implements PropertyMapper, CompositeMapperB
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// set the component
|
// set the component
|
||||||
try {
|
|
||||||
final Object subObj = ReflectHelper.getDefaultConstructor( componentClass ).newInstance();
|
|
||||||
setter.set( obj, subObj, null );
|
setter.set( obj, subObj, null );
|
||||||
|
|
||||||
delegate.mapToEntityFromMap( enversService, subObj, data, primaryKey, versionsReader, revision );
|
delegate.mapToEntityFromMap( enversService, subObj, data, primaryKey, versionsReader, revision );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
catch ( Exception e ) {
|
catch ( Exception e ) {
|
||||||
throw new AuditException( e );
|
throw new AuditException( e );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
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();
|
||||||
|
}
|
|
@ -12,13 +12,12 @@ import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.internal.entities.PropertyData;
|
import org.hibernate.envers.internal.entities.PropertyData;
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
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)
|
* Multi mapper for dynamic components (it knows that component is a map, not a class)
|
||||||
*
|
*
|
||||||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||||
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
||||||
|
|
||||||
|
@ -28,6 +27,22 @@ public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
||||||
this.dynamicComponentData = dynamicComponentData;
|
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
|
@Override
|
||||||
public boolean mapToMapFromEntity(
|
public boolean mapToMapFromEntity(
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
|
@ -96,7 +111,6 @@ public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void mapToEntityFromMap(
|
public void mapToEntityFromMap(
|
||||||
EnversService enversService,
|
EnversService enversService,
|
||||||
Object obj,
|
Object obj,
|
||||||
|
@ -104,22 +118,8 @@ public class MultiDynamicComponentMapper extends MultiPropertyMapper {
|
||||||
Object primaryKey,
|
Object primaryKey,
|
||||||
AuditReaderImplementor versionsReader,
|
AuditReaderImplementor versionsReader,
|
||||||
Number revision) {
|
Number revision) {
|
||||||
Object mapProxy = MapProxyTool.newInstanceOfBeanProxyForMap(
|
|
||||||
generateClassName( data, dynamicComponentData.getBeanName() ),
|
|
||||||
(Map) obj,
|
|
||||||
properties.keySet(),
|
|
||||||
enversService.getClassLoaderService()
|
|
||||||
);
|
|
||||||
for ( PropertyMapper mapper : properties.values() ) {
|
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 Lukasz Zuchowski (author at zuchos dot com)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class MultiPropertyMapper implements ExtendedPropertyMapper {
|
public class MultiPropertyMapper extends AbstractPropertyMapper implements ExtendedPropertyMapper {
|
||||||
protected final Map<PropertyData, PropertyMapper> properties;
|
protected final Map<PropertyData, PropertyMapper> properties;
|
||||||
private final Map<String, PropertyData> propertyDatas;
|
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 Michal Skowronek (mskowr at o2 dot pl)
|
||||||
* @author Chris Cranford
|
* @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.
|
* 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 Michal Skowronek (mskowr at o2 dot pl)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder {
|
public class SinglePropertyMapper extends AbstractPropertyMapper implements SimpleMapperBuilder {
|
||||||
private PropertyData propertyData;
|
private PropertyData propertyData;
|
||||||
|
|
||||||
public SinglePropertyMapper(PropertyData propertyData) {
|
public SinglePropertyMapper(PropertyData propertyData) {
|
||||||
|
@ -97,6 +97,14 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
AccessController.doPrivileged(
|
||||||
new PrivilegedAction<Object>() {
|
new PrivilegedAction<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -107,8 +115,6 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
|
||||||
enversService.getServiceRegistry()
|
enversService.getServiceRegistry()
|
||||||
);
|
);
|
||||||
|
|
||||||
final Object value = data.get( propertyData.getName() );
|
|
||||||
|
|
||||||
// We only set a null value if the field is not primitive. Otherwise, we leave it intact.
|
// 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() ) ) {
|
if ( value != null || !isPrimitive( setter, propertyData, obj.getClass() ) ) {
|
||||||
setter.set( obj, value, null );
|
setter.set( obj, value, null );
|
||||||
|
@ -119,6 +125,7 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isPrimitive(Setter setter, PropertyData propertyData, Class<?> cls) {
|
private boolean isPrimitive(Setter setter, PropertyData propertyData, Class<?> cls) {
|
||||||
if ( cls == null ) {
|
if ( cls == null ) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class SubclassPropertyMapper implements ExtendedPropertyMapper {
|
public class SubclassPropertyMapper extends AbstractPropertyMapper implements ExtendedPropertyMapper {
|
||||||
private ExtendedPropertyMapper main;
|
private ExtendedPropertyMapper main;
|
||||||
private ExtendedPropertyMapper parentMapper;
|
private ExtendedPropertyMapper parentMapper;
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ import org.hibernate.envers.RevisionType;
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
import org.hibernate.envers.exception.AuditException;
|
||||||
import org.hibernate.envers.internal.entities.PropertyData;
|
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.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.entities.mapper.relation.lazy.initializor.Initializor;
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.ReflectionTools;
|
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 Michal Skowronek (mskowr at o2 dot pl)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
|
public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper {
|
||||||
protected final CommonCollectionMapperData commonCollectionMapperData;
|
protected final CommonCollectionMapperData commonCollectionMapperData;
|
||||||
protected final Class<? extends T> collectionClass;
|
protected final Class<? extends T> collectionClass;
|
||||||
protected final boolean ordinalInId;
|
protected final boolean ordinalInId;
|
||||||
|
@ -256,6 +256,28 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
|
||||||
final AuditReaderImplementor versionsReader,
|
final AuditReaderImplementor versionsReader,
|
||||||
final Number revision) {
|
final Number revision) {
|
||||||
final String revisionTypePropertyName = enversService.getAuditEntitiesConfiguration().getRevisionTypePropName();
|
final String revisionTypePropertyName = enversService.getAuditEntitiesConfiguration().getRevisionTypePropName();
|
||||||
|
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(
|
AccessController.doPrivileged(
|
||||||
new PrivilegedAction<Object>() {
|
new PrivilegedAction<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -296,6 +318,7 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map collection changes using hash identity.
|
* Map collection changes using hash identity.
|
||||||
|
|
|
@ -17,8 +17,8 @@ import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.internal.entities.EntityConfiguration;
|
import org.hibernate.envers.internal.entities.EntityConfiguration;
|
||||||
import org.hibernate.envers.internal.entities.PropertyData;
|
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.PersistentCollectionChangeData;
|
||||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.ReflectionTools;
|
import org.hibernate.envers.internal.tools.ReflectionTools;
|
||||||
import org.hibernate.property.access.spi.Setter;
|
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 Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractToOneMapper implements PropertyMapper {
|
public abstract class AbstractToOneMapper extends AbstractPropertyMapper {
|
||||||
private final ServiceRegistry serviceRegistry;
|
private final ServiceRegistry serviceRegistry;
|
||||||
private final PropertyData propertyData;
|
private final PropertyData propertyData;
|
||||||
|
|
||||||
|
@ -90,6 +90,12 @@ public abstract class AbstractToOneMapper implements PropertyMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setPropertyValue(Object targetObject, Object value) {
|
protected void setPropertyValue(Object targetObject, Object value) {
|
||||||
|
if ( isDynamicComponentMap() ) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String, Object> map = (Map<String, Object>) targetObject;
|
||||||
|
map.put( propertyData.getBeanName(), value );
|
||||||
|
}
|
||||||
|
else {
|
||||||
AccessController.doPrivileged(
|
AccessController.doPrivileged(
|
||||||
new PrivilegedAction<Object>() {
|
new PrivilegedAction<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -106,6 +112,7 @@ public abstract class AbstractToOneMapper implements PropertyMapper {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Bean property that represents the relation.
|
* @return Bean property that represents the relation.
|
||||||
|
|
|
@ -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