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){