]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WOServerSessionStore.m
added some WebDrive WebDAV properties
[sope] / sope-appserver / NGObjWeb / WOServerSessionStore.m
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
61 static BOOL logExpiredSessions = NO;
62
63 + (int)version {
64   return [super version] + 0;
65 }
66 + (void)initialize {
67   NSAssert2([super version] == 2,
68             @"invalid superclass (%@) version %i !",
69             NSStringFromClass([self superclass]), [super version]);
70 }
71
72 - (id)init {
73   if ((self = [super init])) {
74     self->idToSession = NSCreateMapTable(NSObjectMapKeyCallBacks,
75                                          NSObjectMapValueCallBacks,
76                                          128);
77     self->activeSessions = NSCreateMapTable(NSObjectMapKeyCallBacks,
78                                             NSObjectMapValueCallBacks,
79                                             128);
80     self->checkedOutSessions = [[NSMutableSet allocWithZone:[self zone]]
81                                               initWithCapacity:64];
82     
83     if ([[[NSUserDefaults standardUserDefaults]
84                           objectForKey:@"WORunMultithreaded"]
85                           boolValue]) {
86       self->lock         = [[NSRecursiveLock allocWithZone:[self zone]] init];
87       self->checkoutLock = [[NSConditionLock allocWithZone:[self zone]]
88                                              initWithCondition:0];
89     }
90   }
91   return self;
92 }
93
94 - (void)dealloc {
95   if (self->activeSessions) {
96     NSFreeMapTable(self->activeSessions);
97     self->activeSessions = NULL;
98   }
99   if (self->idToSession) {
100     NSFreeMapTable(self->idToSession);
101     self->idToSession = NULL;
102   }
103   [self->checkedOutSessions release];
104   [self->checkoutLock       release];
105   [self->lock               release];
106   [super dealloc];
107 }
108
109 /* accessors */
110
111 - (int)activeSessionsCount {
112   int count;
113   
114   [self->lock lock];
115   count = NSCountMapTable(self->idToSession);
116   [self->lock unlock];
117
118   return count;
119 }
120
121 /* store */
122
123 - (void)saveSessionForContext:(WOContext *)_context {
124   if (![_context hasSession])
125     return;
126   
127   [self->lock lock];
128   {
129     WOSession *sn = [_context session];
130       
131     if ([sn isTerminating]) {
132       sn = [sn retain];
133         
134       NSMapRemove(self->idToSession,    [sn sessionID]);
135       NSMapRemove(self->activeSessions, [sn sessionID]);
136         
137       NSLog(@"session %@ terminated at %@ ..",
138             [sn sessionID], [NSCalendarDate calendarDate]);
139         
140       [sn release]; sn = nil;
141     }
142     else {
143       WOSessionInfo *info;
144         
145       NSMapInsert(self->idToSession, [sn sessionID], sn);
146         
147       info = [WOSessionInfo infoForSession:sn];
148       NSMapInsert(self->activeSessions, [sn sessionID], info);
149     }
150   }
151   [self->lock unlock];
152 }
153
154 - (id)restoreSessionWithID:(NSString *)_sid request:(WORequest *)_request {
155   WOSession *session = nil;
156
157   if ([_sid length] == 0)
158     return nil;
159   
160   if (![_sid isKindOfClass:[NSString class]]) {
161     [self warnWithFormat:@"%s: got invalid session id (expected string !): %@",
162             __PRETTY_FUNCTION__, _sid];
163     return nil;
164   }
165   
166   if ([_sid isEqualToString:@"expired"])
167     return nil;
168
169   [self->lock lock];
170   session = NSMapGet(self->idToSession, _sid);
171   [self->lock unlock];
172
173   if (logExpiredSessions) {
174     if (session == nil)
175       [self logWithFormat:@"session with id %@ expired.", _sid];
176   }
177
178   return session;
179 }
180
181 /* termination */
182
183 - (void)sessionExpired:(NSString *)_sessionID {
184   [self->lock lock];
185   NSMapRemove(self->idToSession, _sessionID);
186   [self->lock unlock];
187 }
188
189 - (void)sessionTerminated:(WOSession *)_session {
190   _session = [_session retain];
191   [self->lock lock];
192   NSMapRemove(self->idToSession, [_session sessionID]);
193   [self->lock unlock];
194   [_session release];
195   
196   [[WOApplication application]
197                   logWithFormat:
198                     @"WOServerSessionStore: session %@ terminated.",
199                     [_session sessionID]];
200 }
201
202 /* expiration check */
203
204 - (void)performExpirationCheck:(NSTimer *)_timer {
205   NSNotificationCenter *nc;
206   NSMutableArray  *timedOut = nil;
207   NSMapEnumerator e;
208   NSString        *sid  = nil;
209   WOSessionInfo   *info = nil;
210   NSDate          *now;
211   unsigned cnt, count;
212     
213   //NSLog(@"%s: perform expiration check ...", __PRETTY_FUNCTION__);
214   
215   if (self->activeSessions == NULL)
216     count = 0;
217   else
218     count = NSCountMapTable(self->activeSessions);
219   
220   if (!(count > 0 && (self->activeSessions != NULL)))
221     return;
222   
223   e   = NSEnumerateMapTable(self->activeSessions);
224   now = [NSDate date];
225       
226   /* search for expired sessions */
227   while (NSNextMapEnumeratorPair(&e, (void **)&sid, (void **)&info)) {
228     NSDate *timeOutDate = [info timeoutDate];
229     
230     if (timeOutDate == nil) continue;
231
232     if ([now compare:timeOutDate] != NSOrderedAscending) {
233         [self logWithFormat:@"session %@ expired at %@.", sid, now];
234           
235         if (timedOut == nil)
236           timedOut = [NSMutableArray arrayWithCapacity:32];
237         [timedOut addObject:info];
238     }
239   }
240       
241   /* Expire sessions */
242   if (!timedOut)
243     return;
244
245   nc = [NSNotificationCenter defaultCenter];
246         
247   for (cnt = 0, count = [timedOut count]; cnt < count; cnt++) {
248     NSString *sid;
249           
250     info = [timedOut objectAtIndex:cnt];
251     sid  = [[info sessionID] copy];
252           
253     NSMapRemove(self->activeSessions, sid);
254     NSMapRemove(self->idToSession,    sid);
255           
256     [nc postNotificationName:WOSessionDidTimeOutNotification
257         object:sid];
258     
259     [sid release];
260   }
261 }
262
263 /* description */
264
265 - (NSString *)description {
266   return [NSString stringWithFormat:@"<%@[0x%08X]: active=%i>",
267                      NSStringFromClass([self class]), self,
268                      [self activeSessionsCount]
269                    ];
270 }
271
272 @end /* WOServerSessionStore */
273
274 @implementation WOSessionInfo
275
276 - (id)initWithSession:(WOSession *)_session {
277   self->sessionID = RETAIN([_session sessionID]);
278
279   if ([_session respondsToSelector:@selector(timeoutDate)]) {
280     self->timeoutDate = [(id)_session timeoutDate];
281   }
282   else {
283     NSTimeInterval timeOut = [_session timeOut];
284     
285     self->timeoutDate = (timeOut > 0.0)
286       ? [NSDate dateWithTimeIntervalSinceNow:(timeOut + 1.0)]
287       : [NSDate distantFuture];
288   }
289   self->timeoutDate = RETAIN(self->timeoutDate);
290   
291   return self;
292 }
293
294 + (WOSessionInfo *)infoForSession:(WOSession *)_session {
295   return AUTORELEASE([[self alloc] initWithSession:_session]);
296 }
297
298 - (void)dealloc {
299   [self->sessionID   release];
300   [self->timeoutDate release];
301   [super dealloc];
302 }
303
304 - (NSString *)sessionID {
305   return self->sessionID;
306 }
307 - (NSDate *)timeoutDate {
308   return self->timeoutDate;
309 }
310
311 @end /* WOSessionInfo */