Mercurial > hg > blitz_stable
diff src/org/dancres/blitz/EntryLeaseHandlerImpl.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/dancres/blitz/EntryLeaseHandlerImpl.java Sat Mar 21 11:00:06 2009 +0000 @@ -0,0 +1,291 @@ +package org.dancres.blitz; + +import java.io.IOException; + +import java.util.logging.Level; + +import net.jini.core.lease.UnknownLeaseException; +import net.jini.core.lease.LeaseDeniedException; + +import net.jini.core.transaction.TransactionException; + +import org.dancres.blitz.disk.DiskTxn; + +import org.dancres.blitz.lease.SpaceUID; +import org.dancres.blitz.lease.LeaseHandler; +import org.dancres.blitz.lease.LeaseBounds; + +import org.dancres.blitz.entry.EntryRepositoryFactory; +import org.dancres.blitz.entry.EntryRepository; + +import org.dancres.blitz.txn.TxnId; +import org.dancres.blitz.txn.TxnOp; +import org.dancres.blitz.txn.TxnState; +import org.dancres.blitz.txn.TxnManager; + +import org.dancres.blitz.txnlock.*; + +import org.dancres.blitz.task.Task; + +import org.dancres.blitz.oid.OID; + +import org.dancres.blitz.util.Time; + +public class EntryLeaseHandlerImpl implements LeaseHandler { + public boolean recognizes(SpaceUID aUID) { + return (aUID instanceof SpaceEntryUID); + } + + public long renew(SpaceUID aUID, long aLeaseDuration) + throws UnknownLeaseException, LeaseDeniedException, IOException { + + long myDuration = LeaseBounds.boundWrite(aLeaseDuration); + long myExpiry = Time.getAbsoluteTime(myDuration); + + boolean myResult; + + String myType = ((SpaceEntryUID) aUID).getType(); + OID myOID = ((SpaceEntryUID) aUID).getOID(); + + DiskTxn myTxn = DiskTxn.newTxn(); + + try { + myResult = + EntryRepositoryFactory.get().find(myType).renew(myOID, + myExpiry); + } finally { + myTxn.commit(); + } + + if (!myResult) + throw new UnknownLeaseException(); + + LeaseRenewal myRenewal = new LeaseRenewal(myType, myOID, myExpiry); + + // Now figure out when we're gonna log this change + LockMgr myMgr = TxnLocks.getLockMgr(myType); + TxnLock myLock = myMgr.getLock(myOID); + TxnId myWriter = null; + + synchronized(myLock) { + myWriter = myLock.getWriter(); + } + + /* + If there is a writer present, the lease renewal should only be issued + on completion of the associated transaction. + + This ordering is critical for correct recovery. If we immediately + write the lease renewal it will appear in the logs before the + transaction that write's the Entry. Thus, if the Entry was never + flushed to disk, it won't be present in the cache and the lease + renewal will fail whilst apparently having succeeded from the user's + perspective. + */ + if (myWriter == null) { + log(myRenewal); + } else { + TxnState myState = null; + + try { + myState = TxnManager.get().getTxnFor(myWriter); + } catch (Exception anE) { + /* + It's either UnknownTransaction or Remote - in this case + as there's a writer, it can't be Remote so it can only be + UnknownTransaction which means the transaction completed + before we tag the lease renewal onto it. + */ + } + + if (myState == null) { + // Too late, transaction is gone + log(myRenewal); + } else { + try { + myState.add(myRenewal); + } catch (TransactionException aTE) { + // Couldn't tag it onto the transaction - must have + // resolved + log(myRenewal); + } + } + } + + return myDuration; + } + + public void cancel(SpaceUID aUID) + throws UnknownLeaseException, IOException { + + boolean myResult; + + String myType = ((SpaceEntryUID) aUID).getType(); + OID myOID = ((SpaceEntryUID) aUID).getOID(); + + DiskTxn myTxn = DiskTxn.newTxn(); + + try { + myResult = + EntryRepositoryFactory.get().find(myType).cancel(myOID); + } finally { + myTxn.commit(); + } + + if (!myResult) + throw new UnknownLeaseException(); + + LeaseCancel myCancel = new LeaseCancel(myType, myOID); + + // Now figure out when we're gonna log this change + LockMgr myMgr = TxnLocks.getLockMgr(myType); + TxnLock myLock = myMgr.getLock(myOID); + TxnId myWriter = null; + + synchronized(myLock) { + myWriter = myLock.getWriter(); + } + + /* + If there is a writer present, the lease cancel should only be issued + on completion of the associated transaction. + + This ordering is critical for correct recovery. If we immediately + write the lease cancel it will appear in the logs before the + transaction that write's the Entry. Thus, if the Entry was never + flushed to disk, it won't be present in the cache and the lease + cancel will fail whilst apparently having succeeded from the user's + perspective. + */ + if (myWriter == null) { + log(myCancel); + } else { + TxnState myState = null; + + try { + myState = TxnManager.get().getTxnFor(myWriter); + } catch (Exception anE) { + /* + It's either UnknownTransaction or Remote - in this case + as there's a writer, it can't be Remote so it can only be + UnknownTransaction which means the transaction completed + before we tag the lease cancel onto it. + */ + } + + if (myState == null) { + // Too late, transaction is gone + log(myCancel); + } else { + try { + myState.add(myCancel); + } catch (TransactionException aTE) { + // Couldn't tag it onto the transaction - must have + // resolved + log(myCancel); + } + } + } + } + + private void log(TxnOp anAction) throws IOException { + try { + TxnManager.get().log(anAction); + } catch (TransactionException aTE) { + throw new IOException("Failed to log action"); + } + } + + private static final class LeaseRenewal implements TxnOp { + + private String theType; + private OID theOID; + private long theExpiry; + + LeaseRenewal(String aType, OID aOID, long anExpiry) { + theType = aType; + theOID = aOID; + theExpiry = anExpiry; + } + + public void restore(TxnState aState) throws IOException { + EntryRepository myRepos = + EntryRepositoryFactory.get().get(theType); + + DiskTxn myTxn = DiskTxn.newTxn(); + + try { + myRepos.renew(theOID, theExpiry); + } catch (IOException anIOE) { + } finally { + myTxn.commit(); + } + } + + public void commit(TxnState aState) throws IOException { + // Nothing to do - already applied + } + + public void abort(TxnState aState) throws IOException { + // Never called + } + + public String toString() { + return " ER : " + theType + " : " + theOID + " : " + theExpiry; + } + } + + private static final class LeaseCancel implements TxnOp { + private String theType; + private OID theOID; + + LeaseCancel(String aType, OID aOID) { + theType = aType; + theOID = aOID; + } + + public void restore(TxnState aState) throws IOException { + EntryRepository myRepos = + EntryRepositoryFactory.get().get(theType); + + DiskTxn myTxn = DiskTxn.newTxn(); + + try { + myRepos.cancel(theOID); + } catch (IOException anIOE) { + } finally { + myTxn.commit(); + } + } + + public void commit(TxnState aState) throws IOException { + // Nothing to do - already applied + } + + public void abort(TxnState aState) throws IOException { + // Never called + } + + public String toString() { + return " EC : " + theType + " : " + theOID; + } + } + + private static final class LogTask implements Task { + private TxnOp theAction; + + LogTask(TxnOp anAction) { + theAction = anAction; + } + + public void run() { + try { + TxnManager.get().log(theAction); + } catch (Exception anException) { + SpaceImpl.theLogger.log(Level.SEVERE, + "Failed to log lease action", + anException); + } + } + } +}