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