001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.ext;
022:
023: import com.db4o.*;
024: import com.db4o.foundation.*;
025: import com.db4o.internal.*;
026: import com.db4o.query.*;
027: import com.db4o.types.*;
028:
029: /**
030: * Class to identify a database by it's signature.
031: * <br><br>db4o UUID handling uses a reference to the Db4oDatabase object, that
032: * represents the database an object was created on.
033: *
034: * @persistent
035: * @exclude
036: */
037: public class Db4oDatabase implements Db4oType, Internal4 {
038:
039: public static final Db4oDatabase STATIC_IDENTITY = Debug.staticIdentity ? new Db4oDatabase(
040: new byte[] { (byte) 'd', (byte) 'e', (byte) 'b',
041: (byte) 'u', (byte) 'g' }, 1)
042: : null;
043:
044: public static final int STATIC_ID = -1;
045:
046: /**
047: * Field is public for implementation reasons, DO NOT TOUCH!
048: */
049: public byte[] i_signature;
050:
051: /**
052: * Field is public for implementation reasons, DO NOT TOUCH!
053: *
054: * This field is badly named, it really is the creation time.
055: */
056: // TODO: change to _creationTime with PersistentFormatUpdater
057: public long i_uuid;
058:
059: private static final String CREATIONTIME_FIELD = "i_uuid";
060:
061: /**
062: * cached ObjectContainer for getting the own ID.
063: */
064: private transient ObjectContainerBase i_stream;
065:
066: /**
067: * cached ID, only valid in combination with i_objectContainer
068: */
069: private transient int i_id;
070:
071: /**
072: * constructor for persistence
073: */
074: public Db4oDatabase() {
075: }
076:
077: /**
078: * constructor for comparison and to store new ones
079: */
080: public Db4oDatabase(byte[] signature, long creationTime) {
081: // FIXME: make sure signature is null
082: i_signature = signature;
083: i_uuid = creationTime;
084: }
085:
086: /**
087: * generates a new Db4oDatabase object with a unique signature.
088: */
089: public static Db4oDatabase generate() {
090: if (Debug.staticIdentity) {
091: return STATIC_IDENTITY;
092: }
093: return new Db4oDatabase(Unobfuscated.generateSignature(),
094: System.currentTimeMillis());
095: }
096:
097: /**
098: * comparison by signature.
099: */
100: public boolean equals(Object obj) {
101: if (obj == this ) {
102: return true;
103: }
104: if (obj == null || this .getClass() != obj.getClass()) {
105: return false;
106: }
107: Db4oDatabase other = (Db4oDatabase) obj;
108: if (null == other.i_signature || null == this .i_signature) {
109: return false;
110: }
111: return Arrays4.areEqual(other.i_signature, this .i_signature);
112: }
113:
114: public int hashCode() {
115: return i_signature.hashCode();
116: }
117:
118: /**
119: * gets the db4o ID, and may cache it for performance reasons.
120: *
121: * @return the db4o ID for the ObjectContainer
122: */
123: public int getID(Transaction trans) {
124: if (Debug.staticIdentity) {
125: return STATIC_ID;
126: }
127: ObjectContainerBase stream = trans.container();
128: if (stream != i_stream) {
129: i_stream = stream;
130: i_id = bind(trans);
131: }
132: return i_id;
133: }
134:
135: public long getCreationTime() {
136: return i_uuid;
137: }
138:
139: /**
140: * returns the unique signature
141: */
142: public byte[] getSignature() {
143: return i_signature;
144: }
145:
146: public String toString() {
147: return "db " + i_signature;
148: }
149:
150: public boolean isOlderThan(Db4oDatabase peer) {
151:
152: if (peer == this )
153: throw new IllegalArgumentException();
154:
155: if (i_uuid != peer.i_uuid) {
156: return i_uuid < peer.i_uuid;
157: }
158:
159: // the above logic has failed, both are the same
160: // age but we still want to distinguish in some
161: // way, to have an order in the ReplicationRecord
162:
163: // The following is arbitrary, it only needs to
164: // be repeatable.
165:
166: // Let's distinguish by signature length
167:
168: if (i_signature.length != peer.i_signature.length) {
169: return i_signature.length < peer.i_signature.length;
170: }
171:
172: for (int i = 0; i < i_signature.length; i++) {
173: if (i_signature[i] != peer.i_signature[i]) {
174: return i_signature[i] < peer.i_signature[i];
175: }
176: }
177:
178: // This should never happen.
179:
180: // FIXME: Add a message and move to Messages.
181: //
182: throw new RuntimeException();
183: }
184:
185: /**
186: * make sure this Db4oDatabase is stored. Return the ID.
187: */
188: public int bind(Transaction trans) {
189: ObjectContainerBase stream = trans.container();
190: Db4oDatabase stored = (Db4oDatabase) stream.db4oTypeStored(
191: trans, this );
192: if (stored == null) {
193: return storeAndGetId(trans);
194: }
195: if (stored == this ) {
196: return stream.getID(trans, this );
197: }
198: if (i_uuid == 0) {
199: i_uuid = stored.i_uuid;
200: }
201: stream.showInternalClasses(true);
202: try {
203: int id = stream.getID(trans, stored);
204: stream.bind(trans, this , id);
205: return id;
206: } finally {
207: stream.showInternalClasses(false);
208: }
209: }
210:
211: private int storeAndGetId(Transaction trans) {
212: ObjectContainerBase stream = trans.container();
213: stream.showInternalClasses(true);
214: try {
215: stream.set3(trans, this , 2, false);
216: return stream.getID(trans, this );
217: } finally {
218: stream.showInternalClasses(false);
219: }
220: }
221:
222: /**
223: * find a Db4oDatabase with the same signature as this one
224: */
225: public Db4oDatabase query(Transaction trans) {
226: // showInternalClasses(true); has to be set for this method to be successful
227: if (i_uuid > 0) {
228: // try fast query over uuid (creation time) first
229: Db4oDatabase res = query(trans, true);
230: if (res != null) {
231: return res;
232: }
233: }
234: // if not found, try to find with signature
235: return query(trans, false);
236: }
237:
238: private Db4oDatabase query(Transaction trans,
239: boolean constrainByUUID) {
240: ObjectContainerBase stream = trans.container();
241: Query q = stream.query(trans);
242: q.constrain(getClass());
243: if (constrainByUUID) {
244: q.descend(CREATIONTIME_FIELD).constrain(new Long(i_uuid));
245: }
246: ObjectSet objectSet = q.execute();
247: while (objectSet.hasNext()) {
248: Db4oDatabase storedDatabase = (Db4oDatabase) objectSet
249: .next();
250: stream.activate(null, storedDatabase, 4);
251: if (storedDatabase.equals(this)) {
252: return storedDatabase;
253: }
254: }
255: return null;
256: }
257:
258: }
|