/*! @header FTGraphImpl @abstract Module of FT @availability OS X, GNUstep @copyright 2004, 2005, 2006 Free Software Foundation, Inc. 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  -------------------------------------------------------------------------
  Modification history

  23.02.05 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  
*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FTNotifications.h" @implementation FTGraphImpl - init { self = [super init]; self->graphDatabaseName = nil; self->graphDatabaseDirectory = nil; self->objectToIdMapper = nil; self->idToRecnoDB = nil; self->nodeDB = nil; self->databasesMounted = NO; self->idToNodeCache = nil; self->keyToGraphInfo = nil; return self; } - initForServer: (FTServerImpl *) theServer graphManager: (FTGraphManagerImpl *) theGraphManager withId: (id ) anId { self = [self init]; self->server = [theServer retain]; self->graphId = [anId retain]; self->graphManager = [theGraphManager retain]; self->idToNodeCache = [[ECCache alloc] init]; self->globalLock = [[NSLock alloc] init]; return self; } - initWithCoder: (NSCoder *) coder { self = [self init]; self->graphId = [[coder decodeObject] retain]; self->graphDatabaseName = [[coder decodeObject] retain]; self->graphDatabaseDirectory = [[NSString alloc] initWithFormat: @"%@/%@", [self->server baseDataDir], self->graphDatabaseName]; self->idToNodeCache = nil; self->globalLock = [[NSLock alloc] init]; return self; } - initAfterDecodeForServer: (FTServerImpl *) theServer graphManager: (FTGraphManagerImpl *) theGraphManager { self->server = [theServer retain]; self->graphManager = [theGraphManager retain]; /** * init graphDatabaseDirectory here: */ self->graphDatabaseDirectory = [[NSString alloc] initWithFormat: @"%@/%@", [self->server baseDataDir], self->graphDatabaseName]; return self; } - (void) dealloc { [self unmountDatabases]; [self->idToNodeCache release]; [self->globalLock release]; [self->graphId release]; [self->graphDatabaseDirectory release]; [self->graphDatabaseName release]; [self->graphManager release]; [self->server release]; [self->keyToGraphInfo release]; [super dealloc]; } - (BOOL) databasesMounted { return self->databasesMounted; } - (NSString *) createDatabaseGraphDirectory { int creationRetries = 0; int directoryNameExaminationRetries = 0; BOOL directoryCreated = NO; BOOL gotValidDirName; NSString *toReturn = nil; EC_AUTORELEASEPOOL_BEGIN NSString *graphDBName; NSString *dirName; while( (creationRetries < 3) && (!directoryCreated) ) { gotValidDirName = NO; directoryNameExaminationRetries = 0; /** * Try to find a directory name which does not exists in the base directory */ while( (directoryNameExaminationRetries < 20) && (!gotValidDirName) ) { graphDBName = [[self->server systemDictionary] newGraphDatabaseName]; if( nil == graphDBName ) { [[[ECIllegalStateException alloc] initWithIllegalStateInfo: @"FTGraphImpl::setup: The system dictionary "\ "returned a nil reference for the graph database name to be used!"] raise]; } dirName = [[[NSString alloc] initWithFormat: @"%@/%@", [self->server baseDataDir], graphDBName] autorelease]; /** * Check wether a directory of this name already exists */ if( [[NSFileManager defaultManager] fileExistsAtPath: dirName] ) { if( [[FTLogging coreLog] isWarnEnabled] ) { [[FTLogging coreLog] warn: @"FTGraphImpl::createDatabaseGraphDirectory: Target directory"\ " for the new graph already exists: %@", dirName]; } directoryNameExaminationRetries++; } else { gotValidDirName = YES; } } if( (!gotValidDirName) ) { [[FTLogging coreLog] error: @"FTGraphImpl::createDatabaseGraphDirectory: Unable to find a "\ "valid directory name for the graph to be created."]; [[[FTDatabaseCreationFailedException alloc] initWithReason: @"FTGraphImpl::createDatabaseGraphDirectory: Unable to "\ "find a valid directory name for the graph to be created."] raise]; } /** * create the directory. If this fails then try the whole procedure again. */ if ( NO == [[NSFileManager defaultManager] createDirectoryAtPath: dirName attributes: nil] ) { [[FTLogging coreLog] error: @"FTGraphImpl::createDatabaseGraphDirectory: Unable to create "\ "directory \"%@\" for new graph in base directory \"%@\"", dirName, [self->server baseDataDir]]; creationRetries++; } else { directoryCreated = YES; toReturn = [graphDBName retain]; } } if( nil == toReturn ) { [[FTLogging coreLog] fatal: @"FTGraphImpl::createDatabaseGraphDirectory: Unable to "\ "create a directory for the new graph within the base directory "\ "\"%@\".", [self->server baseDataDir]]; [[[FTDatabaseCreationFailedException alloc] initWithReason: [NSString stringWithFormat:@"FTGraphImpl::createDatabaseGraphDirectory: "\ "Unable to create a directory for the new graph within the base "\ "directory \"%@\".", [self->server baseDataDir]]] raise]; } EC_AUTORELEASEPOOL_END if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::createDatabaseGraphDirectory: Directory created "\ "for graph. Directory name: %@", toReturn ]; } return [toReturn autorelease]; } - (id ) createNodeWithId: (id ) aNodeId { FTNodeImpl *toReturn; BDBDatabaseEntry *key, *value; BDBDatabaseRecordNumber *recno; BDBOperationStatus status; /** * Todo: Here we should check whether aNodeId belongs to the objectToIdMapper * of this graph */ toReturn = [[FTNodeImpl alloc] initWithNodeId: aNodeId forGraph: self]; /* now add this node to the database: */ EC_AUTORELEASEPOOL_BEGIN recno = [[[BDBDatabaseRecordNumber alloc] init] autorelease]; value = [[[BDBDatabaseEntry alloc] initWithObject: toReturn] autorelease]; NS_DURING /** * append the node to the database "node". * Use the returned record nr. and insert it into the database * "idToRecno". */ status = [self->nodeDB appendEntryWithTransaction: nil entryToAppend: value resultingRecordNr: recno]; if( BDB_STATUS_SUCCESS != status ) { [[FTLogging coreLog] error:@"FTGraphImpl::createNodeWithId: Unable to append a node to the "\ "node database!" ]; [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } /* * add the record number to the database "idToRecno": */ key = [[[BDBDatabaseEntry alloc] initWithObject: aNodeId] autorelease]; status = [self->idToRecnoDB putEntryWithTransaction: nil key: key value: recno]; if( BDB_STATUS_SUCCESS != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::createNodeWithId: Unable to add the node id and "\ "record number to the database idToRecno" ]; [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } /** * Add node to cache */ [self->keyToGraphInfo addObject: aNodeId]; [self->idToNodeCache addObject: toReturn forKey: [toReturn nodeId]]; NS_HANDLER [[FTLogging coreLog] error: @"FTGraphImpl::createNodeWithId: Unable to append a node to the "\ "databases (idToRecno or node)!" ]; [[[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] setCause: localException] raise]; NS_ENDHANDLER if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::createNodeWithId: Node created" ]; } EC_AUTORELEASEPOOL_END return toReturn; } - (void) encodeWithCoder: (NSCoder *) coder { [coder encodeObject: self->graphId]; [coder encodeObject: self->graphDatabaseName]; } - (void) close { if( [[FTLogging coreLog] isTraceEnabled] ) { [[FTLogging coreLog] trace: @"FTGraphImpl::close: closing database..." ]; } [self unmountDatabases]; [[NSNotificationCenter defaultCenter] postNotificationName: FTNotification_Graph_afterClosed object: self]; [self->graphManager releaseGraph: self]; } - (NSString *) graphDatabaseDirectory { return self->graphDatabaseDirectory; } - (id ) graphId { return self->graphId; } - (BDBDatabaseConfig *) idToRecnoDBConfig { BDBDatabaseConfig * dbConfig = [[BDBDatabaseConfig alloc] init]; [dbConfig setDatabaseType: BDB_BTREE]; [dbConfig setBTreeRecordNumbering: NO]; [dbConfig setAllowDuplicates: YES]; return dbConfig; } - insertNodeToDatabase: (FTNodeImpl *) node { BDBDatabaseEntry *key, *value; BDBDatabaseRecordNumber *recno; BDBOperationStatus status; /** * First check, wether the node already exists in the database */ EC_AUTORELEASEPOOL_BEGIN [self->globalLock lock]; recno = [self recordNumberOfNode: node]; if( nil != recno ) { [[FTLogging coreLog] error: @"FTGraphImpl::insertNodeToDatabase: A node with the same node id "\ "already exists in the database!" ]; [recno release]; [self->globalLock unlock]; [[[ECIllegalArgumentException alloc] initWithArgumentInfo: @"FTGraphImpl::insertNodeToDatabase: A node with "\ "the same node id already exists in the database!"] raise]; } /* now add this node to the database: */ recno = [[[BDBDatabaseRecordNumber alloc] init] autorelease]; value = [[[BDBDatabaseEntry alloc] initWithObject: node] autorelease]; NS_DURING /** * append the node to the database "node". * Use the returned record nr. and insert it into the database * "idToRecno". */ status = [self->nodeDB appendEntryWithTransaction: nil entryToAppend: value resultingRecordNr: recno]; if( BDB_STATUS_SUCCESS != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::insertNodeToDatabase: Unable to append a node "\ "to the node database!" ]; EC_AUTORELEASEPOOL_RELEASE [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } /* * add the record number to the database "idToRecno": */ key = [[[BDBDatabaseEntry alloc] initWithObject: [node nodeId]] autorelease]; status = [self->idToRecnoDB putEntryWithTransaction: nil key: key value: recno]; if( BDB_STATUS_SUCCESS != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::insertNodeToDatabase: Unable to add the node id "\ "and record number to the database idToRecno" ]; EC_AUTORELEASEPOOL_RELEASE [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } /** * Add node to cache */ [self->idToNodeCache addObject: node forKey: [node nodeId]]; NS_HANDLER [self->globalLock unlock]; [[FTLogging coreLog] error: @"FTGraphImpl::insertNodeToDatabase: Unable to add the node to "\ "the database." ]; if(![localException isKindOfClass: [FTInternalDatamanagementException class]] ) { [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raiseWithPredecessor: localException ]; } else { [localException raise]; } NS_ENDHANDLER [self->globalLock unlock]; EC_AUTORELEASEPOOL_END return self; } - internalStateChanged: (FTNodeImpl *) nodeToUpdate { FTSessionImpl *currentSession; id transaction; FTTransactionContext *context; FTGraphImplTransactions *nextStep; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::Updating node..." ]; } currentSession = [FTSessionImpl currentSession]; transaction = [[self->server transactionManager] currentTransactionForSession: currentSession]; NSAssert( nil != transaction, @"No transaction available for current "\ "session!" ); NSAssert( [transaction isKindOfClass: [FTTransactionImpl class]], @"Only instances of FTTransactionImp are supported right now!" ); context = [((FTTransactionImpl *) transaction) createContext]; nextStep = [FTGraphImplTransactions createForUpdateOfNode: nodeToUpdate withContext: context withGraph: self]; [((FTTransactionImpl *) transaction) addTransactionStep: nextStep withContext: context ]; [nextStep release]; [context release]; return self; } - (BDBDatabaseConfig *) keyToGraphInfoConfig { BDBDatabaseConfig * dbConfig = [[BDBDatabaseConfig alloc] init]; [dbConfig setDatabaseType: BDB_BTREE]; [dbConfig setBTreeRecordNumbering: NO]; [dbConfig setAllowDuplicates: NO]; return dbConfig; } - mountDatabases { BDBDatabaseConfig *dbConfig; if( self->databasesMounted ) { [[[ECIllegalStateException alloc] initWithIllegalStateInfo: @"FTGraphImpl::mountDatabases: Databases are "\ "already mounted!" ] raise]; } /** * Does the subdirectory, where all databases rely in, exist? */ if( !([[NSFileManager defaultManager] fileExistsAtPath: [self graphDatabaseDirectory]]) ) { [[FTLogging coreLog] error: @"FTGraphImpl::mountDatabases: File already exists at path %@", [self graphDatabaseDirectory] ]; [[[ECAlreadyExistsException alloc] initWithResourceInformation: @"FTGraphImpl::mountDatabases: "\ "Directory for graph database to create already exists" ] raise]; } EC_AUTORELEASEPOOL_BEGIN if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::mountDatabases: Opening databases..." ]; } if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: Opening database for objectToIdMapper..." ]; } objectToIdMapper = [[FTDefaultObjectToIdMapper alloc] initWithDatabaseName: [[self nameOfObjectToIdMapperDatabase] autorelease] forServer: self->server]; [objectToIdMapper mountDatabase]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::mountDatabases: ...DONE" ]; } if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: Opening database for idToRecno..." ]; } if( ![[NSFileManager defaultManager] fileExistsAtPath: [self nameOfIdToRecnoDatabase]] ) { NSMutableString *info = [[NSMutableString alloc] initWithFormat: @"Database named %@ does not exists", [[self nameOfIdToRecnoDatabase] autorelease]]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: info] raise]; } dbConfig = [[self idToRecnoDBConfig] autorelease]; self->idToRecnoDB = [BDBDatabase initWithFilename: [[self nameOfIdToRecnoDatabase] autorelease] databaseName: nil databaseConfig: dbConfig]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: Opening database for node..." ]; } if( ![[NSFileManager defaultManager] fileExistsAtPath: [self nameOfNodesDatabase]] ) { NSMutableString *info = [[NSMutableString alloc] initWithFormat: @"Database named %@ does not exists", [[self nameOfNodesDatabase] autorelease]]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: info] raise]; } dbConfig = [[self nodeDBConfig] autorelease]; self->nodeDB = [BDBDatabase initWithFilename: [[self nameOfNodesDatabase] autorelease] databaseName: nil databaseConfig: dbConfig]; self->keyToGraphInfo = [[[[[[FTBootstrap bootstrap] config] classObjectFactory] classObjectForKey: @"FTPersistentSet"] alloc] init]; [self->keyToGraphInfo openDBUsingDataFile: [self nameOfkeyToGraphInfoDatabase]]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::mount:Info database for graph opened!"]; } if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: ... DONE" ]; } if( nil != self->idToNodeCache ) { [self->idToNodeCache release]; } self->idToNodeCache = [[ECCache alloc] init]; self->databasesMounted = YES; EC_AUTORELEASEPOOL_END return self; } - (NSString *) nameOfkeyToGraphInfoDatabase { NSString *dbfile = [[[self->server config] databaseNames] databaseNameForEntry: FT_IP_KEY_2_GENERAL_GRAPH_INFO_DBNAME]; NSString *fullyQualifiedName = [[NSString alloc] initWithFormat: @"%@/%@", [self graphDatabaseDirectory], dbfile]; return [fullyQualifiedName autorelease]; } - (NSString *) nameOfNodesDatabase { NSString *dbfile = [[[self->server config] databaseNames] databaseNameForEntry: FT_IP_NODES_DBNAME]; NSString *fullyQualifiedName = [[NSString alloc] initWithFormat: @"%@/%@", [self graphDatabaseDirectory], dbfile]; return fullyQualifiedName; } - (NSString *) nameOfIdToRecnoDatabase { NSString *dbfile = [[[self->server config] databaseNames] databaseNameForEntry: FT_IP_ID_2_RECNO_DBNAME]; NSString *fullyQualifiedName = [[NSString alloc] initWithFormat: @"%@/%@", [self graphDatabaseDirectory], dbfile]; return fullyQualifiedName; } - (NSString *) nameOfObjectToIdMapperDatabase { NSString *dbfile = [[[self->server config] databaseNames] databaseNameForEntry: FT_IP_OBJECT_2_IDMAPPER]; NSString *fullyQualifiedName = [[NSString alloc] initWithFormat: @"%@/%@", [self graphDatabaseDirectory], dbfile]; return fullyQualifiedName; } - (FTNodeImpl *) nodeAtRecordNumber: (BDBDatabaseRecordNumber *) recno { FTNodeImpl *toReturn = nil; BDBOperationStatus status; EC_AUTORELEASEPOOL_BEGIN NS_DURING BDBDatabaseEntry *value; value = [[[BDBDatabaseEntry alloc] init] autorelease]; status = [self->nodeDB getEntryWithTransaction: nil key: recno data: value ]; if( BDB_STATUS_SUCCESS != status ) { if( BDB_STATUS_NOTFOUND != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::nodeAtRecordNumber: Unable to fetch node by record "\ "number!" ]; EC_AUTORELEASEPOOL_RELEASE [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } } else { toReturn = (FTNodeImpl *) [[value object] retain]; } NS_HANDLER if( ![localException isKindOfClass: [BDBException class]] ) { [[[FTInternalDatamanagementException alloc] initWithBDBException: (BDBException *) localException ] raiseWithPredecessor: localException ]; } else { [localException raise]; } NS_ENDHANDLER EC_AUTORELEASEPOOL_END return [toReturn autorelease]; } - (BDBDatabaseConfig *) nodeDBConfig { BDBDatabaseConfig * dbConfig = [[BDBDatabaseConfig alloc] init]; [dbConfig setDatabaseType: BDB_RECNO]; return dbConfig; } - (id ) nodeIterator { return [[[FTGraphNodeIteratorImpl alloc] initForGraph: self usingIdIterator: [self->keyToGraphInfo iterator ]] autorelease]; } - (id ) nodeWithId: (id ) aNodeId { FTNodeImpl *toReturn = nil; id cacheLookup; id recno = nil; cacheLookup = [self->idToNodeCache objectForKey: aNodeId incrementRefCounter: YES]; if( nil != cacheLookup ) { toReturn = (FTNodeImpl *) cacheLookup; } else { /** * Load the node from the database... */ NS_DURING recno = [self recordNumberOfNodeId: aNodeId]; if( nil != recno ) { NSAssert( [recno isKindOfClass: [BDBDatabaseRecordNumber class]], @"FTGraphImpl::nodeWithId: recno is NOT of type "\ "BDBDatabaseRecordNumber" ); toReturn = [self nodeAtRecordNumber: recno]; [self->idToNodeCache addObject: toReturn forKey: aNodeId]; } NS_HANDLER if( nil != recno ) { [recno release]; } [localException raise]; NS_ENDHANDLER } return toReturn; } - (BOOL) performAction { BOOL success = NO; return success; } - (id ) objectToIdMapper { return objectToIdMapper; } - recordNumberOfNode: (FTNodeImpl *) node { return [self recordNumberOfNodeId: [node nodeId]]; } - recordNumberOfNodeId: (id ) nodeId { BDBDatabaseEntry *key; BDBDatabaseRecordNumber *recno = nil; BDBOperationStatus status; EC_AUTORELEASEPOOL_BEGIN key = [[[BDBDatabaseEntry alloc] initWithObject: nodeId] autorelease]; recno = [[BDBDatabaseRecordNumber alloc] init]; status = [self->idToRecnoDB getEntryWithTransaction: nil key: key data: recno ]; if( BDB_STATUS_SUCCESS != status ) { [recno release]; recno = nil; if( BDB_STATUS_NOTFOUND != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::recordNumberOfNode: Unable to fetch "\ "record number of given node!" ]; EC_AUTORELEASEPOOL_RELEASE [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } } EC_AUTORELEASEPOOL_END return recno; } - (BOOL) remove { if( [[FTLogging coreLog] isTraceEnabled] ) { [[FTLogging coreLog] trace: @"FTGraphImpl::remove"]; } [self close]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::remove: Removing whole directore: %@", self->graphDatabaseDirectory]; } return [[NSFileManager defaultManager] removeFileAtPath: self->graphDatabaseDirectory handler: nil]; } - removeNode: (id ) nodeToRemove { id transaction; FTTransactionContext *context; FTGraphImplTransactions *nextStep; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::Removing node..." ]; } EC_AUTORELEASEPOOL_BEGIN /* check wether the node contains incoming or outgoing references: */ if( 0 < [nodeToRemove countIncomingReferences] || 0 < [nodeToRemove countOutgoingReferences] ) { [[[ECIllegalStateException alloc] initWithIllegalStateInfo: [NSString stringWithFormat: @"FTGraphImpl::removeNode: unable to remove a node"\ " which has incoming/outgoing references. Node:%@", nodeToRemove]] raise]; } /* todo: first check wether node exists in db ... */ transaction = [[FTSessionImpl currentSession] currentTransaction]; NSAssert( nil != transaction, @"No transaction available for current "\ "session!" ); NSAssert( [transaction isKindOfClass: [FTTransactionImpl class]], @"Only instances of FTTransactionImp are supported right now!" ); context = [[((FTTransactionImpl *) transaction) createContext] autorelease]; nextStep = [[FTGraphImplTransactions createForDeletionOfNode: (FTNodeImpl *) nodeToRemove withContext: context withGraph: self] autorelease]; [((FTTransactionImpl *) transaction) addTransactionStep: nextStep withContext: context ]; /* also remove it from the persistent set keyToGraphInfo: */ [self->keyToGraphInfo removeObject: [nodeToRemove nodeId]]; EC_AUTORELEASEPOOL_END return self; } - removeNodeFromIdToRecnoDB: (FTNodeImpl *) node { id recno; EC_AUTORELEASEPOOL_BEGIN recno = [self recordNumberOfNode: node ]; if( nil != recno ) { /** * Entry found - remove it: */ BDBDatabaseEntry *key; BDBOperationStatus status; key = [[[BDBDatabaseEntry alloc] initWithObject: [node nodeId]] autorelease]; status = [self->idToRecnoDB deleteEntryWithTransaction: nil key: key ]; if( BDB_STATUS_SUCCESS != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::removeNodeFromIdToRecnoDB: Unable to remove "\ " given node from record database: idToRecnoDB" ]; EC_AUTORELEASEPOOL_RELEASE [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } } EC_AUTORELEASEPOOL_END return recno; } - (BOOL) removeNodeWithRecordNumber: (id) recordNumber { BDBOperationStatus status; if( nil == recordNumber ) { [[[ECIllegalArgumentException alloc] initWithArgumentInfo: @"FTGraphImpl::removeNodeWithRecordNumber: "\ "recordNumber may not be nil" ] raise]; } if( ![recordNumber isKindOfClass: [BDBDatabaseRecordNumber class]] ) { [[[ECIllegalArgumentException alloc] initWithArgumentInfo: @"FTGraphImpl::removeNodeWithRecordNumber: "\ "recordNumber is not of class BDBDatabaseRecordNumber" ] raise]; } status = [self->nodeDB deleteEntryWithTransaction: nil key: recordNumber]; if( BDB_STATUS_SUCCESS != status ) { [[FTLogging coreLog] error: @"FTGraphImpl::removeNodeWithRecordNumber: Unable to remove "\ " given node from node database: nodeDB" ]; [[[FTInternalDatamanagementException alloc] initWithOperationStatus: status ] raise]; } return YES; } - removeNodeFromDatabase: (FTNodeImpl *) node { id recordNumber; recordNumber = [self removeNodeFromIdToRecnoDB: node]; if( nil != recordNumber ) { [self removeNodeWithRecordNumber: recordNumber]; } [self->idToNodeCache removeObjectForKey: [node nodeId]]; return self; } - (id ) serviceWithId: (NSString *) aServiceId { return [[self->server serviceManager] serviceWithId: aServiceId forGraph: self]; } - (id ) serviceWithId: (NSString *) aServiceId forNode: (FTNodeImpl *) aNode { return [[self->server serviceManager] serviceWithId: aServiceId forGraph: self forNode: aNode]; } - setupDatabases { BDBDatabaseConfig *dbConfig; if( self->databasesMounted ) { return self; } EC_AUTORELEASEPOOL_BEGIN if( nil != graphDatabaseName ) { [[[ECIllegalStateException alloc] initWithIllegalStateInfo: @"FTGraphImpl::setup: setup has been called "\ "although a graph database name is already given?!"] raise]; } self->graphDatabaseName = [[self createDatabaseGraphDirectory] retain]; self->graphDatabaseDirectory = [[NSString alloc] initWithFormat: @"%@/%@", [self->server baseDataDir], self->graphDatabaseName]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: Creating database for objectToIdMapper..." ]; } objectToIdMapper = [[FTDefaultObjectToIdMapper alloc] initWithDatabaseName: [self nameOfObjectToIdMapperDatabase] forServer: self->server]; [objectToIdMapper setupDatabase]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: Creating database for idToRecno..." ]; } if( [[NSFileManager defaultManager] fileExistsAtPath: [[self nameOfIdToRecnoDatabase] autorelease]] ) { NSMutableString *info = [[NSMutableString alloc] initWithFormat: @"Database named %@ already exists", [self nameOfIdToRecnoDatabase]]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: info] raise]; } dbConfig = [[self idToRecnoDBConfig] autorelease]; [dbConfig setAllowCreate: YES]; self->idToRecnoDB = [BDBDatabase initWithFilename: [[self nameOfIdToRecnoDatabase] autorelease] databaseName: nil databaseConfig: dbConfig]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Creating database nodes..." ]; } if( [[NSFileManager defaultManager] fileExistsAtPath: [[self nameOfNodesDatabase] autorelease]] ) { NSMutableString *info = [[NSMutableString alloc] initWithFormat: @"Database named %@ already exists", [self nameOfNodesDatabase]]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: info] raise]; } dbConfig = [[self nodeDBConfig] autorelease]; [dbConfig setAllowCreate: YES]; self->nodeDB = [BDBDatabase initWithFilename:[[self nameOfNodesDatabase] autorelease] databaseName: nil databaseConfig: dbConfig]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Creating database keyToGraphInfo..." ]; } self->keyToGraphInfo = [[[[[[FTBootstrap bootstrap] config] classObjectFactory] classObjectForKey: @"FTPersistentSet"] alloc] init]; [self->keyToGraphInfo createDBUsingDataFile: [self nameOfkeyToGraphInfoDatabase]]; self->idToNodeCache = [[ECCache alloc] init]; self->databasesMounted = YES; //TODO if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTGraphImpl::setup: ... DONE" ]; } EC_AUTORELEASEPOOL_END return self; } - updateNode: (FTNodeImpl *) node { id recno = nil; recno = [self removeNodeFromDatabase: node]; if( nil != recno ) { /** * remove element from cache so that the insert call does not fail. * The insert call itself will add the element to the cache */ [self insertNodeToDatabase: node]; } return self; } - unmountDatabases { if( nil != self->objectToIdMapper ) { NS_DURING [self->objectToIdMapper unmountDatabase]; NS_HANDLER [[FTLogging coreLog] error: @"FTGraphImpl::unmountDatabases: Unmounting database for "\ "objectToIdMapper database FAILED! Exception: %@", localException ]; NS_ENDHANDLER [self->objectToIdMapper release]; self->objectToIdMapper = nil; } if( nil != self->idToRecnoDB ) { NS_DURING [self->idToRecnoDB close]; NS_HANDLER [[FTLogging coreLog] error: @"FTGraphImpl::unmountDatabases: Unmounting idToRecno "\ "database FAILED! Exception: %@", localException ]; NS_ENDHANDLER [self->idToRecnoDB release]; self->idToRecnoDB = nil; } if( nil != self->nodeDB ) { NS_DURING [self->nodeDB close]; NS_HANDLER [[FTLogging coreLog] error: @"FTGraphImpl::unmountDatabases: Unmounting node "\ "database FAILED! Exception: %@", localException ]; NS_ENDHANDLER [self->nodeDB release]; self->nodeDB = nil; } if( nil != self->keyToGraphInfo ) { NS_DURING [self->keyToGraphInfo closeDB]; NS_HANDLER [[FTLogging coreLog] error: @"FTGraphImpl::unmountDatabases: Unmounting graph info "\ "database FAILED! Exception: %@", localException ]; NS_ENDHANDLER [self->keyToGraphInfo release]; self->keyToGraphInfo = nil; } //TODO if( nil != self->idToNodeCache ) { [self->idToNodeCache release]; self->idToNodeCache = nil; } self->databasesMounted = NO; return self; } @end