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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 package jdbm.recman;
49
50 import java.io.*;
51 import java.util.*;
52
53
54
55
56
57
58
59
60
61 public final class RecordFile {
62 final TransactionManager txnMgr;
63
64
65
66
67 private final LinkedList free = new LinkedList();
68 private final HashMap inUse = new HashMap();
69 private final HashMap dirty = new HashMap();
70 private final HashMap inTxn = new HashMap();
71
72
73 private boolean transactionsDisabled = false;
74
75
76 public final static int BLOCK_SIZE = 8192;
77
78
79 final static String extension = ".db";
80
81
82 final static byte[] cleanData = new byte[BLOCK_SIZE];
83
84 private RandomAccessFile file;
85 private final String fileName;
86
87
88
89
90
91
92
93
94
95
96 RecordFile(String fileName) throws IOException {
97 this.fileName = fileName;
98 file = new RandomAccessFile(fileName + extension, "rw");
99 txnMgr = new TransactionManager(this);
100 }
101
102
103
104
105 String getFileName() {
106 return fileName;
107 }
108
109
110
111
112
113 void disableTransactions() {
114 transactionsDisabled = true;
115 }
116
117
118
119
120
121
122
123
124
125 BlockIo get(long blockid) throws IOException {
126 Long key = new Long(blockid);
127
128
129 BlockIo node = (BlockIo) inTxn.get(key);
130 if (node != null) {
131 inTxn.remove(key);
132 inUse.put(key, node);
133 return node;
134 }
135 node = (BlockIo) dirty.get(key);
136 if (node != null) {
137 dirty.remove(key);
138 inUse.put(key, node);
139 return node;
140 }
141 for (Iterator i = free.iterator(); i.hasNext(); ) {
142 BlockIo cur = (BlockIo) i.next();
143 if (cur.getBlockId() == blockid) {
144 node = cur;
145 i.remove();
146 inUse.put(key, node);
147 return node;
148 }
149 }
150
151
152 if (inUse.get(key) != null) {
153 throw new Error("double get for block " + blockid);
154 }
155
156
157 node = getNewNode(blockid);
158 long offset = blockid * BLOCK_SIZE;
159 if (file.length() > 0 && offset <= file.length()) {
160 read(file, offset, node.getData(), BLOCK_SIZE);
161 } else {
162 System.arraycopy(cleanData, 0, node.getData(), 0, BLOCK_SIZE);
163 }
164 inUse.put(key, node);
165 node.setClean();
166 return node;
167 }
168
169
170
171
172
173
174
175
176 void release(long blockid, boolean isDirty)
177 throws IOException {
178 BlockIo node = (BlockIo) inUse.get(new Long(blockid));
179 if (node == null)
180 throw new IOException("bad blockid " + blockid + " on release");
181 if (!node.isDirty() && isDirty)
182 node.setDirty();
183 release(node);
184 }
185
186
187
188
189
190
191 void release(BlockIo block) {
192 Long key = new Long(block.getBlockId());
193 inUse.remove(key);
194 if (block.isDirty()) {
195
196 dirty.put(key, block);
197 } else {
198 if (!transactionsDisabled && block.isInTransaction()) {
199 inTxn.put(key, block);
200 } else {
201 free.add(block);
202 }
203 }
204 }
205
206
207
208
209
210
211 void discard(BlockIo block) {
212 Long key = new Long(block.getBlockId());
213 inUse.remove(key);
214
215
216
217 }
218
219
220
221
222
223 void commit() throws IOException {
224
225 if (!inUse.isEmpty() && inUse.size() > 1) {
226 showList(inUse.values().iterator());
227 throw new Error("in use list not empty at commit time ("
228 + inUse.size() + ")");
229 }
230
231
232
233 if ( dirty.size() == 0 ) {
234
235 return;
236 }
237
238 if (!transactionsDisabled) {
239 txnMgr.start();
240 }
241
242 for (Iterator i = dirty.values().iterator(); i.hasNext(); ) {
243 BlockIo node = (BlockIo) i.next();
244 i.remove();
245
246 if (transactionsDisabled) {
247 long offset = node.getBlockId() * BLOCK_SIZE;
248 file.seek(offset);
249 file.write(node.getData());
250 node.setClean();
251 free.add(node);
252 }
253 else {
254 txnMgr.add(node);
255 inTxn.put(new Long(node.getBlockId()), node);
256 }
257 }
258 if (!transactionsDisabled) {
259 txnMgr.commit();
260 }
261 }
262
263
264
265
266 void rollback() throws IOException {
267
268 if (!inUse.isEmpty()) {
269 showList(inUse.values().iterator());
270 throw new Error("in use list not empty at rollback time ("
271 + inUse.size() + ")");
272 }
273
274 dirty.clear();
275
276 txnMgr.synchronizeLogFromDisk();
277
278 if (!inTxn.isEmpty()) {
279 showList(inTxn.values().iterator());
280 throw new Error("in txn list not empty at rollback time ("
281 + inTxn.size() + ")");
282 };
283 }
284
285
286
287
288 void close() throws IOException {
289 if (!dirty.isEmpty()) {
290 commit();
291 }
292 txnMgr.shutdown();
293
294 if (!inTxn.isEmpty()) {
295 showList(inTxn.values().iterator());
296 throw new Error("In transaction not empty");
297 }
298
299
300 if (!dirty.isEmpty()) {
301 System.out.println("ERROR: dirty blocks at close time");
302 showList(dirty.values().iterator());
303 throw new Error("Dirty blocks at close time");
304 }
305 if (!inUse.isEmpty()) {
306 System.out.println("ERROR: inUse blocks at close time");
307 showList(inUse.values().iterator());
308 throw new Error("inUse blocks at close time");
309 }
310
311
312
313 file.close();
314 file = null;
315 }
316
317
318
319
320
321
322 void forceClose() throws IOException {
323 txnMgr.forceClose();
324 file.close();
325 }
326
327
328
329
330 private void showList(Iterator i) {
331 int cnt = 0;
332 while (i.hasNext()) {
333 System.out.println("elem " + cnt + ": " + i.next());
334 cnt++;
335 }
336 }
337
338
339
340
341
342
343 private BlockIo getNewNode(long blockid)
344 throws IOException {
345
346 BlockIo retval = null;
347 if (!free.isEmpty()) {
348 retval = (BlockIo) free.removeFirst();
349 }
350 if (retval == null)
351 retval = new BlockIo(0, new byte[BLOCK_SIZE]);
352
353 retval.setBlockId(blockid);
354 retval.setView(null);
355 return retval;
356 }
357
358
359
360
361
362 void synch(BlockIo node) throws IOException {
363 byte[] data = node.getData();
364 if (data != null) {
365 long offset = node.getBlockId() * BLOCK_SIZE;
366 file.seek(offset);
367 file.write(data);
368 }
369 }
370
371
372
373
374
375
376
377 void releaseFromTransaction(BlockIo node, boolean recycle)
378 throws IOException {
379 Long key = new Long(node.getBlockId());
380 if ((inTxn.remove(key) != null) && recycle) {
381 free.add(node);
382 }
383 }
384
385
386
387
388 void sync() throws IOException {
389 file.getFD().sync();
390 }
391
392
393
394
395
396 private static void read(RandomAccessFile file, long offset,
397 byte[] buffer, int nBytes) throws IOException {
398 file.seek(offset);
399 int remaining = nBytes;
400 int pos = 0;
401 while (remaining > 0) {
402 int read = file.read(buffer, pos, remaining);
403 if (read == -1) {
404 System.arraycopy(cleanData, 0, buffer, pos, remaining);
405 break;
406 }
407 remaining -= read;
408 pos += read;
409 }
410 }
411
412 }