/* * Copyright (C) 2009 eXo Platform SAS. * * This 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. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.commons.serialization.serial; import java.io.IOException; import java.io.InputStream; import java.io.InvalidClassException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.io.StreamCorruptedException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.exoplatform.commons.serialization.SerializationContext; import org.exoplatform.commons.serialization.api.TypeConverter; import org.exoplatform.commons.serialization.api.factory.ObjectFactory; import org.exoplatform.commons.serialization.model.ClassTypeModel; import org.exoplatform.commons.serialization.model.ConvertedTypeModel; import org.exoplatform.commons.serialization.model.FieldModel; /** * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> * @version $Revision$ */ public class ObjectReader extends ObjectInputStream { /** . */ private final SerializationContext context; /** . */ private final Map<Integer, Object> idToObject; /** . */ private final Map<Integer, List<FutureFieldUpdate<?>>> idToResolutions; public ObjectReader(SerializationContext context, InputStream in) throws IOException { super(in); // enableResolveObject(true); // this.context = context; this.idToObject = new HashMap<Integer, Object>(); this.idToResolutions = new HashMap<Integer, List<FutureFieldUpdate<?>>>(); } private <O> O instantiate(ClassTypeModel<O> typeModel, Map<FieldModel<? super O, ?>, ?> state) throws InvalidClassException { try { ObjectFactory<? super O> factory = context.getFactory(typeModel.getJavaType()); // return factory.create(typeModel.getJavaType(), state); } catch (Exception e) { InvalidClassException ice = new InvalidClassException("Cannot instantiate object from class " + typeModel.getJavaType().getName()); ice.initCause(e); throw ice; } } protected <O> O instantiate(int id, DataContainer container, ClassTypeModel<O> typeModel) throws IOException { Map<FieldModel<? super O, ?>, Object> state = new HashMap<FieldModel<? super O, ?>, Object>(); ClassTypeModel<? super O> currentTypeModel = typeModel; List<FieldUpdate<O>> sets = new ArrayList<FieldUpdate<O>>(); while (currentTypeModel != null) { if (currentTypeModel instanceof ClassTypeModel) { for (FieldModel<? super O, ?> fieldModel : currentTypeModel.getFields()) { if (!fieldModel.isTransient()) { switch (container.readInt()) { case DataKind.NULL_VALUE: state.put(fieldModel, null); break; case DataKind.OBJECT_REF: int refId = container.readInt(); Object refO = idToObject.get(refId); if (refO != null) { state.put(fieldModel, refO); } else { sets.add(new FieldUpdate<O>(refId, fieldModel)); } break; case DataKind.OBJECT: Object o = container.readObject(); state.put(fieldModel, o); break; } } } } currentTypeModel = currentTypeModel.getSuperType(); } // O instance = instantiate(typeModel, state); // Create future field updates for (FieldUpdate<O> set : sets) { List<FutureFieldUpdate<?>> resolutions = idToResolutions.get(set.ref); if (resolutions == null) { resolutions = new ArrayList<FutureFieldUpdate<?>>(); idToResolutions.put(set.ref, resolutions); } resolutions.add(new FutureFieldUpdate<O>(instance, set.fieldModel)); } // idToObject.put(id, instance); // Resolve future field updates List<FutureFieldUpdate<?>> resolutions = idToResolutions.remove(id); if (resolutions != null) { for (FutureFieldUpdate<?> resolution : resolutions) { resolution.fieldModel.castAndSet(resolution.target, instance); } } // return instance; } private static class FieldUpdate<O> { /** . */ private final int ref; /** . */ private final FieldModel<? super O, ?> fieldModel; private FieldUpdate(int ref, FieldModel<? super O, ?> fieldModel) { this.ref = ref; this.fieldModel = fieldModel; } } private static class FutureFieldUpdate<O> { /** . */ private final O target; /** . */ private final FieldModel<? super O, ?> fieldModel; private FutureFieldUpdate(O target, FieldModel<? super O, ?> fieldModel) { this.target = target; this.fieldModel = fieldModel; } } private Object read(DataContainer container) throws IOException { int sw = container.readInt(); switch (sw) { case DataKind.OBJECT_REF: { int id = container.readInt(); Object o1 = idToObject.get(id); if (o1 == null) { throw new AssertionError(); } return o1; } case DataKind.OBJECT: { int id = container.readInt(); Class<?> clazz = (Class) container.readObject(); ClassTypeModel<?> typeModel = (ClassTypeModel<?>) context.getTypeDomain().getTypeModel(clazz); return instantiate(id, container, typeModel); } case DataKind.CONVERTED_OBJECT: { Class<?> tclazz = (Class<?>) container.readObject(); ConvertedTypeModel<?, ?> ctm = (ConvertedTypeModel<?, ?>) context.getTypeDomain().getTypeModel(tclazz); return convertObject(container, ctm); } case DataKind.SERIALIZED_OBJECT: return container.readObject(); default: throw new StreamCorruptedException("Unrecognized data " + sw); } } private <O, T> O convertObject(DataContainer container, ConvertedTypeModel<O, T> convertedType) throws IOException { Object inner = resolveObject(container); T t = convertedType.getTargetType().getJavaType().cast(inner); TypeConverter<O, T> converter; try { converter = convertedType.getConverterJavaType().newInstance(); } catch (Exception e) { throw new AssertionError(e); } // O o = null; try { o = converter.read(t); } catch (Exception e) { InvalidObjectException ioe = new InvalidObjectException("The object " + t + " conversion throw an exception " + converter); ioe.initCause(e); throw ioe; } if (o == null) { throw new InvalidObjectException("The object " + t + " was converted to null by converter " + converter); } // return o; } @Override protected Object resolveObject(Object obj) throws IOException { if (obj instanceof DataContainer) { return read((DataContainer) obj); } else { return obj; } } @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); String name = desc.getName(); return Class.forName(name, false, cl); } catch (ClassNotFoundException ex) { return super.resolveClass(desc); } } }