Save This Page
Home » JBoss-5.1.0 » org » jboss » ejb » plugins » lock » [javadoc | source]
    1   /*
    2    * JBoss, Home of Professional Open Source.
    3    * Copyright 2008, Red Hat Middleware LLC, and individual contributors
    4    * as indicated by the @author tags. See the copyright.txt file in the
    5    * distribution for a full listing of individual contributors.
    6    *
    7    * This is free software; you can redistribute it and/or modify it
    8    * under the terms of the GNU Lesser General Public License as
    9    * published by the Free Software Foundation; either version 2.1 of
   10    * the License, or (at your option) any later version.
   11    *
   12    * This software is distributed in the hope that it will be useful,
   13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15    * Lesser General Public License for more details.
   16    *
   17    * You should have received a copy of the GNU Lesser General Public
   18    * License along with this software; if not, write to the Free
   19    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21    */
   22   package org.jboss.ejb.plugins.lock;
   23   
   24   import java.util.LinkedList;
   25   import java.util.HashMap;
   26   import java.util.HashSet;
   27   import java.util.Stack;
   28   import java.util.Collections;
   29   import java.lang.reflect.Method;
   30   
   31   import javax.ejb.EJBObject;
   32   import javax.ejb.EJBException;
   33   import javax.transaction.Status;
   34   import javax.transaction.Transaction;
   35   import javax.transaction.Synchronization;
   36   
   37   import org.jboss.invocation.Invocation;
   38   
   39   /**
   40    *
   41    * This lock allows multiple read locks concurrently.  Once a writer 
   42    * has requested the lock, future read-lock requests whose transactions 
   43    * do not already have the read lock will block until all writers are 
   44    * done -- then all the waiting readers will concurrently go (depending
   45    * on the reentrant setting / methodLock).  A reader who promotes gets 
   46    * first crack at the write lock -- ahead of other waiting writers.  If 
   47    * there is already a reader that is promoting, we throw an inconsistent 
   48    * read exception.  Of course, writers have to wait for all read-locks 
   49    * to release before taking the write lock.
   50    *
   51    * @author <a href="pete@subx.com">Peter Murray</a>
   52    *
   53    * @version $Revision: 81030 $
   54    *
   55    * <p><b>Revisions:</b><br>
   56    * <p><b>2002/6/4: yarrumretep</b>
   57    *  <ol>
   58    *  <li>Initial revision
   59    *  </ol>
   60    */
   61   public class SimpleReadWriteEJBLock extends BeanLockSupport
   62   {
   63       int writersWaiting = 0;
   64       Transaction promotingReader = null;
   65       Transaction writer = null;
   66       HashSet readers = new HashSet();
   67       Object methodLock = new Object();
   68       boolean trace = log.isTraceEnabled();
   69   
   70       private void trace(Transaction tx, String message)
   71       {
   72   	trace(tx, message, null);
   73       }
   74   
   75       private void trace(Transaction tx, String message, Method method)
   76       {
   77   	if(method != null)
   78   	    log.trace("LOCK(" + id + "):" + message + " : " +  tx + " - " + method.getDeclaringClass().getName() + "." + method.getName());
   79   	else
   80   	    log.trace("LOCK(" + id + "):" + message + " : " +  tx);
   81       }
   82   
   83       public void schedule(Invocation mi)
   84       {
   85           boolean reading = mi.getMethod() == null ? false : container.getBeanMetaData().isMethodReadOnly(mi.getMethod().getName());
   86   	Transaction miTx = mi.getTransaction();
   87   
   88   	sync();
   89   	try
   90   	{
   91   	    if(reading)
   92   	    {
   93   		if(trace)
   94   		    trace(miTx, "READ  (RQ)", mi.getMethod());
   95   		getReadLock(miTx);
   96   		if(trace)
   97   		    trace(miTx, "READ  (GT)", mi.getMethod());
   98   	    }
   99   	    else
  100   	    {
  101   		if(trace)
  102   		    trace(miTx, "WRITE (RQ)", mi.getMethod());
  103   		getWriteLock(miTx);
  104   		if(trace)
  105   		    trace(miTx, "WRITE (GT)", mi.getMethod());
  106   	    }
  107   	}
  108   	finally
  109   	{
  110   	    releaseSync();
  111   	}
  112       }
  113   
  114       private void getReadLock(Transaction tx)
  115       {
  116   	boolean done = false;
  117   
  118   	while(!done)
  119   	{
  120   	    if(tx == null)
  121   	    {
  122   		done = writer == null;
  123   	    }
  124   	    else if(readers.contains(tx))
  125   	    {
  126   		done = true;
  127   	    }
  128   	    else if(writer == null && promotingReader == null && writersWaiting == 0)
  129   	    {
  130   		try
  131   		{
  132   		    ReadLockReliever reliever = getReliever();
  133   		    reliever.setup(this, tx);
  134   		    tx.registerSynchronization(reliever);
  135   		}
  136   		catch (Exception e)
  137   		{
  138   		    throw new EJBException(e);
  139   		}
  140   		readers.add(tx);
  141   		done = true;
  142   	    }
  143   	    else if (writer != null && writer.equals(tx))
  144   	    {
  145   		done = true;
  146   	    }
  147   
  148   	    if(!done)
  149   	    {
  150   		if(trace)
  151    		    trace(tx, "READ (WT) writer:" + writer + " writers waiting: " + writersWaiting + " reader count: " + readers.size());
  152   		
  153   		waitAWhile(tx);
  154   	    }
  155   	}
  156       }
  157   
  158       private void getWriteLock(Transaction tx)
  159       {
  160   	boolean done = false;
  161   	boolean isReader;
  162   
  163   	if(tx == null)
  164   	    throw new EJBException("Write lock requested without transaction.");
  165   
  166   	isReader = readers.contains(tx);
  167   	writersWaiting++;
  168   	while(!done)
  169   	{
  170   	    if(writer == null && (readers.isEmpty() || (readers.size() == 1 && isReader)))
  171   	    {
  172   		writersWaiting--;
  173   		promotingReader = null;
  174   		writer = tx;
  175   		done = true;
  176   	    }
  177   	    else if (writer != null && writer.equals(tx))
  178   	    {
  179   		writersWaiting--;
  180   		done = true;
  181   	    }
  182   	    else
  183   	    {
  184   		if(isReader)
  185   		{
  186   		    if(promotingReader != null && !promotingReader.equals(tx))
  187   		    {
  188   			writersWaiting--;
  189   			throw new EJBException("Contention on read lock promotion for bean.  Exception in second transaction");
  190   		    }
  191   		    promotingReader = tx;
  192   		}
  193   
  194   		if(trace)
  195    		    trace(tx, "WRITE (WT) writer:" + writer + " writers waiting: " + writersWaiting + " reader count: " + readers.size());
  196   
  197   		waitAWhile(tx);
  198   	    }
  199   	}
  200       }
  201   
  202       /**
  203        * Use readers as a semaphore object to avoid
  204        * creating another object
  205        */
  206       private void waitAWhile(Transaction tx)
  207       {
  208   	releaseSync();
  209   	try
  210   	{
  211   	    synchronized(readers)
  212   	    {
  213   		try
  214   		{
  215   		    readers.wait(txTimeout);
  216   		}
  217   		catch(InterruptedException e)
  218   		{}
  219   		checkTransaction(tx);
  220   	    }
  221   	}
  222   	finally
  223   	{
  224   	    sync();
  225   	}
  226       }
  227       
  228       /**
  229        * Use readers as a semaphore object to avoid
  230        * creating another object
  231        */
  232       private void notifyWaiters()
  233       {
  234   	synchronized(readers)
  235   	{
  236   	    readers.notifyAll();
  237   	}
  238       }
  239   
  240       private void releaseReadLock(Transaction transaction)
  241       {
  242   	if(trace)
  243   	    trace(transaction, "READ  (UL)");
  244   
  245   	if(!readers.remove(transaction))
  246   	    throw new IllegalStateException("ReadWriteEJBLock: Read lock released when it wasn't taken");
  247   
  248   	notifyWaiters();
  249       }
  250   
  251       private void releaseWriteLock(Transaction transaction)
  252       {
  253   	if(trace)
  254   	    trace(transaction, "WRITE (UL)");
  255   
  256   	if (synched == null)
  257   	    throw new IllegalStateException("ReadWriteEJBLock: Do not call nextTransaction while not synched!");
  258   
  259   	if(writer != null && !writer.equals(transaction))
  260   	    throw new IllegalStateException("ReadWriteEJBLock: can't unlock a write lock with a different transaction");
  261   
  262   	writer = null;
  263   	notifyWaiters();
  264       }
  265   
  266       public void endTransaction(Transaction transaction)
  267       {
  268   	releaseWriteLock(transaction);
  269       }
  270       
  271       public void wontSynchronize(Transaction transaction)
  272       {
  273   	releaseWriteLock(transaction);
  274       }
  275   
  276       public void endInvocation(Invocation mi)
  277       {
  278       }
  279   
  280       private static Stack kRecycledRelievers = new Stack();
  281   
  282       static synchronized ReadLockReliever getReliever()
  283       {
  284   	ReadLockReliever reliever;
  285   	if(!kRecycledRelievers.empty())
  286   	    reliever = (ReadLockReliever)kRecycledRelievers.pop();
  287   	else
  288   	    reliever = new ReadLockReliever();
  289   
  290   	return reliever;
  291       }
  292   
  293       private static class ReadLockReliever implements Synchronization
  294       {
  295   	SimpleReadWriteEJBLock lock;
  296   	Transaction transaction;
  297   	
  298   	protected void finalize()
  299   	{
  300   	    recycle();
  301   	}
  302   
  303   	protected void recycle()
  304   	{
  305   	    lock = null;
  306   	    transaction = null;
  307   	    kRecycledRelievers.push(this);
  308   	}
  309   
  310   	void setup(SimpleReadWriteEJBLock lock, Transaction transaction)
  311   	{
  312   	    this.lock = lock;
  313   	    this.transaction = transaction;
  314   	}
  315   
  316   	public void beforeCompletion()
  317   	{
  318   	}
  319   	
  320   	public void afterCompletion(int status)
  321   	{
  322   	    lock.sync();
  323   	    try
  324   	    {
  325   		lock.releaseReadLock(transaction);
  326   	    }
  327   	    finally
  328   	    {
  329   		lock.releaseSync();
  330   	    }
  331   	    recycle();
  332   	}
  333       }
  334       
  335       private void checkTransaction(Transaction tx)
  336       {
  337   	try
  338   	{
  339   	    if(tx != null && tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
  340   		throw new EJBException ("Transaction marked for rollback - probably a timeout.");
  341   	}
  342   	catch (Exception e)
  343   	{
  344   	    throw new EJBException(e);
  345   	}
  346       }
  347   }

Save This Page
Home » JBoss-5.1.0 » org » jboss » ejb » plugins » lock » [javadoc | source]