/* 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.ext;
import com.db4o.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.activation.*;
import com.db4o.internal.encoding.*;
import com.db4o.query.*;
import com.db4o.types.*;
/**
* Class to identify a database by it's signature.
* <br><br>db4o UUID handling uses a reference to the Db4oDatabase object, that
* represents the database an object was created on.
*
* @persistent
* @exclude
*/
public class Db4oDatabase implements Db4oType, Internal4{
public static final Db4oDatabase STATIC_IDENTITY = Debug4.staticIdentity ? new Db4oDatabase(new byte[] {(byte)'d' , (byte)'e', (byte)'b', (byte)'u', (byte)'g'}, 1) : null;
public static final int STATIC_ID = -1;
/**
* Field is public for implementation reasons, DO NOT TOUCH!
*/
public byte[] i_signature;
/**
* Field is public for implementation reasons, DO NOT TOUCH!
*
* This field is badly named, it really is the creation time.
*/
// TODO: change to _creationTime with PersistentFormatUpdater
public long i_uuid;
private static final String CREATIONTIME_FIELD = "i_uuid";
/**
* cached ObjectContainer for getting the own ID.
*/
private transient ObjectContainerBase i_stream;
/**
* cached ID, only valid in combination with i_objectContainer
*/
private transient int i_id;
/**
* constructor for persistence
*/
public Db4oDatabase(){
}
/**
* constructor for comparison and to store new ones
*/
public Db4oDatabase(byte[] signature, long creationTime){
// FIXME: make sure signature is null
i_signature = signature;
i_uuid = creationTime;
}
/**
* generates a new Db4oDatabase object with a unique signature.
*/
public static Db4oDatabase generate() {
if(Debug4.staticIdentity){
return STATIC_IDENTITY;
}
StatefulBuffer writer = new StatefulBuffer(null, 300);
new LatinStringIO().write(writer, SignatureGenerator.generateSignature());
return new Db4oDatabase(
writer.getWrittenBytes(),
System.currentTimeMillis());
}
/**
* comparison by signature.
*/
public boolean equals(Object obj) {
if(obj==this) {
return true;
}
if(obj==null||this.getClass()!=obj.getClass()) {
return false;
}
Db4oDatabase other = (Db4oDatabase)obj;
if (null == other.i_signature || null == this.i_signature) {
return false;
}
return Arrays4.equals(other.i_signature, this.i_signature);
}
public int hashCode() {
return i_signature.hashCode();
}
/**
* gets the db4o ID, and may cache it for performance reasons.
*
* @return the db4o ID for the ObjectContainer
*/
public int getID(Transaction trans) {
if(Debug4.staticIdentity){
return STATIC_ID;
}
ObjectContainerBase stream = trans.container();
if(stream != i_stream) {
i_stream = stream;
i_id = bind(trans);
}
return i_id;
}
public long getCreationTime(){
return i_uuid;
}
/**
* returns the unique signature
*/
public byte[] getSignature(){
return i_signature;
}
public String toString(){
return "db " + i_signature;
}
public boolean isOlderThan(Db4oDatabase peer){
if(peer == this)
throw new IllegalArgumentException();
if(i_uuid != peer.i_uuid){
return i_uuid < peer.i_uuid;
}
// the above logic has failed, both are the same
// age but we still want to distinguish in some
// way, to have an order in the ReplicationRecord
// The following is arbitrary, it only needs to
// be repeatable.
// Let's distinguish by signature length
if(i_signature.length != peer.i_signature.length ){
return i_signature.length < peer.i_signature.length;
}
for (int i = 0; i < i_signature.length; i++) {
if(i_signature[i] != peer.i_signature[i]){
return i_signature[i] < peer.i_signature[i];
}
}
// This should never happen.
// FIXME: Add a message and move to Messages.
//
throw new RuntimeException();
}
/**
* make sure this Db4oDatabase is stored. Return the ID.
*/
public int bind(Transaction trans){
ObjectContainerBase stream = trans.container();
Db4oDatabase stored = (Db4oDatabase)stream.db4oTypeStored(trans,this);
if (stored == null) {
return storeAndGetId(trans);
}
if(stored == this){
return stream.getID(trans, this);
}
if(i_uuid == 0){
i_uuid = stored.i_uuid;
}
stream.showInternalClasses(true);
try {
int id = stream.getID(trans, stored);
stream.bind(trans, this, id);
return id;
} finally {
stream.showInternalClasses(false);
}
}
private int storeAndGetId(Transaction trans) {
ObjectContainerBase stream = trans.container();
stream.showInternalClasses(true);
try {
stream.store2(trans,this, stream.updateDepthProvider().forDepth(2), false);
return stream.getID(trans, this);
} finally {
stream.showInternalClasses(false);
}
}
/**
* find a Db4oDatabase with the same signature as this one
*/
public Db4oDatabase query(Transaction trans){
// showInternalClasses(true); has to be set for this method to be successful
if(i_uuid > 0){
// try fast query over uuid (creation time) first
Db4oDatabase res = query(trans, true);
if(res != null){
return res;
}
}
// if not found, try to find with signature
return query(trans, false);
}
private Db4oDatabase query(Transaction trans, boolean constrainByUUID){
ObjectContainerBase stream = trans.container();
Query q = stream.query(trans);
q.constrain(getClass());
if(constrainByUUID){
q.descend(CREATIONTIME_FIELD).constrain(new Long(i_uuid));
}
ObjectSet objectSet = q.execute();
while (objectSet.hasNext()) {
Db4oDatabase storedDatabase = (Db4oDatabase) objectSet.next();
stream.activate(null, storedDatabase, new FixedActivationDepth(4));
if (storedDatabase.equals(this)) {
return storedDatabase;
}
}
return null;
}
}