/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.internal; import com.db4o.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.activation.*; import com.db4o.internal.btree.*; import com.db4o.internal.delete.*; import com.db4o.internal.handlers.*; import com.db4o.internal.marshall.*; import com.db4o.internal.slots.*; import com.db4o.marshall.*; /** * @exclude */ public class UUIDFieldMetadata extends VirtualFieldMetadata { UUIDFieldMetadata() { super(Handlers4.LONG_ID, new LongHandler()); setName(Const4.VIRTUAL_FIELD_PREFIX + "uuid"); } public void addFieldIndex(ObjectIdContextImpl context) throws FieldIndexException{ LocalTransaction transaction = (LocalTransaction) context.transaction(); LocalObjectContainer localContainer = (LocalObjectContainer)transaction.container(); Slot oldSlot = transaction.idSystem().committedSlot(context.objectId()); int savedOffset = context.offset(); int db4oDatabaseIdentityID = context.readInt(); long uuid = context.readLong(); context.seek(savedOffset); boolean isnew = (oldSlot.isNull()); if ((uuid == 0 || db4oDatabaseIdentityID == 0) && context.objectId() > 0 && !isnew) { DatabaseIdentityIDAndUUID identityAndUUID = readDatabaseIdentityIDAndUUID( localContainer, context.classMetadata(), oldSlot, false); db4oDatabaseIdentityID = identityAndUUID.databaseIdentityID; uuid = identityAndUUID.uuid; } if(db4oDatabaseIdentityID == 0){ db4oDatabaseIdentityID = localContainer.identity().getID(transaction); } if(uuid == 0){ uuid = localContainer.generateTimeStampId(); } StatefulBuffer writer = (StatefulBuffer) context.buffer(); writer.writeInt(db4oDatabaseIdentityID); writer.writeLong(uuid); if(isnew){ addIndexEntry(writer, new Long(uuid)); } } static class DatabaseIdentityIDAndUUID { public int databaseIdentityID; public long uuid; public DatabaseIdentityIDAndUUID(int databaseIdentityID_, long uuid_) { databaseIdentityID = databaseIdentityID_; uuid = uuid_; } } private DatabaseIdentityIDAndUUID readDatabaseIdentityIDAndUUID(ObjectContainerBase container, ClassMetadata classMetadata, Slot oldSlot, boolean checkClass) throws Db4oIOException { if(DTrace.enabled){ DTrace.REREAD_OLD_UUID.logLength(oldSlot.address(), oldSlot.length()); } ByteArrayBuffer reader = container.decryptedBufferByAddress(oldSlot.address(), oldSlot.length()); if(checkClass){ ClassMetadata realClass = ClassMetadata.readClass(container,reader); if(realClass != classMetadata){ return null; } } if (classMetadata.seekToField(container.transaction(), reader, this) == HandlerVersion.INVALID ) { return null; } return new DatabaseIdentityIDAndUUID(reader.readInt(), reader.readLong()); } public void delete(DeleteContextImpl context, boolean isUpdate){ if(isUpdate){ context.seek(context.offset() + linkLength(context)); return; } context.seek(context.offset() + Const4.INT_LENGTH); long longPart = context.readLong(); if(longPart > 0){ if (context.container().maintainsIndices()){ removeIndexEntry(context.transaction(), context.objectId(), new Long(longPart)); } } } public boolean hasIndex() { return true; } public BTree getIndex(Transaction transaction) { ensureIndex(transaction); return super.getIndex(transaction); } protected void rebuildIndexForObject(LocalObjectContainer container, ClassMetadata classMetadata, int objectId) throws FieldIndexException { Slot slot = container.systemTransaction().idSystem().currentSlot(objectId); DatabaseIdentityIDAndUUID data = readDatabaseIdentityIDAndUUID(container, classMetadata, slot, true); if (null == data) { return; } addIndexEntry(container.localSystemTransaction(), objectId, new Long( data.uuid)); } private void ensureIndex(Transaction transaction) { if (null == transaction) { throw new ArgumentNullException(); } if (null != super.getIndex(transaction)) { return; } LocalObjectContainer file = ((LocalObjectContainer)transaction.container()); SystemData sd = file.systemData(); if(sd == null){ // too early, in new file, try again later. return; } initIndex(transaction, sd.uuidIndexId()); if (sd.uuidIndexId() == 0) { sd.uuidIndexId(super.getIndex(transaction).getID()); file.getFileHeader().writeVariablePart(file); } } void instantiate1(ObjectReferenceContext context) { int dbID = context.readInt(); Transaction trans = context.transaction(); ObjectContainerBase container = trans.container(); container.showInternalClasses(true); try { Db4oDatabase db = (Db4oDatabase)container.getByID2(trans, dbID); if(db != null && db.i_signature == null){ container.activate(trans, db, new FixedActivationDepth(2)); } VirtualAttributes va = context.objectReference().virtualAttributes(); va.i_database = db; va.i_uuid = context.readLong(); } finally { container.showInternalClasses(false); } } public int linkLength(HandlerVersionContext context) { return Const4.LONG_LENGTH + Const4.ID_LENGTH; } void marshall(Transaction trans, ObjectReference ref, WriteBuffer buffer, boolean isMigrating, boolean isNew) { VirtualAttributes attr = ref.virtualAttributes(); ObjectContainerBase container = trans.container(); boolean doAddIndexEntry = isNew && container.maintainsIndices(); int dbID = 0; boolean linkToDatabase = (attr != null && attr.i_database == null) ? true : ! isMigrating; if(linkToDatabase){ Db4oDatabase db = ((InternalObjectContainer)container).identity(); if(db == null){ // can happen on early classes like Metaxxx, no problem attr = null; }else{ if (attr.i_database == null) { attr.i_database = db; // TODO: Should be check for ! client instead of instanceof if (container instanceof LocalObjectContainer){ attr.i_uuid = container.generateTimeStampId(); doAddIndexEntry = true; } } db = attr.i_database; if(db != null) { dbID = db.getID(trans); } } }else{ if(attr != null){ dbID = attr.i_database.getID(trans); } } buffer.writeInt(dbID); if(attr == null){ buffer.writeLong(0); return; } buffer.writeLong(attr.i_uuid); if(doAddIndexEntry){ addIndexEntry(trans, ref.getID(), new Long(attr.i_uuid)); } } void marshallIgnore(WriteBuffer buffer) { buffer.writeInt(0); buffer.writeLong(0); } public final HardObjectReference getHardObjectReferenceBySignature(final Transaction transaction, final long longPart, final byte[] signature) { final BTreeRange range = search(transaction, new Long(longPart)); final Iterator4 keys = range.keys(); while (keys.moveNext()) { final FieldIndexKey current = (FieldIndexKey) keys.current(); final HardObjectReference hardRef = getHardObjectReferenceById(transaction, current.parentID(), signature); if (null != hardRef) { return hardRef; } } return HardObjectReference.INVALID; } protected final HardObjectReference getHardObjectReferenceById(Transaction transaction, int parentId, byte[] signature) { HardObjectReference hardRef = transaction.container().getHardObjectReferenceById(transaction, parentId); if (hardRef._reference == null) { return null; } VirtualAttributes vad = hardRef._reference.virtualAttributes(transaction, false); if (!Arrays4.equals(signature, vad.i_database.i_signature)) { return null; } return hardRef; } public void defragAspect(DefragmentContext context) { // database id context.copyID(); // uuid context.incrementOffset(Const4.LONG_LENGTH); } }