001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.hibernate; 023 024import java.io.Serializable; 025import java.lang.reflect.Field; 026import java.lang.reflect.Method; 027import java.lang.reflect.Type; 028import java.util.concurrent.ConcurrentHashMap; 029 030import javax.persistence.EmbeddedId; 031import javax.persistence.Id; 032 033import org.granite.config.GraniteConfig; 034import org.granite.context.GraniteContext; 035import org.granite.messaging.service.ServiceException; 036import org.granite.util.TypeUtil; 037import org.granite.util.Introspector; 038import org.granite.util.PropertyDescriptor; 039import org.hibernate.engine.SessionImplementor; 040import org.hibernate.proxy.HibernateProxy; 041import org.hibernate.type.AbstractComponentType; 042 043/** 044 * @author Franck WOLFF 045 */ 046@SuppressWarnings("deprecation") 047public class ProxyFactory { 048 049 private static final Class<?>[] INTERFACES = new Class[]{HibernateProxy.class}; 050 051 protected final ConcurrentHashMap<Class<?>, Object[]> identifierInfos = new ConcurrentHashMap<Class<?>, Object[]>(); 052 053 private final Method getProxyFactory; 054 private final Method getProxy; 055 056 public ProxyFactory(String initializerClassName) { 057 try { 058 // Get proxy methods: even if CGLIB/Javassist LazyInitializer implementations share a common 059 // superclass, getProxyFactory/getProxy methods are declared as static in each inherited 060 // class with the same signature. 061 Class<?> initializerClass = TypeUtil.forName(initializerClassName); 062 getProxyFactory = initializerClass.getMethod("getProxyFactory", new Class[]{Class.class, Class[].class}); 063 Class<?> componentTypeClass = AbstractComponentType.class; 064 try { 065 // Hibernate 3.6 066 componentTypeClass = TypeUtil.forName("org.hibernate.type.CompositeType"); 067 } 068 catch (ClassNotFoundException e) { 069 // Hibernate until 3.5 070 } 071 getProxy = initializerClass.getMethod("getProxy", new Class[]{ 072 Class.class, String.class, Class.class, Class[].class, Method.class, Method.class, 073 componentTypeClass, Serializable.class, SessionImplementor.class 074 }); 075 } 076 catch (Exception e) { 077 throw new ServiceException("Could not introspect initializer class: " + initializerClassName, e); 078 } 079 } 080 081 public HibernateProxy getProxyInstance(String persistentClassName, String entityName, Serializable id) { 082 try { 083 // Get ProxyFactory. 084 Class<?> persistentClass = TypeUtil.forName(persistentClassName); 085 Class<?> factory = (Class<?>)getProxyFactory.invoke(null, new Object[]{persistentClass, INTERFACES}); 086 087 // Convert id (if necessary). 088 Object[] identifierInfo = getIdentifierInfo(persistentClass); 089 Type identifierType = (Type)identifierInfo[0]; 090 Method identifierGetter = (Method)identifierInfo[1]; 091 if (id == null || !identifierType.equals(id.getClass())) { 092 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig(); 093 id = (Serializable)config.getConverters().convert(id, identifierType); 094 } 095 096 // Get Proxy 097 return (HibernateProxy)getProxy.invoke(null, new Object[]{factory, entityName, persistentClass, INTERFACES, identifierGetter, null, null, id, null}); 098 } 099 catch (Exception e) { 100 throw new ServiceException("Error with proxy description: " + persistentClassName + '/' + entityName + " and id: " + id, e); 101 } 102 } 103 104 protected Object[] getIdentifierInfo(Class<?> persistentClass) { 105 106 Object[] info = identifierInfos.get(persistentClass); 107 if (info != null) 108 return info; 109 110 Type type = null; 111 Method getter = null; 112 for (Class<?> clazz = persistentClass; clazz != Object.class && clazz != null; clazz = clazz.getSuperclass()) { 113 for (Field field : clazz.getDeclaredFields()) { 114 if (field.isAnnotationPresent(Id.class) || field.isAnnotationPresent(EmbeddedId.class)) { 115 type = field.getGenericType(); 116 break; 117 } 118 } 119 } 120 121 if (type == null) { 122 PropertyDescriptor[] propertyDescriptors = Introspector.getPropertyDescriptors(persistentClass); 123 124 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { 125 Method method = propertyDescriptor.getReadMethod(); 126 if (method != null && ( 127 method.isAnnotationPresent(Id.class) || 128 method.isAnnotationPresent(EmbeddedId.class))) { 129 type = method.getGenericReturnType(); 130 getter = method; 131 break; 132 } 133 method = propertyDescriptor.getWriteMethod(); 134 if (method != null && ( 135 method.isAnnotationPresent(Id.class) || 136 method.isAnnotationPresent(EmbeddedId.class))) { 137 type = method.getGenericParameterTypes()[0]; 138 break; 139 } 140 } 141 } 142 143 if (type != null) { 144 info = new Object[] { type, getter }; 145 Object[] previousInfo = identifierInfos.putIfAbsent(persistentClass, info); 146 if (previousInfo != null) 147 info = previousInfo; // should be the same... 148 return info; 149 } 150 151 throw new IllegalArgumentException("Could not find id in: " + persistentClass); 152 } 153}