FileDocCategorySizeDatePackage
Persistent.javaAPI DocExample18684Sat Feb 01 07:06:48 GMT 1997imaginary.persist

Persistent

public abstract class Persistent extends imaginary.util.Observable implements RemotePersistent
The Persistent class is an abstract base class for all persistent objects. A persistent object is any object which needs to be saved to a data store. The class does not care what type of data store is used. All code specific to a particular kind of persistence is pawned off onto the PersistentPeer.
see
imaginary.persist.PersistentPeer

Fields Summary
public static final int
UNMODIFIED
The object is in the same form that is stored in the data store.
public static final int
NEW
The object is brand new and has not yet been saved to the data store.
public static final int
MODIFIED
The object has been modified either since creation or restoring from the data store.
public static final int
DELETED
The object has been deleted but is still in the data store.
private static Hashtable
objects
private int
id
Instance attributes
private Lock
lock
private int
modifications
private boolean
saving
Constructors Summary
public Persistent()
Constructor for the Persistent class. Exports the object for remote viewing. Persistent is an abstract class, so it cannot be instantiated directly.

exception
java.rmi.RemoteException Failed to export object

 
    
      
                                       
        
        super();
    
Methods Summary
protected synchronized voidabort()
When a save to the data store is aborted for any reason, this method gets called in order to give the object a chance to clean up. The object should be restored to its state immediately before a save was attempted. Within the Persistent class, that simply means changing the flag indicating a save is in progress. Of course, objects extending this class may or may not have their own clean up to do.

        if( !isSaving() ) { // If this object is not saving, it needs no abort
            return;
        }
        saving = false; // stop it from saving
        // keep the lock in place
    
protected synchronized voidcommit()
When one or more saves have been sent to the data store successfully, they are committed. This method allows an object to know the pending save was successful and release any locks.

        Transaction t;
    
        // If the object is not being saved, commit not needed         
        if( !isSaving() ) { 
            return;
        }
        if( lock == null ) {
            throw new PersistenceException("Attempt to commit an unlocked " +
                                           "object.");
        }
        // end saving
        saving = false;
        // release lock
        lock.releaseLock(this);
        lock = null;
        // reset modification state
        if( isDeleted() ) {
            modifications = Persistent.DELETED;
        }
        else {
            modifications = Persistent.UNMODIFIED;
        }
    
public synchronized intgetId()
The ID is a unique identifier for this type of object. In real situations, you probably cannot always use an int for an ID field. To keep this library simple, however, we have assumed that all ID's are integers.

return
the unique identifier for this object

        return id;
    
public synchronized intgetModifications()
Modifications is a bitmask of changes which have occurred to an object since it was either restored or created. Unmodified objects have a Modifications value equal to Persistent.UNMODIFIED.

return
the modification state of this object
see
imaginary.persist.Persistent#UNMODIFIED
see
imaginary.persist.Persistent#NEW
see
imaginary.persist.Persistent#MODIFIED
see
imaginary.persist.Persistent#DELETED

        return modifications;
    
public static imaginary.persist.PersistentgetPersistent(Transaction trans, java.util.Hashtable data, java.lang.String class_name)
Gives a hashtable of values taken from a data store, this class method will find an existing object for that data or instantiate a new one. Once it has created that object, it will tell the object to restore itself using that data.

param
trans the Transaction object to use for any further data store access
param
data the data taken from the data store for this object
param
class_name the name of the class to instantiate for this set of data
exception
imaginary.persist.PersistenceException An error occurred retrieving the object.


       
                                                                                                    
        
                                            
                                            
      
        Persistent p;
        Hashtable h;
        Integer i;
        
        // create an instance of the specified class name
        // so that we know what the ID is
        try {
            p = (Persistent)Class.forName(class_name).newInstance();
            p.setId(data);
        }
        catch( ClassNotFoundException e ) {
            e.printStackTrace();
            return null;
        }
        catch( InstantiationException e ) {
            e.printStackTrace();
            return null;
        }
        catch( IllegalAccessException e ) {
            e.printStackTrace();
            return null;
        }
        // find the hashtable for this class in the objects hashtable
        if( objects.containsKey(class_name) ) {
            h = (Hashtable)objects.get(class_name);
        }
        else {
            h = new Hashtable();
            objects.put(class_name, h);
        }
        // see if this object is already in that list
        i = new Integer(p.getId());
        if( h.containsKey(i) ) {
            return (Persistent)h.get(i);
        }
        else {
            h.put(i, p);
            // restore the new object
            p.restore(trans, data);
            return p;
        }
    
public static imaginary.persist.PersistentgetPersistent(Transaction trans, int id, java.lang.String class_name)
Find or restore an object of the specified class based on an ID.

param
trans the Transaction to use for data store access
param
id the ID of the object to be retrieved
param
class_name the name of the class to instantiate
exception
imaginary.persist.PersistenceException An error occurred restoring the requested object.

        Integer i = new Integer(id);
        Hashtable h;
        
        // get the hashtable of objects for that class
        if( objects.containsKey(class_name) ) {
            h = (Hashtable)objects.get(class_name);
        }
        else {
            h = new Hashtable();
            objects.put(class_name, h);
        }
        // check if the object already exists
        if( h.containsKey(i) ) {
            return (Persistent)h.get(i);
        }
        else {
            Persistent p;
            
            try {
                // no instance found, load a new one
                p = (Persistent)Class.forName(class_name).newInstance();
                // set its ID
                p.setId(id);
                h.put(i, p);
                // restore it from the data store
                p.restore();
            }
            catch( ClassNotFoundException e ) {
                e.printStackTrace();
                return null;
            }
            catch( InstantiationException e ) {
                e.printStackTrace();
                return null;
            }
            catch( IllegalAccessException e ) {
                e.printStackTrace();
                return null;
            }
            return p;
        }
    
protected abstract PersistentPeergetPersistentPeer()
Persistent prescribes that a subclass implement this method to provide it with an instance of the peer which will perform all data store access for it.

return
the PersistentPeer for this class
see
imaginary.persist.PersistentPeer

public synchronized booleanisDeleted()
Checks to see if the object will be deleted from the data store at the next save.

return
true if the object has been deleted

        return ((modifications & Persistent.DELETED) != 0);
    
public synchronized booleanisLocked()
Checks to see if a lock is being held on this object.

return
true if some object is locking this one

        return (lock != null);
    
public synchronized booleanisModified()
Checks to see if this object differs from the data store.

return
true if the object is new, modified, or deleted

        // if any modifiers are present, it has been modified
        return !(modifications == Persistent.UNMODIFIED);
    
public synchronized booleanisNew()
Checks to see if this object is new and not yet in the data store. This will also return false if the object has been deleted.

return
true if the object is new

        if( isDeleted() ) {
            return false;
        }
        else {
            return ((modifications & Persistent.NEW) != 0);
        }
    
public synchronized booleanisSaving()
Checks to see if a save operation is in progress.

return
true if a save operation is in progress

        return saving;
    
protected synchronized voidloseLock()
For any number of reasons, a client holding a lock might unexpectedly lose that lock. The most common reason is simply a network failure between client and server. In that event, we want to release the lock and restore the object to its unmodified state.

        lock = null;
        modifications = Persistent.UNMODIFIED;
    
protected synchronized voidmodify(RemoteLockHolder h)
Whenever an attribute in this object changes from its initial state, this method should be called in order to change the object's state. If an attempt is made to modify a locked by object a RemoteLockHolder that does not hold the lock, then a LockException gets thrown.

param
h the client making the modifications
exception
imaginary.persist.LockException Attempt to modify locked object by a Transaction not holding the lock.

        // Someone is doing something they should not!
        if( lock != null ) {
            if( h.hashCode() != lock.getHolder().hashCode() ) {
                throw new LockException("Illegal attempt to modify " +
                                        "object without a lock.");
            }
        }
        // First modification!
        if( lock == null ) {
            lock = Lock.createLock(h, this);
        }
        // Add Persistent.MODIFIED to the bitmask
        modifications |= Persistent.MODIFIED;
        setChanged();
    
protected synchronized voidmonitorLock()
The lock has a thread that simply triggers this method every now and then. If the criteria for maintaining a lock are still in force, then everything is ok. Otherwise, a LockException is thrown. The only criteria for this lock is that the client holding the lock is still accessible. You could add timeouts or whatever you like.

exception
imaginary.persist.LockException The lock should be lost

        // If we cared, we might add a last touched check in here
        // to have the lock timeout
        // If so, we would throw a LockException
        notifyObservers();
    
public synchronized voidremove(RemoteLockHolder h)
This method flags the object for deletion.

param
h the client make the modification to this object
exception
imaginary.persist.LockException Attempt to delete locked object by a client not holding the lock

        // Check for a lock
        if( lock != null ) {
            if( h.hashCode() != lock.getHolder().hashCode() ) {
                throw new LockException("Illegal attempt to delete object " +
                                        "without a lock.");
            }
        }
        // Create the lock if it does not exist
        if( lock == null ) {
            lock = Lock.createLock(h, this);
        }
        // Mark the object deleted
        modifications |= Persistent.DELETED;
        setChanged();
    
protected synchronized voidrestore()
This version of restore() is called for a one-off restore. In other words, you know the object ID and you want to restore just this object.

exception
imaginary.persist.PersistenceException An error occured accessing the data store.

        Transaction t = Transaction.getTransaction();
        
        t.restore(this);
    
protected final synchronized voidrestore(java.util.Hashtable data)
Given a set of query data, restore this object.

param
data the query data to use to restore this object
exception
imaginary.persist.PersistenceException An error occurred accessing the data store.

        Transaction t = Transaction.getTransaction();
        
        t.restore(this, data);
    
public voidrestore(Transaction t)
Restore this object using only its ID for the query.

param
t the Transaction to use to access the data store
exception
imaginary.persist.PersistenceException An error occurred accessing the data store.

        getPersistentPeer().restore(this, t);
    
public abstract voidrestore(Transaction t, java.util.Hashtable data)
This object's peer will go to the data store and grab all of the attributes for this object. Once it has those values, it will shove them into a Hashtable and pass them to this method. Objects extending the Persistent class should therefore implement this method so that it takes the values out of the Hashtable and assigns them to the appropriate object attributes.

param
t the Transaction used for the restore
param
data the Hashtable of object data
exception
imaginary.persist.PersistenceException An error occured access the data store.

protected synchronized voidsave()
This method flags the object as saving and then tells its peer to perform the actual save.

exception
imaginary.persist.PersistenceException An error occured access the data store

        PersistentPeer peer;

        if( lock == null ) {
            throw new PersistenceException("Attempt to save an unlocked: " +
                                           "object.");
        }
        peer = getPersistentPeer();
        // flag the object as saving
        saving = true;
        // determine what sort of operation is required and tell the
        // peer to do it
        if( isDeleted() ) {
            peer.remove(this, lock.getTransaction());
        }
        else if( isNew() ) {
            peer.insert(this, lock.getTransaction());
        }
        else {
            peer.update(this, lock.getTransaction());
        }
    
public abstract voidsetId(java.util.Hashtable h)
Classes extending Persistent must know how to pull an ID field from a Hashtable describing restoration values. Any such class should implement this method by grabbing the ID field and calling setId(int). Example:
public void setId(Hashtable h) {
setId(((Integer)h.get("t_customer.cust_id")).intValue());
}

param
h the Hashtable of values gotten from the data store on restore
see
imaginary.persist.Persistent#restore

public synchronized voidsetId(int i)
Sets the id field for the object. Once an id is set, it cannot be changed.

param
i the id to assign to the object

        if( id != -1 ) {
            return;
        }
        id = i;
    
public synchronized voidsetNew()
Sets the modifications bitmask to NEW

        modifications = Persistent.NEW;