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