/*!
@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