arlut.csd.ganymede
Class DBEditSet

java.lang.Object
  |
  +--arlut.csd.ganymede.DBEditSet

public class DBEditSet
extends java.lang.Object

DBEditSet is the basic transactional unit. All changes to the database during normal operations are made in the context of a DBEditSet, which may then be committed or rolled back as an atomic operation. Each DBSession will have at most one DBEditSet transaction object active at any time.

A DBEditSet tracks several things for the server, including instances of DBEditObject's that were created or checked-out from the DBStore, DBNameSpace values that were reserved during the course of the transaction, and DBLogEvent objects to be recorded in the DBLog and/or mailed out to various interested parties when the transaction is committed.

DBEditSet's transaction logic is based on a two-phase commit protocol, where all DBEditObject's involved in the transaction are given an initial opportunity to approve or reject the transaction's commit before the DBEditSet commit method goes back and 'locks-in' the changes. DBEditObjects are able to initiate changes external to the Ganymede database in their commitPhase2() methods, if needed.

When a DBEditSet is committed, a DBWriteLock is established on all DBObjectBase's involved in the transaction. All objects checked out by that transaction are then updated in the DBStore, and a summary of changes is recorded to the DBStore DBJournal. The database as a whole will not be dumped to disk unless and until the dumpTask is run, or until the server undergoes a formal shutdown.

Typically, the commit() method is called by the GanymedeSession.commitTransaction() method, which will induce the server to schedule any commit-time build tasks registered with the GanymedeScheduler.

If a DBEditSet commit() operation fails catastrophically, or if abort() is called, all DBEditObjects created or checked out during the course of the transaction will be discarded, all DBNameSpace values allocated will be relinquished, and any logging information for the abandoned transaction will be forgotten.

As if all that wasn't enough, the DBEditSet class also maintains a stack of DBCheckPoint objects to enable users to set checkpoints during the course of a transaction. These objects are basically a snapshot of the transaction's state at the moment of the checkpoint, and are used to rollback the transaction to a known state if a series of linked operations within a transaction cannot all be completed.

Finally, note that the DBEditSet class does not actually track namespace value allocations.. instead, the DBNameSpace class is responsible for recording a list of values allocated by each active DBEditSet. When a DBEditSet commits or releases, all DBNameSpace objects in the server are informed of this, whereupon they do their own cleanup.


Field Summary
private  java.util.Hashtable basesModified
          A record of the DBObjectBase's touched by this transaction.
private  arlut.csd.Util.NamedStack checkpoints
          A stack of DBCheckPoint objects to keep track of check points performed during the course of this transaction.
private  java.lang.Thread currentCheckpointThread
          We keep track of the thread that is doing checkpointing..
(package private)  arlut.csd.ganymede.DBStore dbStore
          Who's our daddy?
(package private) static boolean debug
           
(package private)  java.lang.String description
          A brief description of the client associated with this transaction, used in logging to identify what was done by the main client, what by a password-changing utility, etc.
private  boolean interactive
          True if this DBEditSet is operating in interactive mode.
private  java.util.Vector logEvents
          A list of DBLogEvent's to be written to the Ganymede logfile and/or mailed out when this transaction commits.
private  boolean mustAbort
          True if this DBEditSet is operating in non-interactive mode and a rollback was ordered.
private  java.util.Hashtable objects
          A hashtable mapping Invids to DBEditObjects checked out in care of this transaction.
(package private)  arlut.csd.ganymede.DBSession session
          A reference to the DBSession that this transaction is attached to.
private  arlut.csd.ganymede.DBWriteLock wLock
          The writelock acquired during the course of a commit attempt.
 
Constructor Summary
DBEditSet(arlut.csd.ganymede.DBStore dbStore, arlut.csd.ganymede.DBSession session, java.lang.String description, boolean interactive)
          Constructor for DBEditSet
 
Method Summary
 boolean abort()
          This method is intended for use by DBSession's abortTransaction() method, and returns true if the transaction could be aborted, false otherwise.
 boolean addObject(arlut.csd.ganymede.DBEditObject object)
          Method to associate a DBEditObject with this transaction.
 void checkpoint(java.lang.String name)
          This method checkpoints the current transaction at its current state.
private  void commit_checkObjectMissingFields(arlut.csd.ganymede.DBEditObject eObj)
          This private helper method for the commit() method runs a check looking for missing mandatory fields on an object involved with this transaction.
private  void commit_createLogEvent(arlut.csd.ganymede.DBEditObject eObj)
          This private helper method is executed in the middle of the commit() method, and handles logging for any changes made to a DBEditObject during the committed transaction.
private  void commit_createLogEvents()
          This private helper method is executed in the middle of the commit() method, and handles logging for any changes made to objects during the committed transaction.
private  void commit_handlePhase1()
          This private helper method for the commit() method handles phase 1 of transaction commit.
private  void commit_handlePhase2()
          This private helper method for the commit() method records the creation/modification timestamp for the vector of committed objects, then calls commitPhase2() on them.
private  void commit_integrateChanges()
          This private helper method for commit() integrates all committed objects back into the DBStore, handling on-disk change journaling, transaction logging, namespaces, and more.
private  java.util.Vector commit_lockBases()
          Obtain a write lock on all bases modified by this transaction.
private  void commit_logTransaction()
          This method handles the on-disk and email logging for events that have built up over the course of this transaction.
private  void commit_persistTransaction()
          This private helper method for commit() writes the transaction to the on-disk transactions journal, which will persist our transaction's changes.
private  void commit_replaceObjects()
          Private helper method for commit() that integrates committed objects back into the DBStore hashes.
private  void commit_updateBases()
          Private helper method for commit() which causes all bases that were touched by this transaction to be updated.
private  void commit_updateNamespaces()
          Private helper method for commit() which causes all namespaces to update themselves in conjunction with a commit.
 arlut.csd.ganymede.ReturnVal commit()
          commit is used to cause all changes in association with this DBEditSet to be performed.
private  void deconstruct()
          Private helper method for commit() and release(), which breaks apart and nulls references to data structures maintained for this transaction to aid GC.
 arlut.csd.ganymede.DBEditObject findObject(arlut.csd.ganymede.DBObject object)
          Method to find a DBObject / DBEditObject if it has previously been checked out to this EditSet in some fashion.
 arlut.csd.ganymede.DBEditObject findObject(arlut.csd.ganymede.Invid invid)
          Method to find a DBObject / DBEditObject if it has previously been checked out to this EditSet in some fashion.
 arlut.csd.ganymede.GanymedeSession getGSession()
          Returns the user-level GanymedeSession object associated with this transaction, or null if there is no GanymedeSession associated.
 java.util.Hashtable getObjectHashClone()
          To allow the GanymedeSession to get a copy of our object hash.
 arlut.csd.ganymede.DBEditObject[] getObjectList()
          Return a list of objects that we are currently working on.
 arlut.csd.ganymede.DBSession getSession()
          Method to return the DBSession handle owning this transaction.
 boolean isInteractive()
          This method returns true if this transaction is being carried out by an interactive client.
 boolean isOversightOn()
          Returns true if the GanymedeSession associated with this transaction has oversight turned on.
 void logEvent(arlut.csd.ganymede.DBLogEvent event)
          This method is used to register a log event with this transaction.
 void logEvent(java.lang.String eventClassToken, java.lang.String description, arlut.csd.ganymede.Invid admin, java.lang.String adminName, java.util.Vector objects, java.util.Vector notifyList)
          This method is used to register a log event with this transaction.
 void logMail(java.util.Vector addresses, java.lang.String subject, java.lang.String message)
          This method is used to record a message to be sent out when the transaction is committed.
 void logMail(java.util.Vector addresses, java.lang.String subject, java.lang.String message, arlut.csd.ganymede.Invid admin, java.lang.String adminName, java.util.Vector objects)
          This method is used to record a message to be sent out when the transaction is committed.
 arlut.csd.ganymede.DBCheckPoint popCheckpoint(java.lang.String name)
          This method is used to pop a checkpoint off the checkpoint stack.
 arlut.csd.ganymede.DBCheckPoint popCheckpoint(java.lang.String name, boolean inRollback)
          This method is used to pop a checkpoint off the checkpoint stack.
private  void release()
          release is used to abandon all changes made in association with this DBEditSet.
private  void releaseWriteLock()
          This is a dinky little private helper method to keep things clean.
 boolean rollback(java.lang.String name)
          This brings this transaction back to the state it was at at the time of the matching checkPoint() call.
private  void syncObjBackPointers(arlut.csd.ganymede.DBEditObject obj)
          This method is executed towards the end of a transaction commit, and compares the current state of this object with its original state, and makes the appropriate changes to the backPointers hash in the server's DBStore object.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

debug

static final boolean debug
See Also:
Constant Field Values

objects

private java.util.Hashtable objects

A hashtable mapping Invids to DBEditObjects checked out in care of this transaction.


logEvents

private java.util.Vector logEvents

A list of DBLogEvent's to be written to the Ganymede logfile and/or mailed out when this transaction commits.


basesModified

private java.util.Hashtable basesModified

A record of the DBObjectBase's touched by this transaction. These DBObjectBase's will be locked when this transaction is committed.

This Hashtable maps DBObjectBase's to this, serving as a pre-Java 2 HashSet.


dbStore

arlut.csd.ganymede.DBStore dbStore
Who's our daddy?


session

arlut.csd.ganymede.DBSession session
A reference to the DBSession that this transaction is attached to.


description

java.lang.String description
A brief description of the client associated with this transaction, used in logging to identify what was done by the main client, what by a password-changing utility, etc.


checkpoints

private arlut.csd.Util.NamedStack checkpoints
A stack of DBCheckPoint objects to keep track of check points performed during the course of this transaction.


currentCheckpointThread

private java.lang.Thread currentCheckpointThread

We keep track of the thread that is doing checkpointing.. once a thread starts a checkpoint on this transaction, we don't allow any other threads to checkpoint until the first thread releases its checkpoint. This is to prevent problems resulting from interleaved checkpoint/popCheckpoint/rollback activities across multiple threads.


wLock

private arlut.csd.ganymede.DBWriteLock wLock
The writelock acquired during the course of a commit attempt. We keep this around as a DBEditSet field so that we can use the handy releaseWriteLock() method, but wLock should really never be non-null outside of the context of the commit() call.


interactive

private boolean interactive

True if this DBEditSet is operating in interactive mode.


mustAbort

private boolean mustAbort

True if this DBEditSet is operating in non-interactive mode and a rollback was ordered. In such cases, the server skipped doing the checkpoint and so has no choice but to condemn the whole transaction.

Constructor Detail

DBEditSet

public DBEditSet(arlut.csd.ganymede.DBStore dbStore,
                 arlut.csd.ganymede.DBSession session,
                 java.lang.String description,
                 boolean interactive)
Constructor for DBEditSet

Parameters:
dbStore - The owning DBStore object.
session - The DBStore session owning this transaction.
description - An optional string to identify this transaction
interactive - If false, this transaction will operate in non-interactive mode. Certain Invid operations will be optimized to avoid doing choice list queries and bind checkpoint operations. When a transaction is operating in non-interactive mode, any failure that cannot be handled cleanly due to the optimizations will result in the transaction refusing to commit when commitTransaction() is attempted. This mode is intended for batch operations.
Method Detail

getSession

public arlut.csd.ganymede.DBSession getSession()

Method to return the DBSession handle owning this transaction.


getGSession

public final arlut.csd.ganymede.GanymedeSession getGSession()

Returns the user-level GanymedeSession object associated with this transaction, or null if there is no GanymedeSession associated.


isOversightOn

public final boolean isOversightOn()

Returns true if the GanymedeSession associated with this transaction has oversight turned on.


isInteractive

public boolean isInteractive()

This method returns true if this transaction is being carried out by an interactive client.


getObjectHashClone

public java.util.Hashtable getObjectHashClone()

To allow the GanymedeSession to get a copy of our object hash.


getObjectList

public arlut.csd.ganymede.DBEditObject[] getObjectList()

Return a list of objects that we are currently working on.


findObject

public arlut.csd.ganymede.DBEditObject findObject(arlut.csd.ganymede.DBObject object)

Method to find a DBObject / DBEditObject if it has previously been checked out to this EditSet in some fashion.


findObject

public arlut.csd.ganymede.DBEditObject findObject(arlut.csd.ganymede.Invid invid)

Method to find a DBObject / DBEditObject if it has previously been checked out to this EditSet in some fashion. This method is used to allow consistency check code in the DBEditObjects to get a transaction consistent view of the system as it stands with the transaction's changes made.


addObject

public boolean addObject(arlut.csd.ganymede.DBEditObject object)

Method to associate a DBEditObject with this transaction.

This method is called by the createDBObject and editDBObject methods in DBSession.

Parameters:
object - The newly created DBEditObject.

logEvent

public void logEvent(java.lang.String eventClassToken,
                     java.lang.String description,
                     arlut.csd.ganymede.Invid admin,
                     java.lang.String adminName,
                     java.util.Vector objects,
                     java.util.Vector notifyList)

This method is used to register a log event with this transaction. If the transaction successfully commits, the provided log event will be recorded in the Ganymede log file and mail notification will be sent out if appropriate.

Parameters:
eventClassToken - a short string specifying a DBObject record describing the general category for the event
description - Descriptive text to be entered in the record of the event
admin - Invid pointing to the adminPersona that fired the event, if any
adminName - String containing the name of the adminPersona that fired the event, if any
objects - A vector of invids of objects involved in this event.
notifyList - A vector of Strings listing email addresses to send notification of this event to.

logEvent

public void logEvent(arlut.csd.ganymede.DBLogEvent event)

This method is used to register a log event with this transaction. If the transaction successfully commits, the provided log event will be recorded in the Ganymede log file and mail notification will be sent out if appropriate.

Parameters:
event - A pre-formed log event to register with this transaction.

logMail

public void logMail(java.util.Vector addresses,
                    java.lang.String subject,
                    java.lang.String message,
                    arlut.csd.ganymede.Invid admin,
                    java.lang.String adminName,
                    java.util.Vector objects)

This method is used to record a message to be sent out when the transaction is committed.

Parameters:
subject - The subject line of the message
message - The body of the message
admin - The invid of the admin whose action resulted in the mail
adminName - The name of the admin whose actin resulted in the mail
objects - A vector of invids of objects involved in the mail

logMail

public void logMail(java.util.Vector addresses,
                    java.lang.String subject,
                    java.lang.String message)

This method is used to record a message to be sent out when the transaction is committed.

Parameters:
subject - The subject line of the message
message - The body of the message

checkpoint

public void checkpoint(java.lang.String name)

This method checkpoints the current transaction at its current state. If need be, this transaction can later be rolled back to this point by calling the rollback() method.

Once a thread checkpoints a transaction, no other thread can checkpoint a transaction until some thread clears the checkpoint, either by doing a rollback() or a popCheckpoint(). checkpoint() will block any threads that try to establish a checkpoint() until the prior thread's checkpoint is resolved.

See DBSession.deleteDBObject() and DBSesssion.createDBObject() for instances of this.

Parameters:
name - An identifier for this checkpoint

popCheckpoint

public arlut.csd.ganymede.DBCheckPoint popCheckpoint(java.lang.String name)

This method is used to pop a checkpoint off the checkpoint stack. This method is equivalent to a rollback where the checkpoint information is taken off the stack, but this DBEditSet's state is not reverted.

Any checkpoints that were placed on the stack after the checkpoint matching <name> will also be removed from the checkpoint stack.

Parameters:
name - An identifier for the checkpoint to take off the checkpoint stack.
Returns:
null if the checkpoint could not be found on the stack, or the DBCheckPoint object representing the state of the transaction at the checkpoint time if the checkpoint could be found.

popCheckpoint

public arlut.csd.ganymede.DBCheckPoint popCheckpoint(java.lang.String name,
                                                     boolean inRollback)

This method is used to pop a checkpoint off the checkpoint stack. This method is equivalent to a rollback where the checkpoint information is taken off the stack, but this DBEditSet's state is not reverted.

Any checkpoints that were placed on the stack after the checkpoint matching <name> will also be removed from the checkpoint stack.

Parameters:
name - An identifier for the checkpoint to take off the checkpoint stack.
inRollback - If true, popCheckpoint will not actually pop the checkpoint states out of the DBStore namespaces, leaving that for rollback() to finish up.
Returns:
null if the checkpoint could not be found on the stack, or the DBCheckPoint object representing the state of the transaction at the checkpoint time if the checkpoint could be found.

rollback

public boolean rollback(java.lang.String name)

This brings this transaction back to the state it was at at the time of the matching checkPoint() call. Any objects that were checked out in care of this transaction since the checkPoint() will be checked back into the database and made available for other transactions to access. All namespace changes made by this transaction will likewise be rolled back to their state at the checkpoint.

Parameters:
name - An identifier for the checkpoint to be rolled back to.

commit

public arlut.csd.ganymede.ReturnVal commit()

commit is used to cause all changes in association with this DBEditSet to be performed. If commit() cannot make the changes for any reason, commit() will return a ReturnVal indicating failure and the cause of the commit failure. Depending on the source of the failure, the transaction may be left open for a subsequent transaction commit or release.

The returned ReturnVal will have doNormalProcessing set to false if the transaction was completely aborted. Both DBSession and the client should take a false doNormalProcessing boolean as an indicator that the transaction was simply wiped out and a new transaction should be opened for subsequent activity. A true doNormalProcessing value indicates that the client can try the commit again at a later time, perhaps after making changes to fix the commit problem.

This method is synchronized and calls a synchronized method on the DBSession which contains this DBEditSet. Because of this, this method should really only be called by way of the DBSession.commitTransaction() method, to avoid the possibility of nested monitor deadlock.

Returns:
a ReturnVal indicating success or failure, or null on success without comment.

commit_lockBases

private final java.util.Vector commit_lockBases()
                                         throws CommitNonFatalException

Obtain a write lock on all bases modified by this transaction. This method may block indefinitely, waiting on other transactions which are in the process of modifying the DBStore hashes.

Returns a Vector of DBObjectBases that we have locked if we succeed.

Throws a CommitNonFatalException if we can't get the lock.

CommitNonFatalException

commit_handlePhase1

private final void commit_handlePhase1()
                                throws CommitNonFatalException

This private helper method for the commit() method handles phase 1 of transaction commit.

If an object refuses transaction commit, we'll throw a CommitNonFatalException with ReturnVal information encoded.

CommitNonFatalException

commit_checkObjectMissingFields

private final void commit_checkObjectMissingFields(arlut.csd.ganymede.DBEditObject eObj)
                                            throws CommitNonFatalException

This private helper method for the commit() method runs a check looking for missing mandatory fields on an object involved with this transaction.

If the object is missing fields, a CommitNonFatalException will be thrown.

CommitNonFatalException

commit_handlePhase2

private final void commit_handlePhase2()

This private helper method for the commit() method records the creation/modification timestamp for the vector of committed objects, then calls commitPhase2() on them.


commit_integrateChanges

private final void commit_integrateChanges()
                                    throws CommitFatalException

This private helper method for commit() integrates all committed objects back into the DBStore, handling on-disk change journaling, transaction logging, namespaces, and more.

CommitFatalException

commit_createLogEvents

private final void commit_createLogEvents()

This private helper method is executed in the middle of the commit() method, and handles logging for any changes made to objects during the committed transaction.


commit_createLogEvent

private final void commit_createLogEvent(arlut.csd.ganymede.DBEditObject eObj)

This private helper method is executed in the middle of the commit() method, and handles logging for any changes made to a DBEditObject during the committed transaction.


commit_persistTransaction

private final void commit_persistTransaction()
                                      throws CommitFatalException

This private helper method for commit() writes the transaction to the on-disk transactions journal, which will persist our transaction's changes.

Will throw a CommitException if a failure was detected.

CommitFatalException

commit_logTransaction

private final void commit_logTransaction()

This method handles the on-disk and email logging for events that have built up over the course of this transaction.


commit_replaceObjects

private final void commit_replaceObjects()

Private helper method for commit() that integrates committed objects back into the DBStore hashes.


commit_updateNamespaces

private final void commit_updateNamespaces()

Private helper method for commit() which causes all namespaces to update themselves in conjunction with a commit.


commit_updateBases

private final void commit_updateBases()

Private helper method for commit() which causes all bases that were touched by this transaction to be updated.

This should be run as late as possible in the commit() sequence to minimize the chance that a previously scheduled builder task completes and updates its lastRunTime field after we have touched the timestamps on the changed bases.


syncObjBackPointers

private void syncObjBackPointers(arlut.csd.ganymede.DBEditObject obj)

This method is executed towards the end of a transaction commit, and compares the current state of this object with its original state, and makes the appropriate changes to the backPointers hash in the server's DBStore object.

The purpose of this is to support the decoupling of an object from its backlinks, so that objects can be asymmetrically linked to an object without having to check that object out for editing.


abort

public boolean abort()

This method is intended for use by DBSession's abortTransaction() method, and returns true if the transaction could be aborted, false otherwise.


release

private final void release()

release is used to abandon all changes made in association with this DBEditSet. All DBObjects created, deleted, or modified, and all unique values allocated and freed during the course of actions on this transaction will be reverted to their state when this transaction was created.

Note that this does not mean that the entire DBStore will revert to its state at the beginning of this transaction; any changes not relating to objects and namespace values connected to this transaction will not be affected by this transaction's release.


deconstruct

private void deconstruct()

Private helper method for commit() and release(), which breaks apart and nulls references to data structures maintained for this transaction to aid GC.

This method also does a notifyAll() to wake up any checkpoint threads that are blocking waiting for the ability to checkpoint.


releaseWriteLock

private void releaseWriteLock()

This is a dinky little private helper method to keep things clean. It's essential that wLock be released if things go wrong, else next time this session tries to commit a transaction, it'll wind up waiting forever for the old lock to be released.