Package com.mobixess.jodb.core.transaction

Source Code of com.mobixess.jodb.core.transaction.TransactionAssembler$ByteHolder

/**
* Copyright Mobixess Inc. 2007
*/
package com.mobixess.jodb.core.transaction;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.mobixess.jodb.core.IllegalClassTypeException;
import com.mobixess.jodb.core.JODBConfig;
import com.mobixess.jodb.core.JodbIOException;
import com.mobixess.jodb.core.index.IndexingRecord;
import com.mobixess.jodb.core.index.JODBIndexingRootAgent;
import com.mobixess.jodb.core.io.IOTicket;
import com.mobixess.jodb.core.io.IRandomAccessDataBuffer;
import com.mobixess.jodb.core.io.IOBase;
import com.mobixess.jodb.core.io.JODBIOBase;
import com.mobixess.jodb.core.io.JODBOperationContext;
import com.mobixess.jodb.core.io.ObjectDataContainer;
import com.mobixess.jodb.core.io.ObjectDataContainer.FieldsIterator;
import com.mobixess.jodb.core.plugin.IClassProcessor;
import com.mobixess.jodb.core.plugin.JODBPluginRegistry;
import com.mobixess.jodb.core.transaction.JODBSession.ClassDescriptor;
import com.mobixess.jodb.core.transaction.JODBSession.FieldAndIDRecord;
import com.mobixess.jodb.core.transaction.TransactionUtils.DataContainersCache;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil;
import com.mobixess.jodb.util.Utils;

/**
* @author Mobixess
*
*/
public class TransactionAssembler {
   
    public final static byte TRANSACTION_REPLACEMENT_ENTRY_TYPE_STATIC = 0xf;
    public final static byte TRANSACTION_REPLACEMENT_ENTRY_TYPE_REDIRECTOR = 0x7f;
    private static Logger _logger = Utils.getLogger(TransactionAssembler.class.getName());
    private final static long MAX_ABSOLUTE_SHORT_ADDR = 0xFFFFFFFFL;
    //WARNING: this is hard transaction limit.
    private final static long TRANSACTION_SIZE_LIMIT = Math.min(Integer.MAX_VALUE, -Integer.MIN_VALUE)/2;
 
   
    /**
     *
     * @param context
     * @param tContainer
     * @throws IOException
     * @throws IllegalClassTypeException
     */
    public static void assembleTransactionData(JODBOperationContext context, TransactionContainer tContainer) throws IOException, IllegalClassTypeException{
        if(tContainer.isAgentsMode()){
            //make sure the root agent processed first
            JODBIndexingRootAgent indexingRootAgent = context.getIndexingRootAgent();
            if(tContainer.getHandleForObject(indexingRootAgent)!=null){
                assembleTransactionDataForObject(context, indexingRootAgent, tContainer);
            }
        }
        Map<Object, TransactionHandle> transactionObjects = tContainer.getTransactionObjects();
        Iterator<Object> iter = transactionObjects.keySet().iterator();
        while (iter.hasNext() ) {
            tContainer.resetTransactionBufferToEnd();
            Object element = iter.next();
            assembleTransactionDataForObject(context,  element, tContainer);
        }
    }

    private static long estimateHeaderAndAuxDataLength(TransactionHandle tHandle, /*int classHierarhyLen,*/ boolean translated){
        long lengthEstimate = 0;
        lengthEstimate += 3; // mask + version
        if(tHandle.generateUID()){
            lengthEstimate += 8; // uid
        }
        if(tHandle.generateCreationTS()){
            lengthEstimate += 8; // creation TS
        }
        if(tHandle.generateModificationTS()){
            lengthEstimate += 8; // modification TS
        }
       
        lengthEstimate += 2;// class hierarhy
        if(translated){
            lengthEstimate += 2;
        }
//        lengthEstimate += 2; //class names counter
//        lengthEstimate += 2 * classHierarhyLen;// class hierarhy
        return lengthEstimate;
    }
   
    private static long estimateObjectLength(TransactionHandle tHandle, /*int classHierarhyLen,*/
                                                int fieldsWithAbsoluteLen, int fieldsWithRelativeLen,
                                                ClassDescriptor classDescriptor, boolean translated) throws JodbIOException{
        long lengthEstimate = estimateHeaderAndAuxDataLength(tHandle, translated);
       
        if(fieldsWithAbsoluteLen > 0){
            lengthEstimate += 2; // directly addressed fields count
            lengthEstimate += (2+8)*fieldsWithAbsoluteLen;// size for fields ids and pointers
        }
       
        if(fieldsWithRelativeLen > 0){
            lengthEstimate += 2; // inderectly addressed fields count
            lengthEstimate += (2+4)*fieldsWithRelativeLen;// size for fields ids and pointers
        }
       
        if(classDescriptor != null){
            lengthEstimate += 2; // inderectly addressed fields count
            lengthEstimate += classDescriptor.getPrimitiveFieldsStorageEstimate(2);
        }
        return lengthEstimate;
    }
   
    private static long estimateArrayObjectLength(JODBOperationContext context, TransactionHandle tHandle, Object array, ClassDescriptor classDescriptor, ByteHolder elementsSizeOutParam, boolean translated) throws JodbIOException{
        int arraySize = Array.getLength(array);
        long lengthEstimate = estimateObjectLength(tHandle, 0, 0, null, translated);//header
        lengthEstimate+= 4; //array length entry
        lengthEstimate+= 1; //array's element size entry
        if(classDescriptor.isPrimitiveArray()){
            elementsSizeOutParam._value = (byte)PrimitiveJavaTypesUtil.getDataOutputWriteLen(classDescriptor.getArrayType());
            lengthEstimate+=elementsSizeOutParam._value*arraySize;
        }else{
            JODBSession session = context.getSession();
            TransactionContainer tContainer = context.getTransactionContainer();
            long transactionOffset = context.getTransactionOffset();
            boolean newArray = tHandle.isNewObject();
            elementsSizeOutParam._value = 4;
            for (int i = 0; i < arraySize; i++) {
                Object value = Array.get(array, i);
                if(value == null){
                    continue;
                }

                TransactionHandle valueTransactionHandle = tContainer.getHandleForObject(value);
                if(valueTransactionHandle == null){
                    continue;
                }
               
                if(newArray){
                    if(!valueTransactionHandle.isNewObject()){
                        long valueObjectOffset = valueTransactionHandle.getHandle().getObjectEntryOffset();
                        if(valueObjectOffset > MAX_ABSOLUTE_SHORT_ADDR && transactionOffset - valueObjectOffset > TRANSACTION_SIZE_LIMIT){
                            elementsSizeOutParam._value = 8;
                            break;
                        }
                    }
                }else{
                    if(valueTransactionHandle.isNewObject()){
                        if( transactionOffset >  MAX_ABSOLUTE_SHORT_ADDR - TRANSACTION_SIZE_LIMIT ){
                            elementsSizeOutParam._value = 8;
                            break;
                        }
                    }else{
                        long valueObjectOffset = valueTransactionHandle.getHandle().getObjectEntryOffset();
                        if(valueObjectOffset > MAX_ABSOLUTE_SHORT_ADDR){
                            elementsSizeOutParam._value = 8;
                            break;
                        }
                    }
                }
               
                PersistentObjectHandle objectHandle = session.getHandleForActiveObject(value);
                if(newArray){
                   
                }
                if(objectHandle == null){
                    continue;
                }
                if(objectHandle.getObjectEntryOffset() > MAX_ABSOLUTE_SHORT_ADDR){
                    elementsSizeOutParam._value = 8;
                    break;
                }
            }
            lengthEstimate+=elementsSizeOutParam._value*arraySize+arraySize/8+1;
        }
        return lengthEstimate;
    }
   
    private static int composeEntryID(short entryID, long length){
        int lenModifierID = 0;
        if(length <= 0xff){
            lenModifierID = JODBIOBase.LEN_MODIFIER_BYTE;
        }else if(length > 0xFFFF ){
            lenModifierID = JODBIOBase.LEN_MODIFIER_LONG;
        }
        return entryID|lenModifierID;
    }
   
    private static void classifyFields(Object object, ClassDescriptor classDescr, Map<Object, TransactionHandle> transactionObjects, Vector<ObjectFieldRecord> fieldsWithAbsoluteAddr,
                                        Vector<ObjectFieldRecord> fieldsWithRelativeAddr,Vector<Field> primitiveFields) throws IOException
    {
        FieldAndIDRecord[] fields = classDescr.getAllFields();
        for (int i = 0; i < fields.length; i++) {
            Field next = fields[i]._field;
            if(next.getType().isPrimitive()){
                primitiveFields.add(next);
                continue;
            }
            Object value;
            try {
                value = next.get(object);
            } catch (Exception e) {
                e.printStackTrace();
                throw new JodbIOException(e);
            }
            TransactionHandle childObjectHandle = transactionObjects.get(value);
            if(value == null || childObjectHandle == null || childObjectHandle.is_DELETE_Transaction()){
                //null fieds may not require
                //nullFields.add(next);
            }else{
                if(!childObjectHandle.isNewObject()){
                    fieldsWithAbsoluteAddr.add(new ObjectFieldRecord(next, value));
                }else{
                    fieldsWithRelativeAddr.add(new ObjectFieldRecord(next, value));
                }
            }
        }
    }
   
    private static void writeEntryLenForID(int id, long length, IRandomAccessDataBuffer dataBuffer) throws IOException{
        switch (id&~JODBIOBase.LEN_MODIFIER_EXCLUSION_MASK) {//reserve space for length
        case JODBIOBase.LEN_MODIFIER_BYTE:
            dataBuffer.writeByte((byte)length);
            break;
        case JODBIOBase.LEN_MODIFIER_LONG:
            dataBuffer.writeLong(length);
            break;
        default:
            dataBuffer.writeShort((short)length);
        }
    }
   
    private static int formPrimaryObjectMask(int initalMask, boolean fieldsWithAbsoluteAddr, boolean fieldsWithRelativeAddr, boolean primitiveFields, boolean translated, TransactionHandle tHandle, ClassDescriptor classDescr){
        if(fieldsWithAbsoluteAddr){
            initalMask = ObjectDataContainer.addDirectlyAddressedFieldsBit(initalMask);
        }
        if(fieldsWithRelativeAddr){
            initalMask= ObjectDataContainer.addRelativelyAddressedFieldsID(initalMask);
        }
        if(primitiveFields){
            initalMask = ObjectDataContainer.addPrimitiveFieldsBit(initalMask);
        }
       
        if(translated){
            initalMask = ObjectDataContainer.addTranslatedBit(initalMask);
        }
       
        if(tHandle.generateCreationTS()){
            initalMask = ObjectDataContainer.addCreationTSFieldBit(initalMask);
        }
       
        if(tHandle.generateModificationTS()){
            initalMask = ObjectDataContainer.addModificationTSFieldBit(initalMask);
        }
       
        if(classDescr.isArray()){
            initalMask = ObjectDataContainer.addArrayIDBit(initalMask);
        }
        return initalMask;
    }
   
    private static int formSecondaryObjectMask(int initalMask, TransactionHandle transactionHandle){
        if(transactionHandle.isAgent()){
            initalMask = ObjectDataContainer.addAgentBit(initalMask);
        }
        initalMask = ObjectDataContainer.addCyclicalCounterBit(initalMask);
        return initalMask;
    }

    private static long assembleTransactionDataForObject(JODBOperationContext context, Object rootObject, TransactionContainer tContainer) throws IOException, IllegalClassTypeException{
        DataContainersCache dataContainersCache = TransactionUtils.getObjectDataContainerCache();
        ObjectDataContainer persistentCopyObjectDataContainer =  dataContainersCache.pullObjectDataContainer();
        try {
            return writeObjects(context, rootObject, tContainer, persistentCopyObjectDataContainer);
        } finally {
            dataContainersCache.pushObjectDataContainer(persistentCopyObjectDataContainer);
            tContainer.fireOnCommitFinished(rootObject, context.getSession());
        }   
    }
   
    /**
     *
     * @param context
     * @param rootObject
     * @param tContainer
     * @param persistentCopyObjectDataContainer - data container to read already persisted copy if applicable
     * @return
     * @throws IOException
     * @throws IllegalClassTypeException
     */
    private static long writeObjects(JODBOperationContext context, Object rootObject, TransactionContainer tContainer, ObjectDataContainer persistentCopyObjectDataContainer) throws IOException, IllegalClassTypeException{
        //TODO add verification "fields count" < short
        Map<Object, TransactionHandle> transactionObjects = tContainer.getTransactionObjects();
        TransactionHandle tHandle = transactionObjects.get(rootObject);
        if(tHandle == null){
            throw new IOException("transaction handle unavailable");
        }
        if(!tContainer.isAgentsMode() && tHandle.isAgent()){
            return 0;
        }
        IOTicket ioTicket = context.getIoTicket();
        JODBSession session = context.getSession();
        if(tHandle.isTranslated()){
            return tHandle.getTransactionOffset();
        }
        tContainer.fireOnCommitStarted(rootObject, context.getSession());
        if(tHandle.is_DELETE_Transaction()){
            deleteObject(ioTicket, session, tHandle, tContainer);
            return -1;
        }
        IOBase base = ioTicket.getBase();
       
       
        IClassProcessor classProcessor = JODBPluginRegistry.getInstance().getClassProcessor(rootObject.getClass());
       
        Object objectToPersist = classProcessor.translate(rootObject);
       
        ClassDescriptor classDescr = session.getDescriptorForClass(objectToPersist.getClass());
        FieldAndIDRecord[] fields = classDescr.getAllFields();
       
        Vector<IndexingRecord> indexes = null;
       
        Class rootObjectType = rootObject.getClass();
        int rootObjectClassID;
        if(rootObjectType.isArray()){
            rootObjectClassID = base.getOrSetClassTypeSubstitutionID(rootObjectType.getComponentType().getName());
        }else{
            rootObjectClassID = base.getOrSetClassTypeSubstitutionID(rootObjectType.getName());
            indexes = TransactionUtils.getObjectDataContainerCache().pullVector();
            JODBIndexingRootAgent indexingAgent = context.getIndexingRootAgent();
            indexingAgent.getAgentsForClassId(indexes, rootObjectClassID);
            if(indexes.size() == 0){
                //no indexes for this class
                TransactionUtils.getObjectDataContainerCache().pushVector(indexes);
                indexes = null;
            }
        }
        tHandle.setIndexes(indexes);
        //String[] classTypes = classDescr.getTypes();
       
        if( checkActiveObjectUnchanged(classProcessor, context, objectToPersist, tHandle, persistentCopyObjectDataContainer, indexes) ){//should not happen in recursive sub call
            tHandle.setIndexes(null);//reset indexes info in handle to prevent post processing
            long offset = tHandle.getHandle().getObjectEntryOffset();//JODBIOUtils.addAbsoluteOffsetIdentifierBit(tHandle.getHandle().getObjectEntryOffset());
            tHandle.setTransactionOffset(offset);
            if( classDescr.isArray()){
                if(classDescr.isPrimitiveArray()){
                    return offset;
                }
                int arrayLen = Array.getLength(objectToPersist);
                for (int i = 0; i < arrayLen; i++) {
                    Object childObj = Array.get(objectToPersist, i);
                    if(childObj == null || transactionObjects.get(childObj) == null){
                        continue;
                    }
                    assembleTransactionDataForObject(context, childObj, tContainer);
                }
            } else {
                try {
                    for (int i = 0; i < fields.length; i++) {
                        Field field = fields[i]._field;
                        if (field.getType().isPrimitive()) {
                            continue;
                        }
                        Object childObj = field.get(objectToPersist);
                        if (childObj == null || transactionObjects.get(childObj) == null){
                            continue;
                        }
                        assembleTransactionDataForObject(context, childObj, tContainer);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new JodbIOException(e);
                }
            }           
            return offset;
        }
       
        IRandomAccessDataBuffer transactionFile = tContainer.getTransactionNewDataFile();
        transactionFile.resetToEnd();
       
        Vector<ObjectFieldRecord> fieldsWithAbsoluteAddr = new Vector<ObjectFieldRecord>();
        Vector<ObjectFieldRecord> fieldsWithRelativeAddr = new Vector<ObjectFieldRecord>();
        Vector<Field> primitiveFields = new Vector<Field>();
       
        if(!classDescr.isArray()){
            classifyFields(objectToPersist, classDescr, transactionObjects, fieldsWithAbsoluteAddr, fieldsWithRelativeAddr, primitiveFields);
        }
       
       
        long objectIDOffset = transactionFile.getCursorOffset();
       
        tHandle.setTransactionOffset(objectIDOffset);

        if(JODBConfig.DEBUG){
            _logger.info(" >>> Transaction: Object "+rootObject.getClass()+" "+rootObject+" start offset ="+objectIDOffset);
        }
       
        boolean translated = rootObject!=objectToPersist;
        byte arrayElementSize = 0;
        long lengthEstimate;
        if(!classDescr.isArray()){
            lengthEstimate = estimateObjectLength(tHandle, fieldsWithAbsoluteAddr.size(), fieldsWithRelativeAddr.size(), classDescr, translated);
        }else{
            ByteHolder byteHolder = new ByteHolder();
            lengthEstimate = estimateArrayObjectLength(context, tHandle, objectToPersist, classDescr, byteHolder, translated);
            arrayElementSize = byteHolder._value;
        }
       
        int objId = composeEntryID( JODBIOBase.ENTRY_OBJECT_ID, lengthEstimate);
        int objIdWithRedirectionBit = tHandle.isNewObject()? objId : JODBIOBase.addRedirectedObjectModifier(objId);
        transactionFile.writeShort(objIdWithRedirectionBit);
        writeEntryLenForID(objId,0,transactionFile);//reserve space for length
       
        long objectBodyOffset = transactionFile.getCursorOffset();
       
//        if(JODBConfig.DEBUG){
//            _logger.info("Transaction: Object "+rootObject.getClass()+" "+rootObject+" header len ="+headerLen);
//        }
       
        int primaryMask = formPrimaryObjectMask(0, fieldsWithAbsoluteAddr.size()>0, fieldsWithRelativeAddr.size()>0, primitiveFields.size()>0, translated, tHandle, classDescr);;
       
        transactionFile.writeByte(primaryMask);
       
        int secondaryMask = formSecondaryObjectMask(0, tHandle);
       
        transactionFile.writeByte(secondaryMask);
       
        short newCyclicCounter =  (short) (tHandle.getCyclicalVersionCounter()+1);
       
        if(newCyclicCounter == 256){
            newCyclicCounter = 0;
        }
        tHandle.setCyclicalVersionCounter(newCyclicCounter);
       
        transactionFile.writeByte(newCyclicCounter);
       
        tHandle.setTranslatedObjectDataMask((byte) primaryMask);
       
        //
        if(tHandle.generateUID()){
            Random random = new Random();
            transactionFile.writeLong(random.nextLong());
        }
        long time = System.currentTimeMillis();
        if(tHandle.generateCreationTS()){
            transactionFile.writeLong(time);
        }
        if(tHandle.generateModificationTS()){
            transactionFile.writeLong(time);
        }
       
        //
        transactionFile.writeShort(rootObjectClassID);
       
        if(translated){
            int translatedObjectClassID = base.getOrSetClassTypeSubstitutionID(classDescr.getTypes()[0]);// base.getOrSetClassTypeSubstitutionID( objectToPersist.getClass().getName());
            transactionFile.writeShort(translatedObjectClassID);
        }
       
//        transactionFile.writeShort(classTypes.length);
//       
//        for (int i = 0; i < classTypes.length; i++) {
//            int id = base.getOrSetClassTypeSubstitutionID(classTypes[i]);
//            transactionFile.writeShort(id);
//        }

        if(fieldsWithAbsoluteAddr.size()>0){//with absolute offsets
            transactionFile.writeShort(fieldsWithAbsoluteAddr.size());
            for (int i = 0; i < fieldsWithAbsoluteAddr.size(); i++) {//writing links of unchanged objects
                ObjectFieldRecord next = fieldsWithAbsoluteAddr.elementAt(i);
                int id = base.getOrSetFieldSubstitutionID(next._field);
                transactionFile.writeShort(id);
                TransactionHandle valueHandle = transactionObjects.get(next._value);
                transactionFile.writeLong(valueHandle.getHandle().getObjectEntryOffset());
            }
        }
       
        long objectsWithRelativeAddrStartOffsetShift = -1;
        if(fieldsWithRelativeAddr.size()>0){//with relative offsets
            transactionFile.writeShort(fieldsWithRelativeAddr.size());
            objectsWithRelativeAddrStartOffsetShift = transactionFile.getCursorOffset() - objectIDOffset;
            transactionFile.setLength(transactionFile.length()+ fieldsWithRelativeAddr.size()*(2+4));
            transactionFile.seek(transactionFile.length());
        }
       
        if(primitiveFields.size()>0){
            transactionFile.writeShort(primitiveFields.size());
        }
        for (int i = 0; i < primitiveFields.size(); i++) {
            Field next = primitiveFields.elementAt(i);
            int id = base.getOrSetFieldSubstitutionID(next);
            transactionFile.writeShort(id);
            IndexingRecord record = IndexingRecord.findIndexingRecord(id, indexes);
            if(record!=null){
                //ByteBuffer currentlyPersistedValue = record.getPersistedDataBuffer();
                ByteBuffer pendingValue = record.getPendingDataBuffer();
                pendingValue.clear();
                PrimitiveJavaTypesUtil.primitiveToByteBuffer(objectToPersist, next, pendingValue);
                pendingValue.flip();
                transactionFile.getChannel().write(pendingValue);
                pendingValue.rewind();
            }else {
                try {
                    Utils.writePrimitive(objectToPersist, next,transactionFile);
                } catch (Exception e) {
                    throw new JodbIOException(e);
                }
            }           
        }
       
        long arrayDataShift = 0;
        if(classDescr.isArray()){
            int arrayLength = Array.getLength(objectToPersist);
            transactionFile.writeInt(arrayLength);
            transactionFile.writeByte(arrayElementSize);//write length of each element in array
            arrayDataShift = transactionFile.getCursorOffset() - objectIDOffset;
            boolean primitive = classDescr.isPrimitiveArray();
            if(primitive){//completely write primitive array
                try {
                    Utils.writePrimitiveArray(objectToPersist, classDescr.getArrayType(), 0, arrayLength, transactionFile);
                } catch (Exception e) {
                    _logger.log(Level.SEVERE,"",e);
                    throw new JodbIOException(e);
                }
            }else{//reserve space for references
                long spaceToReserve = arrayElementSize*arrayLength;
                long slotMasksTotal = arrayLength/8;
                if( slotMasksTotal*8 != arrayLength){
                    slotMasksTotal++;//trailing mask entry for slot <8
                }
                spaceToReserve+=slotMasksTotal;
                if(transactionFile.getCursorOffset() + spaceToReserve > transactionFile.length()  ){
                    transactionFile.setLength(transactionFile.getCursorOffset() + spaceToReserve );
                }
                transactionFile.skip(spaceToReserve);
               
            }
        }
       
        if(JODBConfig.DEBUG){
            _logger.info(" <<< Transaction: Object "+rootObject.getClass()+" "+rootObject+" end offset ="+transactionFile.getCursorOffset());
        }
       
        long objectEndOffset = transactionFile.getCursorOffset();
        long objectBodyLength = objectEndOffset - objectBodyOffset;
        if(lengthEstimate < objectBodyLength){
            throw new JodbIOException("Object length estimate error");
        }
        //long targetObjectBodyLength = objectBodyLength;
       
        if(!tHandle.isNewObject()){
            DataContainersCache dataContainersCache = TransactionUtils.getObjectDataContainerCache();
            ObjectDataContainer existingObjectHeaderData =  dataContainersCache.pullObjectDataContainer();// tContainer.getTempObjectDataContainer();

            //ioTicket.getRandomAccessBuffer().seek(tHandle.getHandle().getObjectEntryOffset());
            //JODBIOUtils.readObjectHeader(ioTicket, existingObjectHeaderData, false);
            existingObjectHeaderData.readHeader(ioTicket.getRandomAccessBuffer(),tHandle.getHandle().getObjectEntryOffset(), false);
            tHandle.setTransactionOffset(existingObjectHeaderData.getOffset());//JODBIOUtils.addAbsoluteOffsetIdentifierBit(existingObjectHeaderData.getOffset()));//if object already existed than alvays point to initial object position
            long redirectorOffset = existingObjectHeaderData.isRedirection()?existingObjectHeaderData.getOffset():-1;
            if(objectBodyLength > existingObjectHeaderData.getBodyLength() || fieldsWithRelativeAddr.size() > 0 ){
                if( existingObjectHeaderData.isRedirection() ){
                    //redirection entry space is too small, let see what is under redirection offset
                    //ioTicket.getRandomAccessBuffer().seek(existingObjectHeaderData.getRedirectionOffset());
                    long existingObjectRedirectionOffset = existingObjectHeaderData.getRedirectionOffset();
                    existingObjectHeaderData.reset();
                    existingObjectHeaderData.readHeader(ioTicket.getRandomAccessBuffer(), existingObjectRedirectionOffset, true);
                    //JODBIOUtils.readObjectHeader(ioTicket, existingObjectHeaderData, true);
                }
            }

            if(objectBodyLength <= existingObjectHeaderData.getBodyLength() && fieldsWithRelativeAddr.size() == 0){
                boolean isRedirection = existingObjectHeaderData.isRedirection();
                long redirectionOffset = existingObjectHeaderData.getRedirectionOffset();
                //long targetObjectBodyLength = existingObjectHeaderData.getBodyLength();//length for new object's header
               
                //object id(length bits) may change as we fit to maybe bigger space
                objId = JODBIOBase.ENTRY_OBJECT_ID | existingObjectHeaderData.getLengthModifierFromID();// composeEntryID( JODBIOBase.ENTRY_OBJECT_ID, targetObjectBodyLength);
               
                if(existingObjectHeaderData.isRedirectedObject()){
                    objIdWithRedirectionBit = JODBIOBase.addRedirectedObjectModifier(objId);//this is redirected entry
                }else{
                    objIdWithRedirectionBit = objId;
                }
               
                if( isRedirection ){//if we fit into redirection record than delete record under redirection offset
                    deleteObject(ioTicket, session, redirectionOffset, tContainer);//delete/backup record under redirection offset
                }
               
               
                //object can fit to old spot, write it to replacements file insteard of transaction file
                IRandomAccessDataBuffer replacementsFile = tContainer.getTransactionReplacementsDataFile();
                replacementsFile.resetToEnd();
                replacementsFile.writeByte(TRANSACTION_REPLACEMENT_ENTRY_TYPE_STATIC);
                replacementsFile.writeLong(existingObjectHeaderData.getOffset());
                long replacementLengthEntryOffset = replacementsFile.getCursorOffset();
                replacementsFile.writeLong(0);//reserve space for replacement length entry. //TODO skip faster?
                long newObjectIDOffset = replacementsFile.getCursorOffset();
               
                replacementsFile.writeShort(objIdWithRedirectionBit);//write new ID to replacements file
                writeEntryLenForID(objId,objectBodyLength,replacementsFile);//write length of replacements file, could be bigger than actual object's data occupies
               
                long newObjectBodyOffset = replacementsFile.getCursorOffset();
                //return to write actual length of replacement entry
                replacementsFile.seek(replacementLengthEntryOffset);
                replacementsFile.writeLong(newObjectBodyOffset-newObjectIDOffset+objectBodyLength);
                replacementsFile.seek(newObjectBodyOffset);//back to the header end
               
                transactionFile.transferTo(objectBodyOffset, objectBodyLength, replacementsFile.getChannel());
                transactionFile.seek(objectIDOffset);//return position in transaction file to the start of object(like it wasn't here)
                transactionFile.setLength(objectIDOffset);//truncate "new data" file
                objectIDOffset = newObjectIDOffset;//this now offset in replacements file
                objectBodyOffset = newObjectBodyOffset;
                objectBodyLength = existingObjectHeaderData.getBodyLength();//length for new object
                transactionFile = replacementsFile;
                replacementsFile.resetToEnd();//replacements file to the end
                objectEndOffset = replacementsFile.getCursorOffset();
            }else{
                if(redirectorOffset!=-1){
                    //this is record under redirection offset
                    deleteObject(ioTicket, session, existingObjectHeaderData.getOffset(), tContainer);
                }
                IRandomAccessDataBuffer replacementsFile = tContainer.getTransactionReplacementsDataFile();
                long offset = tHandle.getHandle().getObjectEntryOffset();//offset of record that will be replaced with redirector
                //backupObject(ioTicket, offset, tContainer);
                //write redirector entry with relative offset
                replacementsFile.writeByte(TRANSACTION_REPLACEMENT_ENTRY_TYPE_REDIRECTOR);
                replacementsFile.writeLong(offset);
                replacementsFile.writeLong(objectIDOffset);//relative offset in new data transaction file
            }
            dataContainersCache.pushObjectDataContainer(existingObjectHeaderData);
        }
       
        for (int i = 0; i < fieldsWithRelativeAddr.size(); i++) {
            ObjectFieldRecord next = fieldsWithRelativeAddr.elementAt(i);
            next._offset = assembleTransactionDataForObject(context, next._value , tContainer);
//            if(JODBIOUtils.isAbsoluteOffset(next._offset)){
//                throw new IOException("internal transaction error");
//            }
        }
       
        if(classDescr.isArray() && !classDescr.isPrimitiveArray()){
            int arrayLength = Array.getLength(objectToPersist);
            for (int i = 0; i < arrayLength; i++) {
                Object value = Array.get(objectToPersist, i);
                if (value == null) {
                    continue;
                }
                TransactionHandle transactionHandle = transactionObjects.get(value);
                if(transactionHandle == null || transactionHandle.isTranslated()){
                    continue;
                }
                if(transactionHandle.isNewObject()){//only write new objects as we need relative offset, the offset for existing objects already known
                    assembleTransactionDataForObject(context, value , tContainer);
                }
            }
        }
       
        transactionFile.seek(objectIDOffset+2);
       
        writeEntryLenForID(objId,objectBodyLength,transactionFile);//TODO make sure the same size estimate is used

       
        if(fieldsWithRelativeAddr.size() > 0){
            transactionFile.seek(objectIDOffset+objectsWithRelativeAddrStartOffsetShift);
//            if(JODBConfig.DEBUG){
//                _logger.info("Transaction: Object "+rootObject.getClass()+" "+rootObject+" fields with relative pos ="+transactionFile.getCursorOffset());
//            }
        }
        for (int i = 0; i < fieldsWithRelativeAddr.size(); i++) {
            ObjectFieldRecord next = fieldsWithRelativeAddr.elementAt(i);
            int id = base.getOrSetFieldSubstitutionID(next._field);
            transactionFile.writeShort(id);
            transactionFile.writeInt((int)(next._offset - transactionFile.getCursorOffset()-4));//4 bytes to assume the position after offset entry
        }
       
       
        if(classDescr.isArray() && !classDescr.isPrimitiveArray()){
            transactionFile.seek(objectIDOffset+arrayDataShift);
            int arrayLength = Array.getLength(objectToPersist);
            int slotMask = 0;
            int slotCounter = 0;
            for (int i = 0; i < arrayLength; i++, slotCounter++) {
                if(slotCounter == 8){
                    transactionFile.writeByte(slotMask);
                    slotCounter = 0;
                    slotMask = 0;
                }
                Object value = Array.get(objectToPersist, i);
                TransactionHandle cellValueTransactionHandle;
                if (value == null || (cellValueTransactionHandle = transactionObjects.get(value))==null) {
                    //transaction handle can be null only if this object is last in depth
                    if (arrayElementSize == 4) {
                        transactionFile.writeInt(0);
                    } else {
                        transactionFile.writeLong(0);
                    }
                    continue;
                }
                if(cellValueTransactionHandle.isNewObject()&!cellValueTransactionHandle.isTranslated()){
                    throw new IOException();
                }
                long offset;
                boolean absoluteOffset;
                if(!cellValueTransactionHandle.isNewObject()){
                    offset = cellValueTransactionHandle.getHandle().getObjectEntryOffset();
                    absoluteOffset = true;
                    if(offset > MAX_ABSOLUTE_SHORT_ADDR && arrayElementSize == 4){
                        if(tHandle.isNewObject()){
                            //try relative address
                            if(context.getTransactionOffset() - offset > TRANSACTION_SIZE_LIMIT){
                                throw new JodbIOException("Illegal array size estimation TRANSACTION_SIZE_LIMIT");
                            }
                            offset = offset - (context.getTransactionOffset()+transactionFile.getCursorOffset()+ arrayElementSize);
                            absoluteOffset = false;
                        }else{
                            throw new JodbIOException("Illegal array size estimation");
                        }
                    }
                }else{
                    if(tHandle.isNewObject()){//definitely relative offset
                        offset = cellValueTransactionHandle.getTransactionOffset() - (transactionFile.getCursorOffset()+arrayElementSize);
                        absoluteOffset = false;
                    }else{//only absolute addr from here
                        offset = cellValueTransactionHandle.getTransactionOffset();
                        if(arrayElementSize == 4 && offset > MAX_ABSOLUTE_SHORT_ADDR - TRANSACTION_SIZE_LIMIT ){
                            throw new JodbIOException("Illegal array size estimation > MAX_ABSOLUTE_SHORT_ADDR - TRANSACTION_SIZE_LIMIT");
                        }
                        offset+=context.getTransactionOffset();
                        absoluteOffset = true;
                    }
                   
                }
                if (arrayElementSize == 4) {
                    transactionFile.writeInt((int) offset);
                } else {
                    transactionFile.writeLong(offset);
                }
                if(absoluteOffset){    
                    slotMask|=1<<slotCounter;//absolute address
                }
            }
            if(slotCounter != 0){//write trailing mask entry
                transactionFile.writeByte(slotMask);
                slotCounter = 0;
                slotMask = 0;
            }
        }
        if(session.getBase().isRemote()){//calc hash identity in Client/Server mode
            transactionFile.seek(objectIDOffset);
            int hash = Utils.oathash(transactionFile, objectEndOffset - objectIDOffset);
            tHandle.setIdentityHash(hash);
        }
       
        transactionFile.seek(objectEndOffset);
        return tHandle.getTransactionOffset();
    }
   
    private static void deleteObject(IOTicket ioTicket, JODBSession session, TransactionHandle transactionHandle, TransactionContainer tContainer) throws IOException{
        PersistentObjectHandle handle = transactionHandle.getHandle();
        long persistentObjectOffset = handle.getObjectEntryOffset();
        transactionHandle.setTransactionOffset(0);
        deleteObject(ioTicket, session, persistentObjectOffset, tContainer);
    }
   
    private static void deleteObject(IOTicket ioTicket, JODBSession session, long persistentObjectOffset, TransactionContainer tContainer) throws IOException{
        //ioTicket.getRandomAccessBuffer().seek(persistentObjectOffset);
        IRandomAccessDataBuffer replacementsFile =  tContainer.getTransactionReplacementsDataFile();
        DataContainersCache dataContainersCache = TransactionUtils.getObjectDataContainerCache();
        ObjectDataContainer container = dataContainersCache.pullObjectDataContainer();
        //JODBIOUtils.readObjectHeader(ioTicket, container, false);
        container.readHeader(ioTicket.getRandomAccessBuffer(),persistentObjectOffset, false);
        replacementsFile.writeByte(TRANSACTION_REPLACEMENT_ENTRY_TYPE_STATIC);
        replacementsFile.writeLong(persistentObjectOffset);//write offset of object to replace
        replacementsFile.writeLong(0);//reserving space for entry's length
        long entryOffsetStart = replacementsFile.getCursorOffset();
        TransactionUtils.writeEmptyObjectEntry(replacementsFile , container.getBodyLength());
        long entryEnd = replacementsFile.getCursorOffset();
        long entryLength = entryEnd - entryOffsetStart;
        replacementsFile.seek(entryOffsetStart-8);//go back to entry's length
        replacementsFile.writeLong(entryLength);
        if(container.isRedirection()){
            persistentObjectOffset = container.getOffset();
            deleteObject(ioTicket, session, persistentObjectOffset, tContainer);
        }
        dataContainersCache.pushObjectDataContainer(container);
        replacementsFile.seek(entryEnd);
    }

   
    private static boolean checkActiveObjectUnchanged(IClassProcessor processor, JODBOperationContext context, Object obj, TransactionHandle tHandle, ObjectDataContainer persistentCopyObjectDataContainer, Vector<IndexingRecord> indexes) throws IOException, IllegalClassTypeException{
        //TODO add mask verification
        if(tHandle.isUnchanged()){
            return true;
        }
        if(tHandle.isNewObject()){
            return false;
        }
       
        FieldsIterator fieldsIterator = persistentCopyObjectDataContainer.readObject(context, tHandle.getHandle().getObjectEntryOffset(), true, indexes);
        tHandle.setCyclicalVersionCounter(persistentCopyObjectDataContainer.getCyclicVersionCounter());
        if (fieldsIterator == null) {
            return false;
        }
        return processor.equals(obj, persistentCopyObjectDataContainer, context, null);
 
    }
   
   
   
    private static class ByteHolder{
        public byte _value;
       
        public ByteHolder() {
        }

        /**
         * @param value
         */
        public ByteHolder(byte value) {
            super();
            _value = value;
        }
       
    }
   
    private static class ObjectFieldRecord{
        public Field _field;
        public Object _value;
        public long _offset;
       
        public ObjectFieldRecord(Field field, Object value) {
            super();
            _field = field;
            _value = value;
        }
       
    }
}
TOP

Related Classes of com.mobixess.jodb.core.transaction.TransactionAssembler$ByteHolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.