]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WOServerSessionStore.m
minor change with great impact on gnustep-base usability ;-)
[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 - (WOSession *)restoreSessionWithID:(NSString *)_sid
155   request:(WORequest *)_request
156 {
157   WOSession *session = nil;
158
159   if ([_sid length] == 0)
160     return nil;
161   
162   if (![_sid isKindOfClass:[NSString class]]) {
163     [self warnWithFormat:@"%s: got invalid session id (expected string !): %@",
164             __PRETTY_FUNCTION__, _sid];
165     return nil;
166   }
167   
168   if ([_sid isEqualToString:@"expired"])
169     return nil;
170
171   [self->lock lock];
172   session = NSMapGet(self->idToSession, _sid);
173   [self->lock unlock];
174
175   if (logExpiredSessions) {
176     if (session == nil)
177       [self logWithFormat:@"session with id %@ expired.", _sid];
178   }
179
180   return session;
181 }
182
183 /* termination */
184
185 - (void)sessionExpired:(NSString *)_sessionID {
186   [self->lock lock];
187   NSMapRemove(self->idToSession, _sessionID);
188   [self->lock unlock];
189 }
190
191 - (void)sessionTerminated:(WOSession *)_session {
192   _session = [_session retain];
193   [self->lock lock];
194   NSMapRemove(self->idToSession, [_session sessionID]);
195   [self->lock unlock];
196   [_session release];
197   
198   [[WOApplication application]
199                   logWithFormat:
200                     @"WOServerSessionStore: session %@ terminated.",
201                     [_session sessionID]];
202 }
203
204 /* expiration check */
205
206 - (void)performExpirationCheck:(NSTimer *)_timer {
207   NSNotificationCenter *nc;
208   NSMutableArray  *timedOut = nil;
209   NSMapEnumerator e;
210   NSString        *sid  = nil;
211   WOSessionInfo   *info = nil;
212   NSDate          *now;
213   unsigned cnt, count;
214     
215   //NSLog(@"%s: perform expiration check ...", __PRETTY_FUNCTION__);
216   
217   if (self->activeSessions == NULL)
218     count = 0;
219   else
220     count = NSCountMapTable(self->activeSessions);
221   
222   if (!(count > 0 && (self->activeSessions != NULL)))
223     return;
224   
225   e   = NSEnumerateMapTable(self->activeSessions);
226   now = [NSDate date];
227       
228   /* search for expired sessions */
229   while (NSNextMapEnumeratorPair(&e, (void **)&sid, (void **)&info)) {
230     NSDate *timeOutDate = [info timeoutDate];
231     
232     if (timeOutDate == nil) continue;
233
234     if ([now compare:timeOutDate] != NSOrderedAscending) {
235         [self logWithFormat:@"session %@ expired at %@.", sid, now];
236           
237         if (timedOut == nil)
238           timedOut = [NSMutableArray arrayWithCapacity:32];
239         [timedOut addObject:info];
240     }
241   }
242       
243   /* Expire sessions */
244   if (!timedOut)
245     return;
246
247   nc = [NSNotificationCenter defaultCenter];
248         
249   for (cnt = 0, count = [timedOut count]; cnt < count; cnt++) {
250     NSString *sid;
251           
252         info = [timedOut objectAtIndex:cnt];
253         sid  = [[info sessionID] copy];
254           
255         NSMapRemove(self->activeSessions, sid);
256         NSMapRemove(self->idToSession,    sid);
257           
258         [nc postNotificationName:WOSessionDidTimeOutNotification
259             object:sid];
260
261         [sid release];
262   }
263 }
264
265 /* description */
266
267 - (NSString *)description {
268   return [NSString stringWithFormat:@"<%@[0x%08X]: active=%i>",
269                      NSStringFromClass([self class]), self,
270                      [self activeSessionsCount]
271                    ];
272 }
273
274 @end /* WOServerSessionStore */
275
276 @implementation WOSessionInfo
277
278 - (id)initWithSession:(WOSession *)_session {
279   self->sessionID = RETAIN([_session sessionID]);
280
281   if ([_session respondsToSelector:@selector(timeoutDate)]) {
282     self->timeoutDate = [(id)_session timeoutDate];
283   }
284   else {
285     NSTimeInterval timeOut = [_session timeOut];
286     
287     self->timeoutDate = (timeOut > 0.0)
288       ? [NSDate dateWithTimeIntervalSinceNow:(timeOut + 1.0)]
289       : [NSDate distantFuture];
290   }
291   self->timeoutDate = RETAIN(self->timeoutDate);
292   
293   return self;
294 }
295
296 + (WOSessionInfo *)infoForSession:(WOSession *)_session {
297   return AUTORELEASE([[self alloc] initWithSession:_session]);
298 }
299
300 - (void)dealloc {
301   [self->sessionID   release];
302   [self->timeoutDate release];
303   [super dealloc];
304 }
305
306 - (NSString *)sessionID {
307   return self->sessionID;
308 }
309 - (NSDate *)timeoutDate {
310   return self->timeoutDate;
311 }
312
313 @end /* WOSessionInfo */