User: mnf999
Date: 01/08/02 19:59:04
Added: src/main/org/jboss/ejb/plugins/lock
SimplePessimisticEJBLock.java
Log:
nuff said
Revision Changes Path
1.1
jboss/src/main/org/jboss/ejb/plugins/lock/SimplePessimisticEJBLock.java
Index: SimplePessimisticEJBLock.java
===================================================================
/*
* JBoss, the OpenSource EJB server
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb.plugins.lock;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Collections;
import java.lang.reflect.Method;
import javax.transaction.Transaction;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.TransactionManager;
import javax.transaction.RollbackException;
import javax.ejb.EJBObject;
import org.jboss.ejb.MethodInvocation;
import org.jboss.logging.log4j.JBossCategory;
/**
* Holds all locks for entity beans, not used for stateful. <p>
*
* All BeanLocks have a reference count.
* When the reference count goes to 0, the lock is released from the
* id -> lock mapping.
*
* @author <a href="[EMAIL PROTECTED]">Bill Burke</a>
* @author <a href="[EMAIL PROTECTED]">Marc Fleury</a>
*
* @version $Revision: 1.1 $
*
* <p><b>Revisions:</b><br>
* <p><b>2001/07/29: billb</b>
* <ol>
* <li>Initial revision
* </ol>
* <p><b>2001/08/01: marcf</b>
* <ol>
* <li>Added the schedule method
* <li>The bean lock is now responsible for implementing the locking policies, it
was before in the
* interceptor code it is now factored out allowing for pluggable lock policies
(optimistic for ex)
* <li>Implemented pessimistic locking and straight spec requirement in the
schedule method
* would need to factor this in an abstract class for the BeanLock that extending
policies can use
* </ol>
<p><b>2001/08/02: marcf</b>
* <ol>
* <li>Did what was said above, moved to an extension based mech with an abstract
base class.
* <li>This is the simple lock, won't scale well (imho) but is robust in normal
operation
* <li>The class must now implement schedule and the various notification calls.
EndTransaction
* wontSynchronize can be radically different (N-Lock)
* </ol>
*/
public class SimplePessimisticEJBLock extends BeanLockSupport
{
/**
* Schedule(MethodInvocation)
*
* Schedule implements a particular policy for scheduling the threads coming in.
* There is always the spec required "serialization" but we can add custom
scheduling in here
*
* Synchronizing on lock: a failure to get scheduled must result in a wait() call
and a
* release of the lock. Schedulation must return with lock.
*
* @return boolean returns true if the thread is scheduled to go through the rest
of the
* interceptors. Returns false if the interceptor must try the scheduling
again.
*/
public boolean schedule(MethodInvocation mi)
throws Exception
{
this.sync();
boolean syncAlreadyReleased = false;
try
{
Transaction miTx = mi.getTransaction();
boolean trace = true;//log.isTraceEnabled();
if( trace ) log.trace("Begin schedule, key="+mi.getId());
// Maybe my transaction already expired?
if (miTx != null && miTx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
log.error("Saw rolled back tx="+miTx);
//Wake up the next ones, this won't scale, under
stress we will wake everyone...
//synchronized(lock) {lock.notifyAll();}
//and get out of here
throw new RuntimeException("Transaction marked for rollback, possibly
a timeout");
}
//Next test is independent of whether the context is locked or not, it is
purely transactional
// Is the instance involved with another transaction? if so we implement
pessimistic locking
// Do we have a running transaction with the context?
if (tx != null &&
// And are we trying to enter with another transaction?
!tx.equals(miTx))
{
// That's no good, only one transaction per context
// Let's put the thread to sleep the transaction demarcation will wake
them up
if( trace ) log.trace("Transactional contention on context"+id);
try
{
if( trace ) log.trace("Begin wait on Tx="+tx);
// And lock the threads on the lock corresponding to the Tx in MI
synchronized(lock)
{
// We are going to wait on one of the implementation locks
therefore
// we need to release the sync on the BeanLock object itself
syncAlreadyReleased = true;
releaseSync();
//Wait a thread coming out will wake
you up
//lock.wait(txTimeout);
lock.wait(5000);
}
if( trace ) log.trace("End wait on TxLock="+tx);
}
// We need to try again
finally {return false;}
}
// The following code should really be done in a superclass of the lock
// it implements the default serialization from the specification
// The next test is the pure serialization from the EJB specification.
// If we are here we either did not have a tx(tx == null) or this is a
// recursive call and the current ctx.tx == mi.tx
// The transaction is good and current.
//Is the context used already (with or without a transaction), is it locked?
if (isMethodLocked())
{
// It is locked but re-entrant calls permitted (reentrant home ones are
ok as well)
if (!isCallAllowed(mi))
{
// This instance is in use and you are not permitted to reenter
// Go to sleep and wait for the lock to be released
if( trace ) log.trace("Thread contention on lock"+id);
// Threads finishing invocation will notify() on the lock
try
{
if( trace ) log.trace("Begin lock.wait(), id="+mi.getId());
synchronized(lock)
{
// we're about to wait on a different object, so release synch
on beanlock
// Also, this notify should always be within the synch(lock)
block
syncAlreadyReleased = true;
releaseSync();
//lock.wait(txTimeout);
lock.wait(5000);
}
}
catch (InterruptedException ignored) {}
// We need to try again
finally
{
if( trace ) log.trace("End lock.wait(), id="+mi.getId()+",
isLocked="+isMethodLocked());
return false;
}
}
else
{
//We are in a valid reentrant call so add a method lock
addMethodLock();
}
}
// No one is using that instance
else
{
// We are now using the instance
addMethodLock();
}
// keep track of the transaction in the lock
tx = miTx;
}
finally
{
if (!syncAlreadyReleased) this.releaseSync();
}
//If we reach here we are properly scheduled to go through so return true
return true;
}
//This is called if the synchronization missed registration (Sync interceptor)
public void wontSynchronize(Transaction transaction)
{
tx = null;
synchronized(lock) {lock.notifyAll();}
}
// This is called up synchronization to notify the end of the transaction
public void endTransaction(Transaction transaction)
{
//The tx is done
tx = null;
synchronized(lock) {lock.notifyAll();}
}
/**
* releaseMethodLock
*
* if we reach the count of zero it means the instance is free from threads (and
reentrency)
* we wake up the next thread in the currentLock
*/
public void releaseMethodLock()
{
numMethodLocks--;
//Wake up a thread to do work on the instance within the current
transaction
if (numMethodLocks ==0) synchronized(lock) {lock.notifyAll();}
}
}
_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/jboss-development