Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   *  Copyright (c) 2012 Jan Kotek
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
  *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
 package org.mapdb;
 
 import java.io.*;
 import java.util.List;
 import java.util.Map;
Serializer which handles POJO, object graphs etc.

Author(s):
Jan Kotek TODO make this class thread safe
 
 public class SerializerPojo extends SerializerBase implements Serializable{
 
 
     protected static final Serializer<CopyOnWriteArrayList<ClassInfo>> serializer = new Serializer<CopyOnWriteArrayList<ClassInfo>>() {
 
         @Override
 		public void serialize(DataOutput outCopyOnWriteArrayList<ClassInfoobjthrows IOException {
             Utils.packInt(outobj.size());
             for (ClassInfo ci : obj) {
                 out.writeUTF(ci.getName());
                 out.writeBoolean(ci.isEnum);
                 out.writeBoolean(ci.useObjectStream);
                 if(ci.useObjectStreamcontinue//no fields
 
                 Utils.packInt(outci.fields.size());
                 for (FieldInfo fi : ci.fields) {
                     out.writeUTF(fi.getName());
                     out.writeBoolean(fi.isPrimitive());
                     out.writeUTF(fi.getType());
                 }
             }
         }
 
         @Override
 		public CopyOnWriteArrayList<ClassInfodeserialize(DataInput inint availablethrows IOException{
             if(available==0) return new CopyOnWriteArrayList<ClassInfo>();
 
             int size = Utils.unpackInt(in);
             ArrayList<ClassInforet = new ArrayList<ClassInfo>(size);
 
             for (int i = 0; i < sizei++) {
                 String className = in.readUTF();
                 boolean isEnum = in.readBoolean();
                 boolean isExternalizable = in.readBoolean();
 
                 int fieldsNum = isExternalizable? 0 : Utils.unpackInt(in);
                 FieldInfo[] fields = new FieldInfo[fieldsNum];
                 for (int j = 0; j < fieldsNumj++) {
                     fields[j] = new FieldInfo(in.readUTF(), in.readBoolean(), in.readUTF(), classForName(className));
                 }
                 ret.add(new ClassInfo(classNamefields,isEnum,isExternalizable));
             }
             return new CopyOnWriteArrayList<ClassInfo>(ret);
         }
     };
 
     protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 
     protected static Class<?> classForName(String className) {
         try {
             return Class.forName(className);
         } catch (ClassNotFoundException e) {
             throw new RuntimeException(e);
         }
     }
 
 
 
 
     public SerializerPojo(CopyOnWriteArrayList<ClassInforegistered){
         if(registered == null)
             registered = new CopyOnWriteArrayList<ClassInfo>();
        this. = registered;
        for(int i=0;i<registered.size();i++)
        {
            ClassInfo ci = registered.get(i);
            Class clazz = classForName(ci.getName());
            .put(clazzi);
            .put(iclazz);
        }
    }

    
Stores info about single class stored in MapDB. Roughly corresponds to 'java.io.ObjectStreamClass'
    protected static final class ClassInfo {
        protected final String name;
        protected final List<FieldInfofields = new ArrayList<FieldInfo>();
        protected final Map<StringFieldInfoname2fieldInfo = new HashMap<StringFieldInfo>();
        protected final Map<StringIntegername2fieldId = new HashMap<StringInteger>();
        protected ObjectStreamField[] objectStreamFields;
        protected final boolean isEnum;
        protected final boolean useObjectStream;
        public ClassInfo(final String namefinal FieldInfo[] fieldsfinal boolean isEnumfinal boolean isExternalizable) {
            this. = name;
            this. = isEnum;
            this. = isExternalizable;
            for (FieldInfo f : fields) {
                this..put(f.getName(), this..size());
                this..add(f);
                this..put(f.getName(), f);
            }
        }
        public String getName() {
            return ;
        }
        public FieldInfo[] getFields() {
            return .toArray(new FieldInfo[.size()]);
        }
        public FieldInfo getField(String name) {
            return .get(name);
        }
        public int getFieldId(String name) {
            Integer fieldId = .get(name);
            if(fieldId != null)
                return fieldId;
            return -1;
        }
        public FieldInfo getField(int serialId) {
            return .get(serialId);
        }
        public int addFieldInfo(FieldInfo field) {
            .put(field.getName(), .size());
            .put(field.getName(), field);
            .add(field);
            return .size() - 1;
        }
        public ObjectStreamField[] getObjectStreamFields() {
            return ;
        }
        public void setObjectStreamFields(ObjectStreamField[] objectStreamFields) {
            this. = objectStreamFields;
        }
        @Override public String toString(){
            return super.toString()+ "["+getName()+"]";
        }
    }

    
Stores info about single field stored in MapDB. Roughly corresponds to 'java.io.ObjectFieldClass'
    protected static class FieldInfo {
        protected final String name;
        protected final boolean primitive;
        protected final String type;
        protected Class<?> typeClass;
        // Class containing this field
        protected final Class<?> clazz;
        protected Field field;
        public FieldInfo(String nameboolean primitiveString typeClass<?> clazz) {
            this. = name;
            this. = primitive;
            this. = type;
            this. = clazz;
            try {
                this. = Class.forName(type);
            } catch (ClassNotFoundException e) {
                this. = null;
            }
            //init field
            Class<?> aClazz = clazz;
            // iterate over class hierarchy, until root class
            while (aClazz != Object.class) {
                // access field directly
                try {
                    Field f = aClazz.getDeclaredField(name);
                    // security manager may not be happy about this
                    if (!f.isAccessible())
                        f.setAccessible(true);
                     = f;
                    break;
                } catch (Exception e) {
//					e.printStackTrace();
                }
                // move to superclass
                aClazz = aClazz.getSuperclass();
            }
        }
        public FieldInfo(ObjectStreamField sfClass<?> clazz) {
            this(sf.getName(), sf.isPrimitive(), sf.getType().getName(), clazz);
        }
        public String getName() {
            return ;
        }
        public boolean isPrimitive() {
            return ;
        }
        public String getType() {
            return ;
        }
        protected String firstCharCap(String s) {
            return Character.toUpperCase(s.charAt(0)) + s.substring(1);
        }
    }
    protected Map<Class<?>, Integerclass2classId = new HashMap<Class<?>, Integer>();
    protected Map<IntegerClass<?>> classId2class = new HashMap<IntegerClass<?>>();
    public void registerClass(Class<?> clazzthrows IOException {
        if(clazz != Object.class)
            assertClassSerializable(clazz);
        if (containsClass(clazz))
            return;
        ObjectStreamField[] streamFields = getFields(clazz);
        FieldInfo[] fields = new FieldInfo[streamFields.length];
        for (int i = 0; i < fields.lengthi++) {
            ObjectStreamField sf = streamFields[i];
            fields[i] = new FieldInfo(sfclazz);
        }
        ClassInfo i = new ClassInfo(clazz.getName(), fields,clazz.isEnum(), usesAdvancedSerialization(clazz));
        .put(clazz.size());
        .put(.size(), clazz);
        .add(i);
        saveClassInfo();
    }
    protected boolean usesAdvancedSerialization(Class<?> clazz) {
        if(Externalizable.class.isAssignableFrom(clazz)) return true;
        try {
            if(clazz.getDeclaredMethod("readObject",ObjectInputStream.class)!=nullreturn true;
        } catch (NoSuchMethodException e) {
        }
        try {
            if(clazz.getDeclaredMethod("writeObject",ObjectOutputStream.class)!=nullreturn true;
        } catch (NoSuchMethodException e) {
        }
        return false;
    }

    
action performed after classInfo was modified, feel free to override
    protected void saveClassInfo() {
    }
    protected ObjectStreamField[] getFields(Class<?> clazz) {
        ObjectStreamField[] fields = null;
        ClassInfo classInfo = null;
        Integer classId = .get(clazz);
        if (classId != null) {
            classInfo = .get(classId);
            fields = classInfo.getObjectStreamFields();
        }
        if (fields == null) {
            ObjectStreamClass streamClass = ObjectStreamClass.lookup(clazz);
            FastArrayList<ObjectStreamFieldfieldsList = new FastArrayList<ObjectStreamField>();
            while (streamClass != null) {
                for (ObjectStreamField f : streamClass.getFields()) {
                    fieldsList.add(f);
                }
                clazz = clazz.getSuperclass();
                streamClass = ObjectStreamClass.lookup(clazz);
            }
            fields = new ObjectStreamField[fieldsList
                    .size];
            System.arraycopy(fieldsList.data, 0, fields, 0, fields.length);
            if(classInfo != null)
                classInfo.setObjectStreamFields(fields);
        }
        return fields;
    }
    protected void assertClassSerializable(Class<?> clazzthrows NotSerializableExceptionInvalidClassException {
        if(containsClass(clazz))
            return;
        if (!Serializable.class.isAssignableFrom(clazz))
            throw new NotSerializableException(clazz.getName());
    }
    public Object getFieldValue(String fieldNameObject object) {
        try {
            registerClass(object.getClass());
        } catch (IOException e) {
            e.printStackTrace();
        }
        ClassInfo classInfo = .get(.get(object.getClass()));
        return getFieldValue(classInfo.getField(fieldName), object);
    }
    public Object getFieldValue(FieldInfo fieldInfoObject object) {
        if(fieldInfo.field==null){
            throw new NoSuchFieldError(object.getClass() + "." + fieldInfo.getName());
        }
        try {
            return fieldInfo.field.get(object);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    public void setFieldValue(String fieldNameObject objectObject value) {
        try {
            registerClass(object.getClass());
        } catch (IOException e) {
            e.printStackTrace();
        }
        ClassInfo classInfo = .get(.get(object.getClass()));
        setFieldValue(classInfo.getField(fieldName), objectvalue);
    }
    public void setFieldValue(FieldInfo fieldInfoObject objectObject value) {
        if(fieldInfo.field==null)
            throw new NoSuchFieldError(object.getClass() + "." + fieldInfo.getName());
        try{
           fieldInfo.field.set(objectvalue);
        } catch (IllegalAccessException e) {
           throw new RuntimeException(e);
        }
    }
    public boolean containsClass(Class<?> clazz) {
        return (.get(clazz) != null);
    }
    public int getClassId(Class<?> clazz) {
        Integer classId = .get(clazz);
        if(classId != null) {
            return classId;
        }
        throw new Error("Class is not registered: " + clazz);
    }
    @Override
    protected void serializeUnknownObject(DataOutput outObject objFastArrayList<ObjectobjectStackthrows IOException {
        out.write(.);
        registerClass(obj.getClass());
        //write class header
        int classId = getClassId(obj.getClass());
        Utils.packInt(outclassId);
        ClassInfo classInfo = .get(classId);
        if(classInfo.useObjectStream){
            ObjectOutputStream2 out2 = new ObjectOutputStream2((OutputStreamout);
            out2.writeObject(obj);
            return;
        }
        if(classInfo.isEnum) {
            int ordinal = ((Enum<?>)obj).ordinal();
            Utils.packInt(outordinal);
        }
        ObjectStreamField[] fields = getFields(obj.getClass());
        Utils.packInt(outfields.length);
        for (ObjectStreamField f : fields) {
            //write field ID
            int fieldId = classInfo.getFieldId(f.getName());
            if (fieldId == -1) {
                //field does not exists in class definition stored in db,
                //propably new field was added so add field descriptor
                fieldId = classInfo.addFieldInfo(new FieldInfo(fobj.getClass()));
                saveClassInfo();
            }
            Utils.packInt(outfieldId);
            //and write value
            Object fieldValue = getFieldValue(classInfo.getField(fieldId), obj);
            serialize(outfieldValueobjectStack);
        }
    }
    @Override
    protected Object deserializeUnknownHeader(DataInput inint headFastArrayList<ObjectobjectStackthrows IOException {
        if(head!= .throw new InternalError();
        //read class header
        try {
            int classId = Utils.unpackInt(in);
            ClassInfo classInfo = .get(classId);
//            Class clazz = Class.forName(classInfo.getName());
            Class<?> clazz = .get(classId);
            if(clazz == null)
                clazz = Class.forName(classInfo.getName());
            assertClassSerializable(clazz);
            Object o;
            if(classInfo.useObjectStream){
                ObjectInputStream2 in2 = new ObjectInputStream2((InputStreamin);
                o = in2.readObject();
            }else if(classInfo.isEnum) {
                int ordinal = Utils.unpackInt(in);
                o = clazz.getEnumConstants()[ordinal];
            }
            else{
                o = createInstanceSkippinkConstructor(clazz);
            }
            objectStack.add(o);
            if(!classInfo.useObjectStream){
                int fieldCount = Utils.unpackInt(in);
                for (int i = 0; i < fieldCounti++) {
                    int fieldId = Utils.unpackInt(in);
                    FieldInfo f = classInfo.getField(fieldId);
                    Object fieldValue = deserialize(inobjectStack);
                    setFieldValue(fofieldValue);
                }
            }
            return o;
        } catch (Exception e) {
            throw new Error("Could not instanciate class"e);
        }
    }
    static protected Method sunConstructor = null;
    static protected Object sunReflFac = null;
    static protected Method androidConstructor = null;
    static private Method androidConstructorGinger = null;
    static private int constructorId;
    static{
        try{
            Class clazz = Class.forName("sun.reflect.ReflectionFactory");
            if(clazz!=null){
                Method getReflectionFactory = clazz.getMethod("getReflectionFactory");
                 = getReflectionFactory.invoke(null);
                 = clazz.getMethod("newConstructorForSerialization",
                        java.lang.Class.classjava.lang.reflect.Constructor.class);
            }
        }catch(Exception e){
            //ignore
        }
        if( == null)try{
            //try android way
            Method newInstance = ObjectInputStream.class.getDeclaredMethod("newInstance"Class.classClass.class);
            newInstance.setAccessible(true);
             = newInstance;
        }catch(Exception e){
            //ignore
        }
        //this method was taken from 
        //http://dexmaker.googlecode.com/git-history/5a7820356e68a977711afc854d6cd71296c56391/src/mockito/java/com/google/dexmaker/mockito/UnsafeAllocator.java
        //Copyright (C) 2012 The Android Open Source Project, licenced under Apache 2 license
        if( == null &&  == null)try{
            //try android post ginger way
            Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod("getConstructorId"Class.class);
            getConstructorId.setAccessible(true);
             = (IntegergetConstructorId.invoke(nullObject.class);
            Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance"Class.classint.class);
            newInstance.setAccessible(true);
             = newInstance;
        }catch(Exception e){
            //ignore
        }
    }
    protected static Map<Class<?>, Constructor<?>> class2constuctor = new ConcurrentHashMap<Class<?>, Constructor<?>>();

    
For pojo serialization we need to instanciate class without invoking its constructor. There are two ways to do it:

Using proprietary API on Oracle JDK and OpenJDK sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization() more at http://www.javaspecialists.eu/archive/Issue175.html

Using 'ObjectInputStream.newInstance' on Android http://stackoverflow.com/a/3448384

If non of these works we fallback into usual reflection which requires an no-arg constructor

    @SuppressWarnings("restriction")
	protected <T> T createInstanceSkippinkConstructor(Class<T> clazz)
        if( !=null){
            //Sun specific way
            Constructor<?> intConstr = .get(clazz);
            if (intConstr == null) {
                Constructor<?> objDef = Object.class.getDeclaredConstructor();
                intConstr = (Constructor<?>) .invoke(clazzobjDef);
                .put(clazzintConstr);
            }
            return (T)intConstr.newInstance();
        }else if(!=null){
            //android (harmony) specific way
            return (T).invoke(nullclazzObject.class);
        }else if(!=null){
            //android (post ginger) specific way
            return (T).invoke(nullclazz);
        }
        else{
            //try usual generic stuff which does not skip constructor
            Constructor<?> c = .get(clazz);
            if(c==null){
                c =clazz.getConstructor();
                if(!c.isAccessible()) c.setAccessible(true);
                .put(clazz,c);
            }
            return (T)c.newInstance();
        }
    }
    protected final class ObjectOutputStream2 extends ObjectOutputStream{
        protected ObjectOutputStream2(OutputStream outthrows IOExceptionSecurityException {
            super(out);
        }
        @Override
        protected void writeClassDescriptor(ObjectStreamClass descthrows IOException {
            Integer classId = .get(desc.forClass());
            if(classId ==null){
                registerClass(desc.forClass());
                classId = .get(desc.forClass());
            }
            Utils.packInt(this,classId);
        }
    }
    protected final class ObjectInputStream2 extends ObjectInputStream{
        protected ObjectInputStream2(InputStream inthrows IOExceptionSecurityException {
            super(in);
        }
        @Override
            Integer classId = Utils.unpackInt(this);
            Class clazz = .get(classId);
            return ObjectStreamClass.lookup(clazz);
        }
    }
New to GrepCode? Check out our FAQ X