|
|
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.