/****************************************************************************** @header FTServer @abstract Base class for all FT classes @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

  30.01.2005 ola     initial version
  23.08.2006 ola     license changed
  -------------------------------------------------------------------------
  
******************************************************************************/ #include #include #include #include #include #include /** * Mutex used for synchronization within server impl. */ static NSLock *_ftserverimpl_lock = nil; @implementation FTServerImpl + initialize { _ftserverimpl_lock = [[NSLock alloc] init]; return self; } - initWithConfig: (FTConfig *) theConfig { self = [super init]; self->server_state = FT_SERVER_STATE_STARTED; self->notificationCenter = [[NSNotificationCenter alloc] init]; self->config = [theConfig retain]; self->sessionManager = nil; self->nodeidToProviderManagerDB = nil; self->graphIdToGraphDB = nil; self->graphManager = nil; self->defaultDictionaryProviderDB = nil; self->systemDictionary = nil; self->transactionManager = nil; self->serviceManager = [[FTDefaultServiceManagerImpl alloc] initWithServer: self]; return self; } - (void) dealloc { if( FT_SERVER_STATE_MOUNTED == self->server_state ) { NS_DURING [self unmount]; NS_HANDLER [[FTLogging coreLog] error: @"FTServerImpl::dealloc: unmounting all databases FAILED!" ]; NS_ENDHANDLER } NS_DURING [self->serviceManager release]; NS_HANDLER [[FTLogging coreLog] error: @"FTServerImpl::dealloc: ERROR while releasing service manager!" ]; NS_ENDHANDLER [self->notificationCenter release]; if( nil != self->config ) { [self->config release]; } self->server_state = FT_SERVER_STATE_UNKNOWN; [super dealloc]; } - addOberver: (id) anObserver selector: (SEL) aSelector { [self->notificationCenter addObserver: anObserver selector: aSelector name: [FTServerNotification notificationName] object: nil]; return self; } - (NSString *) baseDataDir { return [self->config baseDataDir]; } - checkServerState: (__ft_req_operation_t) reqOperation { if( reqOperation & __FT_REQ_MOUNT_DATABASES ) { if( !(FT_SERVER_STATE_STARTED & self->server_state) ) { [[FTLogging coreLog] error: @"FTServerImpl::checkServerState: Server is not in the "\ "state FT_SERVER_STATE_STARTED!" ]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: @"FTServerImpl::checkServerState: Server is not in the "\ "state FT_SERVER_STATE_STARTED!" ] raise]; } } if( reqOperation & __FT_REQ_DATA_ACCESS ) { /* * Check wether all databases are mounted */ if( !(FT_SERVER_STATE_MOUNTED & self->server_state) ) { [[[ECIllegalStateException alloc] initWithIllegalStateInfo: @"FTServerImpl::checkServerState: Data access not allowed since "\ "databases are not mounted" ] raise]; } } if( reqOperation & __FT_REQ_UNMOUNT_DATABASES ) { /** * Are the databases mount? */ if( !(FT_SERVER_STATE_MOUNTED & self->server_state) ) { [[[ECIllegalStateException alloc] initWithIllegalStateInfo: @"FTServerImpl::checkServerState: Umount not possible since "\ "databases are not mounted" ] raise]; } } return self; } - (FTConfig *) config { return self->config; } - (NSString *) constructDatabaseFilename: (NSString *) databaseName { NSMutableString *filename = nil; if( nil == [self baseDataDir] ) { [[[ECIncompleteInitializationException alloc] initWithReason: @"Base data directory not set"] raise]; } if( nil == databaseName ) { [[[ECIllegalArgumentException alloc] initWithArgumentInfo: @"Argument databaseName equals nil"] raise]; } filename = [[NSMutableString alloc] initWithString: [self baseDataDir] ]; [filename appendString: @"/"]; [filename appendString: databaseName ]; return filename; } - (BDBDatabase *) createDatabaseWithName: (NSString *) databaseName withConfig: (BDBDatabaseConfig *) aConfig { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSFileManager *fileMgr = [NSFileManager defaultManager]; NSMutableString *filename; BDBDatabase *toReturn = nil; filename = [[self constructDatabaseFilename: databaseName] autorelease]; if( [fileMgr fileExistsAtPath: filename] ) { NSMutableString *info = [[NSMutableString alloc] initWithFormat: @"Database named %@ already exists", filename]; [pool release]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: info] raise]; } toReturn = [BDBDatabase initWithFilename: filename databaseName: nil databaseConfig: aConfig]; [pool release]; return toReturn; } - (BDBDatabaseConfig *) dbConfigForDefaultDictionaryProvider { BDBDatabaseConfig * dbConfig = [[BDBDatabaseConfig alloc] init]; [dbConfig setDatabaseType: BDB_BTREE]; [dbConfig setBTreeRecordNumbering: YES]; [dbConfig setAllowDuplicates: NO]; return dbConfig; } - (BDBDatabaseConfig *) dbConfigForGraphIdToGraphDatabase { BDBDatabaseConfig * dbConfig = [[BDBDatabaseConfig alloc] init]; [dbConfig setDatabaseType: BDB_BTREE]; [dbConfig setBTreeRecordNumbering: YES]; [dbConfig setAllowDuplicates: NO]; return dbConfig; } - (BDBDatabaseConfig *) dbConfigForNodeIdToProviderManager { BDBDatabaseConfig * dbConfig = [[BDBDatabaseConfig alloc] init]; [dbConfig setDatabaseType: BDB_BTREE]; [dbConfig setBTreeRecordNumbering: YES]; [dbConfig setAllowDuplicates: NO]; return dbConfig; } - (id ) defaultDictionaryProvider { return self->defaultDictionaryProvider; } - (id ) defaultObjectToIdMapper { return self->defaultObjectToIdMapper; } - (FTGraphManagerImpl *) graphManager { return self->graphManager; } - (BOOL) databasesMounted { return FT_SERVER_STATE_MOUNTED == self->server_state; } - mountDatabases { NSString *dbName; BDBDatabaseConfig *dbConfig; EC_AUTORELEASEPOOL_BEGIN [self checkServerState: __FT_REQ_MOUNT_DATABASES]; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"Opening database connections...." ]; } NS_DURING [_ftserverimpl_lock lock]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Database: DefaultObjectToIdMapper...." ]; } dbName = [self nameOfDefaultObjectToIdMapperDatabase]; defaultObjectToIdMapper = [[FTDefaultObjectToIdMapper alloc] initWithDatabaseName: [self constructDatabaseFilename: dbName] forServer: self]; [defaultObjectToIdMapper mountDatabase]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Database: GraphIdToGraphDatabase...." ]; } dbName = [self nameOfGraphIdToGraphDatabase]; dbConfig = [[self dbConfigForGraphIdToGraphDatabase] autorelease]; [dbConfig setAllowCreate: NO]; self->graphIdToGraphDB = [self openDatabaseWithName: dbName withConfig: dbConfig]; self->graphManager = [[FTGraphManagerImpl alloc] initForServer: self withGraphIdToGraphDatabase: self->graphIdToGraphDB ]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] error: @"Database: NodeIdToProviderManager...." ]; } dbName = [self nameOfNodeIdToProviderManagerDatabase]; dbConfig = [[self dbConfigForNodeIdToProviderManager] autorelease]; [dbConfig setAllowCreate: YES]; self->nodeidToProviderManagerDB = [self openDatabaseWithName: dbName withConfig: dbConfig]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Database: DefaultDictionaryProvider...." ]; } dbName = [self nameOfDefaultDictionaryProviderDatabase]; dbConfig = [[self dbConfigForDefaultDictionaryProvider] autorelease]; self->defaultDictionaryProviderDB = [self openDatabaseWithName: dbName withConfig: dbConfig]; self->defaultDictionaryProvider = [[FTGenericDictionaryProviderImpl alloc] initWithDatabase: self->defaultDictionaryProviderDB ]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Reading system dictionary...." ]; } self->systemDictionary = [[FTSystemDictionary alloc] initWithDictionaryProvider: self->defaultDictionaryProvider forServer: self]; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"Connected to all databases." ]; } NS_HANDLER [_ftserverimpl_lock unlock]; EC_AUTORELEASEPOOL_RELEASE; [localException raise]; NS_ENDHANDLER [_ftserverimpl_lock unlock]; self->sessionManager = [[FTSessionManagerImpl alloc] initForServer: self]; self->transactionManager = [[FTTransactionManagerImpl alloc] init]; [self->transactionManager addTransactionOptimizer: [[FTGenericTransactionOptimizer alloc] init] withPriority: 0]; self->server_state = FT_SERVER_STATE_MOUNTED; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"FTServerImpl::mountDatabases: Now switching services ONLINE..." ]; } [self->serviceManager switchAllServicesToMode: FT_SERVICE_MODE_ONLINE]; EC_AUTORELEASEPOOL_END return self; } - (NSString *) nameOfGraphIdToGraphDatabase { return [[self->config databaseNames] databaseNameForEntry: FT_IP_GRAPHID_2_GRAPH_DBNAME]; } - (NSString *) nameOfNodeIdToProviderManagerDatabase { return [[self->config databaseNames] databaseNameForEntry: FT_IP_NODEID_2_PROVIDERMGR_DBNAME]; } - (NSString *) nameOfDefaultDictionaryProviderDatabase { return [[self->config databaseNames] databaseNameForEntry: FT_IP_DEFAULT_DICTIONARYPROVIDER_DBNAME]; } - (NSString *) nameOfDefaultObjectToIdMapperDatabase { return [[self->config databaseNames] databaseNameForEntry: FT_IP_DefaultObject_2_IDMAPPER_DBNAME]; } - (BDBDatabase *) openDatabaseWithName: (NSString *) databaseName withConfig: (BDBDatabaseConfig *) aConfig { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSFileManager *fileMgr = [NSFileManager defaultManager]; NSMutableString *filename; BDBDatabase *toReturn = nil; filename = [[self constructDatabaseFilename: databaseName] autorelease]; if( ![fileMgr fileExistsAtPath: filename] ) { NSMutableString *info = [[NSMutableString alloc] initWithFormat: @"Database named %@ does not exist", filename]; [pool release]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: info] raise]; } toReturn = [BDBDatabase initWithFilename: filename databaseName: nil databaseConfig: aConfig]; [pool release]; return toReturn; } - (id ) sessionManager { return self->sessionManager; } - (id ) serviceManager { return self->serviceManager; } - setupDatabases { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [self checkServerState: __FT_REQ_MOUNT_DATABASES]; NS_DURING NSString *dbName; BDBDatabaseConfig *dbConfig; [_ftserverimpl_lock lock]; /** */ if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Creating databases....\n"\ "Database: DefaultObjectToIdMapper...." ]; } dbName = [self nameOfDefaultObjectToIdMapperDatabase]; defaultObjectToIdMapper = [[FTDefaultObjectToIdMapper alloc] initWithDatabaseName: [self constructDatabaseFilename: dbName] forServer: self]; [defaultObjectToIdMapper setupDatabase]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Database: GraphIdToGraphDatabase...." ]; } dbName = [self nameOfGraphIdToGraphDatabase]; dbConfig = [[self dbConfigForGraphIdToGraphDatabase] autorelease]; [dbConfig setAllowCreate: YES]; self->graphIdToGraphDB = [self createDatabaseWithName: dbName withConfig: dbConfig]; self->graphManager = [[FTGraphManagerImpl alloc] initForServer: self withGraphIdToGraphDatabase: self->graphIdToGraphDB ]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Database: NodeIdToProviderManager...." ]; } dbName = [self nameOfNodeIdToProviderManagerDatabase]; dbConfig = [[self dbConfigForNodeIdToProviderManager] autorelease]; [dbConfig setAllowCreate: YES]; self->nodeidToProviderManagerDB = [self createDatabaseWithName: dbName withConfig: dbConfig]; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Database: DefaultDictionaryProvider...." ]; } dbName = [self nameOfDefaultDictionaryProviderDatabase]; dbConfig = [[self dbConfigForDefaultDictionaryProvider] autorelease]; [dbConfig setAllowCreate: YES]; self->defaultDictionaryProviderDB = [self createDatabaseWithName: dbName withConfig: dbConfig]; self->defaultDictionaryProvider = [[FTGenericDictionaryProviderImpl alloc] initWithDatabase: self->defaultDictionaryProviderDB ]; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"Setting up system dictionary..." ]; } self->systemDictionary = [[FTSystemDictionary alloc] initWithDictionaryProvider: self->defaultDictionaryProvider forServer: self]; [self->systemDictionary setup]; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"Databases successfully created!" ]; } NS_HANDLER [_ftserverimpl_lock unlock]; [pool release]; [localException raise]; NS_ENDHANDLER self->sessionManager = [[FTSessionManagerImpl alloc] initForServer: self]; self->transactionManager = [[FTTransactionManagerImpl alloc] init]; [self->transactionManager addTransactionOptimizer: [[FTGenericTransactionOptimizer alloc] init] withPriority: 0]; self->server_state = FT_SERVER_STATE_MOUNTED; [_ftserverimpl_lock unlock]; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"FTServerImpl::setupDatabases: Now switching services ONLINE..." ]; } [self->serviceManager switchAllServicesToMode: FT_SERVICE_MODE_ONLINE]; [pool release]; return self; } - mountDatabasesByAdminSession: (id ) adminSession { [self mountDatabases]; [self->sessionManager reconnectSession: adminSession]; return self; } - shutdown { if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"FTServerImpl::shutdown: Shutting down server..." ]; } [self checkServerState: __FT_REQ_UNMOUNT_DATABASES]; // TODO 1: Kill all sessions /** * TODO 2: At present we use a very simple mechanism here: We simply close * all databases and inform the related instances about it. In future * we better introduce proxy objects to be hand-off to the client instead * of the real objects. */ // informing all listeners first: [FTServerNotification sendShutdownNotificationTo: self->notificationCenter ]; return [self unmount]; } - unmount { if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"Unmounting databases...\n"\ "FTServerImpl::umount: Switching all services to mode OFFLINE..." ]; } [self->serviceManager switchAllServicesToMode: FT_SERVICE_MODE_SHUTDOWN]; /** * TODO: better handling for the case where closing a database fails! */ NS_DURING if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Closing graphIdToGraphDB..." ]; } [self->graphManager release]; self->graphManager = nil; [self->graphIdToGraphDB close]; [self->graphIdToGraphDB release]; self->graphIdToGraphDB = nil; NS_HANDLER [[FTLogging coreLog] error:@"FTServerImpl::unmount: Closing database graphIdToGraphDB FAILED!"\ " Exception: %@", localException ]; NS_ENDHANDLER NS_DURING if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Closing nodeidToProviderManagerDB..." ]; } [self->nodeidToProviderManagerDB close]; [self->nodeidToProviderManagerDB release]; self->nodeidToProviderManagerDB = nil; NS_HANDLER [[FTLogging coreLog] error: @"FTServerImpl::unmount: Closing database "\ "nodeidToProviderManagerDB FAILED! Exception: %@", localException ]; NS_ENDHANDLER NS_DURING if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Closing defaultDictionaryProviderDB..." ]; } [self->systemDictionary release]; self->systemDictionary = nil; [self->defaultDictionaryProvider release]; self->defaultDictionaryProvider = nil; [self->defaultDictionaryProviderDB close]; [self->defaultDictionaryProviderDB release]; self->defaultDictionaryProviderDB = nil; NS_HANDLER [[FTLogging coreLog] error: @"FTServerImpl::unmount: Closing database "\ "defaultDictionaryProviderDB FAILED! Exception: %@", localException ]; NS_ENDHANDLER NS_DURING if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"Closing defaultObjectToIdMapperDB..." ]; } [self->defaultObjectToIdMapper unmountDatabase]; [self->defaultObjectToIdMapper release]; self->defaultObjectToIdMapper = nil; NS_HANDLER [[FTLogging coreLog] error: @"FTServerImpl::unmount: Closing database "\ "defaultObjectToIdMapperDB FAILED! Exception: %@", localException ]; NS_ENDHANDLER [self->sessionManager release]; self->sessionManager = nil; [self->transactionManager release]; self->transactionManager = nil; if( [[FTLogging coreLog] isInfoEnabled] ) { [[FTLogging coreLog] info: @"All datbases have been unmounted..." ]; } self->server_state = FT_SERVER_STATE_STARTED; return self; } - (FTSystemDictionary *) systemDictionary { return self->systemDictionary; } - (id ) transactionManager { return transactionManager; } @end // ------------------------------------------------------- FTServerNotification @implementation FTServerNotification + (void) sendShutdownNotificationTo: (NSNotificationCenter *) center { FTServerNotification *n = [[FTServerNotification alloc] initWithServerActionHint: FT_SERVER_ACTION_SHUTDOWN ]; NS_DURING [ center postNotificationName: [self notificationName] object: n ]; NS_HANDLER [n release]; [localException raise]; NS_ENDHANDLER } + (NSString *) notificationName { return @"FTServerNotification"; } - initWithServerActionHint: (ft_server_action_hint_t) actionHint; { self = [super init]; self->serverActionHint = actionHint; return self; } - (ft_server_action_hint_t) serverActionHint { return self->serverActionHint; } @end