/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.eclipselink.jmf; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Entity; import javax.persistence.IdClass; import javax.persistence.MappedSuperclass; import org.eclipse.persistence.indirection.ValueHolderInterface; import org.granite.eclipselink.EclipseLinkProxy; import org.granite.eclipselink.EclipseLinkValueHolder; import org.granite.logging.Logger; import org.granite.messaging.jmf.ExtendedObjectInput; import org.granite.messaging.jmf.ExtendedObjectOutput; import org.granite.messaging.jmf.codec.ExtendedObjectCodec; import org.granite.messaging.reflect.Property; import org.granite.util.TypeUtil; /** * @author Franck WOLFF */ public class EntityCodec implements ExtendedObjectCodec { private static final Logger log = Logger.getLogger(EntityCodec.class); public boolean canEncode(ExtendedObjectOutput out, Object v) { Class<?> cls = getClass(out, v); return (cls.isAnnotationPresent(Entity.class) || cls.isAnnotationPresent(MappedSuperclass.class) || v instanceof EclipseLinkProxy); } public String getEncodedClassName(ExtendedObjectOutput out, Object v) { return getClass(out, v).getName(); } public void encode(ExtendedObjectOutput out, Object v) throws IOException, IllegalAccessException, InvocationTargetException { if (v instanceof EclipseLinkProxy) { out.writeBoolean(false); out.writeUTF(null); out.writeObject(null); return; } // Write initialized flag & detachedState. out.writeBoolean(true); out.writeUTF(null); // Write all properties in lexical order. Set<String> lazyFieldNames = new HashSet<String>(); List<Property> properties = out.getReflection().findSerializableProperties(v.getClass()); for (Property property : properties) { if (isIgnoredProperty(property)) continue; if (ValueHolderInterface.class.isAssignableFrom(property.getType())) { ValueHolderInterface vh = (ValueHolderInterface)property.getObject(v); if (vh != null && !vh.isInstantiated()) lazyFieldNames.add(property.getName().substring("_persistence_".length(), property.getName().length() - 3)); } else if (lazyFieldNames.contains(property.getName())) out.writeObject(new EclipseLinkProxy(property.getType())); else out.getAndWriteProperty(v, property); } } public boolean canDecode(ExtendedObjectInput in, String className) throws ClassNotFoundException { Class<?> cls = in.getReflection().loadClass(className); return (cls.isAnnotationPresent(Entity.class) || cls.isAnnotationPresent(MappedSuperclass.class)); } public String getDecodedClassName(ExtendedObjectInput in, String className) { return in.getAlias(className); } public Object newInstance(ExtendedObjectInput in, String className) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, IOException { Class<?> cls = in.getReflection().loadClass(className); // Read initialized flag & detachedState. boolean initialized = in.readBoolean(); in.readUTF(); if (initialized) return in.getReflection().newInstance(cls); // Create a (pseudo) proxy. Serializable id = (Serializable)in.readObject(); if (id != null && (!cls.isAnnotationPresent(IdClass.class) || !cls.getAnnotation(IdClass.class).value().equals(id.getClass()))) throw new RuntimeException("Id for EclipseLink pseudo-proxy should be null or IdClass (" + className + ")"); return new EclipseLinkValueHolder(); } public void decode(ExtendedObjectInput in, Object v) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException { if (!(v instanceof EclipseLinkValueHolder)) { Map<String, Property> lazyFields = new HashMap<String, Property>(); List<Property> properties = in.getReflection().findSerializableProperties(v.getClass()); for (Property property : properties) { if (isIgnoredProperty(property)) continue; if (ValueHolderInterface.class.isAssignableFrom(property.getType())) lazyFields.put(property.getName(), property); else { Object value = in.readObject(); if (value instanceof ValueHolderInterface) lazyFields.get("_persistence_" + property.getName() + "_vh").setObject(v, value); else if (property.isWritable()) property.setObject(v, value); } } } } protected boolean isIgnoredProperty(Property property) { return "_persistence_fetchGroup".equals(property.getName()); } protected Class<?> getClass(ExtendedObjectOutput out, Object v) { if (v instanceof ValueHolderInterface) { ValueHolderInterface holder = (ValueHolderInterface)v; String className = ( holder.isInstantiated() ? holder.getValue().getClass().getName() : Object.class.getName() ); if (className != null && className.length() > 0) { try { return TypeUtil.forName(className); } catch (Exception e) { log.warn(e, "Could not get class with initializer: %s for: %s", className, className); } } // fallback... return Object.class; } if (v instanceof EclipseLinkProxy) return ((EclipseLinkProxy)v).getProxiedClass(); return v.getClass(); } }