1/* 2 Copyright (C) 2000-2005 SKYRIX Software AG 3 4 This file is part of SOPE. 5 6 SOPE is free software; you can redistribute it and/or modify it under 7 the terms of the GNU Lesser General Public License as published by the 8 Free Software Foundation; either version 2, or (at your option) any 9 later version. 10 11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 14 License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with SOPE; see the file COPYING. If not, write to the 18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 19 02111-1307, USA. 20*/ 21 22#include <NGObjWeb/WOSessionStore.h> 23 24/* 25 The default session store. It stores all the sessions in memory 26 (it's basically a simple hashmap of the session-id to the session object). 27 28 If the application goes down, all sessions will go down - this store 29 doesn't provide "session-failover". 30*/ 31 32@interface WOServerSessionStore : WOSessionStore 33{ 34 NSMapTable *idToSession; 35 NSMapTable *activeSessions; 36} 37@end 38 39#include <NGObjWeb/WOContext.h> 40#include <NGObjWeb/WOApplication.h> 41#include <NGObjWeb/WOSession.h> 42#include "common.h" 43 44/* hh: moved in here from application, check whether it's required ... */ 45@interface WOSessionInfo : NSObject 46{ 47@private 48 NSString *sessionID; 49 NSDate *timeoutDate; 50} 51 52+ (WOSessionInfo *)infoForSession:(WOSession *)_session; 53 54- (NSString *)sessionID; 55- (NSDate *)timeoutDate; 56 57@end 58 59@implementation WOServerSessionStore 60 61static BOOL logExpiredSessions = NO; 62 63- (id)init { 64 if ((self = [super init])) { 65 self->idToSession = NSCreateMapTable(NSObjectMapKeyCallBacks, 66 NSObjectMapValueCallBacks, 67 128); 68 self->activeSessions = NSCreateMapTable(NSObjectMapKeyCallBacks, 69 NSObjectMapValueCallBacks, 70 128); 71 self->checkedOutSessions = [[NSMutableSet allocWithZone:[self zone]] 72 initWithCapacity:64]; 73 74 if ([[[NSUserDefaults standardUserDefaults] 75 objectForKey:@"WORunMultithreaded"] 76 boolValue]) { 77 self->lock = [[NSRecursiveLock allocWithZone:[self zone]] init]; 78 self->checkoutLock = [[NSConditionLock allocWithZone:[self zone]] 79 initWithCondition:0]; 80 } 81 } 82 return self; 83} 84 85- (void)dealloc { 86 if (self->activeSessions) { 87 NSFreeMapTable(self->activeSessions); 88 self->activeSessions = NULL; 89 } 90 if (self->idToSession) { 91 NSFreeMapTable(self->idToSession); 92 self->idToSession = NULL; 93 } 94 [self->checkedOutSessions release]; 95 [self->checkoutLock release]; 96 [self->lock release]; 97 [super dealloc]; 98} 99 100/* accessors */ 101 102- (int)activeSessionsCount { 103 int count; 104 105 [self->lock lock]; 106 count = NSCountMapTable(self->idToSession); 107 [self->lock unlock]; 108 109 return count; 110} 111 112/* store */ 113 114- (void)saveSessionForContext:(WOContext *)_context { 115 if (![_context hasSession]) 116 return; 117 118 [self->lock lock]; 119 { 120 WOSession *sn = [_context session]; 121 122 if ([sn isTerminating]) { 123 sn = [sn retain]; 124 125 NSMapRemove(self->idToSession, [sn sessionID]); 126 NSMapRemove(self->activeSessions, [sn sessionID]); 127 128 NSLog(@"session %@ terminated at %@ ..", 129 [sn sessionID], [NSCalendarDate calendarDate]); 130 131 [sn release]; sn = nil; 132 } 133 else { 134 WOSessionInfo *info; 135 136 NSMapInsert(self->idToSession, [sn sessionID], sn); 137 138 info = [WOSessionInfo infoForSession:sn]; 139 NSMapInsert(self->activeSessions, [sn sessionID], info); 140 } 141 } 142 [self->lock unlock]; 143} 144 145- (id)restoreSessionWithID:(NSString *)_sid request:(WORequest *)_request { 146 WOSession *session = nil; 147 148 if ([_sid length] == 0) 149 return nil; 150 151 if (![_sid isKindOfClass:[NSString class]]) { 152 [self warnWithFormat:@"%s: got invalid session id (expected string !): %@", 153 __PRETTY_FUNCTION__, _sid]; 154 return nil; 155 } 156 157 if ([_sid isEqualToString:@"expired"]) 158 return nil; 159 160 [self->lock lock]; 161 session = NSMapGet(self->idToSession, _sid); 162 [self->lock unlock]; 163 164 if (logExpiredSessions) { 165 if (session == nil) 166 [self logWithFormat:@"session with id %@ expired.", _sid]; 167 } 168 169 return session; 170} 171 172/* termination */ 173 174- (void)sessionExpired:(NSString *)_sessionID { 175 [self->lock lock]; 176 NSMapRemove(self->idToSession, _sessionID); 177 [self->lock unlock]; 178} 179 180- (void)sessionTerminated:(WOSession *)_session { 181 _session = [_session retain]; 182 [self->lock lock]; 183 NSMapRemove(self->idToSession, [_session sessionID]); 184 [self->lock unlock]; 185 [_session release]; 186 187 [[WOApplication application] 188 logWithFormat: 189 @"WOServerSessionStore: session %@ terminated.", 190 [_session sessionID]]; 191} 192 193/* expiration check */ 194 195- (void)performExpirationCheck:(NSTimer *)_timer { 196 NSNotificationCenter *nc; 197 NSMutableArray *timedOut = nil; 198 NSMapEnumerator e; 199 NSString *sid = nil; 200 WOSessionInfo *info = nil; 201 NSDate *now; 202 unsigned cnt, count; 203 204 //NSLog(@"%s: perform expiration check ...", __PRETTY_FUNCTION__); 205 206 if (self->activeSessions == NULL) 207 count = 0; 208 else 209 count = NSCountMapTable(self->activeSessions); 210 211 if (!(count > 0 && (self->activeSessions != NULL))) 212 return; 213 214 e = NSEnumerateMapTable(self->activeSessions); 215 now = [NSDate date]; 216 217 /* search for expired sessions */ 218 while (NSNextMapEnumeratorPair(&e, (void **)&sid, (void **)&info)) { 219 NSDate *timeOutDate = [info timeoutDate]; 220 221 if (timeOutDate == nil) continue; 222 223 if ([now compare:timeOutDate] != NSOrderedAscending) { 224 [self logWithFormat:@"session %@ expired at %@.", sid, now]; 225 226 if (timedOut == nil) 227 timedOut = [NSMutableArray arrayWithCapacity:32]; 228 [timedOut addObject:info]; 229 } 230 } 231 232 /* Expire sessions */ 233 if (!timedOut) 234 return; 235 236 nc = [NSNotificationCenter defaultCenter]; 237 238 for (cnt = 0, count = [timedOut count]; cnt < count; cnt++) { 239 NSString *sid; 240 241 info = [timedOut objectAtIndex:cnt]; 242 sid = [[info sessionID] copy]; 243 244 NSMapRemove(self->activeSessions, sid); 245 NSMapRemove(self->idToSession, sid); 246 247 [nc postNotificationName:WOSessionDidTimeOutNotification 248 object:sid]; 249 250 [sid release]; 251 } 252} 253 254/* description */ 255 256- (NSString *)description { 257 return [NSString stringWithFormat:@"<%@[0x%p]: active=%i>", 258 NSStringFromClass([self class]), self, 259 [self activeSessionsCount] 260 ]; 261} 262 263@end /* WOServerSessionStore */ 264 265@implementation WOSessionInfo 266 267- (id)initWithSession:(WOSession *)_session { 268 self->sessionID = RETAIN([_session sessionID]); 269 270 if ([_session respondsToSelector:@selector(timeoutDate)]) { 271 self->timeoutDate = [(id)_session timeoutDate]; 272 } 273 else { 274 NSTimeInterval timeOut = [_session timeOut]; 275 276 self->timeoutDate = (timeOut > 0.0) 277 ? [NSDate dateWithTimeIntervalSinceNow:(timeOut + 1.0)] 278 : [NSDate distantFuture]; 279 } 280 self->timeoutDate = RETAIN(self->timeoutDate); 281 282 return self; 283} 284 285+ (WOSessionInfo *)infoForSession:(WOSession *)_session { 286 return AUTORELEASE([[self alloc] initWithSession:_session]); 287} 288 289- (void)dealloc { 290 [self->sessionID release]; 291 [self->timeoutDate release]; 292 [super dealloc]; 293} 294 295- (NSString *)sessionID { 296 return self->sessionID; 297} 298- (NSDate *)timeoutDate { 299 return self->timeoutDate; 300} 301 302@end /* WOSessionInfo */ 303