/* 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.events.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.btree.*;
import com.db4o.internal.handlers.*;
import com.db4o.marshall.*;
public class CommitTimestampSupport {
private BTree _idToTimestamp;
private BTree _timestampToId;
private final LocalObjectContainer _container;
public CommitTimestampSupport(LocalObjectContainer container) {
_container = container;
}
public void ensureInitialized() {
if(_idToTimestamp != null){
return;
}
if (! _container.config().generateCommitTimestamps().definiteYes()) {
return;
}
initialize();
}
public BTree idToTimestamp() {
if(_idToTimestamp != null){
return _idToTimestamp;
}
ensureInitialized();
return _idToTimestamp;
}
public BTree timestampToId() {
if(_timestampToId != null){
return _timestampToId;
}
ensureInitialized();
return _timestampToId;
}
private void initialize() {
int idToTimestampIndexId = _container.systemData().idToTimestampIndexId();
int timestampToIdIndexId = _container.systemData().timestampToIdIndexId();
if(_container.config().isReadOnly()){
if(idToTimestampIndexId == 0){
return;
}
}
_idToTimestamp = new BTree(_container.systemTransaction(), idToTimestampIndexId, new TimestampEntryById());
_timestampToId = new BTree(_container.systemTransaction(), timestampToIdIndexId, new IdEntryByTimestamp());
if (idToTimestampIndexId != _idToTimestamp.getID()) {
storeBtreesIds();
}
EventRegistryFactory.forObjectContainer(_container).committing().addListener(new EventListener4<CommitEventArgs>() {
public void onEvent(Event4<CommitEventArgs> e, CommitEventArgs args) {
LocalTransaction trans = (LocalTransaction) args.transaction();
long transactionTimestamp = trans.timestamp();
long commitTimestamp =
(transactionTimestamp > 0) ? transactionTimestamp :_container.generateTimeStampId();
Transaction sysTrans = trans.systemTransaction();
addTimestamp(sysTrans, args.added().iterator(), commitTimestamp);
addTimestamp(sysTrans, args.updated().iterator(), commitTimestamp);
addTimestamp(sysTrans, args.deleted().iterator(), 0);
}
private void addTimestamp(Transaction trans, Iterator4 it, long commitTimestamp) {
while (it.moveNext()) {
ObjectInfo objInfo = (ObjectInfo) it.current();
TimestampEntry te = new TimestampEntry((int) objInfo.getInternalID(), commitTimestamp);
TimestampEntry oldEntry = (TimestampEntry) _idToTimestamp.remove(trans, te);
if(oldEntry != null){
_timestampToId.remove(trans, oldEntry);
}
if (commitTimestamp != 0) {
_idToTimestamp.add(trans, te);
_timestampToId.add(trans, te);
}
}
}
});
}
private void storeBtreesIds() {
_container.systemData().idToTimestampIndexId(_idToTimestamp.getID());
_container.systemData().timestampToIdIndexId(_timestampToId.getID());
_container.getFileHeader().writeVariablePart(_container);
}
public static class TimestampEntry implements FieldIndexKey {
public final int objectId;
public final long commitTimestamp;
@Override
public String toString() {
return "TimestampEntry [objectId=" + objectId + ", commitTimestamp=" + commitTimestamp + "]";
}
public TimestampEntry(int objectId, long commitTimestamp) {
this.objectId = objectId;
this.commitTimestamp = commitTimestamp;
}
public int parentID() {
return objectId;
}
public long getCommitTimestamp() {
return commitTimestamp;
}
public Object value() {
return commitTimestamp;
}
}
private static class TimestampEntryById implements Indexable4<TimestampEntry> {
public PreparedComparison prepareComparison(Context context, final TimestampEntry first) {
return new PreparedComparison<TimestampEntry>() {
public int compareTo(TimestampEntry second) {
return IntHandler.compare(first.objectId, second.objectId);
}
};
}
public int linkLength() {
return Const4.INT_LENGTH + Const4.LONG_LENGTH;
}
public TimestampEntry readIndexEntry(Context context, ByteArrayBuffer reader) {
return new TimestampEntry(reader.readInt(), reader.readLong());
}
public void writeIndexEntry(Context context, ByteArrayBuffer writer, TimestampEntry obj) {
writer.writeInt(obj.parentID());
writer.writeLong(obj.getCommitTimestamp());
}
public void defragIndexEntry(DefragmentContextImpl context) {
// we are storing ids in the btree, so the order will change when the ids change
// to properly defrag the btree we need to readd all the entries
throw new UnsupportedOperationException();
}
}
private static final class IdEntryByTimestamp extends TimestampEntryById {
public PreparedComparison prepareComparison(Context context, final TimestampEntry first) {
return new PreparedComparison<TimestampEntry>() {
public int compareTo(TimestampEntry second) {
int result = LongHandler.compare(first.commitTimestamp, second.commitTimestamp);
if(result != 0){
return result;
}
return IntHandler.compare(first.objectId, second.objectId);
}
};
}
}
public long versionForId(int id) {
if (idToTimestamp() == null || id == 0) {
return 0;
}
TimestampEntry te = (TimestampEntry) idToTimestamp().search(_container.systemTransaction(), new TimestampEntry(id, 0));
if (te == null) {
return 0;
}
return te.getCommitTimestamp();
}
public void put(Transaction trans, int objectId, long version) {
TimestampEntry te = new TimestampEntry(objectId, version);
idToTimestamp().add(trans, te);
timestampToId().add(trans, te);
}
}