org.mapdb.SerializerPojo Maven / Gradle / Ivy
/*
* 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.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Serializer which handles POJO, object graphs etc.
*
* @author Jan Kotek
*/
public class SerializerPojo extends SerializerBase implements Serializable{
protected static final Serializer> serializer = new Serializer>() {
@Override
public void serialize(DataOutput out, CopyOnWriteArrayList obj) throws IOException {
DataOutput2.packInt(out, obj.size());
for (ClassInfo ci : obj) {
out.writeUTF(ci.name);
out.writeBoolean(ci.isEnum);
out.writeBoolean(ci.useObjectStream);
if(ci.useObjectStream) continue; //no fields
DataOutput2.packInt(out, ci.fields.size());
for (FieldInfo fi : ci.fields) {
out.writeUTF(fi.name);
out.writeBoolean(fi.primitive);
out.writeUTF(fi.type);
}
}
}
@Override
public CopyOnWriteArrayList deserialize(DataInput in, int available) throws IOException{
if(available==0) return new CopyOnWriteArrayList();
int size = DataInput2.unpackInt(in);
ArrayList ret = new ArrayList(size);
for (int i = 0; i < size; i++) {
String className = in.readUTF();
boolean isEnum = in.readBoolean();
boolean isExternalizable = in.readBoolean();
int fieldsNum = isExternalizable? 0 : DataInput2.unpackInt(in);
FieldInfo[] fields = new FieldInfo[fieldsNum];
for (int j = 0; j < fieldsNum; j++) {
fields[j] = new FieldInfo(in.readUTF(), in.readBoolean(), in.readUTF(), classForName(className));
}
ret.add(new ClassInfo(className, fields,isEnum,isExternalizable));
}
return new CopyOnWriteArrayList(ret);
}
@Override
public int fixedSize() {
return -1;
}
};
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(CC.FAIR_LOCKS);
protected static Class> classForName(String className) {
try {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
return Class.forName(className, true,loader);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
protected DB db;
public SerializerPojo(CopyOnWriteArrayList registered){
if(registered == null)
registered = new CopyOnWriteArrayList();
this.registered = registered;
oldSize = registered.size();
for(int i=0;i fields = new ArrayList();
protected final Map name2fieldInfo = new HashMap();
protected final Map name2fieldId = new HashMap();
protected ObjectStreamField[] objectStreamFields;
protected final boolean isEnum;
protected final boolean useObjectStream;
public ClassInfo(final String name, final FieldInfo[] fields, final boolean isEnum, final boolean isExternalizable) {
this.name = name;
this.isEnum = isEnum;
this.useObjectStream = isExternalizable;
for (FieldInfo f : fields) {
this.name2fieldId.put(f.name, this.fields.size());
this.fields.add(f);
this.name2fieldInfo.put(f.name, f);
}
}
public int getFieldId(String name) {
Integer fieldId = name2fieldId.get(name);
if(fieldId != null)
return fieldId;
return -1;
}
public int addFieldInfo(FieldInfo field) {
name2fieldId.put(field.name, fields.size());
name2fieldInfo.put(field.name, field);
fields.add(field);
return fields.size() - 1;
}
public ObjectStreamField[] getObjectStreamFields() {
return objectStreamFields;
}
public void setObjectStreamFields(ObjectStreamField[] objectStreamFields) {
this.objectStreamFields = objectStreamFields;
}
@Override public String toString(){
return super.toString()+ "["+name+"]";
}
}
/**
* 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 name, boolean primitive, String type, Class> clazz) {
this.name = name;
this.primitive = primitive;
this.type = type;
this.clazz = clazz;
this.typeClass = primitive?null:classForName(type);
//init field
Class> aClazz = clazz;
// iterate over class hierarchy, until root class
while (true) {
if(aClazz == Object.class) throw new RuntimeException("Could not set field value: "+name+" - "+clazz.toString());
// access field directly
try {
Field f = aClazz.getDeclaredField(name);
// security manager may not be happy about this
if (!f.isAccessible())
f.setAccessible(true);
field = f;
break;
} catch (NoSuchFieldException e) {
//field does not exists
}
// move to superclass
aClazz = aClazz.getSuperclass();
}
}
public FieldInfo(ObjectStreamField sf, Class> clazz) {
this(sf.getName(), sf.isPrimitive(), sf.getType().getName(), clazz);
}
}
protected CopyOnWriteArrayList registered;
protected Map, Integer> class2classId = new HashMap, Integer>();
protected Map> classId2class = new HashMap>();
public void registerClass(Class> clazz) throws IOException {
if(clazz != Object.class)
assertClassSerializable(clazz);
if (containsClass(clazz))
return;
assert(lock.isWriteLockedByCurrentThread());
final boolean advancedSer = usesAdvancedSerialization(clazz);
ObjectStreamField[] streamFields = advancedSer? new ObjectStreamField[0]:getFields(clazz);
FieldInfo[] fields = new FieldInfo[streamFields.length];
for (int i = 0; i < fields.length; i++) {
ObjectStreamField sf = streamFields[i];
fields[i] = new FieldInfo(sf, clazz);
}
ClassInfo i = new ClassInfo(clazz.getName(), fields,clazz.isEnum(), advancedSer);
class2classId.put(clazz, registered.size());
classId2class.put(registered.size(), clazz);
registered.add(i);
saveClassInfo();
}
protected boolean usesAdvancedSerialization(Class> clazz) {
if(Externalizable.class.isAssignableFrom(clazz)) return true;
try {
if(clazz.getDeclaredMethod("readObject",ObjectInputStream.class)!=null) return true;
} catch (NoSuchMethodException e) {
}
try {
if(clazz.getDeclaredMethod("writeObject",ObjectOutputStream.class)!=null) return true;
} catch (NoSuchMethodException e) {
}
try {
if(clazz.getDeclaredMethod("writeReplace")!=null) return 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 = class2classId.get(clazz);
if (classId != null) {
classInfo = registered.get(classId);
fields = classInfo.getObjectStreamFields();
}
if (fields == null) {
ObjectStreamClass streamClass = ObjectStreamClass.lookup(clazz);
FastArrayList fieldsList = new FastArrayList();
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> clazz) throws NotSerializableException, InvalidClassException {
if(containsClass(clazz))
return;
if (!Serializable.class.isAssignableFrom(clazz))
throw new NotSerializableException(clazz.getName());
}
public Object getFieldValue(FieldInfo fieldInfo, Object object) {
if(fieldInfo.field==null){
throw new NoSuchFieldError(object.getClass() + "." + fieldInfo.name);
}
try {
return fieldInfo.field.get(object);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not get value from field", e);
}
}
public void setFieldValue(FieldInfo fieldInfo, Object object, Object value) {
if(fieldInfo.field==null)
throw new NoSuchFieldError(object.getClass() + "." + fieldInfo.name);
try{
fieldInfo.field.set(object, value);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not set field value: ",e);
}
}
public boolean containsClass(Class> clazz) {
return (class2classId.get(clazz) != null);
}
public int getClassId(Class> clazz) {
Integer classId = class2classId.get(clazz);
if(classId != null) {
return classId;
}
throw new AssertionError("Class is not registered: " + clazz);
}
@Override
protected Engine getEngine() {
return db.getEngine();
}
@Override
protected void serializeUnknownObject(DataOutput out, Object obj, FastArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy