org.nustaq.offheap.structs.unsafeimpl.FSTStructFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fst Show documentation
Show all versions of fst Show documentation
A fast java serialization drop in-replacement and some serialization based utils such as Structs and OffHeap Memory.
/*
* Copyright 2014 Ruediger Moeller.
*
* 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.nustaq.offheap.structs.unsafeimpl;
import org.nustaq.offheap.bytez.Bytez;
import org.nustaq.offheap.bytez.BytezAllocator;
import org.nustaq.offheap.bytez.onheap.HeapBytezAllocator;
import org.nustaq.offheap.structs.*;
import org.nustaq.offheap.structs.structtypes.StructArray;
import org.nustaq.offheap.structs.structtypes.StructByteString;
import org.nustaq.offheap.structs.structtypes.StructString;
import org.nustaq.serialization.FSTClazzInfo;
import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.util.DefaultFSTInt2ObjectMap;
import org.nustaq.serialization.util.FSTInt2ObjectMap;
import org.nustaq.serialization.util.FSTUtil;
import javassist.*;
import javassist.Modifier;
import javassist.bytecode.*;
import javassist.expr.ExprEditor;
import javassist.expr.FieldAccess;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* manages + generates struct instrumented classes
*/
public class FSTStructFactory {
public static int SIZE_ALIGN = 2;
static FSTStructFactory instance;
public static FSTStructFactory getInstance() {
if (instance==null) {
instance = new FSTStructFactory(); // fixme: should be final
}
return instance;
}
public static final int MAX_CLASSES = 1000;
static FSTConfiguration conf = FSTConfiguration.createStructConfiguration();
ClassPool defaultPool;
Loader proxyLoader;
ClassLoader parentLoader;
{
defaultPool = new ClassPool(null) {
@Override
public CtClass get(String classname) throws NotFoundException {
if ( rawByteClassDefs.containsKey(classname)) {
try {
return makeClass(new ByteArrayInputStream(rawByteClassDefs.get(classname)));
} catch (IOException e) {
e.printStackTrace();
}
}
return super.get(classname);
}
};
defaultPool.appendSystemPath();
proxyLoader = new Loader(FSTStructFactory.class.getClassLoader(), defaultPool)
{
protected Class loadClassByDelegation(String name)
throws ClassNotFoundException
{
try { return delegateToParent(name); } catch (Exception ex) {
return null;
}
}
};
}
ConcurrentHashMap proxyClzMap = new ConcurrentHashMap();
FSTStructGeneration structGen = new FSTByteArrayUnsafeStructGeneration();
ConcurrentHashMap rawByteClassDefs = new ConcurrentHashMap();
boolean autoRegister = true;
BytezAllocator allocator = new HeapBytezAllocator();
public FSTStructFactory() {
registerClz(FSTStruct.class);
registerClz(StructString.class);
registerClz(StructArray.class);
registerClz(StructByteString.class);
}
public void registerRawClass( String name, byte bytes[] ) {
rawByteClassDefs.put(name,bytes);
}
public Class createStructClz( Class clazz ) throws Exception {
//FIXME: ensure FSTStruct is superclass, check protected, no private methods+fields
if ( Modifier.isFinal(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers()) ) {
throw new RuntimeException("Cannot add final classes to structs "+clazz.getName());
}
if ( clazz.getName().endsWith("_Struct") ) {
throw new RuntimeException("cannot create Struct on Struct class. Class "+clazz+" is already instrumented" );
}
String proxyName = clazz.getName()+"_Struct";
Class present = null;
try {
present = proxyLoader.loadClass(proxyName);
} catch (ClassNotFoundException ex) {
//
}
if ( present != null )
return present;
ClassPool pool = defaultPool;
CtClass newClz = pool.makeClass(proxyName);
CtClass orig = null;
if ( rawByteClassDefs.get(clazz.getName()) != null ) {
orig = pool.makeClass( new ByteArrayInputStream(rawByteClassDefs.get(clazz.getName())));
} else {
orig = pool.getOrNull(clazz.getName());
if ( orig == null ) {
pool.insertClassPath(new ClassClassPath(clazz));
orig = pool.get(clazz.getName());
if (orig == null)
{
throw new RuntimeException("unable to locate class byte code for "+clazz.getName());
}
}
}
newClz.setSuperclass(orig);
final FSTClazzInfo clInfo = conf.getClassInfo(clazz);
CtMethod[] methods = orig.getMethods();
for (int i = 0; i < methods.length; i++) {
CtMethod method = methods[i];
final Class curClz = Class.forName( method.getDeclaringClass().getName() );
boolean allowed = ((method.getModifiers() & AccessFlag.ABSTRACT) == 0 ) &&
(method.getModifiers() & AccessFlag.NATIVE) == 0 &&
(method.getModifiers() & AccessFlag.FINAL) == 0 &&
( !method.getDeclaringClass().getName().equals(FSTStruct.class.getName())
||method.getName().equals("getFieldValues")) &&
! method.getDeclaringClass().getName().equals(Object.class.getName());
allowed &= method.getAnnotation(NoAssist.class) == null;
allowed &= (method.getModifiers() & AccessFlag.STATIC) == 0;
if ( allowed && (method.getModifiers() & AccessFlag.FINAL) != 0 && ! method.getDeclaringClass().getName().equals("java.lang.Object") ) {
throw new RuntimeException("final methods are not allowed for struct classes:"+method.getName());
}
if ( allowed && (method.getModifiers() & AccessFlag.PRIVATE) != 0 && ! method.getDeclaringClass().getName().equals("java.lang.Object")) {
throw new RuntimeException("private methods are not allowed for struct classes:"+method.getName());
}
if ( allowed ) {
ClassMap mp = new ClassMap();
mp.fix(clazz.getName());
mp.fix(clazz.getSuperclass().getName()); // ?? only depth 2 ??
method = new CtMethod(method,newClz,mp);
String methName = method.getName();
// array access:
// void [name](int, type)
// [type] [name](int)
FSTClazzInfo.FSTFieldInfo arrayFi = checkForSpecialArrayMethod(clInfo, method, "", null, null);
// array length:
// int [name]Len()
FSTClazzInfo.FSTFieldInfo lenfi = checkForSpecialArrayMethod(clInfo, method, "Len", CtClass.intType, new CtClass[0]);
// get byte index of array data:
// int [name]Index()
FSTClazzInfo.FSTFieldInfo indexfi = checkForSpecialArrayMethod(clInfo, method, "Index", CtClass.intType, new CtClass[0]);
// get size of array element:
// int [name]ElementSize()
FSTClazzInfo.FSTFieldInfo elemlen = checkForSpecialArrayMethod(clInfo, method, "ElementSize", CtClass.intType, new CtClass[0]);
// CREATE non volatile pointer to array[0] element:
// type [name]Pointer() OR type [name]Pointer(pointerToSetup) (for reuse)
FSTClazzInfo.FSTFieldInfo pointerfi = checkForSpecialArrayMethod(clInfo, method, "Pointer", null, null);
// get byte index to structure or array header element:
// type [name]StructIndex()
FSTClazzInfo.FSTFieldInfo structIndex = checkForSpecialMethod(clInfo, method, "StructIndex", CtClass.intType, new CtClass[0], false);
// set with CAS
// boolean [name]CAS(expectedValue,value)
FSTClazzInfo.FSTFieldInfo casAcc = checkForSpecialMethod(clInfo, method, "CAS", CtClass.booleanType, null, false);
if ( casAcc != null ) {
structGen.defineStructSetCAS(casAcc, clInfo, method);
newClz.addMethod(method);
} else
if ( pointerfi != null ) {
structGen.defineArrayPointer(pointerfi, clInfo, method);
newClz.addMethod(method);
} else
if ( structIndex != null ) {
structGen.defineFieldStructIndex(structIndex, clInfo, method);
newClz.addMethod(method);
} else
if ( indexfi != null ) {
structGen.defineArrayIndex(indexfi, clInfo, method);
newClz.addMethod(method);
} else
if ( elemlen != null ) {
structGen.defineArrayElementSize(elemlen, clInfo, method);
newClz.addMethod(method);
} else
if ( arrayFi != null ) {
structGen.defineArrayAccessor(arrayFi, clInfo, method);
newClz.addMethod(method);
} else if ( methName.endsWith("Len") && lenfi != null )
{
structGen.defineArrayLength(lenfi, clInfo, method);
newClz.addMethod(method);
} else {
if ( methName.equals("getFieldValues") &&
( (clInfo.getClazz().getSuperclass().getName().equals("de.nustaq.reallive.impl.RLStructRow")) // oops
|| (curClz != FSTStruct.class) )
) {
FSTClazzInfo.FSTFieldInfo[] fieldInfo = clInfo.getFieldInfo();
StringBuilder body = new StringBuilder("{ return new Object[] { ");
for (int j = 0; j < fieldInfo.length; j++) {
FSTClazzInfo.FSTFieldInfo fstFieldInfo = fieldInfo[j];
int modifiers = fstFieldInfo.getField().getModifiers();
if ( (java.lang.reflect.Modifier.isProtected(modifiers) ||
java.lang.reflect.Modifier.isPublic(modifiers)) &&
!java.lang.reflect.Modifier.isStatic(modifiers)
)
{
body.append( "\""+fstFieldInfo.getName()+"\", " );
Class type = fstFieldInfo.getType();
if ( FSTStruct.class.isAssignableFrom(type) ) {
body.append(fstFieldInfo.getName()).append(".getFieldValues()");
} else {
if ( type.isPrimitive() ) {
if ( long.class == type ) {
body.append("new Long("+fstFieldInfo.getName()+")");
} else if ( float.class == type ||double.class == type ) {
body.append("new Double("+fstFieldInfo.getName()+")");
} else {
body.append("new Integer("+fstFieldInfo.getName()+")");
}
} else {
body.append(fstFieldInfo.getName());
}
}
if ( j != fieldInfo.length-1 )
body.append(",");
}
}
body.append("}; }");
method.setBody(body.toString());
}
newClz.addMethod(method);
method.instrument( new ExprEditor() {
@Override
public void edit(FieldAccess f) throws CannotCompileException {
try {
if ( ! f.isStatic() ) {
CtClass type = null;
type = f.getField().getType();
FSTClazzInfo.FSTFieldInfo fieldInfo = clInfo.getFieldInfo(f.getFieldName(), null);
if ( fieldInfo == null ) {
return;
}
if ( f.isReader() ) {
structGen.defineStructReadAccess(f, type, fieldInfo);
} else if ( f.isWriter() ) {
structGen.defineStructWriteAccess(f, type, fieldInfo);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
return (Class) loadProxyClass(clazz, pool, newClz);
}
FSTClazzInfo.FSTFieldInfo checkForSpecialArrayMethod( FSTClazzInfo clzInfo, CtMethod method, String postFix, Object returnType, CtClass requiredArgs[] ) {
return checkForSpecialMethod(clzInfo, method, postFix, returnType, requiredArgs, true);
}
FSTClazzInfo.FSTFieldInfo checkForSpecialMethod(FSTClazzInfo clzInfo, CtMethod method, String postFix, Object returnType, CtClass requiredArgs[], boolean array) {
int len = postFix.length();
String methName = method.getName();
if ( ! methName.endsWith(postFix) ) {
return null;
}
FSTClazzInfo.FSTFieldInfo res = clzInfo.getFieldInfo(methName.substring(0, methName.length() - len), null);
if ( res == null ) {
return null;
}
if ( array && res.isArray() && res.getArrayType().isArray() ) {
throw new RuntimeException("nested arrays not supported "+res.getDesc());
}
if ( array && !res.isArray() ) {
//throw new RuntimeException("expect array type for field "+res.getDesc()+" special method:"+method);
// just ignore
return null;
}
if ( res.isArray() || ! array ) {
if ( returnType instanceof Class ) {
try {
if ( ! method.getReturnType().getName().equals(((Class) returnType).getName()) ) {
throw new RuntimeException("expected method "+method+" to return "+returnType );
}
} catch (NotFoundException e) {
e.printStackTrace();
}
} else if ( returnType instanceof CtClass ) {
try {
if ( ! method.getReturnType().equals(returnType) ) {
throw new RuntimeException("expected method "+method+" to return "+returnType );
}
} catch (NotFoundException e) {
e.printStackTrace();
}
}
return res;
}
return null;
}
private Class loadProxyClass(Class clazz, ClassPool pool, final CtClass cc) throws ClassNotFoundException {
Class ccClz;
Loader cl = new Loader(clazz.getClassLoader(), pool) {
protected Class loadClassByDelegation(String name)
throws ClassNotFoundException
{
if ( name.equals(cc.getName()) )
return null;
return delegateToParent(name);
}
};
ccClz = cl.loadClass(cc.getName());
return ccClz;
}
public Class getProxyClass(Class clz) throws Exception {
// synchronized (this)
{
Class res = proxyClzMap.get(clz);
if ( res == null ) {
res = createStructClz(clz);
proxyClzMap.put(clz,res);
}
return res;
}
}
public T createWrapper(Class onHeap, Bytez bytes, long index) throws Exception {
Class proxy = getProxyClass(onHeap);
T res = (T) FSTUtil.getUnsafe().allocateInstance(proxy);
res.baseOn(bytes, index, this);
return res;
}
public T createEmptyStructPointer(Class onHeap) {
try {
return createWrapper(onHeap,null,0);
} catch (Exception e) {
FSTUtil.rethrow(e);
}
return null;
}
/**
* allocates a StructAccessor ("pointer") matching the struct data expected in the byte
* array at given position. The resulting pointer object is not "volatile" (not a cached instance)
* @param b
* @param index
* @return
*/
public FSTStruct createStructWrapper(Bytez b, long index) {
int clzId = b.getInt(index + 4);
return createStructPointer(b, index, clzId);
}
/**
* allocates a StructAccessor ("pointer") matching the struct data expected in the byte
* array at given position with given classId. The resulting pointer object is not "volatile" (not a cached instance).
* The class id should match the Struct stored in the byte array. (classId must be the correct struct or a superclass of it)
* @param b
* @param index
* @return
*/
public FSTStruct createStructPointer(Bytez b, long index, int clzId) {
// synchronized (this) // FIXME FIXME FIXME: contention point
// desynced expecting class registering happens on startup
{
Class clazz = mIntToClz.get(clzId);
if (clazz==null)
throw new RuntimeException("unregistered class "+clzId);
try {
return (FSTStruct) createWrapper(clazz, b, index);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public FSTStruct createTypedArrayBasePointer(Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) {
int arrayElementZeroindex = base.getInt(objectBaseOffset + arrayStructIndex);
int elemSiz = base.getInt(objectBaseOffset+arrayStructIndex+8);
int len = base.getInt(objectBaseOffset+arrayStructIndex+4);
int clId = base.getInt(objectBaseOffset+arrayStructIndex+12);
FSTStruct structPointer = null;
if ( clId <= 0 ) { // untyped array
structPointer = new FSTStruct();
structPointer.baseOn(base,objectBaseOffset+arrayElementZeroindex,this);
} else {
structPointer = createStructPointer(base, (int) (objectBaseOffset+arrayElementZeroindex), clId);
}
structPointer.___elementSize = elemSiz;
return structPointer;
}
public void fillTypedArrayBasePointer(FSTStruct result, Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) {
int arrayElementZeroindex = base.getInt(objectBaseOffset+arrayStructIndex);
int elemSiz = base.getInt(objectBaseOffset+arrayStructIndex+8);
// int len = unsafe.getInt(base,objectBaseOffset+arrayStructIndex+4);
// int clId = unsafe.getInt(base,objectBaseOffset+arrayStructIndex+12);
result.baseOn(base, objectBaseOffset + arrayElementZeroindex, this);
result.___elementSize = elemSiz;
}
public void fillPrimitiveArrayBasePointer(FSTStruct result, Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) {
int arrayElementZeroindex = base.getInt(objectBaseOffset+arrayStructIndex);
result.baseOn(base,objectBaseOffset+arrayElementZeroindex,this);
}
public FSTStruct createPrimitiveArrayBasePointer(Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) {
int arrayElementZeroindex = base.getInt(objectBaseOffset+arrayStructIndex);
// int len = unsafe.getInt(base,objectBaseOffset+arrayStructIndex+4);
FSTStruct structPointer = new FSTStruct();
structPointer.baseOn(base,objectBaseOffset+arrayElementZeroindex,this);
return structPointer;
}
public StructArray toStructArray(int size, T onHeap) {
StructArray arr = new StructArray(size,onHeap);
return toStruct(arr);
}
public T toStruct(T onHeap) {
return toStruct(onHeap,allocator);
}
public T toStruct(T onHeap, BytezAllocator alloc) {
if ( onHeap.isOffHeap() ) {
return onHeap;
}
try {
Bytez b = toByteArray(onHeap, alloc);
return (T)createWrapper(onHeap.getClass(),b,0);
} catch (Exception e) {
if ( e instanceof RuntimeException )
throw (RuntimeException)e;
else
throw new RuntimeException(e);
}
}
ThreadLocal
© 2015 - 2024 Weber Informatics LLC | Privacy Policy