/** * Copyright 2009-2013 Oy Vaadin Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.vaadin.addon.jpacontainer.util; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.OneToMany; /** * Utility methods for finding Hibernate specific information about entities * without forcing a compile-time dependency on the Hibernate framework. */ public class HibernateUtil { private static final Logger logger = Logger.getLogger(HibernateUtil.class .getName()); /** * Reflectively finds out if the passed in exception is a Hibernate * LazyInitializationException. * * @param e * @return true if the exception is a Hibernate LazyInitializationException. */ public static boolean isLazyInitializationException(RuntimeException e) { return "LazyInitializationException".equals(e.getClass() .getSimpleName()); } /** * Reflectively finds out whether an object is an uninitialized Hibernate * proxy and unattached. * * @param obj * @return true if the object is an uninitialized and unattached Hibernate * proxy. */ public static boolean isUninitializedAndUnattachedProxy(Object obj) { try { Class<?> hibernateProxyCls = Class .forName("org.hibernate.proxy.HibernateProxy"); boolean isAProxy = hibernateProxyCls.isInstance(obj); if (isAProxy) { Method lazyInitializerGetter = obj.getClass().getMethod( "getHibernateLazyInitializer"); Object lazyInitializer = lazyInitializerGetter.invoke(obj); Method isUninitialized = lazyInitializer.getClass().getMethod( "isUninitialized"); Method getSession = lazyInitializer.getClass().getMethod( "getSession"); Boolean uninited = (Boolean) isUninitialized .invoke(lazyInitializer); boolean hasSession = getSession.invoke(lazyInitializer) == null; return uninited && hasSession; } } catch (ClassNotFoundException e) { // Hibernate is not in use. logger.log(Level.FINEST, "Hibernate not in use", e); } catch (SecurityException e) { // Should never happen, since the ClassNotFoundException would be // triggered first. logger.log( Level.FINEST, "Something happened when trying to figure out " + "if an object is an uninitialized hibernate proxy. " + "This shouldn't happen.", e); } catch (NoSuchMethodException e) { // Should never happen, since the ClassNotFoundException would be // triggered first. logger.log( Level.FINEST, "Something happened when trying to figure out " + "if an object is an uninitialized hibernate proxy. " + "This shouldn't happen.", e); } catch (IllegalArgumentException e) { // Should never happen, since the ClassNotFoundException would be // triggered first. logger.log( Level.FINEST, "Something happened when trying to figure out " + "if an object is an uninitialized hibernate proxy. " + "This shouldn't happen.", e); } catch (IllegalAccessException e) { // Should never happen, since the ClassNotFoundException would be // triggered first. logger.log( Level.FINEST, "Something happened when trying to figure out " + "if an object is an uninitialized hibernate proxy. " + "This shouldn't happen.", e); } catch (InvocationTargetException e) { // Should never happen, since the ClassNotFoundException would be // triggered first. logger.log( Level.FINEST, "Something happened when trying to figure out " + "if an object is an uninitialized hibernate proxy. " + "This shouldn't happen.", e); } return false; } /** * Finds the property's "mappedBy" value. * * @param entity * the entity containing the property * @param propertyName * the name of the property to find the "mappedBy" value for. * @return the value of mappedBy in an annotation. */ public static String getMappedByProperty(Object entity, String propertyName) { Class<?> entityClass = findActualEntityClass(entity); OneToMany otm = getAnnotationForProperty(OneToMany.class, entityClass, propertyName); if (otm != null && !"".equals(otm.mappedBy())) { return otm.mappedBy(); } // Fall back on convention return entityClass.getSimpleName().toLowerCase(); } /** * Finds a given annotation on a property. * * @param annotationType * the annotation to find. * @param entityClass * the class declaring the property * @param propertyName * the name of the property for which to find the annotation. * @return the annotation */ private static <A extends Annotation> A getAnnotationForProperty( Class<A> annotationType, Class<?> entityClass, String propertyName) { A annotation = getAnnotationFromPropertyGetter(annotationType, entityClass, propertyName); if (annotation == null) { annotation = getAnnotationFromField(annotationType, entityClass, propertyName); } return annotation; } /** * Finds a given annotation on a property getter method. */ private static <A extends Annotation> A getAnnotationFromPropertyGetter( Class<A> annotationType, Class<?> entityClass, String propertyName) { // TODO: support for private getters? -> need to recursively search // superclasses as well. Method getter = null; try { getter = entityClass.getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1)); } catch (Exception e) { // Try isXXX try { getter = entityClass.getMethod("is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1)); } catch (Exception e1) { // No getter found. } } if (getter != null && getter.isAnnotationPresent(annotationType)) { return getter.getAnnotation(annotationType); } return null; } /** * Finds a given annotation on a field. */ private static <A extends Annotation> A getAnnotationFromField( Class<A> annotationType, Class<?> entityClass, String propertyName) { Field field = null; try { // TODO: get fields from @mappedsuperclasses as well. field = entityClass.getDeclaredField(propertyName); } catch (Exception e) { // Field not found } if (field != null && field.isAnnotationPresent(annotationType)) { return field.getAnnotation(annotationType); } return null; } /** * Find the actual entity class of an entity object. If Hibernate is in use, * entity.getClass() might return an instance of HibernateProxy, which does * not contain any annotations. * * @param entity * the entity to get the real class for. * @return the real class for the entity. */ private static Class<?> findActualEntityClass(Object entity) { Class<?> cls = entity.getClass(); try { Class<?> hibernateProxyCls = Class .forName("org.hibernate.proxy.HibernateProxy"); if (hibernateProxyCls.isAssignableFrom(cls)) { Method getHibernateLazyInitializer = cls .getMethod("getHibernateLazyInitializer"); Object lazyInitializer = getHibernateLazyInitializer .invoke(entity); Method getImplementation = lazyInitializer.getClass() .getMethod("getImplementation"); return getImplementation.invoke(lazyInitializer).getClass(); } } catch (ClassNotFoundException e) { // Hibernate libraries not found } catch (SecurityException e) { // Don't worry, use entity.getClass() } catch (NoSuchMethodException e) { // Don't worry, use entity.getClass() } catch (IllegalArgumentException e) { // Don't worry, use entity.getClass() } catch (IllegalAccessException e) { // Don't worry, use entity.getClass() } catch (InvocationTargetException e) { // Don't worry, use entity.getClass() } return cls; } }