Author: epbernard
Date: 2010-05-25 09:08:22 -0400 (Tue, 25 May 2010)
New Revision: 19601
Modified:
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java
Log:
HHH-5258 isLoaded and superclasses and private properties now works
Use getDeclaredField and getDeclaredMethod, use the whole class hierarchy
and use a cache to speed up both class hierarchy
and property to Member computation
Modified:
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java
===================================================================
---
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java 2010-05-25
13:07:42 UTC (rev 19600)
+++
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java 2010-05-25
13:08:22 UTC (rev 19601)
@@ -65,6 +65,7 @@
private final Metamodel metamodel;
private final HibernatePersistenceUnitUtil util;
private final Map<String,Object> properties;
+ private final PersistenceUtilHelper.MetadataCache cache = new
PersistenceUtilHelper.MetadataCache();
@SuppressWarnings( "unchecked" )
public EntityManagerFactoryImpl(
@@ -182,13 +183,15 @@
private static class HibernatePersistenceUnitUtil implements PersistenceUnitUtil,
Serializable {
private final HibernateEntityManagerFactory emf;
+ private transient PersistenceUtilHelper.MetadataCache cache;
private HibernatePersistenceUnitUtil(EntityManagerFactoryImpl emf) {
this.emf = emf;
+ this.cache = emf.cache;
}
public boolean isLoaded(Object entity, String attributeName) {
- LoadState state = PersistenceUtilHelper.isLoadedWithoutReference( entity,
attributeName );
+ LoadState state = PersistenceUtilHelper.isLoadedWithoutReference( entity,
attributeName, cache );
if (state == LoadState.LOADED) {
return true;
}
@@ -196,7 +199,7 @@
return false;
}
else {
- return PersistenceUtilHelper.isLoadedWithReference( entity, attributeName ) !=
LoadState.NOT_LOADED;
+ return PersistenceUtilHelper.isLoadedWithReference( entity, attributeName, cache ) !=
LoadState.NOT_LOADED;
}
}
Modified:
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java
===================================================================
---
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java 2010-05-25
13:07:42 UTC (rev 19600)
+++
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java 2010-05-25
13:08:22 UTC (rev 19601)
@@ -38,6 +38,7 @@
* @author Gavin King
*/
public class HibernatePersistence extends AvailableSettings implements
PersistenceProvider {
+ private final PersistenceUtilHelper.MetadataCache cache = new
PersistenceUtilHelper.MetadataCache();
/**
* Get an entity manager factory by its entity manager name, using the specified
@@ -85,11 +86,11 @@
private final ProviderUtil providerUtil = new ProviderUtil() {
public LoadState isLoadedWithoutReference(Object proxy, String property) {
- return PersistenceUtilHelper.isLoadedWithoutReference( proxy, property );
+ return PersistenceUtilHelper.isLoadedWithoutReference( proxy, property, cache );
}
public LoadState isLoadedWithReference(Object proxy, String property) {
- return PersistenceUtilHelper.isLoadedWithReference( proxy, property );
+ return PersistenceUtilHelper.isLoadedWithReference( proxy, property, cache );
}
public LoadState isLoaded(Object o) {
Modified:
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java
===================================================================
---
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java 2010-05-25
13:07:42 UTC (rev 19600)
+++
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/util/PersistenceUtilHelper.java 2010-05-25
13:08:22 UTC (rev 19601)
@@ -1,14 +1,21 @@
package org.hibernate.ejb.util;
+import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.AccessibleObject;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
import javax.persistence.spi.LoadState;
import javax.persistence.PersistenceException;
+import org.hibernate.AssertionFailure;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.intercept.FieldInterceptionHelper;
@@ -19,7 +26,7 @@
* @author Emmanuel Bernard
*/
public class PersistenceUtilHelper {
- public static LoadState isLoadedWithoutReference(Object proxy, String property) {
+ public static LoadState isLoadedWithoutReference(Object proxy, String property,
MetadataCache cache) {
Object entity;
boolean sureFromUs = false;
if ( proxy instanceof HibernateProxy ) {
@@ -44,7 +51,7 @@
if (isInitialized && interceptor != null) {
//property is loaded according to bytecode enhancement, but is it loaded as far as
association?
//it's ours, we can read
- state = isLoaded( get( entity, property ) );
+ state = isLoaded( get( entity, property, cache ) );
//it's ours so we know it's loaded
if (state == LoadState.UNKNOWN) state = LoadState.LOADED;
}
@@ -54,7 +61,7 @@
else if ( sureFromUs ) { //interceptor == null
//property is loaded according to bytecode enhancement, but is it loaded as far as
association?
//it's ours, we can read
- state = isLoaded( get( entity, property ) );
+ state = isLoaded( get( entity, property, cache ) );
//it's ours so we know it's loaded
if (state == LoadState.UNKNOWN) state = LoadState.LOADED;
}
@@ -71,32 +78,26 @@
}
}
- public static LoadState isLoadedWithReference(Object proxy, String property) {
+ public static LoadState isLoadedWithReference(Object proxy, String property,
MetadataCache cache) {
//for sure we don't instrument and for sure it's not a lazy proxy
- Object object = get(proxy, property);
+ Object object = get(proxy, property, cache);
return isLoaded( object );
}
- private static Object get(Object proxy, String property) {
+ private static Object get(Object proxy, String property, MetadataCache cache) {
final Class<?> clazz = proxy.getClass();
+
try {
- try {
- final Field field = clazz.getField( property );
- setAccessibility( field );
- return field.get( proxy );
+ Member member = cache.getMember( clazz, property );
+ if (member instanceof Field) {
+ return ( (Field) member ).get( proxy );
}
- catch ( NoSuchFieldException e ) {
- final Method method = getMethod( clazz, property );
- if (method != null) {
- setAccessibility( method );
- return method.invoke( proxy );
- }
- else {
- throw new PersistenceException( "Unable to find field or method: "
- + clazz + "#"
- + property);
- }
+ else if (member instanceof Method) {
+ return ( (Method) member ).invoke( proxy );
}
+ else {
+ throw new AssertionFailure( "Member object neither Field nor Method: " +
member);
+ }
}
catch ( IllegalAccessException e ) {
throw new PersistenceException( "Unable to access field or method: "
@@ -110,6 +111,27 @@
}
}
+ private static void setAccessibility(Member member) {
+ if ( !Modifier.isPublic( member.getModifiers() ) ) {
+ //Sun's ease of use, sigh...
+ ( ( AccessibleObject ) member ).setAccessible( true );
+ }
+ }
+
+ public static LoadState isLoaded(Object o) {
+ if ( o instanceof HibernateProxy ) {
+ final boolean isInitialized = !( ( HibernateProxy ) o
).getHibernateLazyInitializer().isUninitialized();
+ return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED;
+ }
+ else if ( o instanceof PersistentCollection ) {
+ final boolean isInitialized = ( ( PersistentCollection ) o ).wasInitialized();
+ return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED;
+ }
+ else {
+ return LoadState.UNKNOWN;
+ }
+ }
+
/**
* Returns the method with the specified name or <code>null</code> if it
does not exist.
*
@@ -135,25 +157,84 @@
}
}
- private static void setAccessibility(Member member) {
- if ( !Modifier.isPublic( member.getModifiers() ) ) {
- //Sun's ease of use, sigh...
- ( ( AccessibleObject ) member ).setAccessible( true );
+ /**
+ * Cache hierarchy and member resolution in a weak hash map
+ */
+ //TODO not really thread-safe
+ public static class MetadataCache implements Serializable {
+ private transient Map<Class<?>, ClassCache> classCache = new
WeakHashMap<Class<?>, ClassCache>();
+
+
+ private void readObject(java.io.ObjectInputStream stream) {
+ classCache = new WeakHashMap<Class<?>, ClassCache>();
}
- }
- public static LoadState isLoaded(Object o) {
- if ( o instanceof HibernateProxy ) {
- final boolean isInitialized = !( ( HibernateProxy ) o
).getHibernateLazyInitializer().isUninitialized();
- return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED;
+ Member getMember(Class<?> clazz, String property) {
+ ClassCache cache = classCache.get( clazz );
+ if (cache == null) {
+ cache = new ClassCache(clazz);
+ classCache.put( clazz, cache );
+ }
+ Member member = cache.members.get( property );
+ if ( member == null ) {
+ member = findMember( clazz, property );
+ cache.members.put( property, member );
+ }
+ return member;
}
- else if ( o instanceof PersistentCollection ) {
- final boolean isInitialized = ( ( PersistentCollection ) o ).wasInitialized();
- return isInitialized ? LoadState.LOADED : LoadState.NOT_LOADED;
+
+ private Member findMember(Class<?> clazz, String property) {
+ final List<Class<?>> classes = getClassHierarchy( clazz );
+
+ for (Class current : classes) {
+ final Field field;
+ try {
+ field = current.getDeclaredField( property );
+ setAccessibility( field );
+ return field;
+ }
+ catch ( NoSuchFieldException e ) {
+ final Method method = getMethod( clazz, property );
+ if (method != null) {
+ setAccessibility( method );
+ return method;
+ }
+ }
+ }
+ //we could not find any match
+ throw new PersistenceException( "Unable to find field or method: "
+ + clazz + "#"
+ + property);
}
- else {
- return LoadState.UNKNOWN;
+
+ private List<Class<?>> getClassHierarchy(Class<?> clazz) {
+ ClassCache cache = classCache.get( clazz );
+ if (cache == null) {
+ cache = new ClassCache(clazz);
+ classCache.put( clazz, cache );
+ }
+ return cache.classHierarchy;
}
+
+ private static List<Class<?>> findClassHierarchy(Class<?> clazz) {
+ List<Class<?>> classes = new ArrayList<Class<?>>();
+ Class<?> current = clazz;
+ do {
+ classes.add( current );
+ current = current.getSuperclass();
+ }
+ while ( current != null );
+ return classes;
+ }
+
+ private static class ClassCache {
+ List<Class<?>> classHierarchy;
+ Map<String, Member> members = new HashMap<String, Member>();
+
+ public ClassCache(Class<?> clazz) {
+ classHierarchy = findClassHierarchy( clazz );
+ }
+ }
}
}