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
62
63
64
65
66 public final class TransactionManager {
67 private RecordFile owner;
68
69
70 private FileOutputStream fos;
71 private ObjectOutputStream oos;
72
73
74
75
76
77 static final int DEFAULT_TXNS_IN_LOG = 10;
78
79
80
81
82
83 private int _maxTxns = DEFAULT_TXNS_IN_LOG;
84
85
86
87
88
89
90
91 private ArrayList[] txns = new ArrayList[DEFAULT_TXNS_IN_LOG];
92 private int curTxn = -1;
93
94
95 static final String extension = ".lg";
96
97
98
99
100
101
102
103 TransactionManager(RecordFile owner) throws IOException {
104 this.owner = owner;
105 recover();
106 open();
107 }
108
109
110
111
112
113
114
115
116
117 public void synchronizeLog()
118 throws IOException
119 {
120 synchronizeLogFromMemory();
121 }
122
123
124
125
126
127
128
129
130
131
132 public void setMaximumTransactionsInLog( int maxTxns )
133 throws IOException
134 {
135 if ( maxTxns <= 0 ) {
136 throw new IllegalArgumentException(
137 "Argument 'maxTxns' must be greater than 0." );
138 }
139 if ( curTxn != -1 ) {
140 throw new IllegalStateException(
141 "Cannot change setting while transactions are pending in the log" );
142 }
143 _maxTxns = maxTxns;
144 txns = new ArrayList[ maxTxns ];
145 }
146
147
148
149 private String makeLogName() {
150 return owner.getFileName() + extension;
151 }
152
153
154
155 private void synchronizeLogFromMemory() throws IOException {
156 close();
157
158 TreeSet blockList = new TreeSet( new BlockIoComparator() );
159
160 int numBlocks = 0;
161 int writtenBlocks = 0;
162 for (int i = 0; i < _maxTxns; i++) {
163 if (txns[i] == null)
164 continue;
165
166
167 for (Iterator k = txns[i].iterator(); k.hasNext(); ) {
168 BlockIo block = (BlockIo)k.next();
169 if ( blockList.contains( block ) ) {
170 block.decrementTransactionCount();
171 }
172 else {
173 writtenBlocks++;
174 boolean result = blockList.add( block );
175 }
176 numBlocks++;
177 }
178
179 txns[i] = null;
180 }
181
182 synchronizeBlocks(blockList.iterator(), true);
183
184 owner.sync();
185 open();
186 }
187
188
189
190 private void open() throws IOException {
191 fos = new FileOutputStream(makeLogName());
192 oos = new ObjectOutputStream(fos);
193 oos.writeShort(Magic.LOGFILE_HEADER);
194 oos.flush();
195 curTxn = -1;
196 }
197
198
199 private void recover() throws IOException {
200 String logName = makeLogName();
201 File logFile = new File(logName);
202 if (!logFile.exists())
203 return;
204 if (logFile.length() == 0) {
205 logFile.delete();
206 return;
207 }
208
209 FileInputStream fis = new FileInputStream(logFile);
210 ObjectInputStream ois = new ObjectInputStream(fis);
211
212 try {
213 if (ois.readShort() != Magic.LOGFILE_HEADER)
214 throw new Error("Bad magic on log file");
215 } catch (IOException e) {
216
217 logFile.delete();
218 return;
219 }
220
221 while (true) {
222 ArrayList blocks = null;
223 try {
224 blocks = (ArrayList) ois.readObject();
225 } catch (ClassNotFoundException e) {
226 throw new Error("Unexcepted exception: " + e);
227 } catch (IOException e) {
228
229 break;
230 }
231 synchronizeBlocks(blocks.iterator(), false);
232
233
234
235 try {
236 ois = new ObjectInputStream(fis);
237 } catch (IOException e) {
238
239 break;
240 }
241 }
242 owner.sync();
243 logFile.delete();
244 }
245
246
247 private void synchronizeBlocks(Iterator blockIterator, boolean fromCore)
248 throws IOException {
249
250 while ( blockIterator.hasNext() ) {
251 BlockIo cur = (BlockIo)blockIterator.next();
252 owner.synch(cur);
253 if (fromCore) {
254 cur.decrementTransactionCount();
255 if (!cur.isInTransaction()) {
256 owner.releaseFromTransaction(cur, true);
257 }
258 }
259 }
260 }
261
262
263
264 private void setClean(ArrayList blocks)
265 throws IOException {
266 for (Iterator k = blocks.iterator(); k.hasNext(); ) {
267 BlockIo cur = (BlockIo) k.next();
268 cur.setClean();
269 }
270 }
271
272
273 private void discardBlocks(ArrayList blocks)
274 throws IOException {
275 for (Iterator k = blocks.iterator(); k.hasNext(); ) {
276 BlockIo cur = (BlockIo) k.next();
277 cur.decrementTransactionCount();
278 if (!cur.isInTransaction()) {
279 owner.releaseFromTransaction(cur, false);
280 }
281 }
282 }
283
284
285
286
287
288
289 void start() throws IOException {
290 curTxn++;
291 if (curTxn == _maxTxns) {
292 synchronizeLogFromMemory();
293 curTxn = 0;
294 }
295 txns[curTxn] = new ArrayList();
296 }
297
298
299
300
301 void add(BlockIo block) throws IOException {
302 block.incrementTransactionCount();
303 txns[curTxn].add(block);
304 }
305
306
307
308
309 void commit() throws IOException {
310 oos.writeObject(txns[curTxn]);
311 sync();
312
313
314 setClean(txns[curTxn]);
315
316
317
318 oos = new ObjectOutputStream(fos);
319 }
320
321
322 private void sync() throws IOException {
323 oos.flush();
324 fos.flush();
325 fos.getFD().sync();
326 }
327
328
329
330
331
332 void shutdown() throws IOException {
333 synchronizeLogFromMemory();
334 close();
335 }
336
337
338
339
340 private void close() throws IOException {
341 sync();
342 oos.close();
343 fos.close();
344 oos = null;
345 fos = null;
346 }
347
348
349
350
351
352 void forceClose() throws IOException {
353 oos.close();
354 fos.close();
355 oos = null;
356 fos = null;
357 }
358
359
360
361
362
363
364 void synchronizeLogFromDisk() throws IOException {
365 close();
366
367 for ( int i=0; i < _maxTxns; i++ ) {
368 if (txns[i] == null)
369 continue;
370 discardBlocks(txns[i]);
371 txns[i] = null;
372 }
373
374 recover();
375 open();
376 }
377
378
379
380
381
382
383
384 public static class BlockIoComparator
385 implements Comparator
386 {
387
388 public int compare( Object o1, Object o2 ) {
389 BlockIo block1 = (BlockIo)o1;
390 BlockIo block2 = (BlockIo)o2;
391 int result = 0;
392 if ( block1.getBlockId() == block2.getBlockId() ) {
393 result = 0;
394 }
395 else if ( block1.getBlockId() < block2.getBlockId() ) {
396 result = -1;
397 }
398 else {
399 result = 1;
400 }
401 return result;
402 }
403
404 public boolean equals(Object obj) {
405 return super.equals(obj);
406 }
407 }
408
409 }