Coverage Report - org.melati.poem.transaction.Transactioned
 
Classes in this File Line Coverage Branch Coverage Complexity
Transactioned
75%
60/80
63%
24/38
2.688
 
 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 org.melati.poem.PoemBugPoemException;
 49  
 
 50  
 /**
 51  
  * An object which can have uncommitted state within a {@link Transaction}.
 52  
  */
 53  
 public abstract class Transactioned {
 54  
 
 55  9289
   protected boolean valid = true;
 56  
 
 57  
   /** The transactions which have read us */
 58  9289
   private int seenMask = 0;
 59  
 
 60  
   /** The transaction which is writing to us */
 61  9289
   private Transaction touchedBy = null;
 62  9289
   private TransactionPool transactionPool = null;
 63  
 
 64  
   /**
 65  
    * Constructor.
 66  
    * @param transactionPool the TransactionPool
 67  
    */
 68  9289
   public Transactioned(TransactionPool transactionPool) {
 69  9289
     this.transactionPool = transactionPool;
 70  9289
   }
 71  
 
 72  
   /**
 73  
    * Constructor.
 74  
    */
 75  
   public Transactioned() {
 76  8339
     this(null);
 77  8339
   }
 78  
 
 79  
   /**
 80  
    * Load the transactioned object from its backing store.
 81  
    */
 82  
   protected abstract void load(Transaction transaction);
 83  
 
 84  
   /**
 85  
    * Whether this instance is up-to-date.
 86  
    * <p>
 87  
    * This is a hook to enable subtypes to define under what circumstances
 88  
    * an instance needs to be reloaded when it is marked as
 89  
    * invalid, however the two known subtypes just return 
 90  
    * the inherited valid flag. 
 91  
    */
 92  
   protected abstract boolean upToDate(Transaction transaction);
 93  
 
 94  
   protected abstract void writeDown(Transaction transaction);
 95  
 
 96  
   protected synchronized void reset() {
 97  6
     valid = true;
 98  6
     seenMask = 0;
 99  6
     touchedBy = null;
 100  6
   }
 101  
 
 102  
   protected final TransactionPool transactionPool() {
 103  0
     return transactionPool;
 104  
   }
 105  
 
 106  
   protected synchronized void setTransactionPool(
 107  
       TransactionPool transactionPool) {
 108  15896
     if (transactionPool == null)
 109  0
       throw new NullPointerException();
 110  15896
     if (this.transactionPool != null && 
 111  
         this.transactionPool != transactionPool)
 112  0
       throw new IllegalArgumentException();
 113  
 
 114  15896
     this.transactionPool = transactionPool;
 115  15896
   }
 116  
 
 117  
   /**
 118  
    * We don't synchronize this; under the one-thread-per-transaction 
 119  
    * parity it can't happen, and at worst it means loading twice sometimes.
 120  
    * @param transaction the transaction to check
 121  
    */
 122  
   private void ensureValid(Transaction transaction) {
 123  54891
     if (!valid) {
 124  997
       if (transaction == null)
 125  3
         transaction = touchedBy;
 126  
 
 127  
       // NOTE This could be simplified to if(!valid) 
 128  
       // but that would remove a useful extension hook.
 129  997
       if (!upToDate(transaction))
 130  997
         load(transaction);
 131  
 
 132  997
       valid = true;
 133  
     }
 134  54891
   }
 135  
 
 136  
   protected void readLock(Transaction transaction) {
 137  
 
 138  45959
     if (transaction != null) {
 139  
       // Block on writers until there aren't any
 140  
 
 141  
       for (;;) {
 142  
         Transaction blocker;
 143  45945
         synchronized (this) {
 144  45945
           if (touchedBy != null && touchedBy != transaction)
 145  0
             blocker = touchedBy;
 146  
           else {
 147  45945
             if ((seenMask & transaction.mask) == 0) {
 148  12909
               seenMask |= transaction.mask;
 149  12909
               transaction.notifySeen(this);
 150  
             }
 151  45945
             break;
 152  
           }
 153  0
         }
 154  
 
 155  0
         blocker.block(transaction);
 156  0
       }
 157  
     }
 158  
 
 159  45959
     ensureValid(transaction);
 160  45959
   }
 161  
 
 162  
   /**
 163  
    * Get a write lock on the given object if we do not already
 164  
    * have one.
 165  
    * <p>
 166  
    * This will block until no other transactions have
 167  
    * write locks on the object before claiming the next write
 168  
    * lock. Then it will block until none have read locks.
 169  
    * <p>
 170  
    * Finally it calls {@link #ensureValid(Transaction)}.
 171  
    */
 172  
   protected void writeLock(Transaction transaction) {
 173  
 
 174  8934
     if (transaction == null)
 175  2
       throw new WriteCommittedException(this);
 176  
 
 177  
     // Block on other writers and readers until there aren't any
 178  
 
 179  
     for (;;) {
 180  8932
       Transaction blocker = null;
 181  8932
       synchronized (this) {
 182  8932
         if (touchedBy == transaction)
 183  
           // There's a writer, but it's us
 184  7717
           break;
 185  
 
 186  1215
         else if (touchedBy != null)
 187  
           // There's a writer, and it's not us
 188  0
           blocker = touchedBy;
 189  
 
 190  
         else {
 191  1215
           int othersSeenMask = seenMask & transaction.negMask;
 192  1215
           if (othersSeenMask == 0) {
 193  
             // There are no readers besides us
 194  
 
 195  1215
             touchedBy = transaction;
 196  1215
             transaction.notifyTouched(this);
 197  1215
             break;
 198  
           }
 199  
           else {
 200  
             // There are other readers
 201  
 
 202  
             // We block not on the chronologically first reader but on the one
 203  
             // with the lowest index, i.e. essentially on an arbitrary
 204  
             // one---not perfect, but doing it any other way would be
 205  
             // expensive.
 206  
 
 207  0
             int m = transactionPool().transactionsMax();
 208  
             int t, mask;
 209  0
             for (t = 0, mask = 1;
 210  0
                  t < m && (othersSeenMask & mask) == 0;
 211  0
                  ++t, mask <<= 1)
 212  
               ;
 213  
 
 214  0
             if (t == m)
 215  0
               throw new PoemBugPoemException(
 216  
                   "Thought there was a blocking transaction, " +
 217  
                   "but didn't find it");
 218  
 
 219  0
             blocker = transactionPool().transaction(t);
 220  
           }
 221  
         }
 222  0
       }
 223  
 
 224  0
       blocker.block(transaction);
 225  0
     }
 226  
 
 227  8932
     ensureValid(transaction);
 228  8932
   }
 229  
 
 230  
   protected synchronized void commit(Transaction transaction) {
 231  1201
     if (touchedBy != transaction)
 232  0
       throw new CrossTransactionCommitException(this);
 233  1201
     touchedBy = null;
 234  1201
   }
 235  
 
 236  
   protected synchronized void rollback(Transaction transaction) {
 237  14
     if (touchedBy != transaction)
 238  0
       throw new CrossTransactionCommitException(this);
 239  14
     touchedBy = null;
 240  14
     valid = false;
 241  14
   }
 242  
 
 243  
   /**
 244  
    * Mark as invalid.
 245  
    */
 246  
   public synchronized void invalidate() {
 247  16026
     valid = false;
 248  16026
   }
 249  
 
 250  
   /**
 251  
    * Mark as valid.
 252  
    */
 253  
   public synchronized void markValid() {
 254  2395
     valid = true;
 255  2395
   }
 256  
 
 257  
   protected synchronized void unSee(Transaction transaction) {
 258  12909
     seenMask &= transaction.negMask;
 259  12909
   }
 260  
 }