/* 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.consistency;
import java.util.*;
import com.db4o.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.btree.*;
import com.db4o.internal.classindex.*;
import com.db4o.internal.ids.*;
import com.db4o.internal.slots.*;
public class ConsistencyChecker {
private final List<SlotDetail> _bogusSlots = new ArrayList<SlotDetail>();
private final LocalObjectContainer _db;
private final OverlapMap _overlaps;
public static void main(String[] args) {
EmbeddedObjectContainer db = Db4oEmbedded.openFile(args[0]);
try {
System.out.println(new ConsistencyChecker(db).checkSlotConsistency());
}
finally {
db.close();
}
}
public ConsistencyChecker(ObjectContainer db) {
_db = (LocalObjectContainer) db;
_overlaps = new OverlapMap(_db.blockConverter());
}
public ConsistencyReport checkSlotConsistency() {
return _db.syncExec(new Closure4<ConsistencyReport>() {
@Override
public ConsistencyReport run() {
mapIdSystem();
mapFreespace();
return new ConsistencyReport(
_bogusSlots,
_overlaps,
checkClassIndices(),
checkFieldIndices());
}
});
}
private List<Pair<String,Integer>> checkClassIndices() {
final List<Pair<String,Integer>> invalidIds = new ArrayList<Pair<String,Integer>>();
final IdSystem idSystem= _db.idSystem();
if(!(idSystem instanceof BTreeIdSystem)) {
return invalidIds;
}
ClassMetadataIterator clazzIter = _db.classCollection().iterator();
while(clazzIter.moveNext()) {
final ClassMetadata clazz = clazzIter.currentClass();
if(!clazz.hasClassIndex()) {
continue;
}
BTreeClassIndexStrategy index = (BTreeClassIndexStrategy) clazz.index();
index.traverseIds(_db.systemTransaction(), new Visitor4<Integer>() {
public void visit(Integer id) {
if(!idIsValid(id)) {
invalidIds.add(new Pair(clazz.getName(), id));
}
}
});
}
return invalidIds;
}
private List<Pair<String, Integer>> checkFieldIndices() {
final List<Pair<String,Integer>> invalidIds = new ArrayList<Pair<String,Integer>>();
ClassMetadataIterator clazzIter = _db.classCollection().iterator();
while(clazzIter.moveNext()) {
final ClassMetadata clazz = clazzIter.currentClass();
clazz.traverseDeclaredFields(new Procedure4<FieldMetadata>() {
public void apply(final FieldMetadata field) {
if(!field.hasIndex()) {
return;
}
BTree fieldIndex = field.getIndex(_db.systemTransaction());
fieldIndex.traverseKeys(_db.systemTransaction(), new Visitor4<FieldIndexKey>() {
public void visit(FieldIndexKey fieldIndexKey) {
int parentID = fieldIndexKey.parentID();
if(!idIsValid(parentID)) {
invalidIds.add(new Pair<String, Integer>(clazz.getName() + "#" + field.getName(), parentID));
}
}
});
}
});
}
return invalidIds;
}
private boolean idIsValid(int id) {
try {
return !Slot.isNull(_db.idSystem().committedSlot(id));
}
catch(InvalidIDException exc) {
return false;
}
}
private void mapFreespace() {
_db.freespaceManager().traverse(new Visitor4<Slot>() {
public void visit(Slot slot) {
FreespaceSlotDetail detail = new FreespaceSlotDetail(slot);
if(isBogusSlot(slot.address(), slot.length())) {
_bogusSlots.add(detail);
}
_overlaps.add(detail);
}
});
}
private void mapIdSystem() {
IdSystem idSystem= _db.idSystem();
if(!(idSystem instanceof BTreeIdSystem)) {
System.err.println("No btree id system found - not mapping ids.");
return;
}
((BTreeIdSystem)idSystem).traverseIds(new Visitor4<IdSlotMapping>() {
public void visit(IdSlotMapping mapping) {
SlotDetail detail = new IdObjectSlotDetail(mapping._id, mapping.slot());
if(isBogusSlot(mapping._address, mapping._length)) {
_bogusSlots.add(detail);
}
if(mapping._address > 0) {
_overlaps.add(detail);
}
}
});
idSystem.traverseOwnSlots(new Procedure4<Pair<Integer, Slot>>() {
@Override
public void apply(Pair<Integer, Slot> idSlot) {
int id = idSlot.first;
Slot slot = idSlot.second;
SlotDetail detail = id > 0 ? (SlotDetail)new IdObjectSlotDetail(id, slot) : (SlotDetail)new RawObjectSlotDetail(slot);
if(isBogusSlot(idSlot.second.address(), idSlot.second.length())) {
_bogusSlots.add(detail);
}
_overlaps.add(detail);
}
});
}
private boolean isBogusSlot(int address, int length) {
return address < 0 || (long)address + length > _db.fileLength();
}
}