View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.poem.transaction;
47  
48  import java.util.Vector;
49  
50  import org.melati.poem.UnexpectedExceptionPoemException;
51  
52  /**
53   * A Transaction.
54   */
55  public abstract class Transaction {
56  
57    /** Index of the Transaction. */
58    public final int index;
59    /** Mask. */
60    public final int mask;
61    /** Negative mask. */
62    public final int negMask;
63  
64    /** The transaction we are waiting on. */
65    private Transaction blockedOn = null;
66  
67    /** The transactions that are directly waiting on us. */
68    private Vector<Transaction> blockees = new Vector<Transaction>();
69  
70    /** The transitive closure of the transactions we are waiting on. */
71    private int blockedOnMask;
72  
73    private int seenCapacityMin = 50;
74    private int seenCapacityMax = 1000;
75    private Vector<Transactioned> seen = new Vector<Transactioned>(seenCapacityMin);
76  
77    private int touchedCapacityMin = 50;
78    private int touchedCapacityMax = 1000;
79    private Vector<Transactioned> touched = new Vector<Transactioned>();
80  
81    private TransactionPool transactionPool;
82  
83    /**
84     * Constructor.
85     * 
86     * @param transactionPoolP the pool this transaction belongs to 
87     * @param indexP the key for this Transaction 
88     */
89    public Transaction(TransactionPool transactionPoolP, int indexP) {
90      this.transactionPool = transactionPoolP;
91      if (indexP > transactionPool.transactionsMax())
92        throw new TransactionIndexTooLargeException();
93  
94      this.index = indexP;
95      mask = 1 << index;
96      negMask = ~mask;
97    }
98  
99    protected abstract void backingCommit();
100   protected abstract void backingRollback();
101 
102   /**
103    * The thread calling block will have to wait
104    * until (another thread) calls finish and calls notifyAll.
105    */
106   synchronized void block(Transaction blockee) {
107     blockees.addElement(blockee);
108     blockee.blockedOn = this;
109     blockee.propagateBlockage();
110     try {
111       wait();
112     }
113     catch (InterruptedException e) {
114       throw new UnexpectedExceptionPoemException(e);
115     }
116     finally {
117       blockees.removeElement(blockee);
118       blockee.blockedOn = null;
119       blockee.propagateBlockage();
120     }
121   }
122 
123   private synchronized void propagateBlockage() {
124     if (blockedOn == null)
125       blockedOnMask = mask;  // we are only waiting on ourself
126     else {
127       if ((blockedOn.blockedOnMask & mask) != 0)
128         throw new WouldDeadlockException();
129       blockedOnMask = blockedOn.blockedOnMask | mask;
130     }
131 
132     for (int i = blockees.size() - 1; i >= 0; --i)
133       ((Transaction)blockees.elementAt(i)).propagateBlockage();
134   }
135 
136   final void notifyTouched(Transactioned persistent) {
137     touched.addElement(persistent);
138   }
139 
140   final void notifySeen(Transactioned persistent) {
141     seen.addElement(persistent);
142   }
143 
144   /**
145    * Make persistent ie no longer able to be rolled back.
146    */
147   public void writeDown() {
148     synchronized (touched) {
149       for (Transactioned persistent : touched) 
150         persistent.writeDown(this);
151     }
152   }
153 
154   private void unSee() {
155     synchronized (seen) {
156       for (Transactioned persistent : seen)
157         persistent.unSee(this);
158 
159       if (seen.size() > seenCapacityMax)
160         seen = new Vector<Transactioned>(seenCapacityMin);
161       else
162         seen.setSize(0);
163     }
164   }
165 
166   // This doesn't have to be synchronized.
167   private void finish(boolean commit) {
168     try {
169       if (commit) {
170         writeDown();
171         backingCommit();
172       }
173       else
174         backingRollback();
175 
176       for (Transactioned persistent : touched) { 
177           if (commit)
178               persistent.commit(this);
179             else
180               persistent.rollback(this);
181       }
182     }
183     finally {
184       if (touched.size() > touchedCapacityMax)
185         touched = new Vector<Transactioned>(touchedCapacityMin);
186       else
187         touched.setSize(0);
188 
189       unSee();
190 
191       // notifyAll will wake too many threads if some of them are writers, but
192       // this is really the best we can do without using heavy Lock-ish objects
193 
194       synchronized (this) {
195         notifyAll();
196       }
197     }
198   }
199 
200   /**
201    * Finish up, for example write to database.
202    */
203   public void commit() {
204     try {
205       finish(true);
206     }
207     catch (RuntimeException e) {
208       try {
209         System.err.println("Rolling back due to " + e);
210         finish(false);
211       }
212       catch (Exception ignore) {
213         // Ignore
214         ignore = null; // shut PMD up
215       }
216       throw e;
217     }
218   }
219 
220   /**
221    * Finish without commit.
222    */
223   public void rollback() {
224     finish(false);
225   }
226 
227   /**
228    * @return the Transaction we are waiting for
229    */
230   public Transaction getBlockedOn() {
231     return blockedOn;
232   }
233 
234   /**
235    * The transaction index.
236    * {@inheritDoc}
237    * @see java.lang.Object#toString()
238    */
239   public String toString() {
240     return "transaction" + index;
241   }
242 }
243 
244 
245