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.jmf; 023 024import java.io.IOException; 025import java.io.Serializable; 026import java.lang.reflect.Field; 027import java.lang.reflect.InvocationTargetException; 028import java.lang.reflect.Method; 029import java.util.List; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.ConcurrentMap; 032 033import javax.persistence.Entity; 034import javax.persistence.MappedSuperclass; 035 036import org.granite.logging.Logger; 037import org.granite.messaging.jmf.ExtendedObjectInput; 038import org.granite.messaging.jmf.ExtendedObjectOutput; 039import org.granite.messaging.jmf.codec.ExtendedObjectCodec; 040import org.granite.messaging.reflect.Property; 041import org.hibernate.proxy.HibernateProxy; 042import org.hibernate.proxy.LazyInitializer; 043 044/** 045 * @author Franck WOLFF 046 */ 047public class EntityCodec implements ExtendedObjectCodec { 048 049 private static final Logger log = Logger.getLogger(EntityCodec.class); 050 051 private final ConcurrentMap<Class<?>, SerializableProxyAdapter> serializableProxyAdapters = new ConcurrentHashMap<Class<?>, SerializableProxyAdapter>(); 052 053 static class SerializableProxyAdapter { 054 055 private final Object serializableProxy; 056 private final Field idField; 057 private final Method readResolveMethod; 058 059 public SerializableProxyAdapter(HibernateProxy proxy) throws NoSuchMethodException, SecurityException, NoSuchFieldException { 060 this.serializableProxy = proxy.writeReplace(); 061 062 this.idField = serializableProxy.getClass().getDeclaredField("id"); 063 this.idField.setAccessible(true); 064 065 this.readResolveMethod = serializableProxy.getClass().getDeclaredMethod("readResolve"); 066 this.readResolveMethod.setAccessible(true); 067 } 068 069 public HibernateProxy newProxy(Serializable id) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { 070 synchronized (serializableProxy) { 071 idField.set(serializableProxy, id); 072 return (HibernateProxy)readResolveMethod.invoke(serializableProxy); 073 } 074 } 075 } 076 077 public boolean canEncode(ExtendedObjectOutput out, Object v) { 078 Class<?> cls = getClass(out, v); 079 return (cls.isAnnotationPresent(Entity.class) || cls.isAnnotationPresent(MappedSuperclass.class)); 080 } 081 082 public String getEncodedClassName(ExtendedObjectOutput out, Object v) { 083 return getClass(out, v).getName(); 084 } 085 086 public void encode(ExtendedObjectOutput out, Object v) throws IOException, IllegalAccessException, InvocationTargetException { 087 String detachedState = null; 088 089 if (v instanceof HibernateProxy) { 090 HibernateProxy proxy = (HibernateProxy)v; 091 092 // Only write initialized flag, detachedState & id if v is an uninitialized proxy. 093 if (proxy.getHibernateLazyInitializer().isUninitialized()) { 094 095 Class<?> persistentClass = proxy.getHibernateLazyInitializer().getPersistentClass(); 096 if (!serializableProxyAdapters.containsKey(persistentClass)) { 097 try { 098 SerializableProxyAdapter proxyAdapter = new SerializableProxyAdapter(proxy); 099 serializableProxyAdapters.putIfAbsent(persistentClass, proxyAdapter); 100 } 101 catch (Exception e) { 102 throw new IOException("Could not create SerializableProxyAdapter for: " + proxy); 103 } 104 } 105 106 Serializable id = proxy.getHibernateLazyInitializer().getIdentifier(); 107 log.debug("Writing uninitialized HibernateProxy %s with id %s", detachedState, id); 108 109 out.writeBoolean(false); 110 out.writeUTF(null); 111 out.writeObject(id); 112 return; 113 } 114 115 // Proxy is initialized, get the underlying persistent object. 116 log.debug("Writing initialized HibernateProxy..."); 117 v = proxy.getHibernateLazyInitializer().getImplementation(); 118 } 119 120 // Write initialized flag & detachedState. 121 out.writeBoolean(true); 122 out.writeUTF(null); 123 124 // Write all properties in lexical order. 125 List<Property> properties = out.getReflection().findSerializableProperties(v.getClass()); 126 for (Property property : properties) 127 out.getAndWriteProperty(v, property); 128 } 129 130 public boolean canDecode(ExtendedObjectInput in, String className) throws ClassNotFoundException { 131 Class<?> cls = in.getReflection().loadClass(className); 132 return (cls.isAnnotationPresent(Entity.class) || cls.isAnnotationPresent(MappedSuperclass.class)); 133 } 134 135 public String getDecodedClassName(ExtendedObjectInput in, String className) { 136 return in.getAlias(className); 137 } 138 139 public Object newInstance(ExtendedObjectInput in, String className) 140 throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, 141 InvocationTargetException, SecurityException, NoSuchMethodException, IOException { 142 143 Class<?> cls = in.getReflection().loadClass(className); 144 145 // Read initialized flag & detachedState. 146 boolean initialized = in.readBoolean(); 147 in.readUTF(); 148 149 if (initialized) 150 return in.getReflection().newInstance(cls); 151 152 // Create an HibernateProxy. 153 SerializableProxyAdapter proxyAdapter = serializableProxyAdapters.get(cls); 154 if (proxyAdapter == null) 155 throw new IOException("Could not find SerializableProxyAdapter for: " + cls); 156 Serializable id = (Serializable)in.readObject(); 157 return proxyAdapter.newProxy(id); 158 } 159 160 public void decode(ExtendedObjectInput in, Object v) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException { 161 if (!(v instanceof HibernateProxy)) { 162 List<Property> properties = in.getReflection().findSerializableProperties(v.getClass()); 163 for (Property property : properties) 164 in.readAndSetProperty(v, property); 165 } 166 } 167 168 protected Class<?> getClass(ExtendedObjectOutput out, Object v) { 169 Class<?> cls = v.getClass(); 170 171 if (v instanceof HibernateProxy) { 172 LazyInitializer initializer = ((HibernateProxy)v).getHibernateLazyInitializer(); 173 174 String className = ( 175 initializer.isUninitialized() ? 176 initializer.getEntityName() : 177 initializer.getImplementation().getClass().getName() 178 ); 179 180 if (className != null && className.length() > 0) { 181 try { 182 cls = out.getReflection().loadClass(className); 183 } catch (ClassNotFoundException e) { 184 cls = initializer.getPersistentClass(); 185 } 186 } 187 } 188 189 return cls; 190 } 191}