1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.treetank.access;
29
30 import static com.google.common.base.Objects.toStringHelper;
31 import static com.google.common.base.Preconditions.checkArgument;
32 import static com.google.common.base.Preconditions.checkState;
33
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37
38 import org.treetank.access.conf.ConstructorProps;
39 import org.treetank.api.IBucketReadTrx;
40 import org.treetank.api.IData;
41 import org.treetank.api.ISession;
42 import org.treetank.bucket.IConstants;
43 import org.treetank.bucket.IndirectBucket;
44 import org.treetank.bucket.MetaBucket;
45 import org.treetank.bucket.DataBucket;
46 import org.treetank.bucket.DataBucket.DeletedData;
47 import org.treetank.bucket.RevisionRootBucket;
48 import org.treetank.bucket.UberBucket;
49 import org.treetank.bucket.interfaces.IBucket;
50 import org.treetank.bucket.interfaces.IReferenceBucket;
51 import org.treetank.exception.TTException;
52 import org.treetank.exception.TTIOException;
53 import org.treetank.io.IBackendReader;
54 import org.treetank.revisioning.IRevisioning;
55
56 import com.google.common.cache.Cache;
57 import com.google.common.cache.CacheBuilder;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public class BucketReadTrx implements IBucketReadTrx {
73
74
75 private final IBackendReader mBucketReader;
76
77
78 private final UberBucket mUberBucket;
79
80
81 protected final RevisionRootBucket mRootBucket;
82
83
84 protected final MetaBucket mMetaBucket;
85
86
87 protected final ISession mSession;
88
89
90 private boolean mClose;
91
92
93 protected final Cache<Long, DataBucket> mCache;
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 protected BucketReadTrx(final ISession pSession, final UberBucket pUberBucket,
112 final RevisionRootBucket pRevBucket, final MetaBucket pMetaBucket, final IBackendReader pReader)
113 throws TTException {
114 mSession = pSession;
115 mBucketReader = pReader;
116 mUberBucket = pUberBucket;
117 mRootBucket = pRevBucket;
118 mMetaBucket = pMetaBucket;
119 mClose = false;
120 mCache = CacheBuilder.newBuilder().maximumSize(100).build();
121 }
122
123
124
125
126
127
128
129
130
131
132 public IData getData(final long pDataKey) throws TTIOException {
133 checkArgument(pDataKey >= 0);
134 checkState(!mClose, "Transaction already closed");
135
136 final long seqBucketKey = pDataKey >> IConstants.INDIRECT_BUCKET_COUNT[3];
137 final int dataBucketOffset = dataBucketOffset(pDataKey);
138 DataBucket bucket = mCache.getIfPresent(seqBucketKey);
139 if (bucket == null) {
140 final List<DataBucket> listRevs = getSnapshotBuckets(seqBucketKey);
141 final DataBucket[] revs = listRevs.toArray(new DataBucket[listRevs.size()]);
142 checkState(revs.length > 0, "Number of Buckets to reconstruct must be larger than 0");
143
144 final IRevisioning revision = mSession.getConfig().mRevision;
145 bucket = revision.combineBuckets(revs);
146 mCache.put(seqBucketKey, bucket);
147 }
148 final IData returnVal = bucket.getData(dataBucketOffset);
149
150
151 if (pDataKey == 0) {
152 return returnVal;
153 } else {
154 return checkItemIfDeleted(returnVal);
155 }
156
157 }
158
159
160
161
162
163
164
165 public boolean close() throws TTIOException {
166 if (!mClose) {
167 mSession.deregisterBucketTrx(this);
168 mBucketReader.close();
169 mClose = true;
170 return true;
171 } else {
172 return false;
173 }
174
175 }
176
177
178
179
180 public long getRevision() throws TTIOException {
181 checkState(!mClose, "Transaction already closed");
182 return mRootBucket.getRevision();
183 }
184
185
186
187
188 @Override
189 public boolean isClosed() {
190 return mClose;
191 }
192
193
194
195
196 @Override
197 public MetaBucket getMetaBucket() {
198 checkState(!mClose, "Transaction already closed");
199 return mMetaBucket;
200 }
201
202
203
204
205
206
207
208
209 protected final IData checkItemIfDeleted(final IData pToCheck) {
210 if (pToCheck == null) {
211 throw new IllegalStateException(new StringBuilder("Data not existing.").toString());
212 } else if (pToCheck instanceof DeletedData) {
213 return null;
214 } else {
215 return pToCheck;
216 }
217 }
218
219
220
221
222
223
224
225
226
227
228
229 protected final List<DataBucket> getSnapshotBuckets(final long pSeqDataBucketKey) throws TTIOException {
230
231
232
233 final List<DataBucket> dataBuckets = new ArrayList<DataBucket>();
234
235
236 final long[] pathToRoot =
237 BucketReadTrx.dereferenceLeafOfTree(mBucketReader,
238 mUberBucket.getReferenceKeys()[IReferenceBucket.GUARANTEED_INDIRECT_OFFSET], mRootBucket
239 .getRevision());
240 final RevisionRootBucket rootBucket =
241 (RevisionRootBucket)mBucketReader.read(pathToRoot[IConstants.INDIRECT_BUCKET_COUNT.length]);
242
243 final int numbersToRestore =
244 Integer.parseInt(mSession.getConfig().mProperties.getProperty(ConstructorProps.NUMBERTORESTORE));
245
246 final long[] pathToRecentBucket =
247 dereferenceLeafOfTree(mBucketReader,
248 rootBucket.getReferenceKeys()[IReferenceBucket.GUARANTEED_INDIRECT_OFFSET], pSeqDataBucketKey);
249
250 DataBucket bucket;
251 long bucketKey = pathToRecentBucket[IConstants.INDIRECT_BUCKET_COUNT.length];
252
253 while (dataBuckets.size() < numbersToRestore && bucketKey > -1) {
254 bucket = (DataBucket)mBucketReader.read(bucketKey);
255 dataBuckets.add(bucket);
256 bucketKey = bucket.getLastBucketPointer();
257 }
258
259
260 if (bucketKey > -1) {
261 checkStructure(mBucketReader, pathToRecentBucket, rootBucket, pSeqDataBucketKey);
262 checkStructure(mBucketReader, pathToRoot, mUberBucket, mRootBucket.getRevision());
263 }
264
265 return dataBuckets;
266
267 }
268
269
270
271
272
273
274
275
276 protected static final int dataBucketOffset(final long pDataKey) {
277
278
279 final long dataBucketOffset =
280 (pDataKey - ((pDataKey >> IConstants.INDIRECT_BUCKET_COUNT[3]) << IConstants.INDIRECT_BUCKET_COUNT[3]));
281 return (int)dataBucketOffset;
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296 protected static final long[] dereferenceLeafOfTree(final IBackendReader pReader, final long pStartKey,
297 final long pSeqBucketKey) throws TTIOException {
298
299 final long[] orderNumber = getOrderNumbers(pSeqBucketKey);
300
301
302 final long[] keys = new long[IConstants.INDIRECT_BUCKET_COUNT.length + 1];
303 IndirectBucket bucket = null;
304 keys[0] = pStartKey;
305
306 for (int level = 0; level < orderNumber.length; level++) {
307
308 bucket = (IndirectBucket)pReader.read(keys[level]);
309
310
311 keys[level + 1] = bucket.getReferenceKeys()[dataBucketOffset(orderNumber[level])];
312
313 if (keys[level + 1] == 0) {
314 Arrays.fill(keys, -1);
315 return keys;
316 }
317 }
318
319
320 return keys;
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 private static final void checkStructure(final IBackendReader pReader, final long[] pKeys,
341 final IReferenceBucket mRootOfSubtree, final long pSeqBucketKey) throws TTIOException {
342
343
344 final long[] orderNumbers = getOrderNumbers(pSeqBucketKey);
345
346 IBucket currentBucket = pReader.read(pKeys[pKeys.length - 1]);
347
348 for (int i = orderNumbers.length - 1; i >= 0; i--) {
349
350
351
352
353
354 currentBucket.secureHash().asBytes();
355
356 currentBucket = pReader.read(pKeys[i]);
357
358 final byte[] storedHash =
359 ((IReferenceBucket)currentBucket).getReferenceHashs()[dataBucketOffset(orderNumbers[i])];
360
361 if (Arrays.equals(storedHash, IConstants.NON_HASHED)
362 || Arrays.equals(storedHash, IConstants.BOOTSTRAP_HASHED)) {
363
364 return;
365 }
366
367
368
369
370
371 }
372
373
374
375
376 currentBucket.secureHash().asBytes();
377 final byte[] storedHash =
378 mRootOfSubtree.getReferenceHashs()[IReferenceBucket.GUARANTEED_INDIRECT_OFFSET];
379
380
381 if (Arrays.equals(storedHash, IConstants.NON_HASHED)
382 || Arrays.equals(storedHash, IConstants.BOOTSTRAP_HASHED)) {
383 return;
384
385
386
387
388 }
389 }
390
391 private static final long[] getOrderNumbers(final long pSeqBucketKey) {
392
393
394
395
396 final long[] orderNumber = new long[IConstants.INDIRECT_BUCKET_COUNT.length];
397 for (int level = 0; level < orderNumber.length; level++) {
398 orderNumber[level] = pSeqBucketKey >> IConstants.INDIRECT_BUCKET_COUNT[level];
399 }
400 return orderNumber;
401 }
402
403
404
405
406 @Override
407 public String toString() {
408 return toStringHelper(this).add("mBucketReader", mBucketReader).add("mBucketReader", mUberBucket)
409 .add("mRootBucket", mRootBucket).add("mClose", mClose).toString();
410 }
411
412 }