From f1df83e44c23822d35f4ac545d207f071d34de2e Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 3 Aug 2009 15:46:02 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: 99ca451495fc28364e0d775f0a4b950f0df4164d Monotone-Revision: 740d7bf9e85e6faf504cb29387d81a224a6a92e9 Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2009-08-03T15:46:02 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 8 + SoObjects/SOGo/GNUmakefile | 1 + SoObjects/SOGo/LDAPUserManager.m | 29 +- SoObjects/SOGo/NSDictionary+DAV.m | 2 +- SoObjects/SOGo/NSScanner+BSJSONAdditions.m | 9 +- SoObjects/SOGo/SOGoCache.h | 16 +- SoObjects/SOGo/SOGoCache.m | 431 +++++---------------- SoObjects/SOGo/SOGoUser.h | 1 + SoObjects/SOGo/SOGoUser.m | 133 +++---- SoObjects/SOGo/SOGoUserDefaults.h | 3 +- SoObjects/SOGo/SOGoUserDefaults.m | 27 +- sogo.spec | 4 +- 12 files changed, 195 insertions(+), 469 deletions(-) diff --git a/ChangeLog b/ChangeLog index 92cd3825a..f335bb8ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-08-03 Ludovic Marcotte + + * Dropped the current caching subsystem based on + GNUstep's DO and replaced it with memcached. This + is much more efficient and scalable than DO and will + also prevent obscure libffi/libffcall crashes on + various platforms. + 2009-08-03 Francis Lachapelle * UI/Scheduler/UIxCalView.m ([UIxCalView -setCurrentView:]): avoid diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index 7871ea16b..6db8b586a 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -128,6 +128,7 @@ endif ADDITIONAL_TOOL_LIBS += -Lobj -lSOGo$(LIBRARY_NAME_SUFFIX) ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/ ADDITIONAL_LIB_DIRS += -L../../SOPE/GDLContentStore/obj/ +ADDITIONAL_LDFLAGS += -lmemcached -include GNUmakefile.preamble ifneq ($(FHS_INSTALL_ROOT),) diff --git a/SoObjects/SOGo/LDAPUserManager.m b/SoObjects/SOGo/LDAPUserManager.m index 77c0883c6..780c1a78d 100644 --- a/SoObjects/SOGo/LDAPUserManager.m +++ b/SoObjects/SOGo/LDAPUserManager.m @@ -21,7 +21,6 @@ */ #import #import -#import #import #import #import @@ -309,7 +308,7 @@ static NSLock *lock = nil; if (!currentUser) { currentUser = [NSMutableDictionary dictionary]; - [[SOGoCache sharedCache] cacheAttributes: currentUser forLogin: login]; + [[SOGoCache sharedCache] cacheValues: currentUser ofType: @"attributes" forLogin: login]; } [currentUser setObject: password forKey: @"password"]; } @@ -417,34 +416,18 @@ static NSLock *lock = nil; #endif key = [newUser objectForKey: @"c_uid"]; if (key) - [[SOGoCache sharedCache] cacheAttributes: newUser forLogin: key]; + { + [[SOGoCache sharedCache] cacheValues: newUser ofType: @"attributes" forLogin: key]; + } + emails = [[newUser objectForKey: @"emails"] objectEnumerator]; while ((key = [emails nextObject])) { - [[SOGoCache sharedCache] cacheAttributes: newUser forLogin: key]; + [[SOGoCache sharedCache] cacheValues: newUser ofType: @"attributes" forLogin: key]; } #if defined(THREADSAFE) [lock unlock]; #endif - - // We propagate the loaded LDAP attributes to other sogod instances - // which will cache them in SOGoCache (excluding for the instance - // that actually posts the notification) - if ([newUser objectForKey: @"c_uid"]) - { - NSMutableDictionary *d; - - d = [NSMutableDictionary dictionary]; - [d setObject: newUser forKey: @"values"]; - [d setObject: [newUser objectForKey: @"c_uid"] - forKey: @"uid"]; - - [(NSDistributedNotificationCenter *)[NSDistributedNotificationCenter defaultCenter] - postNotificationName: @"SOGoUserAttributesHaveLoaded" - object: nil - userInfo: d - deliverImmediately: YES]; - } } - (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid diff --git a/SoObjects/SOGo/NSDictionary+DAV.m b/SoObjects/SOGo/NSDictionary+DAV.m index 4b8904e81..bd3d40161 100644 --- a/SoObjects/SOGo/NSDictionary+DAV.m +++ b/SoObjects/SOGo/NSDictionary+DAV.m @@ -1,6 +1,6 @@ /* NSDictionary+DAV.m - this file is part of SOGo * - * Copyright (C) 2008 Inverse inc. + * Copyright (C) 2008-2009 Inverse inc. * * Author: Wolfgang Sourdeau * diff --git a/SoObjects/SOGo/NSScanner+BSJSONAdditions.m b/SoObjects/SOGo/NSScanner+BSJSONAdditions.m index e0c561db6..d1af2b006 100644 --- a/SoObjects/SOGo/NSScanner+BSJSONAdditions.m +++ b/SoObjects/SOGo/NSScanner+BSJSONAdditions.m @@ -255,9 +255,12 @@ NSString *jsonNullString = @"null"; - (BOOL)scanJSONNumber:(NSNumber **)number { - NSDecimal decimal; - BOOL result = [self scanDecimal:&decimal]; - *number = [NSDecimalNumber decimalNumberWithDecimal:decimal]; + //NSDecimal decimal; + //BOOL result = [self scanDecimal:&decimal]; + //*number = [NSDecimalNumber decimalNumberWithDecimal:decimal]; + int value; + BOOL result = [self scanInt: &value]; + *number = [NSNumber numberWithInt: value]; return result; } diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index 7531eef8c..bbb806e3c 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -26,6 +26,8 @@ #import +#include + @class NSMutableDictionary; @class NSString; @class NSTimer; @@ -39,6 +41,8 @@ { @private NSTimer *_cleanupTimer; + memcached_server_st *servers; + memcached_st *handle; } + (NSTimeInterval) cleanupInterval; @@ -54,15 +58,13 @@ - (void) registerUser: (SOGoUser *) user; - (id) userNamed: (NSString *) name; -- (void) cacheAttributes: (NSDictionary *) theAttributes - forLogin: (NSString *) theLogin; - (NSMutableDictionary *) userAttributesForLogin: (NSString *) theLogin; -- (SOGoUserDefaults *) userDefaultsForLogin: (NSString *) theLogin; -- (SOGoUserDefaults *) userSettingsForLogin: (NSString *) theLogin; +- (NSDictionary *) userDefaultsForLogin: (NSString *) theLogin; +- (NSDictionary *) userSettingsForLogin: (NSString *) theLogin; -- (void) setDefaults: (SOGoUserDefaults *) theDefaults - forLogin: (NSString *) theLogin - key: (NSString *) theKey; +- (void) cacheValues: (NSDictionary *) theAttributes + ofType: (NSString *) theType + forLogin: (NSString *) theLogin; @end diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 7d111f972..21e1e1a1a 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -22,26 +22,16 @@ */ /* - * [ Structure ] - * users: key = user ID value = NSMutableDictionary instance - * value: key = @"user" value = SOGOUser instance - * key = @"cleanupDate" value = NSDate instance - * key = @"defaults" value = SOGoUserDefaults instance - * key = @"settings" value = SOGoUserDefaults instance - * key = @"attributes" value = NSDictionary instance (attributes from LDAP) + * [ Cache Structure ] * - * [ Workflows - processes A and B ] - * - * A cache user defaults and posts the notification - * B .... - * - * B crashes - * B receives a notificaion update + * users value = instances of SOGoUser > flushed after the completion of every SOGo requests + * +defaults value = NSDictionary instance > user's defaults + * +settings value = NSDictionary instance > user's settings + * +attributes value = NSMutableDictionary instance > user's LDAP attributes */ #import #import -#import #import #import #import @@ -57,6 +47,8 @@ #import "SOGoCache.h" +#import "NSDictionary+BSJSONAdditions.h" + // We define the default value for cleaning up cached // users' preferences. This value should be relatively // high to avoid useless database calls. @@ -65,6 +57,8 @@ static NSTimeInterval cleanupInterval = 300; static NSMutableDictionary *cache = nil; static NSMutableDictionary *users = nil; +static NSString *memcachedServerName = @"localhost"; + static SOGoCache *sharedCache = nil; #if defined(THREADSAFE) @@ -91,7 +85,7 @@ static NSLock *lock; [lock lock]; #endif if (!sharedCache) - sharedCache = [self new]; + sharedCache = [[self alloc] init]; #if defined(THREADSAFE) [lock unlock]; #endif @@ -105,6 +99,10 @@ static NSLock *lock; [lock lock]; #endif [cache removeAllObjects]; + + // This is essential for refetching the cached values in case something has changed + // accross various sogod processes + [users removeAllObjects]; #if defined(THREADSAFE) [lock unlock]; #endif @@ -115,41 +113,11 @@ static NSLock *lock; if ((self = [super init])) { NSString *cleanupSetting; - + memcached_return error; + cache = [[NSMutableDictionary alloc] init]; users = [[NSMutableDictionary alloc] init]; - // We register ourself for notifications - [[NSDistributedNotificationCenter defaultCenter] - addObserver: self - selector: @selector(_userAttributesHaveLoaded:) - name: @"SOGoUserAttributesHaveLoaded" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - addObserver: self - selector: @selector(_userDefaultsHaveLoaded:) - name: @"SOGoUserDefaultsHaveLoaded" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - addObserver: self - selector: @selector(_userDefaultsHaveChanged:) - name: @"SOGoUserDefaultsHaveChanged" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - addObserver: self - selector: @selector(_userSettingsHaveLoaded:) - name: @"SOGoUserSettingsHaveLoaded" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - addObserver: self - selector: @selector(_userSettingsHaveChanged:) - name: @"SOGoUserSettingsHaveChanged" - object: nil]; - // We fire our timer that will cleanup cache entries cleanupSetting = [[NSUserDefaults standardUserDefaults] objectForKey: @"SOGoCacheCleanupInterval"]; @@ -157,13 +125,16 @@ static NSLock *lock; if (cleanupSetting && [cleanupSetting doubleValue] > 0.0) cleanupInterval = [cleanupSetting doubleValue]; - _cleanupTimer = [NSTimer scheduledTimerWithTimeInterval: cleanupInterval - target: self - selector: @selector(_cleanupSources) - userInfo: nil - repeats: YES]; - [self logWithFormat: @"Cache cleanup interval set every %f seconds", + [self logWithFormat: @"Cache cleanup interval set every %f seconds for memcached", cleanupInterval]; + + handle = memcached_create(NULL); + + if (handle) + { + servers = memcached_server_list_append(NULL, [memcachedServerName UTF8String], 11211, &error); + error = memcached_server_push(handle, servers); + } } return self; @@ -171,31 +142,8 @@ static NSLock *lock; - (void) dealloc { - [[NSDistributedNotificationCenter defaultCenter] - removeObserver: self - name: @"SOGoUserAttributesHaveLoaded" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - removeObserver: self - name: @"SOGoUserDefaultsHaveLoaded" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - removeObserver: self - name: @"SOGoUserDefaultsHaveChanged" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - removeObserver: self - name: @"SOGoUserSettingsHaveLoaded" - object: nil]; - - [[NSDistributedNotificationCenter defaultCenter] - removeObserver: self - name: @"SOGoUserSettingsHaveChanged" - object: nil]; - + memcached_server_free(servers); + memcached_free(handle); [cache release]; [users release]; [super dealloc]; @@ -269,21 +217,10 @@ static NSLock *lock; - (void) registerUser: (SOGoUser *) user { - NSData *cleanupDate; - #if defined(THREADSAFE) [lock lock]; #endif - - cleanupDate = [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]; - - if (![users objectForKey: [user login]]) - [users setObject: [NSMutableDictionary dictionary] forKey: [user login]]; - - [[users objectForKey: [user login]] setObject: user forKey: @"user"]; - [[users objectForKey: [user login]] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]] - forKey: @"cleanupDate"]; - + [users setObject: user forKey: [user login]]; #if defined(THREADSAFE) [lock unlock]; #endif @@ -291,260 +228,92 @@ static NSLock *lock; - (id) userNamed: (NSString *) name { - return [[users objectForKey: name] objectForKey: @"user"]; + return [users objectForKey: name]; } -- (void) cacheAttributes: (NSDictionary *) theAttributes - forLogin: (NSString *) theLogin +// +// For non-blocking cache method, see memcached_behavior_set and MEMCACHED_BEHAVIOR_NO_BLOCK +// memcached is thread-safe so no need to lock here. +// +- (void) cacheValues: (NSDictionary *) theAttributes + ofType: (NSString *) theType + forLogin: (NSString *) theLogin { - if (![users objectForKey: theLogin]) - [users setObject: [NSMutableDictionary dictionary] forKey: theLogin]; + memcached_return error; + const char *key, *value; + unsigned int len, vlen; - [[users objectForKey: theLogin] setObject: theAttributes forKey: @"attributes"]; - [[users objectForKey: theLogin] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]] - forKey: @"cleanupDate"]; + if (!handle) + return; + + key = [[NSString stringWithFormat: @"%@+%@", theLogin, theType] UTF8String]; + len = strlen(key); + + value = [[theAttributes jsonStringValue] UTF8String]; + vlen = strlen(value); + + error = memcached_set(handle, key, len, value, vlen, cleanupInterval, 0); + + if (error != MEMCACHED_SUCCESS) + [self logWithFormat: @"memcached error: unable to cache values with subtype %@ for user %@", theType, theLogin]; + //else + //[self logWithFormat: @"memcached: cached values (%s) with subtype %@ for user %@", value, theType, theLogin]; } +- (NSDictionary *) _valuesOfType: (NSString *) theType + forLogin: (NSString *) theLogin +{ + NSDictionary *d; + + memcached_return rc; + const char *key; + char *s; + unsigned int len, vlen, flags; + + if (!handle) + return nil; + + key = [[NSString stringWithFormat: @"%@+%@", theLogin, theType] UTF8String]; + len = strlen(key); + d = nil; + + s = memcached_get(handle, key, len, &vlen, &flags, &rc); + + if (rc == MEMCACHED_SUCCESS && s) + { + NSString *v; + + v = [NSString stringWithUTF8String: s]; + d = [NSDictionary dictionaryWithJSONString: v]; + //[self logWithFormat: @"read values (%@) for subtype %@ for user %@", [d description], theType, theLogin]; + + free(s); + } + + return d; +} + + - (NSMutableDictionary *) userAttributesForLogin: (NSString *) theLogin { - return [[users objectForKey: theLogin] objectForKey: @"attributes"]; + id o; + + o = [self _valuesOfType: @"attributes" forLogin: theLogin]; + + if (o) + return [NSMutableDictionary dictionaryWithDictionary: o]; + + return nil; } -- (SOGoUserDefaults *) userDefaultsForLogin: (NSString *) theLogin +- (NSDictionary *) userDefaultsForLogin: (NSString *) theLogin { - return [[users objectForKey: theLogin] objectForKey: @"defaults"]; + return [self _valuesOfType: @"defaults" forLogin: theLogin]; } -- (SOGoUserDefaults *) userSettingsForLogin: (NSString *) theLogin +- (NSDictionary *) userSettingsForLogin: (NSString *) theLogin { - return [[users objectForKey: theLogin] objectForKey: @"settings"]; -} - -// -// -// -- (void) setDefaults: (SOGoUserDefaults *) theDefaults - forLogin: (NSString *) theLogin - key: (NSString *) theKey -{ - if (![users objectForKey: theLogin]) - [users setObject: [NSMutableDictionary dictionary] forKey: theLogin]; - - [[users objectForKey: theLogin] setObject: theDefaults forKey: theKey]; - [[users objectForKey: theLogin] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]] - forKey: @"cleanupDate"]; - - //NSLog(@"Set %@ to %@", theKey, [users objectForKey: theLogin]); -} - -// -// Notification callbacks. -// -- (void) _cacheValues: (NSDictionary *) theValues - login: (NSString *) theLogin - url: (NSString *) theURL - key: (NSString *) theKey -{ - SOGoUserDefaults *defaults; - NSURL *url; - -#if defined(THREADSAFE) - [lock lock]; -#endif - url = [[[NSURL alloc] initWithString: theURL] autorelease]; - - defaults = [[[SOGoUserDefaults alloc] initWithTableURL: url - uid: theLogin - fieldName: [NSString stringWithFormat: @"c_%@", theKey] - shouldPropagate: YES] - autorelease]; - [defaults setValues: theValues]; - [self setDefaults: defaults forLogin: theLogin key: theKey]; - -#if defined(THREADSAFE) - [lock unlock]; -#endif -} - -// -// -// -- (void) _userAttributesHaveLoaded: (NSNotification *) theNotification -{ - NSString *uid; - - uid = [[theNotification userInfo] objectForKey: @"uid"]; - //NSLog(@"Caching user attributes for UID: %@", uid); - if (![self userAttributesForLogin: uid]) - { - NSEnumerator *emails; - NSDictionary *values; - NSString *key; - - if (![users objectForKey: uid]) - [users setObject: [NSMutableDictionary dictionary] forKey: uid]; - - values = [[theNotification userInfo] objectForKey: @"values"]; - [self cacheAttributes: values forLogin: uid]; - - emails = [[values objectForKey: @"emails"] objectEnumerator]; - while ((key = [emails nextObject])) - { - [self cacheAttributes: values forLogin: key]; - } - - [[users objectForKey: uid] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]] - forKey: @"cleanupDate"]; - } -} - -// -// -// -- (void) _userDefaultsHaveLoaded: (NSNotification *) theNotification -{ - NSString *uid; - - uid = [[theNotification userInfo] objectForKey: @"uid"]; - //NSLog(@"Loading user defaults for UID: %@", uid); - if (![self userDefaultsForLogin: uid]) - { - [self _cacheValues: [[theNotification userInfo] objectForKey: @"values"] - login: uid - url: [[theNotification userInfo] objectForKey: @"url"] - key: @"defaults"]; - } -} - -// -// -// -- (void) _userDefaultsHaveChanged: (NSNotification *) theNotification -{ - SOGoUser *user; - SOGoUserDefaults *defaults; - NSString *uid; - - uid = [[theNotification userInfo] objectForKey: @"uid"]; - - // When the user defaults changed, we must invalidate the - // ivar language for the user object. - user = [self userNamed: uid]; - if (user) - [user invalidateLanguage]; - - //NSLog(@"Updating user defaults for UID: %@", uid); - defaults = (SOGoUserDefaults *)[self userDefaultsForLogin: uid]; - if (defaults) - { -#if defined(THREADSAFE) - [lock lock]; -#endif - [defaults setValues: [[theNotification userInfo] objectForKey: @"values"]]; - [[users objectForKey: uid] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]] - forKey: @"cleanupDate"]; -#if defined(THREADSAFE) - [lock unlock]; -#endif - } - else - { - [self _cacheValues: [[theNotification userInfo] objectForKey: @"values"] - login: uid - url: [[theNotification userInfo] objectForKey: @"url"] - key: @"defaults"]; - } -} - -// -// -// -- (void) _userSettingsHaveLoaded: (NSNotification *) theNotification -{ - NSString *uid; - - uid = [[theNotification userInfo] objectForKey: @"uid"]; - //NSLog(@"Loading user settings for UID: %@", uid); - if (![self userSettingsForLogin: uid]) - { - [self _cacheValues: [[theNotification userInfo] objectForKey: @"values"] - login: uid - url: [[theNotification userInfo] objectForKey: @"url"] - key: @"settings"]; - } -} - -// -// -// -- (void) _userSettingsHaveChanged: (NSNotification *) theNotification -{ - SOGoUserDefaults *settings; - NSString *uid; - - uid = [[theNotification userInfo] objectForKey: @"uid"]; - //NSLog(@"Updating user settings for UID: %@", uid); - settings = (SOGoUserDefaults *)[self userSettingsForLogin: uid]; - if (settings) - { -#if defined(THREADSAFE) - [lock lock]; -#endif - [settings setValues: [[theNotification userInfo] objectForKey: @"values"]]; - [[users objectForKey: uid] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]] - forKey: @"cleanupDate"]; -#if defined(THREADSAFE) - [lock unlock]; -#endif - } - else - { - [self _cacheValues: [[theNotification userInfo] objectForKey: @"values"] - login: uid - url: [[theNotification userInfo] objectForKey: @"url"] - key: @"settings"]; - } -} - -// -// -// -- (void) _cleanupSources -{ - NSDictionary *currentEntry; - NSEnumerator *userIDs; - NSString *currentID; - NSDate *now; - - unsigned int count; - -#if defined(THREADSAFE) - [lock lock]; -#endif - - now = [NSDate date]; - - // We cleanup the user cache - userIDs = [[users allKeys] objectEnumerator]; - count = 0; - while ((currentID = [userIDs nextObject])) - { - currentEntry = [users objectForKey: currentID]; - - if ([now earlierDate: [currentEntry objectForKey: @"cleanupDate"]] == now) - { - [users removeObjectForKey: currentID]; - count++; - } - } - - if (count) - [self logWithFormat: @"cleaned %d users records from users cache", - count]; - -#if defined(THREADSAFE) - [lock unlock]; -#endif + return [self _valuesOfType: @"settings" forLogin: theLogin]; } @end diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index e5e52ecff..cee56d1ce 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -64,6 +64,7 @@ extern NSString *SOGoWeekStartFirstFullWeek; @interface SOGoUser : SoUser { + SOGoUserDefaults *_defaults, *_settings; SOGoUserFolder *homeFolder; NSString *currentPassword; NSString *language; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 2a2646cf9..98ca8776c 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -23,7 +23,6 @@ #import #import #import -#import #import #import #import @@ -257,6 +256,9 @@ _timeValue (NSString *key) { LDAPUserManager *um; NSString *realUID; + + _defaults = nil; + _settings = nil; // We propagate the cache if we do NOT trust the login names. // When trusting login names, we 'assume' we're dealing with a @@ -300,6 +302,8 @@ _timeValue (NSString *key) - (void) dealloc { + [_defaults release]; + [_settings release]; [allEmails release]; [currentPassword release]; [cn release]; @@ -454,7 +458,6 @@ _timeValue (NSString *key) uid: login fieldName: @"c_defaults" shouldPropagate: propagateCache]; - [o autorelease]; return o; } @@ -467,113 +470,91 @@ _timeValue (NSString *key) uid: login fieldName: @"c_settings" shouldPropagate: propagateCache]; - [o autorelease]; return o; } - (NSUserDefaults *) userDefaults { - SOGoUserDefaults *defaults; NSDictionary *values; - NSMutableDictionary *d; - defaults = [[SOGoCache sharedCache] userDefaultsForLogin: login]; - - if (!defaults) + if (!_defaults) { - defaults = [self primaryUserDefaults]; - if (defaults) + _defaults = [self primaryUserDefaults]; + if (_defaults) { - [defaults fetchProfile]; - values = [defaults values]; - + values = [[SOGoCache sharedCache] userDefaultsForLogin: login]; + if (values) { - // See explanation in -language - [self invalidateLanguage]; - - // Required parameters for the Web interface. This will trigger the - // preferences to load so it's important to leave those calls here. - if (![[defaults stringForKey: @"ReplyPlacement"] length]) - [defaults setObject: defaultReplyPlacement forKey: @"ReplyPlacement"]; - if (![[defaults stringForKey: @"SignaturePlacement"] length]) - [defaults setObject: defaultSignaturePlacement forKey: @"SignaturePlacement"]; - if (![[defaults stringForKey: @"MessageForwarding"] length]) - [defaults setObject: defaultMessageForwarding forKey: @"MessageForwarding"]; - if (![[defaults stringForKey: @"MessageCheck"] length]) - [defaults setObject: defaultMessageCheck forKey: @"MessageCheck"]; - - // We propagate the loaded user defaults to other sogod instances - // which will cache them in SOGoCache (including for the instance - // that actually posts the notification) - d = [NSMutableDictionary dictionary]; - [d setObject: values forKey: @"values"]; - [d setObject: login forKey: @"uid"]; - [d setObject: [SOGoProfileURL absoluteString] forKey: @"url"]; - - [[SOGoCache sharedCache] setDefaults: defaults - forLogin: login key: @"defaults"]; - - if (propagateCache) - [(NSDistributedNotificationCenter *)[NSDistributedNotificationCenter defaultCenter] - postNotificationName: @"SOGoUserDefaultsHaveLoaded" - object: nil - userInfo: d - deliverImmediately: YES]; + [_defaults setValues: values]; } + else + { + [_defaults fetchProfile]; + values = [_defaults values]; + + if (values) + { + // Required parameters for the Web interface. This will trigger the + // preferences to load so it's important to leave those calls here. + if (![[_defaults stringForKey: @"ReplyPlacement"] length]) + [_defaults setObject: defaultReplyPlacement forKey: @"ReplyPlacement"]; + if (![[_defaults stringForKey: @"SignaturePlacement"] length]) + [_defaults setObject: defaultSignaturePlacement forKey: @"SignaturePlacement"]; + if (![[_defaults stringForKey: @"MessageForwarding"] length]) + [_defaults setObject: defaultMessageForwarding forKey: @"MessageForwarding"]; + if (![[_defaults stringForKey: @"MessageCheck"] length]) + [_defaults setObject: defaultMessageCheck forKey: @"MessageCheck"]; + + [[SOGoCache sharedCache] cacheValues: [_defaults values] ofType: @"defaults" forLogin: login]; + } + } + + // See explanation in -language + [self invalidateLanguage]; } } //else // NSLog(@"User defaults cache hit for %@", login); - return (NSUserDefaults *) defaults; + return (NSUserDefaults *) _defaults; } - (NSUserDefaults *) userSettings { - SOGoUserDefaults *settings; NSDictionary *values; - settings = [[SOGoCache sharedCache] userSettingsForLogin: login]; - - if (!settings) + if (!_settings) { - NSMutableDictionary *d; - - settings = [self primaryUserSettings]; - if (settings) + _settings = [self primaryUserSettings]; + if (_settings) { - [settings fetchProfile]; - values = [settings values]; + values = [[SOGoCache sharedCache] userSettingsForLogin: login]; + if (values) { - // See explanation in -language - [self invalidateLanguage]; - - // We propagate the loaded user settings to other sogod instances - // which will cache them in SOGoCache (including for the instance - // that actually posts the notification) - d = [NSMutableDictionary dictionary]; - [d setObject: values forKey: @"values"]; - [d setObject: login forKey: @"uid"]; - [d setObject: [SOGoProfileURL absoluteString] forKey: @"url"]; - - [[SOGoCache sharedCache] setDefaults: settings forLogin: login key: @"settings"]; - - if (propagateCache) - [(NSDistributedNotificationCenter *)[NSDistributedNotificationCenter defaultCenter] - postNotificationName: @"SOGoUserSettingsHaveLoaded" - object: nil - userInfo: d - deliverImmediately: YES]; + [_settings setValues: values]; } + else + { + [_settings fetchProfile]; + values = [_settings values]; + + if (values) + { + [[SOGoCache sharedCache] cacheValues: values ofType: @"settings" forLogin: login]; + } + } + + // See explanation in -language + [self invalidateLanguage]; } } //else // NSLog(@"User settings cache hit for %@", login); - return (NSUserDefaults *) settings; + return (NSUserDefaults *) _settings; } - (void) invalidateLanguage @@ -586,7 +567,7 @@ _timeValue (NSString *key) if (![language length]) { language = [[self userDefaults] stringForKey: @"Language"]; - // This is a hack until we handle the connection errors to the db a + // This is a workaround until we handle the connection errors to the db in a // better way. It enables us to avoid retrieving the userDefaults too // many times when the DB is down, causing a huge delay. if (![language length]) diff --git a/SoObjects/SOGo/SOGoUserDefaults.h b/SoObjects/SOGo/SOGoUserDefaults.h index 12a4a028a..cf8139954 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.h +++ b/SoObjects/SOGo/SOGoUserDefaults.h @@ -1,6 +1,6 @@ /* - Copyright (C) 2005 SKYRIX Software AG Copyright (C) 2008-2009 Inverse inc. + Copyright (C) 2005 SKYRIX Software AG This file is part of SOGo. @@ -43,7 +43,6 @@ NSURL *url; NSString *uid; NSString *fieldName; - NSCalendarDate *lastFetch; NSMutableDictionary *values; BOOL propagateCache; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index 1481c7688..631ddee41 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -21,7 +21,6 @@ */ #import -#import #import #import #import @@ -74,7 +73,6 @@ static NSString *uidColumnName = @"c_uid"; - (void) dealloc { [values release]; - [lastFetch release]; [url release]; [uid release]; [fieldName release]; @@ -160,7 +158,6 @@ static NSString *uidColumnName = @"c_uid"; [values addEntriesFromDictionary: v]; } - ASSIGN(lastFetch, [NSCalendarDate date]); defFlags.modified = NO; rc = YES; } @@ -295,25 +292,10 @@ static NSString *uidColumnName = @"c_uid"; if (rc) { - NSMutableDictionary *d; - - d = [NSMutableDictionary dictionary]; - [d setObject: values forKey: @"values"]; - [d setObject: uid forKey: @"uid"]; - [d setObject: [url absoluteString] forKey: @"url"]; - - [[SOGoCache sharedCache] setDefaults: self - forLogin: uid - key: ([fieldName isEqualToString: @"c_defaults"] ? @"defaults" : @"settings")]; - if (propagateCache) - [(NSDistributedNotificationCenter *)[NSDistributedNotificationCenter defaultCenter] - postNotificationName: ([fieldName isEqualToString: @"c_defaults"] - ? @"SOGoUserDefaultsHaveChanged" - : @"SOGoUserSettingsHaveChanged") - object: nil - userInfo: d - deliverImmediately: YES]; + [[SOGoCache sharedCache] cacheValues: values + ofType: ([fieldName isEqualToString: @"c_defaults"] ? @"defaults" : @"settings") + forLogin: uid]; } return rc; @@ -344,7 +326,6 @@ static NSString *uidColumnName = @"c_uid"; values = [[NSMutableDictionary alloc] init]; [values addEntriesFromDictionary: theValues]; - ASSIGN(lastFetch, [NSCalendarDate date]); defFlags.modified = NO; defFlags.isNew = NO; } @@ -429,8 +410,6 @@ static NSString *uidColumnName = @"c_uid"; - (void) flush { [values removeAllObjects]; - [lastFetch release]; - lastFetch = nil; defFlags.modified = NO; defFlags.isNew = NO; } diff --git a/sogo.spec b/sogo.spec index 86f54da4e..44c9a73c5 100644 --- a/sogo.spec +++ b/sogo.spec @@ -10,9 +10,9 @@ Group: Productivity/Groupware Source: SOGo-%{sogo_version}.tar.gz Prefix: %{sogo_prefix} AutoReqProv: off -Requires: gnustep-base sope%{sope_major_version}%{sope_minor_version}-core httpd sope%{sope_major_version}%{sope_minor_version}-core sope%{sope_major_version}%{sope_minor_version}-appserver sope%{sope_major_version}%{sope_minor_version}-ldap sope%{sope_major_version}%{sope_minor_version}-cards sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore +Requires: gnustep-base sope%{sope_major_version}%{sope_minor_version}-core httpd sope%{sope_major_version}%{sope_minor_version}-core sope%{sope_major_version}%{sope_minor_version}-appserver sope%{sope_major_version}%{sope_minor_version}-ldap sope%{sope_major_version}%{sope_minor_version}-cards sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore memcached libmemcached BuildRoot: %{_tmppath}/%{name}-%{version}-%{release} -BuildPreReq: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel +BuildPreReq: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel libmemcached-devel %description SOGo is a groupware server built around OpenGroupware.org (OGo) and the SOPE application server. It focuses on scalability.