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

  <pre>
  -------------------------------------------------------------------------
  Modification history

  23.02.05 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  </pre>
*/

#include <Encore/Encore.h>
#include <BDB/BDB.h>
#include <FT/FTDefines.h>
#include <FT/FTGraphImpl.h>
#include <FT/FTServerImpl.h>
#include <FT/FTNodeImpl.h>
#include <FT/FTTransactionContext.h>
#include <FT/FTGraphImplTransactions.h>
#include <FT/FTTransactionImpl.h>
#include <FT/FTSessionImpl.h>
#include <FT/FTLogging.h>
#include <FT/FTBootstrap.h>
#include <FTGraphNodeIteratorImpl.h>
#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 <FTId>) 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 <FTNode>) createNodeWithId: (id <FTId>) 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 <FTId>) 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 <FTTransaction> 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 <ECIterator>) nodeIterator {
  return [[[FTGraphNodeIteratorImpl alloc] 
    initForGraph: self
    usingIdIterator: [self->keyToGraphInfo iterator ]] autorelease];
}


- (id <FTNode>) nodeWithId: (id <FTId>) 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 <FTObjectToIdMapper>) objectToIdMapper {
  return objectToIdMapper;
}


- recordNumberOfNode: (FTNodeImpl *) node {
  return [self recordNumberOfNodeId: [node nodeId]];
}



- recordNumberOfNodeId: (id <FTId>) 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 <FTNode>) nodeToRemove {
  id <FTTransaction> 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 <FTService>) serviceWithId: (NSString *) aServiceId {
  return [[self->server serviceManager] serviceWithId: aServiceId
    forGraph: self];
}


- (id <FTService>) 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
