org.breizhbeans.thrift.tools.thriftmongobridge.protocol.TBSONUnstackedProtocol.java Source code

Java tutorial

Introduction

Here is the source code for org.breizhbeans.thrift.tools.thriftmongobridge.protocol.TBSONUnstackedProtocol.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.breizhbeans.thrift.tools.thriftmongobridge.protocol;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.apache.commons.codec.binary.Hex;
import org.apache.thrift.TBase;
import org.apache.thrift.TException;
import org.apache.thrift.TFieldIdEnum;
import org.apache.thrift.meta_data.*;
import org.apache.thrift.protocol.*;
import org.apache.thrift.transport.TTransport;
import org.breizhbeans.thrift.tools.thriftmongobridge.secured.TBSONSecuredWrapper;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.*;

public class TBSONUnstackedProtocol extends TProtocol {

    private static ThreadLocal<Stack<ThriftFieldMetadata>> threadSafeFieldsStack = new ThreadLocal<>();

    // Contains the Thrift object + DBObject -> IO because I code it the Google IO's day :D
    private static ThreadLocal<Stack<ThriftIO>> threadSafeSIOStack = new ThreadLocal<>();

    // Input/Output DBObject and fields
    private static ThreadLocal<DBObject> threadSafeDBObject = new ThreadLocal<>();
    private static ThreadLocal<TBase<?, ?>> threadSafeTBase = new ThreadLocal<>();

    // Fields filter
    private static ThreadLocal<Map<Class<?>, List<Short>>> threadSafeFieldIdsFilter = new ThreadLocal<>();

    //Cache objects Contains the metadata map the TField id Enums
    private static ThreadLocal<Map<Class<?>, Map<Short, ThriftFieldMetadata>>> threadSafeTFields = new ThreadLocal<>();
    private static ThreadLocal<Map<Class<?>, Map<String, org.apache.thrift.TFieldIdEnum>>> threadSafeTFieldsIdEnum = new ThreadLocal<>();

    private static final TStruct ANONYMOUS_STRUCT = new TStruct();
    private static final ThriftFieldMetadata STOP_THRIFT_FIELD_METADATA = new ThriftFieldMetadata("STOP FIELD",
            TType.STOP, (short) 0);

    private static TBSONSecuredWrapper tbsonSecuredWrapper = new DefaultUnsecuredWrapper();

    // Secured warpper
    public static void addSecuredWrapper(TBSONSecuredWrapper tbsonSecuredWrapper) {
        TBSONUnstackedProtocol.tbsonSecuredWrapper = tbsonSecuredWrapper;
    }

    public static void resetSecuredWrapper() {
        TBSONUnstackedProtocol.tbsonSecuredWrapper = new DefaultUnsecuredWrapper();
        TBSONUnstackedProtocol.tbsonSecuredWrapper.removeAll();
        TBSONUnstackedProtocol.resetCache();
    }

    public static TBSONSecuredWrapper getSecuredWrapper() {
        return TBSONUnstackedProtocol.tbsonSecuredWrapper;
    }

    public static void resetCache() {
        threadSafeTFields.remove();
        threadSafeTFieldsIdEnum.remove();
    }

    /**
     * Factory
     */
    public static class Factory implements TProtocolFactory {
        public TProtocol getProtocol(TTransport trans) {
            return new TBSONUnstackedProtocol();
        }
    }

    /**
     * Constructor
     */
    public TBSONUnstackedProtocol() {
        super(null);
    }

    public DBObject getDBObject() {
        return threadSafeDBObject.get();
    }

    public void setDBOject(DBObject dbObject) {
        threadSafeDBObject.set(dbObject);
    }

    public void setBaseObject(TBase<?, ?> base) {
        threadSafeTBase.set(base);
        // clear all remaing stacks
        threadSafeSIOStack.set(null);
        threadSafeFieldsStack.set(null);
    }

    public void setFieldIdsFilter(TBase<?, ?> base, TFieldIdEnum[] fieldIds) {
        base.getClass();
        List<Short> filteredFields = new ArrayList<>();

        for (TFieldIdEnum tFieldIdEnum : fieldIds) {
            filteredFields.add(tFieldIdEnum.getThriftFieldId());
        }
        Map<Class<?>, List<Short>> filter = new HashMap<>();
        filter.put(base.getClass(), filteredFields);
        threadSafeFieldIdsFilter.set(filter);
    }

    private Map<Short, ThriftFieldMetadata> getTBaseFields(Class<? extends TBase> tbase) throws TException {
        try {
            // First read if the ThriftFieldMetadata was not already readed
            Map<Class<?>, Map<Short, ThriftFieldMetadata>> thriftFields = threadSafeTFields.get();

            if (thriftFields == null) {
                thriftFields = new HashMap<>();
            }

            Map<Short, ThriftFieldMetadata> thriftStructFields = thriftFields.get(tbase);

            // Finded return it
            if (thriftStructFields != null) {
                return thriftStructFields;
            }

            // Load it
            thriftStructFields = new HashMap<>();
            Field metafaField = tbase.getField("metaDataMap");
            Map<?, FieldMetaData> fields = (Map<?, org.apache.thrift.meta_data.FieldMetaData>) metafaField
                    .get(tbase);
            // list all fields
            for (Map.Entry<?, FieldMetaData> entry : fields.entrySet()) {
                TFieldIdEnum field = (TFieldIdEnum) entry.getKey();

                ThriftFieldMetadata tfieldMetadata = new ThriftFieldMetadata();

                // An enum type is deserialized as an I32
                byte type = entry.getValue().valueMetaData.type;
                if (TType.ENUM == type) {
                    type = TType.I32;
                }

                TBSONSecuredWrapper.ThriftSecuredField securedField = tbsonSecuredWrapper.getField(tbase,
                        field.getThriftFieldId());

                tfieldMetadata.tfield = new TField(field.getFieldName(), type, field.getThriftFieldId());
                tfieldMetadata.fieldMetaData = entry.getValue();
                tfieldMetadata.tbaseClass = tbase;
                tfieldMetadata.securedFieldMetaData = securedField;
                thriftStructFields.put(field.getThriftFieldId(), tfieldMetadata);
            }

            //store it
            thriftFields.put(tbase, thriftStructFields);
            threadSafeTFields.set(thriftFields);
            return thriftStructFields;
        } catch (Exception exp) {
            throw new TException(exp);
        }
    }

    public org.apache.thrift.TFieldIdEnum getFieldId(Class<? extends TBase> tbase, String fieldName)
            throws TException {

        try {
            Map<Class<?>, Map<String, org.apache.thrift.TFieldIdEnum>> thriftTFields = threadSafeTFieldsIdEnum
                    .get();

            if (thriftTFields == null) {
                thriftTFields = new HashMap<>();
            }

            Map<String, org.apache.thrift.TFieldIdEnum> tfieldIdEnumByName = thriftTFields.get(tbase);

            // Load it in cache
            if (tfieldIdEnumByName == null) {

                Class<?>[] innerClasses = tbase.getClasses();

                for (Class<?> innerClass : innerClasses) {
                    if ("_Fields".equals(innerClass.getSimpleName())) {
                        Field fieldsByName = innerClass.getDeclaredField("byName");
                        fieldsByName.setAccessible(true);

                        tfieldIdEnumByName = (Map<String, org.apache.thrift.TFieldIdEnum>) fieldsByName
                                .get(fieldsByName);

                        // store in the thread local
                        thriftTFields.put(tbase, tfieldIdEnumByName);
                        threadSafeTFieldsIdEnum.set(thriftTFields);
                    }
                }
            }

            return tfieldIdEnumByName.get(fieldName);

        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public void writeStructBegin(TStruct tStruct) throws TException {
        //System.out.println("writeStructBegin " + tStruct.name);
        Stack<ThriftIO> structClass = threadSafeSIOStack.get();
        if (structClass == null) {
            structClass = new Stack<>();

            Class<? extends TBase> thriftClass = threadSafeTBase.get().getClass();

            if (tbsonSecuredWrapper.isSecured(thriftClass)) {
                structClass.push(
                        new ThriftIO(threadSafeTBase.get().getClass(), new BasicDBObject(), new BasicDBObject()));
            } else {
                structClass.push(new ThriftIO(threadSafeTBase.get().getClass(), new BasicDBObject()));
            }

        } else {
            // The last field stack contains a struct field or a map field
            ThriftFieldMetadata lastField = threadSafeFieldsStack.get().peek();

            switch (lastField.fieldMetaData.valueMetaData.type) {
            case TType.MAP:
                MapMetaData mapMetaData = (MapMetaData) lastField.fieldMetaData.valueMetaData;
                if (mapMetaData.valueMetaData.isStruct()) {
                    structClass.push(new ThriftIO(((StructMetaData) mapMetaData.valueMetaData).structClass,
                            new BasicDBObject()));
                }
                break;
            case TType.LIST:
                ListMetaData listMetaData = (ListMetaData) lastField.fieldMetaData.valueMetaData;
                if (listMetaData.elemMetaData.isStruct()) {
                    structClass.push(new ThriftIO(((StructMetaData) listMetaData.elemMetaData).structClass,
                            new BasicDBObject()));
                }
                break;
            case TType.SET:
                SetMetaData setMetaData = (SetMetaData) lastField.fieldMetaData.valueMetaData;
                if (setMetaData.elemMetaData.isStruct()) {
                    structClass.push(new ThriftIO(((StructMetaData) setMetaData.elemMetaData).structClass,
                            new BasicDBObject()));
                }
                break;
            case TType.STRUCT:
                Class<? extends TBase> structMetadataClass = ((StructMetaData) lastField.fieldMetaData.valueMetaData).structClass;
                if (tbsonSecuredWrapper.isSecured(structMetadataClass)) {
                    structClass.push(new ThriftIO(structMetadataClass, new BasicDBObject(), new BasicDBObject()));
                } else {
                    structClass.push(new ThriftIO(structMetadataClass, new BasicDBObject()));
                }
                break;
            }
        }
        threadSafeSIOStack.set(structClass);
    }

    @Override
    public void writeStructEnd() throws TException {
        DBObject outputDbObject = collapseIOStack();

        if (outputDbObject != null) {
            threadSafeDBObject.set(outputDbObject);
        }
    }

    private DBObject collapseIOStack() throws TException {
        // Get the DBObject produced
        Stack<ThriftIO> thriftIOStack = threadSafeSIOStack.get();
        ThriftIO thriftIO = thriftIOStack.pop();

        //System.out.println("Collapse thrift IO : " + thriftIO.toString());

        //Collapse
        if (thriftIOStack.size() > 0) {
            ThriftIO lastThriftIO = thriftIOStack.peek();

            if (lastThriftIO.map) {
                // add {key:value} to the current object and reset the key
                lastThriftIO.mongoIO.put(lastThriftIO.key, thriftIO.mongoIO);
                lastThriftIO.key = null;
            } else if (lastThriftIO.list) {
                ((BasicDBList) lastThriftIO.mongoIO).add(thriftIO.mongoIO);
            } else {
                // Dont forget to collapse the secured wrap to the current object
                if (thriftIO.securedMongoIO != null) {
                    thriftIO.mongoIO.put("securedwrap", thriftIO.securedMongoIO);
                }

                // add {fieldName:value} to the current object
                String fieldName = peekWriteField().tfield.name;
                lastThriftIO.mongoIO.put(fieldName, thriftIO.mongoIO);

            }
            return null;
        }

        // not collapsed
        // Dont forget to add the secured wrap
        if (thriftIO.securedMongoIO != null) {
            thriftIO.mongoIO.put("securedwrap", thriftIO.securedMongoIO);
        }
        return thriftIO.mongoIO;
    }

    @Override
    public void writeMessageBegin(TMessage tMessage) throws TException {

    }

    @Override
    public void writeMessageEnd() throws TException {

    }

    @Override
    public void writeFieldBegin(TField tField) throws TException {
        //System.out.println("writeFieldBegin " + tField.name);
        pushWriteField(tField.id);
    }

    @Override
    public void writeFieldEnd() throws TException {

        ThriftFieldMetadata thriftFieldMetadata = popWriteField();
        //System.out.println("writeFieldEnd " + thriftFieldMetadata.tfield.name);
    }

    private ThriftFieldMetadata popWriteField() throws TException {
        Stack<ThriftFieldMetadata> writeStack = threadSafeFieldsStack.get();
        if (writeStack.size() > 0) {
            return writeStack.pop();
        }
        return null;
    }

    private void pushWriteField(Class<? extends TBase> tbase, String fieldName) throws TException {
        //get the field ID by name
        org.apache.thrift.TFieldIdEnum tfieldIdEnum = getFieldId(tbase, fieldName);
        if (tfieldIdEnum != null) {
            //pushWriteField(tfieldIdEnum.getThriftFieldId());
            Stack<ThriftFieldMetadata> writeStack = threadSafeFieldsStack.get();

            // First push
            if (writeStack == null) {
                writeStack = new Stack<>();
            }

            // Take the tbase class at the top of the stack
            ThriftFieldMetadata thriftFieldMetadata = getTBaseFields(tbase).get(tfieldIdEnum.getThriftFieldId());
            writeStack.push(thriftFieldMetadata);

            threadSafeFieldsStack.set(writeStack);
        }
    }

    private void pushWriteField(Short id) throws TException {
        Stack<ThriftFieldMetadata> writeStack = threadSafeFieldsStack.get();

        // First push
        if (writeStack == null) {
            writeStack = new Stack<>();
        }

        ThriftIO thriftIO = peekIOStack();

        // Take the tbase class at the top of the stack
        ThriftFieldMetadata thriftFieldMetadata = getTBaseFields(thriftIO.thriftClass).get(id);
        writeStack.push(thriftFieldMetadata);

        threadSafeFieldsStack.set(writeStack);
    }

    private ThriftFieldMetadata peekWriteField() throws TException {
        Stack<ThriftFieldMetadata> writeStack = threadSafeFieldsStack.get();

        if (writeStack.size() > 0) {
            return writeStack.peek();
        } else {
            return STOP_THRIFT_FIELD_METADATA;
        }
    }

    private void pushIOStack(ThriftIO thriftIO) throws TException {
        Stack<ThriftIO> stack = threadSafeSIOStack.get();
        stack.push(thriftIO);
        threadSafeSIOStack.set(stack);
    }

    private ThriftIO peekIOStack() throws TException {
        Stack<ThriftIO> stack = threadSafeSIOStack.get();
        return stack.peek();
    }

    private ThriftIO popIOStack() throws TException {
        Stack<ThriftIO> stack = threadSafeSIOStack.get();
        return stack.pop();
    }

    @Override
    public void writeFieldStop() throws TException {
    }

    @Override
    public void writeMapBegin(TMap tMap) throws TException {
        //System.out.println("writeMapBegin");
        // if the field secured ?
        ThriftFieldMetadata fieldMetadata = peekWriteField();

        BasicDBObject securedFieldSecuredObject = null;
        if (fieldMetadata.securedFieldMetaData.isSecured()) {
            securedFieldSecuredObject = new BasicDBObject();
        }

        threadSafeSIOStack.get()
                .push(new ThriftIO(null, new BasicDBObject(), securedFieldSecuredObject, true, false));
    }

    @Override
    public void writeMapEnd() throws TException {
        //System.out.println("writeMapEnd");
        // Collapse the map into the structure

        // Get the DBObject produced (the map)
        Stack<ThriftIO> thriftIOStack = threadSafeSIOStack.get();
        ThriftIO mapThriftIO = thriftIOStack.pop();

        // add {fieldName:value} to the current object
        ThriftFieldMetadata field = peekWriteField();

        ThriftIO documentThriftIO = thriftIOStack.peek();
        // write the document (hash)
        documentThriftIO.mongoIO.put(field.tfield.name, mapThriftIO.mongoIO);
        // write the secured (hash)
        if (documentThriftIO.securedMongoIO != null) {
            documentThriftIO.securedMongoIO.put(Short.toString(field.tfield.id), mapThriftIO.securedMongoIO);
        }
    }

    @Override
    public void writeListBegin(TList tList) throws TException {
        //System.out.println("writeListBegin");
        // Replace the BasicDbObject by a DBList
        threadSafeSIOStack.get().push(new ThriftIO(null, new BasicDBList(), null, false, true));
    }

    @Override
    public void writeListEnd() throws TException {
        //System.out.println("writeListEnd");
        // collapse the list
        collapseIOStack();
    }

    @Override
    public void writeSetBegin(TSet tSet) throws TException {
        //System.out.println("writeSetBegin");
        // Replace the BasicDbObject by a DBList
        threadSafeSIOStack.get().push(new ThriftIO(null, new BasicDBList(), null, false, true));
    }

    @Override
    public void writeSetEnd() throws TException {
        //System.out.println("writeSetEnd");
        // collapse the list
        collapseIOStack();
    }

    @Override
    public void writeBool(boolean b) throws TException {
        writeByte(b ? (byte) 1 : (byte) 0);
    }

    @Override
    public void writeByte(byte b) throws TException {
        writeNumber((int) b);
    }

    @Override
    public void writeI16(short i) throws TException {
        writeNumber(i);
    }

    @Override
    public void writeI32(int i) throws TException {
        writeNumber(i);
    }

    @Override
    public void writeI64(long l) throws TException {
        writeNumber(l);
    }

    @Override
    public void writeDouble(double v) throws TException {
        writeNumber(v);
    }

    private void writeNumber(Number v) throws TException {
        try {
            ThriftFieldMetadata thriftFieldMetadata = peekWriteField();
            String key = thriftFieldMetadata.tfield.name;
            //System.out.println("write " + key + " " + v);

            ThriftIO thriftIO = threadSafeSIOStack.get().peek();

            //specific map treatment for the map key
            if (thriftIO.map && thriftIO.key == null) {
                thriftIO.key = v.toString();
            } else if (thriftIO.map) {
                thriftIO.mongoIO.put(thriftIO.key, v);
                thriftIO.key = null;
            } else if (thriftIO.list) {
                ((BasicDBList) thriftIO.mongoIO).add(v);
            } else {
                thriftIO.mongoIO.put(key, v);
            }

        } catch (Exception e) {
            throw new TException(e);
        }
    }

    private void writeSecuredString(String s, ThriftFieldMetadata thriftFieldMetadata, ThriftIO thriftIO)
            throws Exception {
        byte[] butf8 = s.getBytes("UTF-8");
        Object sutf8 = new String(butf8);

        // MAP SECURED FIELD
        if (thriftIO.map) {
            // the string is the map key
            //keys are unprotected
            if (thriftIO.key == null) {
                thriftIO.key = (String) sutf8;
                return;
            } else {
                // the string is the value
                // crypt the value and add it to the secured field
                String securedField = Hex.encodeHexString(TBSONUnstackedProtocol.tbsonSecuredWrapper.cipher(butf8));
                thriftIO.securedMongoIO.put(thriftIO.key, securedField);

                //hash if requested
                // the the value value is hashed add the hash in the unsecured document
                if (thriftFieldMetadata.securedFieldMetaData.isHash()) {
                    sutf8 = TBSONUnstackedProtocol.tbsonSecuredWrapper.digest64(butf8);
                    thriftIO.mongoIO.put(thriftIO.key, sutf8);
                    thriftIO.key = null;
                }
            }
            return;
        }

        // STRING SECURED FIELD
        // ADD IT TO THE SECURED DOCUMENT
        String securedField = Hex.encodeHexString(TBSONUnstackedProtocol.tbsonSecuredWrapper.cipher(butf8));
        thriftIO.securedMongoIO.put(Short.toString(thriftFieldMetadata.tfield.id), securedField);

        if (thriftFieldMetadata.securedFieldMetaData.isHash()) {
            Object butf8Hash = TBSONUnstackedProtocol.tbsonSecuredWrapper.digest64(butf8);
            thriftIO.mongoIO.put(thriftFieldMetadata.tfield.name, butf8Hash);
        }
    }

    private void writeUnsecuredString(String s, ThriftFieldMetadata thriftFieldMetadata, ThriftIO thriftIO)
            throws Exception {
        byte[] butf8 = s.getBytes("UTF-8");
        Object sutf8 = new String(butf8);

        //specific map treatment for the map key
        if (thriftIO.map && thriftIO.key == null) {
            thriftIO.key = (String) sutf8;
            return;
        }

        // write the value (unsecured)
        //its a map value
        if (thriftIO.map) {
            thriftIO.mongoIO.put(thriftIO.key, sutf8);
            thriftIO.key = null;
            return;
        }

        //its a list or a set
        if (thriftIO.list) {
            ((BasicDBList) thriftIO.mongoIO).add(sutf8);
            return;
        }

        // classic string case
        thriftIO.mongoIO.put(thriftFieldMetadata.tfield.name, sutf8);
    }

    @Override
    public void writeString(String s) throws TException {
        try {
            //System.out.println("write " + key + " " + s);
            ThriftIO thriftIO = threadSafeSIOStack.get().peek();
            // Its a string field
            ThriftFieldMetadata thriftFieldMetadata = peekWriteField();

            if (thriftFieldMetadata.securedFieldMetaData.isSecured()) {
                writeSecuredString(s, thriftFieldMetadata, thriftIO);
            } else {
                writeUnsecuredString(s, thriftFieldMetadata, thriftIO);
            }
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public void writeBinary(ByteBuffer byteBuffer) throws TException {
        //System.out.println("write " + peekWriteField().tfield.name + byteBuffer);
        try {
            ThriftFieldMetadata thriftFieldMetadata = peekWriteField();
            String key = thriftFieldMetadata.tfield.name;
            //System.out.println("write binary " + key );

            ThriftIO thriftIO = threadSafeSIOStack.get().peek();

            byte[] b = byteBuffer.array();

            //specific map treatment for the map key
            if (thriftIO.map && thriftIO.key == null) {
                thriftIO.key = new String(b);
            } else if (thriftIO.map) {
                thriftIO.mongoIO.put(thriftIO.key, b);
                thriftIO.key = null;
            } else if (thriftIO.list) {
                ((BasicDBList) thriftIO.mongoIO).add(b);
            } else {
                thriftIO.mongoIO.put(key, b);
            }
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public TMessage readMessageBegin() throws TException {
        return null;
    }

    @Override
    public void readMessageEnd() throws TException {

    }

    private Stack<ThriftFieldMetadata> getFieldsStack(Class<? extends TBase> tbase, DBObject dbObject)
            throws TException {
        // extract the fields (key from MongoDB)

        Stack<ThriftFieldMetadata> writeStack = new Stack<>();

        for (Map.Entry<String, Object> pair : ((BasicDBObject) dbObject).entrySet()) {
            //System.out.println("push field " + mongoKey + " of " + tbase.getSimpleName());
            TFieldIdEnum tfieldIdEnum = getFieldId(tbase, pair.getKey());
            if (tfieldIdEnum != null) {
                ThriftFieldMetadata thriftFieldMetadata = getTBaseFields(tbase)
                        .get(tfieldIdEnum.getThriftFieldId());
                writeStack.push(thriftFieldMetadata);
            }
        }

        // extract secure fields stack
        if (dbObject.containsField("securedwrap")) {
            for (String id : ((DBObject) dbObject.get("securedwrap")).keySet()) {
                ThriftFieldMetadata thriftFieldMetadata = getTBaseFields(tbase).get(Short.parseShort(id));
                if (!thriftFieldMetadata.securedFieldMetaData.isHash()) {
                    writeStack.push(thriftFieldMetadata);
                }
            }
        }

        return writeStack;
    }

    @Override
    public TStruct readStructBegin() throws TException {
        Stack<ThriftIO> structClass = threadSafeSIOStack.get();

        // Init the stack with the TBase deserialisation struct
        if (structClass == null) {
            //Initialisation of the IO stack
            structClass = new Stack<>();

            //System.out.println("readStructBegin " + threadSafeTBase.get().getClass().getSimpleName() );

            // extract the fields (key from MongoDB)
            Stack<ThriftFieldMetadata> fieldsStack = getFieldsStack(threadSafeTBase.get().getClass(),
                    threadSafeDBObject.get());

            structClass.push(new ThriftIO(threadSafeTBase.get().getClass(), threadSafeDBObject.get(), fieldsStack));

            threadSafeSIOStack.set(structClass);
            return ANONYMOUS_STRUCT;
        }

        // The stack is already initialized
        ThriftIO currentIO = peekIOStack();

        // Push a struct from the BasicDBList and increment the index
        if (currentIO.list && currentIO.thriftClass != null) {
            // Extract the DBObject
            DBObject dbObject = (DBObject) ((BasicDBList) currentIO.mongoIO).get(currentIO.containerIndex);
            currentIO.containerIndex++;
            Stack<ThriftFieldMetadata> fieldsStack = getFieldsStack(currentIO.thriftClass, dbObject);
            structClass.push(new ThriftIO(currentIO.thriftClass, dbObject, fieldsStack));
            threadSafeSIOStack.set(structClass);
            return ANONYMOUS_STRUCT;
        }

        if (currentIO.map && currentIO.thriftClass != null) {
            DBObject dbObject = (DBObject) currentIO.mapEntry.getValue();
            currentIO.mapEntry = null;
            Stack<ThriftFieldMetadata> fieldsStack = getFieldsStack(currentIO.thriftClass, dbObject);
            structClass.push(new ThriftIO(currentIO.thriftClass, dbObject, fieldsStack));
            threadSafeSIOStack.set(structClass);
            return ANONYMOUS_STRUCT;
        }

        // the next field is a struct
        // push it on the stack
        ThriftFieldMetadata thriftFieldMetadata = currentIO.fieldsStack.peek();

        switch (thriftFieldMetadata.tfield.type) {
        case TType.STRUCT:
            // extract the fields (key from MongoDB)
            // Struct related to the field
            Class<? extends TBase> thriftClass = ((StructMetaData) thriftFieldMetadata.fieldMetaData.valueMetaData).structClass;
            // DbObject related to the field
            DBObject dbObject = (DBObject) peekIOStack().mongoIO.get(thriftFieldMetadata.tfield.name);
            // extraction of the fields
            Stack<ThriftFieldMetadata> fieldsStack = getFieldsStack(thriftClass, dbObject);
            // push the structure
            structClass.push(new ThriftIO(thriftClass, dbObject, fieldsStack));
            break;
        }
        threadSafeSIOStack.set(structClass);
        return ANONYMOUS_STRUCT;
    }

    @Override
    public void readStructEnd() throws TException {
        // only occurs when a Stop field occurs
        ThriftIO thriftIO = popIOStack();
        //System.out.println("readStructEnd " + thriftIO.thriftClass.getSimpleName());
    }

    @Override
    public TField readFieldBegin() throws TException {
        if (peekIOStack().fieldsStack.size() == 0) {
            return STOP_THRIFT_FIELD_METADATA.tfield;
        }

        ThriftFieldMetadata currentField = peekIOStack().fieldsStack.peek();

        // partial desarialize
        // IF the field is skipped change the type to void
        Map<Class<?>, List<Short>> filter = threadSafeFieldIdsFilter.get();

        if (filter != null) {
            List<Short> fieldsFiltered = filter.get(currentField.tbaseClass);
            if (fieldsFiltered != null && fieldsFiltered.contains(currentField.tfield.id)) {
                return new TField(currentField.tfield.name, TType.VOID, currentField.tfield.id);
            }
        }

        return currentField.tfield;
    }

    @Override
    public void readFieldEnd() throws TException {
        ThriftFieldMetadata lastField = peekIOStack().fieldsStack.pop();
        //System.out.println("readFieldEnd " + lastField.tfield.name);
    }

    @Override
    public TMap readMapBegin() throws TException {
        //System.out.println("readMapBegin");
        // Get the IO Stack
        Stack<ThriftIO> stack = threadSafeSIOStack.get();

        ThriftIO currentIO = stack.peek();
        ThriftFieldMetadata thriftFieldMetadata = currentIO.fieldsStack.peek();

        // field related to the list
        MapMetaData mapMetaData = (MapMetaData) thriftFieldMetadata.fieldMetaData.valueMetaData;

        // extract the DBMap (BasicDbObject)
        BasicDBObject dbObject = null;
        BasicDBObject securedDObject = null;
        if (thriftFieldMetadata.securedFieldMetaData.isSecured()) {
            // from the mongoIO securedWrap for a secured map
            BasicDBObject securedWrap = (BasicDBObject) currentIO.mongoIO.get("securedwrap");
            securedDObject = (BasicDBObject) securedWrap
                    .get(Short.toString(currentIO.fieldsStack.peek().tfield.id));
            dbObject = securedDObject;
        } else {
            // from the mongoIO field for an unsecured map
            dbObject = (BasicDBObject) currentIO.mongoIO.get(currentIO.fieldsStack.peek().tfield.name);
        }

        ThriftIO thriftListIO = null;
        if (mapMetaData.valueMetaData.isStruct()) {
            thriftListIO = new ThriftIO(((StructMetaData) mapMetaData.valueMetaData).structClass, dbObject,
                    securedDObject, true, false);
        } else {
            thriftListIO = new ThriftIO(null, dbObject, securedDObject, true, false);
        }
        thriftListIO.mapIterator = dbObject.entrySet().iterator();

        stack.push(thriftListIO);
        threadSafeSIOStack.set(stack);

        return new TMap(TType.STRING, TType.STRING, dbObject.size());
    }

    @Override
    public void readMapEnd() throws TException {
        //System.out.println("readMapEnd");
        popIOStack();
    }

    @Override
    public TList readListBegin() throws TException {
        //System.out.println("readListBegin");

        // Get the IO Stack
        Stack<ThriftIO> stack = threadSafeSIOStack.get();

        ThriftIO currentIO = stack.peek();
        ThriftFieldMetadata thriftFieldMetadata = currentIO.fieldsStack.peek();

        // field related to the list
        ListMetaData listMetaData = (ListMetaData) thriftFieldMetadata.fieldMetaData.valueMetaData;

        // extract the DBList
        BasicDBList dbList = (BasicDBList) currentIO.mongoIO.get(currentIO.fieldsStack.peek().tfield.name);

        ThriftIO thriftListIO = null;
        if (listMetaData.elemMetaData.isStruct()) {
            thriftListIO = new ThriftIO(((StructMetaData) listMetaData.elemMetaData).structClass, dbList, null,
                    false, true);
        } else {
            thriftListIO = new ThriftIO(null, dbList, null, false, true);
        }

        stack.push(thriftListIO);
        threadSafeSIOStack.set(stack);

        return new TList(TType.LIST, dbList.size());
    }

    @Override
    public void readListEnd() throws TException {
        //System.out.println("readListEnd");
        popIOStack();
    }

    @Override
    public TSet readSetBegin() throws TException {
        //System.out.println("readSetBegin");

        // Get the IO Stack
        Stack<ThriftIO> stack = threadSafeSIOStack.get();

        ThriftIO currentIO = stack.peek();
        ThriftFieldMetadata thriftFieldMetadata = currentIO.fieldsStack.peek();

        // field related to the list
        SetMetaData setMetaData = (SetMetaData) thriftFieldMetadata.fieldMetaData.valueMetaData;

        // extract the DBList
        BasicDBList dbList = (BasicDBList) currentIO.mongoIO.get(currentIO.fieldsStack.peek().tfield.name);

        ThriftIO thriftListIO = null;
        if (setMetaData.elemMetaData.isStruct()) {
            thriftListIO = new ThriftIO(((StructMetaData) setMetaData.elemMetaData).structClass, dbList, null,
                    false, true);
        } else {
            thriftListIO = new ThriftIO(null, dbList, null, false, true);
        }

        stack.push(thriftListIO);
        threadSafeSIOStack.set(stack);

        return new TSet(TType.SET, dbList.size());
    }

    @Override
    public void readSetEnd() throws TException {
        //System.out.println("readSetEnd");
        popIOStack();
    }

    public boolean readBool() throws TException {
        return (readByte() == 1);
    }

    public byte readByte() throws TException {
        return ((Number) getCurrentFieldValue(TType.BYTE)).byteValue();
    }

    public short readI16() throws TException {
        return ((Number) getCurrentFieldValue(TType.I16)).shortValue();
    }

    public int readI32() throws TException {
        return ((Number) getCurrentFieldValue(TType.I32)).intValue();
    }

    public long readI64() throws TException {
        return ((Number) getCurrentFieldValue(TType.I64)).longValue();
    }

    public double readDouble() throws TException {
        return ((Number) getCurrentFieldValue(TType.DOUBLE)).doubleValue();
    }

    @Override
    public String readString() throws TException {
        return (String) getCurrentFieldValue(TType.STRING);
    }

    @Override
    public ByteBuffer readBinary() throws TException {
        Object value = getCurrentFieldValue(TType.VOID);
        return ByteBuffer.wrap((byte[]) value);
    }

    public void reset() {
        // reset thread local
        threadSafeFieldsStack.remove();
        threadSafeSIOStack.remove();
        threadSafeTBase.remove();
        threadSafeFieldIdsFilter.remove();
        threadSafeDBObject.remove();
    }

    private Object getCurrentFieldValue(byte ttype) throws TException {
        ThriftIO thriftIO = peekIOStack();

        Object fieldReaded = null;
        if (thriftIO.list) {
            fieldReaded = ((BasicDBList) thriftIO.mongoIO).get(thriftIO.containerIndex);
            thriftIO.containerIndex++;
        } else if (thriftIO.map && thriftIO.mapEntry == null) {
            // a first read for the key
            thriftIO.mapEntry = thriftIO.mapIterator.next();

            // IF YOU READ A KEY YOU MUST CONVERT THE STRING INTO NUMBER
            switch (ttype) {
            case TType.BYTE:
                fieldReaded = Byte.parseByte(thriftIO.mapEntry.getKey());
                break;
            case TType.I32:
            case TType.I16:
                fieldReaded = Integer.parseInt(thriftIO.mapEntry.getKey());
                break;
            case TType.I64:
                fieldReaded = Long.parseLong(thriftIO.mapEntry.getKey());
                break;
            case TType.DOUBLE:
                fieldReaded = Double.parseDouble(thriftIO.mapEntry.getKey());
                break;
            default:
                fieldReaded = thriftIO.mapEntry.getKey();
            }
        } else if (thriftIO.map && thriftIO.mapEntry != null) {
            // a second read for the value
            fieldReaded = thriftIO.mapEntry.getValue();

            // a secured map have a thriftIO.securedMongoIO not null
            if (thriftIO.securedMongoIO != null) {
                byte[] data = TBSONUnstackedProtocol.tbsonSecuredWrapper.decipherValue((String) fieldReaded);
                fieldReaded = "";
                if (data != null) {
                    fieldReaded = new String(data);
                }
            }
            thriftIO.mapEntry = null;
        } else {
            // normal field read
            ThriftFieldMetadata fieldMetadata = thriftIO.fieldsStack.peek();
            if (fieldMetadata.securedFieldMetaData.isSecured()) {
                byte[] data = TBSONUnstackedProtocol.tbsonSecuredWrapper.decipherSecuredField(
                        fieldMetadata.tfield.id, (DBObject) thriftIO.mongoIO.get("securedwrap"));
                if (data != null) {
                    fieldReaded = new String(data);
                }
            } else {
                fieldReaded = thriftIO.mongoIO.get(fieldMetadata.tfield.name);
            }

        }
        return fieldReaded;
    }

}