arlut.csd.ganymede
Class DBNameSpace

java.lang.Object
  |
  +--java.rmi.server.RemoteObject
        |
        +--java.rmi.server.RemoteServer
              |
              +--java.rmi.server.UnicastRemoteObject
                    |
                    +--arlut.csd.ganymede.DBNameSpace
All Implemented Interfaces:
NameSpace, java.rmi.Remote, java.io.Serializable

public final class DBNameSpace
extends java.rmi.server.UnicastRemoteObject
implements NameSpace

DBNameSpaces are the objects used to manage unique value tracking in DBFields that are unique value constrained. DBNameSpace is smart enough to coordinate unique value allocation and management during object editing across concurrent transactions.

In general, transactions in Ganymede are not able to affect each other at all, save through the acquisition of exclusive editing locks on invidivual objects, and through the atomic acquisition of values for unique value constrained DBFields. Once a transaction allocates a unique value using either the mark(), unmark(), or reserve() methods, no other transaction can allocate that value, until the first transaction calls the commit(), abort(), or rollback() methods.

In order to perform this unique value management, DBNameSpace maintains a private Hashtable, uniqueHash, that associates the allocated vales in the namespace with DBNameSpaceHandle objects which track the transaction that is manipulating the value, if any, as well as the DBField object in the database that is checked in with that value. The GanymedeSession query logic takes advantage of this to do optimized, hashed look-ups of values for unique value constrained fields to locate objects in the database rather than having to iterate over all objects of a given type to find a particular match.

DBNameSpaces may be defined in the server's schema editor to be either case sensitive or case insensitive. The DBNameSpace class uses the GHashtable class to handle the representational issues in the unique value hash for this, as well as for things like IP address representation.

See Also:
Serialized Form

Field Summary
private  boolean caseInsensitive
          treat differently-cased Strings as the same for key?
(package private) static boolean debug
           
private  java.lang.String name
          the name of this namespace
private  java.util.Hashtable saveHash
          During schema editing, we keep a copy of the uniqueHash that we had when schema edited started.
private  java.util.Hashtable transactions
          Hashtable mapping DBEditSet's currently active modifying values in this namespace to DBNameSpaceTransaction objects.
(package private) static int TRANSCOUNT
          The number of simultaneous transactions in progress that we will size the transactions hash for.
private  java.util.Hashtable uniqueHash
          Hashtable mapping values allocated (permanently, for objects checked in to the database, or temporarily, for objects being manipulated by active transactions) in this namespace to DBNameSpaceHandle objects that track the current status of the values.
 
Fields inherited from class java.rmi.server.UnicastRemoteObject
 
Fields inherited from class java.rmi.server.RemoteServer
 
Fields inherited from class java.rmi.server.RemoteObject
ref
 
Constructor Summary
DBNameSpace(java.io.DataInput in)
          Create a new DBNameSpace object from a stream definition.
DBNameSpace(java.lang.String name, boolean caseInsensitive)
          Constructor for new DBNameSpace for DBStore initialization.
 
Method Summary
 void abort(arlut.csd.ganymede.DBEditSet editSet)
          Method to revert an editSet's namespace modifications to its original state.
 void checkpoint(arlut.csd.ganymede.DBEditSet editSet, java.lang.String name)
          Method to checkpoint namespace changes made by a specific transaction so that state can be rolled back if necessary at a later time.
 void clearHandle(java.lang.Object value)
          Publicly accessible function used to clear the given value from this namespace in a non-transactional fashion.
 void commit(arlut.csd.ganymede.DBEditSet editSet)
          Method to put the editSet's current namespace modifications into final effect and to make any abandoned values available for other namespaces.
 boolean containsKey(java.lang.Object value)
          Returns true if this namespace has value allocated.
private  void dumpNameSpace()
           
 void emit(java.io.DataOutput out)
          Write out a namespace definition to a DataOutput stream.
 void emitXML(arlut.csd.ganymede.XMLDumpContext xDump)
          Write out an XML entity for this namespace.
 arlut.csd.ganymede.DBNameSpaceHandle getHandle(java.lang.Object value)
          Returns the DBNameSpaceHandle associated with the value in this namespace, or null if the value is not allocated in this namespace.
 java.lang.String getName()
          Returns the name of this namespace.
 arlut.csd.ganymede.DBNameSpaceTransaction getTransactionRecord(arlut.csd.ganymede.DBEditSet transaction)
          This method returns the DBNameSpaceTransaction associated with the given transaction, creating one if one was not previously so associated.
 boolean isCaseInsensitive()
          Returns true if case is to be disregarded in comparing entries in namespace managed fields.
 boolean isSchemaEditInProgress()
          Returns true if this namespace has already been checked out for schema editing.
 arlut.csd.ganymede.DBField lookup(java.lang.Object value)
          This method allows the namespace to be used as a unique valued search index.
 boolean mark(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value, arlut.csd.ganymede.DBField field)
          This method marks a value as being used in this namespace.
 void popCheckpoint(arlut.csd.ganymede.DBEditSet editSet, java.lang.String name)
          Method to remove a checkpoint from this namespace's DBNameSpaceCkPoint hash.
 void putHandle(java.lang.Object value, arlut.csd.ganymede.DBNameSpaceHandle handle)
          Publicly accessible function used to associate the given DBNameSpaceHandle with a value in this namespace in a non-transactional fashion.
 void receive(java.io.DataInput in)
          Read in a namespace definition from a DataInput stream.
private  void remember(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value)
           
 boolean reserve(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value)
          This method reserves a value so that the given editSet is assured of being able to use this value at some point before the transaction is commited or canceled.
 boolean reserve(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value, boolean onlyUnused)
          This method reserves a value so that the given editSet is assured of being able to use this value at some point before the transaction is commited or canceled.
 boolean rollback(arlut.csd.ganymede.DBEditSet editSet, java.lang.String name)
          Method to rollback namespace changes made by a specific transaction to a checkpoint.
 void schemaEditAbort()
          This method aborts any changes made during schema editing.
 void schemaEditCheckout()
          This method prepares this DBNameSpace for changes to be made during schema editing.
 void schemaEditCommit()
          This method locks in any changes made after schema editing is complete.
 boolean schemaEditRegister(java.lang.Object value, arlut.csd.ganymede.DBField field)
          This method links the given value to the specified field.
 boolean schemaEditUnregister(java.lang.Object value, arlut.csd.ganymede.Invid objid, short field)
          This method unlinks a single item from this namespace index, if and only if the value is associated with the object and field id specified.
 void schemaEditUnregister(short objectType, short fieldId)
          This method unlinks all values that are associated with the specified object type and field id from this namespace.
 void setInsensitive(boolean b)
          Turns case sensitivity on/off.
 boolean setName(java.lang.String newName)
          Sets the name of this namespace.
 boolean testmark(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value)
          This method tests to see whether a value in the namespace can be marked as in use.
 boolean testunmark(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value)
          This method tests to see whether a value in the namespace can be marked as not in use.
 java.lang.String toString()
           
 boolean unmark(arlut.csd.ganymede.DBEditSet editSet, java.lang.Object value)
          Used to mark a value as not used in the namespace.
 
Methods inherited from class java.rmi.server.UnicastRemoteObject
clone, exportObject, exportObject, exportObject, unexportObject
 
Methods inherited from class java.rmi.server.RemoteServer
getClientHost, getLog, setLog
 
Methods inherited from class java.rmi.server.RemoteObject
equals, getRef, hashCode, toStub
 
Methods inherited from class java.lang.Object
finalize, getClass, notify, notifyAll, wait, wait, wait
 

Field Detail

debug

static final boolean debug
See Also:
Constant Field Values

TRANSCOUNT

static final int TRANSCOUNT

The number of simultaneous transactions in progress that we will size the transactions hash for.

See Also:
Constant Field Values

caseInsensitive

private boolean caseInsensitive

treat differently-cased Strings as the same for key?


name

private java.lang.String name

the name of this namespace


uniqueHash

private java.util.Hashtable uniqueHash

Hashtable mapping values allocated (permanently, for objects checked in to the database, or temporarily, for objects being manipulated by active transactions) in this namespace to DBNameSpaceHandle objects that track the current status of the values.


saveHash

private java.util.Hashtable saveHash

During schema editing, we keep a copy of the uniqueHash that we had when schema edited started. If fields are detached or attached to this namespace during schema editing, we will make the appropriate changes to the uniqueHash. If the schema edit is committed, uniqueHash is kept and saveHash is cleared. If the schema edit is cancelled, uniqueHash is set back to saveHash and the saveHash reference is cleared. saveHash will always be null except during schema editing.


transactions

private java.util.Hashtable transactions

Hashtable mapping DBEditSet's currently active modifying values in this namespace to DBNameSpaceTransaction objects.

Constructor Detail

DBNameSpace

public DBNameSpace(java.io.DataInput in)
            throws java.io.IOException,
                   java.rmi.RemoteException
Create a new DBNameSpace object from a stream definition.


DBNameSpace

public DBNameSpace(java.lang.String name,
                   boolean caseInsensitive)
            throws java.rmi.RemoteException
Constructor for new DBNameSpace for DBStore initialization.

Parameters:
name - Name for this name space
caseInsensitive - If true, case is disregarded in this namespace
Method Detail

receive

public void receive(java.io.DataInput in)
             throws java.io.IOException
Read in a namespace definition from a DataInput stream.

java.io.IOException

emit

public void emit(java.io.DataOutput out)
          throws java.io.IOException
Write out a namespace definition to a DataOutput stream.

java.io.IOException

emitXML

public void emitXML(arlut.csd.ganymede.XMLDumpContext xDump)
             throws java.io.IOException

Write out an XML entity for this namespace.

java.io.IOException

getName

public java.lang.String getName()
Returns the name of this namespace.

Specified by:
getName in interface NameSpace
See Also:
NameSpace

setName

public boolean setName(java.lang.String newName)
Sets the name of this namespace. Returns false if the name is already taken by another namespace

Specified by:
setName in interface NameSpace
See Also:
NameSpace

isCaseInsensitive

public boolean isCaseInsensitive()
Returns true if case is to be disregarded in comparing entries in namespace managed fields.

Specified by:
isCaseInsensitive in interface NameSpace
See Also:
NameSpace

setInsensitive

public void setInsensitive(boolean b)
Turns case sensitivity on/off. If b is true, case will be disregarded in comparing entries in namespace managed fields.

Specified by:
setInsensitive in interface NameSpace
See Also:
NameSpace

getTransactionRecord

public arlut.csd.ganymede.DBNameSpaceTransaction getTransactionRecord(arlut.csd.ganymede.DBEditSet transaction)

This method returns the DBNameSpaceTransaction associated with the given transaction, creating one if one was not previously so associated.

This method will always return a valid DBNameSpaceTransaction record.


containsKey

public boolean containsKey(java.lang.Object value)

Returns true if this namespace has value allocated.


getHandle

public arlut.csd.ganymede.DBNameSpaceHandle getHandle(java.lang.Object value)

Returns the DBNameSpaceHandle associated with the value in this namespace, or null if the value is not allocated in this namespace.


putHandle

public void putHandle(java.lang.Object value,
                      arlut.csd.ganymede.DBNameSpaceHandle handle)

Publicly accessible function used to associate the given DBNameSpaceHandle with a value in this namespace in a non-transactional fashion.

Used by the DBObject receive() method and the DBJournal's JournalEntry class' process() method to build up the namespace during server start-up.


clearHandle

public void clearHandle(java.lang.Object value)

Publicly accessible function used to clear the given value from this namespace in a non-transactional fashion.

Used by DBJournal's JournalEntry class' process() method to rectify the namespace during server start-up.


lookup

public arlut.csd.ganymede.DBField lookup(java.lang.Object value)

This method allows the namespace to be used as a unique valued search index.

Note that this lookup only works for precise equality lookup.. i.e., strings must be the same capitalization and the whole works.

As well, this method is really probably useful in the context of a DBReadLock, but we're not doing anything to enforce this requirement at this point.

Parameters:
value - The value to search for in the namespace hash.

testmark

public boolean testmark(arlut.csd.ganymede.DBEditSet editSet,
                        java.lang.Object value)

This method tests to see whether a value in the namespace can be marked as in use. Such a marking is done to allow an editset to have the ability to juggle values in namespace associated fields without allowing another editset to acquire a value needed if the editset transaction is aborted.

For array db fields, all elements in the array should be testmark'ed in the context of a synchronized block on the namespace before going back and marking each value (while still in the synchronized block on the * namespace).. this ensures that we won't mark several values in an array before discovering that one of the values in a DBArrayField is already taken.

Parameters:
editSet - The transaction testing permission to claim value.
value - The unique value desired by editSet.

mark

public boolean mark(arlut.csd.ganymede.DBEditSet editSet,
                    java.lang.Object value,
                    arlut.csd.ganymede.DBField field)

This method marks a value as being used in this namespace. Marked values are held in editSet's, which are free to shuffle these reserved values around during processing. The inuse and original fields in the namespace object are used during unique value searches to do direct hash lookups without getting confused by any shuffling being performed by a current thread.

Parameters:
value - The unique value that transaction editset is attempting to claim
field - The DBField which will take the unique value. Used to provide an index.

reserve

public boolean reserve(arlut.csd.ganymede.DBEditSet editSet,
                       java.lang.Object value)

This method reserves a value so that the given editSet is assured of being able to use this value at some point before the transaction is commited or canceled. reserve() is different from mark() in that there is no field specified to be holding the value, and that when the transaction is committed or canceled, the value will be returned to the available list. During the transaction, the transaction code can mark the value at any time with assurance that they will be able to do so.

If a transaction attempts to reserve() a value that is already being held by an object in the transaction, reserve() will return true, even though a subsequent mark() attempt would fail.

Parameters:
value - The unique value that transaction editset is attempting to claim
Returns:
true if the value could be reserved in the given editSet.

reserve

public boolean reserve(arlut.csd.ganymede.DBEditSet editSet,
                       java.lang.Object value,
                       boolean onlyUnused)

This method reserves a value so that the given editSet is assured of being able to use this value at some point before the transaction is commited or canceled. reserve() is different from mark() in that there is no field specified to be holding the value, and that when the transaction is committed or canceled, the value will be returned to the available list. During the transaction, the transaction code can mark the value at any time with assurance that they will be able to do so.

If onlyUsed is false and a transaction attempts to reserve() a value that is already being held by an object in the transaction, reserve() will return true, even though a subsequent mark() attempt would fail.

Parameters:
value - The unique value that transaction editset is attempting to claim
onlyUnused - If true, reserve() will return false if the value is already attached to a field connected to this namespace, even if in an object attached to the editSet provided.
Returns:
true if the value could be reserved in the given editSet.

testunmark

public boolean testunmark(arlut.csd.ganymede.DBEditSet editSet,
                          java.lang.Object value)

This method tests to see whether a value in the namespace can be marked as not in use. Such a marking is done to allow an editset to have the ability to juggle values in namespace associated fields without allowing another editset to acquire a value needed if the editset transaction is aborted.

For array db fields, all elements in the array should be testunmark'ed in the context of a synchronized block on the namespace before actually unmarking all the values. See the comments in testmark() for the logic here. Note that testunmark() is less useful than testmark() because we really aren't expecting anything to prevent us from unmarking() something.

Parameters:
editSet - The transaction that is determining whether value can be freed.
value - The unique value being tested.

unmark

public boolean unmark(arlut.csd.ganymede.DBEditSet editSet,
                      java.lang.Object value)

Used to mark a value as not used in the namespace. Unmarked values are not available for other threads / editset's until commit is called on this namespace on behalf of this editset.

Parameters:
editSet - The transaction that is freeing value.
value - The unique value being tentatively marked as unused.

checkpoint

public void checkpoint(arlut.csd.ganymede.DBEditSet editSet,
                       java.lang.String name)

Method to checkpoint namespace changes made by a specific transaction so that state can be rolled back if necessary at a later time.

Parameters:
editSet - The transaction that needs to be checkpointed.
name - The name of the checkpoint to be marked.

popCheckpoint

public void popCheckpoint(arlut.csd.ganymede.DBEditSet editSet,
                          java.lang.String name)

Method to remove a checkpoint from this namespace's DBNameSpaceCkPoint hash.

This method really isn't very important, because when the transaction is committed or aborted, the checkpoints hashtable will be cleared of editSet anyway.

Parameters:
editSet - The transaction that is requesting the checkpoint pop.
name - The name of the checkpoint to be popped.

rollback

public boolean rollback(arlut.csd.ganymede.DBEditSet editSet,
                        java.lang.String name)

Method to rollback namespace changes made by a specific transaction to a checkpoint.

Parameters:
editSet - The transaction that needs to be checkpointed.
name - The name of the checkpoint to be rolled back to.
Returns:
false if the checkpoint could not be found.

abort

public void abort(arlut.csd.ganymede.DBEditSet editSet)

Method to revert an editSet's namespace modifications to its original state. Used when a transaction is rolled back.

Parameters:
editSet - The transaction whose claimed values in this namespace need to be freed.

commit

public void commit(arlut.csd.ganymede.DBEditSet editSet)

Method to put the editSet's current namespace modifications into final effect and to make any abandoned values available for other namespaces.

Note that a NameSpace should really never fail here. We assume that all NameSpace management code up to this point has functioned properly.. at this point, the EditSet has already committed changes to the DBStore and to any external processes, we're just doing paperwork at this point.

Parameters:
editSet - The transaction being committed.

remember

private void remember(arlut.csd.ganymede.DBEditSet editSet,
                      java.lang.Object value)

dumpNameSpace

private void dumpNameSpace()

toString

public java.lang.String toString()
Overrides:
toString in class java.rmi.server.RemoteObject

schemaEditCheckout

public void schemaEditCheckout()

This method prepares this DBNameSpace for changes to be made during schema editing. A back-up copy of the uniqueHash is made, to allow reversion in case the schema edit is aborted. Between schemaEditCheckout() and either schemaEditCommit() or schemaEditAbort(), the schemaEditRegister() and schemaEditUnregister() methods may be called to handle attaching/detaching fields to the namespace.


isSchemaEditInProgress

public boolean isSchemaEditInProgress()

Returns true if this namespace has already been checked out for schema editing.


schemaEditCommit

public void schemaEditCommit()

This method locks in any changes made after schema editing is complete.


schemaEditAbort

public void schemaEditAbort()

This method aborts any changes made during schema editing.


schemaEditRegister

public boolean schemaEditRegister(java.lang.Object value,
                                  arlut.csd.ganymede.DBField field)

This method links the given value to the specified field. If the value has already been allocated, schemaEditRegister will return false and the value won't be attached to the field in question.

Returns:
true if the value could be registered with the specified field, false otherwise

schemaEditUnregister

public boolean schemaEditUnregister(java.lang.Object value,
                                    arlut.csd.ganymede.Invid objid,
                                    short field)

This method unlinks a single item from this namespace index, if and only if the value is associated with the object and field id specified.

Returns:
true if the value was associated with the object and field specified, false otherwise. If false is returned, no value was removed from this namespace.

schemaEditUnregister

public void schemaEditUnregister(short objectType,
                                 short fieldId)

This method unlinks all values that are associated with the specified object type and field id from this namespace.