KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > db > store > Transaction


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.db.store;
31
32 import com.caucho.db.jdbc.ConnectionImpl;
33 import com.caucho.log.Log;
34 import com.caucho.sql.SQLExceptionWrapper;
35 import com.caucho.util.L10N;
36 import com.caucho.util.LongKeyHashMap;
37
38 import java.io.IOException JavaDoc;
39 import java.sql.SQLException JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.logging.Level JavaDoc;
43 import java.util.logging.Logger JavaDoc;
44
45 /**
46  * Represents a single transaction.
47  */

48 public class Transaction extends StoreTransaction {
49   private static final Logger JavaDoc log = Log.open(Transaction.class);
50   private static final L10N L = new L10N(Transaction.class);
51
52   private static long AUTO_COMMIT_TIMEOUT = 30000L;
53
54   private boolean _isAutoCommit = true;
55   private ConnectionImpl _conn;
56   
57   private ArrayList JavaDoc<Lock> _readLocks;
58   private ArrayList JavaDoc<Lock> _writeLocks;
59   
60   private LongKeyHashMap<WriteBlock> _writeBlocks;
61   
62   private ArrayList JavaDoc<Block> _updateBlocks;
63
64   // inodes that need to be deleted on a commit
65
private ArrayList JavaDoc<Inode> _deleteInodes;
66   
67   // inodes that need to be deleted on a rollback
68
private ArrayList JavaDoc<Inode> _addInodes;
69   
70   // blocks that need deallocating on a commit
71
private ArrayList JavaDoc<Block> _deallocateBlocks;
72
73   private boolean _isRollbackOnly;
74   private SQLException JavaDoc _rollbackExn;
75
76   private long _timeout = AUTO_COMMIT_TIMEOUT;
77
78   private Transaction()
79   {
80   }
81
82   public static Transaction create(ConnectionImpl conn)
83   {
84     Transaction xa = new Transaction();
85     
86     xa.init(conn);
87
88     return xa;
89   }
90
91   public static Transaction create()
92   {
93     Transaction xa = new Transaction();
94
95     return xa;
96   }
97
98   private void init(ConnectionImpl conn)
99   {
100     _conn = conn;
101     _timeout = AUTO_COMMIT_TIMEOUT;
102     _isRollbackOnly = false;
103     _rollbackExn = null;
104   }
105
106   /**
107    * Sets the transaction timeout.
108    */

109   public void setTimeout(long timeout)
110   {
111     _timeout = timeout;
112   }
113   
114   /**
115    * Acquires a new read lock.
116    */

117   /*
118   public void addReadLock(Lock lock)
119   {
120     _readLocks.add(lock);
121   }
122   */

123   
124   /**
125    * Acquires a new read lock.
126    */

127   public boolean hasReadLock(Lock lock)
128   {
129     return _readLocks.contains(lock);
130   }
131
132   /**
133    * Returns true for an auto-commit transaction.
134    */

135   public boolean isAutoCommit()
136   {
137     return _isAutoCommit;
138   }
139
140   /**
141    * Returns true for an auto-commit transaction.
142    */

143   public void setAutoCommit(boolean autoCommit)
144   {
145     _isAutoCommit = autoCommit;
146   }
147   
148   /**
149    * Acquires a new write lock.
150    */

151   public void lockRead(Lock lock)
152     throws SQLException JavaDoc
153   {
154     if (_isRollbackOnly) {
155       if (_rollbackExn != null)
156     throw _rollbackExn;
157       else
158     throw new SQLException JavaDoc(L.l("can't get lock with rollback transaction"));
159     }
160
161     try {
162       if (_readLocks == null)
163     _readLocks = new ArrayList JavaDoc<Lock>();
164       
165       if (! _readLocks.contains(lock)) {
166     lock.lockRead(this, _timeout);
167     _readLocks.add(lock);
168       }
169     } catch (SQLException JavaDoc e) {
170       setRollbackOnly(e);
171       
172       throw e;
173     }
174   }
175
176   
177   /**
178    * Acquires a new write lock.
179    */

180   public void lockReadAndWrite(Lock lock)
181     throws SQLException JavaDoc
182   {
183     lockWrite(lock);
184   }
185   
186   /**
187    * Acquires a new write lock.
188    */

189   public void lockWrite(Lock lock)
190     throws SQLException JavaDoc
191   {
192     if (_isRollbackOnly) {
193       if (_rollbackExn != null)
194     throw _rollbackExn;
195       else
196     throw new SQLException JavaDoc(L.l("can't get lock with rollback transaction"));
197     }
198
199     try {
200       if (_readLocks == null)
201     _readLocks = new ArrayList JavaDoc<Lock>();
202       if (_writeLocks == null)
203     _writeLocks = new ArrayList JavaDoc<Lock>();
204       
205       if (_writeLocks.contains(lock)) {
206     return;
207       }
208       else if (_readLocks.contains(lock)) {
209     lock.lockWrite(this, _timeout);
210     _writeLocks.add(lock);
211       }
212       else {
213     lock.lockReadAndWrite(this, _timeout);
214     _readLocks.add(lock);
215     _writeLocks.add(lock);
216       }
217     } catch (SQLException JavaDoc e) {
218       setRollbackOnly(e);
219       
220       throw e;
221     }
222   }
223
224   /**
225    * Adds a block for update.
226    */

227   public void addUpdateBlock(Block block)
228   {
229     if (block == null)
230       return;
231     
232     if (_updateBlocks == null)
233       _updateBlocks = new ArrayList JavaDoc<Block>();
234
235     if (_updateBlocks.size() == 0
236     || _updateBlocks.get(_updateBlocks.size() - 1) != block)
237       _updateBlocks.add(block);
238   }
239   
240   /**
241    * If auto-commit, commit the read
242    */

243   public void autoCommitRead(Lock lock)
244     throws SQLException JavaDoc
245   {
246     unlockRead(lock);
247   }
248   
249   public void unlockRead(Lock lock)
250     throws SQLException JavaDoc
251   {
252     if (_readLocks.remove(lock))
253       lock.unlockRead();
254   }
255   
256   /**
257    * If auto-commit, commit the write
258    */

259   public void autoCommitWrite(Lock lock)
260     throws SQLException JavaDoc
261   {
262     _readLocks.remove(lock);
263
264     if (_writeLocks.remove(lock)) {
265       try {
266     commit();
267       } finally {
268     lock.unlockWrite();
269       }
270     }
271   }
272   
273   public void unlockReadAndWrite(Lock lock)
274     throws SQLException JavaDoc
275   {
276     _readLocks.remove(lock);
277     
278     if (_writeLocks.remove(lock)) {
279       lock.unlockReadAndWrite();
280     }
281   }
282   
283   public void unlockWrite(Lock lock)
284     throws SQLException JavaDoc
285   {
286     if (_writeLocks.remove(lock)) {
287       lock.unlockWrite();
288     }
289   }
290
291   /**
292    * Returns a read block.
293    */

294   public Block readBlock(Store store, long blockAddress)
295     throws IOException JavaDoc
296   {
297     long blockId = store.addressToBlockId(blockAddress);
298       
299     Block block;
300     
301     if (_writeBlocks != null)
302       block = _writeBlocks.get(blockId);
303     else
304       block = null;
305
306     if (block != null)
307       block.allocate();
308     else
309       block = store.readBlock(blockId);
310
311     return block;
312   }
313
314   /**
315    * Returns a modified block.
316    */

317   public WriteBlock getWriteBlock(long blockId)
318   {
319     if (_writeBlocks == null)
320       return null;
321
322     return _writeBlocks.get(blockId);
323   }
324
325   /**
326    * Returns a modified block.
327    */

328   public WriteBlock createWriteBlock(Block block)
329     throws IOException JavaDoc
330   {
331     if (block instanceof WriteBlock)
332       return (WriteBlock) block;
333
334     WriteBlock writeBlock = getWriteBlock(block.getBlockId());
335
336     if (writeBlock != null) {
337       block.free();
338       writeBlock.allocate();
339       return writeBlock;
340     }
341     
342     if (isAutoCommit())
343       writeBlock = new AutoCommitWriteBlock(block);
344     else {
345       // XXX: locking
346
writeBlock = new XAWriteBlock(block);
347       setBlock(writeBlock);
348     }
349
350
351     return writeBlock;
352   }
353
354   /**
355    * Returns a modified block.
356    */

357   public Block createAutoCommitWriteBlock(Block block)
358     throws IOException JavaDoc
359   {
360     if (block instanceof WriteBlock) {
361       return block;
362     }
363     else {
364       WriteBlock writeBlock = getWriteBlock(block.getBlockId());
365
366       if (writeBlock != null) {
367     block.free();
368     writeBlock.allocate();
369
370     return writeBlock;
371       }
372       
373       writeBlock = new AutoCommitWriteBlock(block);
374
375       // setBlock(writeBlock);
376

377       return writeBlock;
378     }
379   }
380
381   /**
382    * Returns a modified block.
383    */

384   public Block allocateRow(Store store)
385     throws IOException JavaDoc
386   {
387     return store.allocateRow();
388   }
389
390   /**
391    * Returns a modified block.
392    */

393   public void deallocateBlock(Block block)
394     throws IOException JavaDoc
395   {
396     if (isAutoCommit())
397       block.getStore().freeBlock(block.getBlockId());
398     else {
399       if (_deallocateBlocks == null)
400     _deallocateBlocks = new ArrayList JavaDoc<Block>();
401       
402       _deallocateBlocks.add(block);
403     }
404   }
405
406   /**
407    * Returns a modified block.
408    */

409   public Block createWriteBlock(Store store, long blockAddress)
410     throws IOException JavaDoc
411   {
412     Block block = readBlock(store, blockAddress);
413
414     return createWriteBlock(block);
415   }
416
417   /**
418    * Returns a modified block.
419    */

420   private void setBlock(WriteBlock block)
421   {
422     // block.setDirty();
423

424     if (_writeBlocks == null)
425       _writeBlocks = new LongKeyHashMap<WriteBlock>(8);
426
427     _writeBlocks.put(block.getBlockId(), block);
428   }
429
430   /**
431    * Adds inode which should be deleted on a commit.
432    */

433   public void addDeleteInode(Inode inode)
434   {
435     if (_deleteInodes == null)
436       _deleteInodes = new ArrayList JavaDoc<Inode>();
437     
438     _deleteInodes.add(inode);
439   }
440
441   /**
442    * Adds inode which should be deleted on a rollback.
443    */

444   public void addAddInode(Inode inode)
445   {
446     if (_addInodes == null)
447       _addInodes = new ArrayList JavaDoc<Inode>();
448     
449     _addInodes.add(inode);
450   }
451
452   public void autoCommit()
453     throws SQLException JavaDoc
454   {
455     if (_isAutoCommit) {
456       ConnectionImpl conn = _conn;
457       _conn = null;
458       
459       if (conn != null) {
460     conn.setTransaction(null);
461       }
462     }
463   }
464
465   public void setRollbackOnly(SQLException JavaDoc e)
466   {
467     if (_rollbackExn == null)
468       _rollbackExn = e;
469     
470     _isRollbackOnly = true;
471
472     releaseLocks();
473
474     // XXX: release write blocks
475
_writeBlocks = null;
476   }
477
478   public void setRollbackOnly()
479   {
480     setRollbackOnly(null);
481   }
482
483   public void commit()
484     throws SQLException JavaDoc
485   {
486     try {
487       writeData();
488     } finally {
489       releaseLocks();
490
491       close();
492     }
493   }
494
495   public void writeData()
496     throws SQLException JavaDoc
497   {
498     LongKeyHashMap<WriteBlock> writeBlocks = _writeBlocks;
499
500     if (_deleteInodes != null) {
501       while (_deleteInodes.size() > 0) {
502     Inode inode = _deleteInodes.remove(0);
503
504     // XXX: should be allocating based on auto-commit
505
inode.remove();
506       }
507     }
508
509     ArrayList JavaDoc<Block> updateBlocks = _updateBlocks;
510     _updateBlocks = null;
511     
512     if (updateBlocks != null) {
513       while (updateBlocks.size() > 0) {
514     Block block = updateBlocks.remove(updateBlocks.size() - 1);
515
516     try {
517       block.commit();
518     } catch (IOException JavaDoc e) {
519       log.log(Level.WARNING, e.toString(), e);
520     }
521       }
522     }
523
524     if (writeBlocks != null) {
525       Iterator JavaDoc<WriteBlock> blockIter = writeBlocks.valueIterator();
526
527       while (blockIter.hasNext()) {
528     WriteBlock block = blockIter.next();
529
530     try {
531       block.commit();
532     } catch (IOException JavaDoc e) {
533       log.log(Level.WARNING, e.toString(), e);
534     }
535       }
536       
537       // writeBlocks.clear();
538
}
539
540     if (_deallocateBlocks != null) {
541       while (_deallocateBlocks.size() > 0) {
542     Block block = _deallocateBlocks.remove(0);
543
544     try {
545       block.getStore().freeBlock(block.getBlockId());
546     } catch (IOException JavaDoc e) {
547       throw new SQLExceptionWrapper(e);
548     }
549       }
550     }
551   }
552
553   public void rollback()
554     throws SQLException JavaDoc
555   {
556     releaseLocks();
557
558     close();
559   }
560
561   private void releaseLocks()
562   {
563     // need to unlock write before upgrade to block other threads
564
if (_writeLocks != null) {
565       for (int i = 0; i < _writeLocks.size(); i++) {
566     Lock lock = _writeLocks.get(i);
567
568     if (_readLocks != null)
569       _readLocks.remove(lock);
570
571     try {
572       lock.unlockReadAndWrite();
573     } catch (Throwable JavaDoc e) {
574       log.log(Level.WARNING, e.toString(), e);
575     }
576       }
577
578       _writeLocks.clear();
579     }
580     
581     if (_readLocks != null) {
582       for (int i = 0; i < _readLocks.size(); i++) {
583     Lock lock = _readLocks.get(i);
584
585     try {
586       lock.unlockRead();
587     } catch (Throwable JavaDoc e) {
588       log.log(Level.WARNING, e.toString(), e);
589     }
590       }
591
592       _readLocks.clear();
593     }
594   }
595
596   void close()
597   {
598     LongKeyHashMap<WriteBlock> writeBlocks = _writeBlocks;
599     _writeBlocks = null;
600
601     if (writeBlocks != null) {
602       Iterator JavaDoc<WriteBlock> blockIter = writeBlocks.valueIterator();
603
604       while (blockIter.hasNext()) {
605     WriteBlock block = blockIter.next();
606
607     block.destroy();
608       }
609       
610       // writeBlocks.clear();
611
}
612
613     _isRollbackOnly = false;
614     _rollbackExn = null;
615   }
616 }
617
Popular Tags