diff options
Diffstat (limited to 'projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java')
| -rw-r--r-- | projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java new file mode 100644 index 0000000..af696fe --- /dev/null +++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/access/EODatabaseContext.java @@ -0,0 +1,568 @@ +/* + Wotonomy: OpenStep design patterns for pure Java applications. + Copyright (C) 2001 Michael Powers + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see http://www.gnu.org + */ +package net.wotonomy.access; + +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import net.wotonomy.control.EOAndQualifier; +import net.wotonomy.control.EOCooperatingObjectStore; +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.EOEnterpriseObject; +import net.wotonomy.control.EOFaultHandler; +import net.wotonomy.control.EOFaulting; +import net.wotonomy.control.EOFetchSpecification; +import net.wotonomy.control.EOGlobalID; +import net.wotonomy.control.EOKeyGlobalID; +import net.wotonomy.control.EOKeyValueCoding; +import net.wotonomy.control.EOKeyValueCodingSupport; +import net.wotonomy.control.EOKeyValueQualifier; +import net.wotonomy.control.EOObjectStoreCoordinator; +import net.wotonomy.control.EOQualifier; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSDictionary; +import net.wotonomy.foundation.NSLocking; +import net.wotonomy.foundation.NSMutableArray; +import net.wotonomy.foundation.NSMutableDictionary; + +/** +* +* @author ezamudio@nasoft.com +* @author $Author: cgruber $ +* @version $Revision: 894 $ +*/ +public class EODatabaseContext + extends EOCooperatingObjectStore implements NSLocking { + + private static Class _contextClass; + protected EODatabase _database; + protected EOAdaptorContext _context; + protected NSMutableArray _channels = new NSMutableArray(); + protected NSMutableArray _lockedObjects = new NSMutableArray(); + + protected NSMutableDictionary _simpleSnaps; + protected NSMutableDictionary _manySnaps; + + protected EOObjectStoreCoordinator _coordinator; + protected EOEditingContext _currEC; + protected int _updateStrategy; + + public EODatabaseContext(EODatabase database) { + super(); + _database = database; + _context = _database.adaptor().createAdaptorContext(); + } + + public EOAdaptorContext adaptorContext() { + return _context; + } + + public EODatabase database() { + return _database; + } + + public EODatabaseChannel availableChannel() { + for (int i = 0; i < _channels.count(); i++) { + EODatabaseChannel c = (EODatabaseChannel)_channels.objectAtIndex(i); + if (!c.isFetchInProgress()) + return c; + } + EODatabaseChannel c = new EODatabaseChannel(this); + registerChannel(c); + return c; + } + + public void batchFetchRelationship(EORelationship rel, NSArray arr, EOEditingContext ec) { + } + + public static void setContextClassToRegister(Class contextClass) { + _contextClass = contextClass; + } + public static Class contextClassToRegister() { + if (_contextClass == null) + _contextClass = EODatabaseContext.class; + return _contextClass; + } + + public EOObjectStoreCoordinator coordinator() { + return _coordinator; + } + + public void editingContextDidForgetObjectWithGlobalID(EOEditingContext ec, EOGlobalID gid) { + database().decrementSnapshotCountForGlobalID(gid); + } + + public void handleDroppedConnection() { + //TODO: unregister channels + adaptorContext().handleDroppedConnection(); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#ownsGlobalID(net.wotonomy.control.EOGlobalID) + */ + public boolean ownsGlobalID(EOGlobalID gid) { + if (!(gid instanceof EOKeyGlobalID)) + return false; + return (database().entityNamed(((EOKeyGlobalID)gid).entityName()) != null); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#ownsObject(net.wotonomy.control.EOEnterpriseObject) + */ + public boolean ownsObject(EOEnterpriseObject eo) { + if (eo.entityName() == null) + return false; + return (database().entityNamed(eo.entityName()) != null); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#handlesFetchSpecification(net.wotonomy.control.EOFetchSpecification) + */ + public boolean handlesFetchSpecification(EOFetchSpecification fspec) { + String ename = fspec.entityName(); + return (database().entityNamed(ename) != null); + } + + public boolean hasBusyChannels() { + return adaptorContext().hasBusyChannels(); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#prepareForSaveWithCoordinator(net.wotonomy.control.EOObjectStoreCoordinator, net.wotonomy.control.EOEditingContext) + */ + public void prepareForSaveWithCoordinator(EOObjectStoreCoordinator coord, EOEditingContext ec) { + // TODO Auto-generated method stub + _coordinator = coord; + _currEC = ec; + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#recordChangesInEditingContext() + */ + public void recordChangesInEditingContext() { + // TODO insert, delete, update + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#recordUpdateForObject(net.wotonomy.control.EOEnterpriseObject, net.wotonomy.foundation.NSDictionary) + */ + public void recordUpdateForObject(EOEnterpriseObject eo, NSDictionary changes) { + // TODO Auto-generated method stub + } + + public void recordSnapshotForGlobalID(NSDictionary snap, EOGlobalID gid) { + if (_simpleSnaps == null) + throw new IllegalArgumentException("Attempt to record a snapshot without a transaction in progress"); + _simpleSnaps.setObjectForKey(snap, gid); + } + + public void recordSnapshotForSourceGlobalID(NSArray gids, EOGlobalID gid, String relationName) { + if (_manySnaps == null) + throw new IllegalArgumentException("Attempt to record a snapshot without a transaction in progress"); + NSMutableDictionary d = (NSMutableDictionary)_manySnaps.objectForKey(gid); + if (d == null) { + d = new NSMutableDictionary(); + _manySnaps.setObjectForKey(d, gid); + } + d.setObjectForKey(gids, relationName); + } + + public void recordSnapshots(NSDictionary snaps) { + if (_simpleSnaps == null) + throw new IllegalArgumentException("Attempt to record snapshots without a transaction in progress."); + _simpleSnaps.addEntriesFromDictionary(snaps); + /* Make sure we don't need to do this instead + Enumeration enumeration = snaps.keyEnumerator(); + while (enumeration.hasMoreElements()) { + EOGlobalID g = (EOGlobalID)enumeration.nextElement(); + NSDictionary d = (NSDictionary)snaps.objectForKey(g); + recordSnapshotForGlobalID(d, g); + }*/ + } + + public void recordToManySnapshots(NSDictionary snaps) { + if (_manySnaps == null) + throw new IllegalArgumentException("Attempt to record snapshots without a transaction in progress."); + Enumeration enumeration = snaps.keyEnumerator(); + while (enumeration.hasMoreElements()) { + Object key = enumeration.nextElement(); + NSDictionary d = (NSDictionary)snaps.objectForKey(key); + NSMutableDictionary d2 = (NSMutableDictionary)_manySnaps.objectForKey(key); + if (d2 == null) { + d2 = new NSMutableDictionary(); + _manySnaps.setObjectForKey(d2, key); + } + //this could also be done with many calls to recordSnapshotForSourceGID + d2.addEntriesFromDictionary(d); + } + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#performChanges() + */ + public void performChanges() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#commitChanges() + */ + public void commitChanges() { + adaptorContext().commitTransaction(); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#rollbackChanges() + */ + public void rollbackChanges() { + adaptorContext().rollbackTransaction(); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOCooperatingObjectStore#valuesForKeys(net.wotonomy.foundation.NSArray, net.wotonomy.control.EOEnterpriseObject) + */ + public NSDictionary valuesForKeys(NSArray keys, EOEnterpriseObject eo) { + // TODO check snapshots; eo could be a fault + return eo.valuesForKeys(keys); + } + + /* (non-Javadoc) + * @see net.wotonomy.foundation.NSLocking#lock() + */ + public void lock() { + EOAccessLock.lock(); + } + + /* (non-Javadoc) + * @see net.wotonomy.foundation.NSLocking#unlock() + */ + public void unlock() { + EOAccessLock.unlock(); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#arrayFaultWithSourceGlobalID(net.wotonomy.control.EOGlobalID, java.lang.String, net.wotonomy.control.EOEditingContext) + */ + public NSArray arrayFaultWithSourceGlobalID( + EOGlobalID gid, String relName, EOEditingContext ec) { + if (!(gid instanceof EOKeyGlobalID)) + throw new IllegalArgumentException("an EOKeyGlobalID is needed."); + EOAccessArrayFaultHandler handler = new EOAccessArrayFaultHandler((EOKeyGlobalID)gid, relName, this, ec); + return new NSArray(handler); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#faultForGlobalID(net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext) + */ + public /*EOEnterpriseObject*/Object faultForGlobalID(EOGlobalID gid, EOEditingContext ec) { + if (!(gid instanceof EOKeyGlobalID)) + throw new IllegalArgumentException("Cannot fault an object that doesn't have a key global ID."); + EOAccessFaultHandler handler = new EOAccessFaultHandler((EOKeyGlobalID)gid, this, ec); + EOEntity e = database().entityNamed(((EOKeyGlobalID)gid).entityName()); + Object o = e.classDescriptionForInstances().createInstanceWithEditingContext(ec, gid); + EOFaultHandler.makeObjectIntoFault(o, handler); + return o; + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#faultForRawRow(java.util.Map, java.lang.String, net.wotonomy.control.EOEditingContext) + */ + public /*EOEnterpriseObject*/ Object faultForRawRow(Map row, String entityName, EOEditingContext ec) { + EOEntity e = database().entityNamed(entityName); + EOGlobalID gid = e.globalIDForRow(row); + return faultForGlobalID(gid, ec); + } + + public void forgetSnapshotForGlobalID(EOGlobalID gid) { + if (_simpleSnaps == null) + throw new IllegalArgumentException("Attempt to forget snapshot with no transaction in progress."); + _simpleSnaps.removeObjectForKey(gid); + _manySnaps.removeObjectForKey(gid); + } + + public void forgetSnapshotsForGlobalIDs(List gids) { + for (int i = 0; i < gids.size(); i++) { + EOGlobalID g = (EOGlobalID)gids.get(i); + forgetSnapshotForGlobalID(g); + database().forgetSnapshotForGlobalID(g); + } + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#initializeObject(java.lang.Object, net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext) + */ + public void initializeObject(/*EOEnterpriseObject*/Object eo, EOGlobalID gid, EOEditingContext ec) { + if (gid.isTemporary()) + return; + NSDictionary snap = snapshotForGlobalID(gid); + Object obj = ec.objectForGlobalID(gid); + EOEntity e = database().entityNamed(((EOKeyGlobalID)gid).entityName()); + NSArray props = e.classProperties(); + for (int i = 0; i < props.count(); i++) { + EOProperty p = (EOProperty)props.objectAtIndex(i); + Object val = snap.objectForKey(p.name()); + if (p instanceof EOAttribute) { + if ( eo instanceof EOKeyValueCoding ) + { + ((EOKeyValueCoding)eo).takeValueForKey(val, p.name()); + } + else + { + EOKeyValueCodingSupport.takeValueForKey( eo, val, p.name() ); + } + } else if (p instanceof EORelationship) { + if (((EORelationship)p).isToMany()) { + val = arrayFaultWithSourceGlobalID(gid, p.name(), ec); + } else { + EOEntity dest = ((EORelationship)p).destinationEntity(); + EOKeyGlobalID kgid = (EOKeyGlobalID)dest.globalIDForRow(snap); + val = ec.objectForGlobalID(kgid); + if (val == null) + val = new EOAccessFaultHandler(kgid, this, ec); + } + if (val == EOKeyValueCoding.NullValue) + val = null; + if ( eo instanceof EOKeyValueCoding ) + { + ((EOKeyValueCoding)eo).takeValueForKey(val, p.name()); + } + else + { + EOKeyValueCodingSupport.takeValueForKey( eo, val, p.name() ); + } + } + } + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#invalidateAllObjects() + */ + public void invalidateAllObjects() { + invalidateObjectsWithGlobalIDs(database().snapshots().allKeys()); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#invalidateObjectsWithGlobalIDs(java.util.List) + */ + public void invalidateObjectsWithGlobalIDs(List aList) { + forgetSnapshotsForGlobalIDs(aList); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#isObjectLockedWithGlobalID(net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext) + */ + public boolean isObjectLockedWithGlobalID(EOGlobalID gid, EOEditingContext ec) { + return isObjectLockedWithGlobalID(gid); + } + + public boolean isObjectLockedWithGlobalID(EOGlobalID gid) { + return _lockedObjects.containsObject(gid); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#lockObjectWithGlobalID(net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext) + */ + public void lockObjectWithGlobalID(EOGlobalID gid, EOEditingContext ec) { + NSDictionary snap = snapshotForGlobalID(gid); + if (snap == null) + return; + if (!(gid instanceof EOKeyGlobalID)) + return; + EOEntity e = database().entityNamed(((EOKeyGlobalID)gid).entityName()); + EOQualifier q = e.qualifierForPrimaryKey(snap); + EOFetchSpecification fspec = new EOFetchSpecification(e.name(), q, null); + fspec.setLocksObjects(true); + NSArray arr = ec.objectsWithFetchSpecification(fspec); + if (arr.count() != 1) + throw new IllegalStateException("Cannot lock object with Global ID " + gid); + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#objectsForSourceGlobalID(net.wotonomy.control.EOGlobalID, java.lang.String, net.wotonomy.control.EOEditingContext) + */ + public NSArray objectsForSourceGlobalID( + EOGlobalID gid, String relationName, EOEditingContext ec) { + EOEnterpriseObject eo = (EOEnterpriseObject)ec.objectForGlobalID(gid); + if (eo == null) + throw new IllegalStateException("Cannot find object for global ID " + gid + " in specified editing context."); + //Get the source object + EOEnterpriseObject source = (EOEnterpriseObject) faultForGlobalID(gid, ec); + if (source == null) + throw new IllegalStateException("There is no snapshot for source global ID " + gid); + + //Check if there is already a value here + NSArray value = (NSArray)source.valueForKey(relationName); + EOAccessArrayFaultHandler handler = null; + if (value != null) { + if (EOFaultHandler.isFault(value)) { + handler = new EOAccessArrayFaultHandler((EOKeyGlobalID)gid, relationName, this, ec); + //TODO: fire the fault an return the value + } else + return value; + } + + //Get the relationship + EOEntity entity = database().entityNamed(eo.entityName()); + EORelationship rel = entity.relationshipNamed(relationName); + if (rel == null) + throw new IllegalStateException("Cannot find relationship named " + relationName + " in entity " + entity.name()); + + //create a fetch specification for this + EOQualifier q = null; + NSArray joins = rel.joins(); + NSMutableArray subq = new NSMutableArray(joins.count()); + for (int i = 0; i < joins.count(); i++) { + EOJoin j = (EOJoin)joins.objectAtIndex(i); + String key = j.destinationAttribute().name(); + Object val = eo.valueForKey(j.sourceAttribute().name()); + subq.addObject(new EOKeyValueQualifier(key, EOQualifier.QualifierOperatorEqual, val)); + } + if (subq.count() == 1) { + q = (EOQualifier)subq.objectAtIndex(0); + } else { + q = new EOAndQualifier(subq); + } + EOFetchSpecification fspec = new EOFetchSpecification(rel.destinationEntity().name(), q, null); + NSArray res = ec.objectsWithFetchSpecification(fspec, ec); + NSMutableArray gids = new NSMutableArray(res.count()); + for (int i = 0; i < res.count(); i++) + gids.addObject(ec.globalIDForObject(res.objectAtIndex(i))); + recordSnapshotForSourceGlobalID(gids, gid, relationName); + return res; + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#objectsWithFetchSpecification(net.wotonomy.control.EOFetchSpecification, net.wotonomy.control.EOEditingContext) + */ + public NSArray objectsWithFetchSpecification(EOFetchSpecification fspec, EOEditingContext ec) { + EODatabaseChannel channel = availableChannel(); + channel.selectObjectsWithFetchSpecification(fspec, ec); + NSMutableArray arr = new NSMutableArray(); + while (channel.isFetchInProgress()) { + Object o = channel.fetchObject(); + if (o != null) { + arr.addObject(o); + } + } + return arr; + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#refaultObject(java.lang.Object, net.wotonomy.control.EOGlobalID, net.wotonomy.control.EOEditingContext) + */ + public void refaultObject(Object obj, EOGlobalID gid, EOEditingContext ec) { + if (!(gid instanceof EOKeyGlobalID)) + throw new IllegalArgumentException("GlobalID must be an EOKeyGlobalID"); + if (obj instanceof EOFaulting) + //check if it's already a fault + if (!EOFaultHandler.isFault(obj)) { + ((EOFaulting)obj).turnIntoFault(EOFaultHandler.handlerForFault(obj)); + } + } + + /* (non-Javadoc) + * @see net.wotonomy.control.EOObjectStore#saveChangesInEditingContext(net.wotonomy.control.EOEditingContext) + */ + public void saveChangesInEditingContext(EOEditingContext ec) { + prepareForSaveWithCoordinator(null, ec); + recordChangesInEditingContext(); + performChanges(); + commitChanges(); + } + + public void registerChannel(EODatabaseChannel channel) { + if (channel.databaseContext() != this) + throw new IllegalArgumentException("Cannot register a channel on a context other than its own."); + if (_channels.containsObject(channel)) + throw new IllegalArgumentException("Attempt to register a channel more than once."); + _channels.addObject(channel); + } + + public void unregisterChannel(EODatabaseChannel channel) { + if (channel.databaseContext() != this) + throw new IllegalArgumentException("Attempt to unregister a channel from a context other than its own."); + _channels.removeObject(channel); + } + + public NSArray registeredChannels() { + return new NSArray(_channels); + } + + public NSDictionary snapshotForGlobalID(EOGlobalID gid) { + NSDictionary d = null; + if (_simpleSnaps != null) { + d = (NSDictionary)_simpleSnaps.objectForKey(gid); + } + if (d == null) + d = database().snapshotForGlobalID(gid); + return d; + } + + public NSArray snapshotForSourceGlobalID(EOGlobalID gid, String name) { + NSArray a = null; + if (_manySnaps != null) { + NSDictionary d = (NSDictionary)_manySnaps.objectForKey(gid); + a = (NSArray)d.objectForKey(name); + } + if (a == null) + a = database().snapshotForSourceGlobalID(gid, name); + return a; + } + + public void setUpdateStrategy(int strategy) { + _updateStrategy = strategy; + } + + public int updateStrategy() { + return _updateStrategy; + } + +} +/* + * $Log$ + * Revision 1.2 2006/02/16 16:47:13 cgruber + * Move some classes in to "internal" packages and re-work imports, etc. + * + * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions. + * + * Revision 1.1 2006/02/16 13:19:57 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.5 2005/05/11 15:21:53 cgruber + * Change enum to enumeration, since enum is now a keyword as of Java 5.0 + * + * A few other comments in the code. + * + * Revision 1.4 2003/12/18 15:37:38 mpowers + * Changes to retain ability to work with objects that don't necessarily + * implement EOEnterpriseObject. I would still like to preserve this case + * for general usage, however the access package is free to assume that + * those objects will be EOs and cast appropriately. + * + * Revision 1.3 2003/08/29 21:14:44 chochos + * implement a couple more methods. + * + * Revision 1.2 2003/08/20 01:16:22 chochos + * more methods have code now, but there's no way to test this yet. The core is still pending. + * + * Revision 1.1 2003/08/19 01:54:43 chochos + * The EODatabase layer still needs a lot of work, but it's on its way... + * + */
\ No newline at end of file |
