|
|
1.1 ! root 1: #import <appkit/appkit.h> ! 2: #import "UniqueKey.h" ! 3: ! 4: ! 5: static BOOL _Debug = YES; ! 6: static id _connectionDictionary = nil; ! 7: ! 8: ! 9: @implementation UniqueKey ! 10: ! 11: /****************************************************************************** ! 12: * Allow the programmer to specify login information for our separate UniqueKey ! 13: * channel. If no information is supplied, we display our own login panel. ! 14: ******************************************************************************/ ! 15: + setConnectionDictionary:(NSDictionary *)connectionDictionary ! 16: { ! 17: if(_connectionDictionary) [_connectionDictionary autorelease]; ! 18: _connectionDictionary = [connectionDictionary retain]; ! 19: return self; ! 20: } ! 21: + connectionDictionary ! 22: { ! 23: return [[_connectionDictionary retain] autorelease]; ! 24: } ! 25: ! 26: ! 27: /****************************************************************************** ! 28: * The UniqueKey objects share a database channel that is sure ! 29: * to be free to allow immediate reservation of a block of keys. ! 30: ******************************************************************************/ ! 31: - initSharedChannel ! 32: { ! 33: NSString *modelName = @"UniqueKey"; ! 34: NSString *path; ! 35: NSString *_entityName = @"UniqueKey"; ! 36: EOModel *eoModel; ! 37: EOAdaptor *eoAdaptor; ! 38: EODatabase *db; ! 39: static EODatabaseContext *_sharedContext; ! 40: static EODatabaseChannel *_sharedChannel; ! 41: static EOEntity *_sharedEntity; ! 42: static BOOL initFlag = NO; ! 43: ! 44: if(!initFlag) { ! 45: path = [EOModel findPathForModelNamed:modelName]; ! 46: if(!path) { ! 47: NXRunAlertPanel([NXApp appName], ! 48: "UniqueKey is unable to find %s",NULL,NULL,NULL,[modelName cString]); ! 49: return nil; ! 50: } ! 51: ! 52: eoModel=[[EOModel alloc] initWithContentsOfFile:path]; ! 53: if(!eoModel) { ! 54: NXRunAlertPanel([NXApp appName], ! 55: "UniqueKey is unable to open %s",NULL,NULL,NULL,[path cString]); ! 56: return nil; ! 57: } ! 58: ! 59: eoAdaptor= [EOAdaptor adaptorWithModel:eoModel]; ! 60: if(!eoAdaptor) { ! 61: NXRunAlertPanel([NXApp appName], ! 62: "UniqueKey is unable to create adaptorWithModel:",NULL,NULL,NULL); ! 63: return nil; ! 64: } ! 65: ! 66: if([UniqueKey connectionDictionary]!=nil) ! 67: [eoAdaptor setConnectionDictionary:[UniqueKey connectionDictionary]]; ! 68: if(![eoAdaptor hasValidConnectionDictionary]) ! 69: [eoAdaptor runLoginPanelAndValidateConnectionDictionary]; ! 70: ! 71: db=[[EODatabase alloc] initWithAdaptor:eoAdaptor]; ! 72: if(!db) { ! 73: NXRunAlertPanel([NXApp appName], ! 74: "UniqueKey is unable to initWithAdaptor:",NULL,NULL,NULL); ! 75: return nil; ! 76: } ! 77: ! 78: _sharedContext=[[EODatabaseContext alloc] initWithDatabase:db]; ! 79: if(!_sharedContext) { ! 80: NXRunAlertPanel([NXApp appName], ! 81: "UniqueKey is unable to initWithDatabase:",NULL,NULL,NULL); ! 82: return nil; ! 83: } ! 84: ! 85: _sharedChannel=[[EODatabaseChannel alloc] initWithDatabaseContext:_sharedContext]; ! 86: if(!_sharedChannel) { ! 87: NXRunAlertPanel([NXApp appName], ! 88: "UniqueKey is unable to initWithDatabaseContext:",NULL,NULL,NULL); ! 89: return nil; ! 90: } ! 91: ! 92: _sharedEntity= [eoModel entityNamed:_entityName]; ! 93: if(!_sharedEntity) { ! 94: NXRunAlertPanel([NXApp appName], ! 95: "UniqueKey is unable to find entityNamed:%s",NULL,NULL,NULL,[_entityName cString]); ! 96: return nil; ! 97: } ! 98: ! 99: [[_sharedChannel adaptorChannel] openChannel]; ! 100: if(_Debug) [[_sharedChannel adaptorChannel] setDelegate:self]; ! 101: ! 102: initFlag=TRUE; ! 103: } ! 104: ! 105: dbContext = [_sharedContext retain]; ! 106: dbChannel = [_sharedChannel retain]; ! 107: uniqueKeyEntity = [_sharedEntity retain]; ! 108: return self; ! 109: } ! 110: ! 111: ! 112: /****************************************************************************** ! 113: * Init a UniqueKey object to reserve integer keys in blocks of <count> keys. Hold ! 114: * off reserving any keys until the first call to nextKey. Each call to nextKey ! 115: * returns a unique integer key. The first call reserves a block of <count> keys. ! 116: * When the block has been used, a new block is reserved. ! 117: * ! 118: * UniqueKey uses a separate table to hold the external entity name and current ! 119: * max reserved integer key. The UniqueKey objects share a database channel that ! 120: * is sure to be free to allow immediate reservation of a block of keys. ! 121: ******************************************************************************/ ! 122: - initWithEntity:(EOEntity*)entity count:(unsigned int)count ! 123: { ! 124: NSString *qualifierString; ! 125: EOQualifier *tableNameQualifier; ! 126: NSNumber *max; ! 127: ! 128: keyCount = count; ! 129: nextKey = 0; ! 130: maxKey = 0; ! 131: ! 132: if(!entity || ![self initSharedChannel]) return nil; ! 133: ! 134: // Construct the qualifier for our name ! 135: qualifierString = [NSString stringWithFormat:@"EntityName = '%@'",[entity externalName]]; ! 136: ! 137: tableNameQualifier = [[[EOQualifier allocWithZone:[self zone]] ! 138: initWithEntity:uniqueKeyEntity qualifierFormat:qualifierString] autorelease]; ! 139: ! 140: // Select the object and check for the presence of our name and "MaxKey" ! 141: [dbContext beginTransaction]; ! 142: [dbChannel selectObjectsDescribedByQualifier:tableNameQualifier fetchOrder:nil]; ! 143: tableMax=[[dbChannel fetchWithZone:[dbChannel zone]] retain]; ! 144: [dbChannel cancelFetch]; ! 145: [dbContext commitTransaction]; ! 146: ! 147: // Was there an entry for our table name? ! 148: if(!tableMax) { ! 149: NSLog(@"Unique key was unable to fetch EntityName = %@",[entity externalName]); ! 150: return nil; ! 151: } ! 152: ! 153: // Then there should be a value for "MaxKey" for our table ! 154: max=[tableMax objectForKey:@"MaxKey"]; ! 155: if(!max) { ! 156: NSLog(@"Unique key method nextKey could not fine MaxKey"); ! 157: return nil; ! 158: } ! 159: return self; ! 160: } ! 161: ! 162: ! 163: /****************************************************************************** ! 164: * Hand out the next unique key from our reserved block. If the block is exhausted, ! 165: * increment the max_id (high-water mark) and attempt to updateObject:. This ! 166: * writes the new max_id back to the table. Under optimistic locking (the default) ! 167: * the update will fail if another client has bumped up the max_id since our last use. ! 168: * In that case, we need to refetchObject: and try again. ! 169: ******************************************************************************/ ! 170: - (int) nextKey ! 171: { ! 172: NSNumber *max; ! 173: ! 174: // Hand out our next reserved key. If we are out of keys, fall through to ! 175: // reserve another block of keyCount keys. ! 176: if(++nextKey<=maxKey) return nextKey; ! 177: ! 178: // Attempt to bump up the count by keyCount. Fail on optomistic lock if someone ! 179: // else reserved a chunck since our original select (or our last update). ! 180: [dbContext beginTransaction]; ! 181: while(1) { ! 182: max = [tableMax objectForKey:@"MaxKey"]; ! 183: maxKey = [max intValue]+keyCount; ! 184: [tableMax setObject:[NSNumber numberWithInt:maxKey] forKey:@"MaxKey"]; ! 185: if([dbChannel updateObject:tableMax]) break; ! 186: ! 187: [dbContext rollbackTransaction]; ! 188: [dbContext beginTransaction]; ! 189: [dbChannel refetchObject:tableMax]; ! 190: } ! 191: [dbContext commitTransaction]; ! 192: ! 193: nextKey=maxKey-keyCount+1; ! 194: return nextKey; ! 195: } ! 196: ! 197: ! 198: /****************************************************************************** ! 199: * Echo SQL when debug is enabled. ! 200: ******************************************************************************/ ! 201: - (EODelegateResponse)adaptorChannel:channel ! 202: willEvaluateExpression:(NSMutableString *)expression ! 203: { ! 204: NSLog(expression); ! 205: return EODelegateApproves; ! 206: } ! 207: ! 208: ! 209: - (void)dealloc ! 210: { ! 211: [dbContext release]; ! 212: [dbChannel release]; ! 213: [uniqueKeyEntity release]; ! 214: [tableMax release]; ! 215: if (_connectionDictionary) ! 216: [_connectionDictionary release]; ! 217: [super dealloc]; ! 218: ! 219: } ! 220: ! 221: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.