2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include <NGObjWeb/WOApplication.h>
23 #include "WOContext+private.h"
24 #include "WOElement+private.h"
25 #include "WOComponent+private.h"
26 #include <NGObjWeb/WOAdaptor.h>
27 #include <NGObjWeb/WORequest.h>
28 #include <NGObjWeb/WORequestHandler.h>
29 #include <NGObjWeb/WOResourceManager.h>
30 #include <NGObjWeb/WOResponse.h>
31 #include <NGObjWeb/WOSession.h>
32 #include <NGObjWeb/WOSessionStore.h>
33 #include <NGObjWeb/WOStatisticsStore.h>
34 #include <NGObjWeb/WODynamicElement.h>
35 #include <NGObjWeb/WOTemplate.h>
36 #include <EOControl/EOControl.h>
41 # include <objc/sarray.h>
44 @interface WOApplication(PrivateMethods)
46 - (id)_loadComponentDefinitionWithName:(NSString *)_name
47 language:(NSArray *)_langs;
48 - (NSDictionary *)memoryStatistics;
51 static NSRecursiveLock *classLock = nil;
52 static NGLogger *perfLogger = nil;
53 static Class NSDateClass = Nil;
54 static Class WOTemplateClass = Nil;
55 static BOOL debugOn = NO;
56 static NSString *rapidTurnAroundPath = nil;
58 @interface WOSessionStore(SnStore)
59 - (void)performExpirationCheck:(NSTimer *)_timer;
62 @implementation WOApplication
65 static NSString *defaultCompRqHandlerClassName = @"OWViewRequestHandler";
67 static NSString *defaultCompRqHandlerClassName = @"WOComponentRequestHandler";
71 return [super version] + 5 /* v6 */;
78 clazz = NSClassFromString(@"SNSConnection");
79 c = [(id<NSObject>)clazz performSelector:@selector(defaultSNSConnection)];
82 [[self logger] fatalWithFormat:@"could not connect SNS, exiting .."];
86 [[self logger] logWithFormat:@"SNS enabled"];
89 + (void)_initializeWOApp {
90 static BOOL isInitialized = NO;
91 NSAutoreleasePool *pool;
95 if (isInitialized) return;
99 pool = [[NSAutoreleasePool alloc] init];
100 debugOn = [WOApplication isDebuggingEnabled];
102 [[self logger] setLogLevel:NGLogLevelInfo];
104 NSLog(@"Note: WOApplication debugging is enabled.");
106 if (classLock == nil) classLock = [[NSRecursiveLock alloc] init];
107 ud = [NSUserDefaults standardUserDefaults];
109 /* setup SNSConnection */
111 if ([ud boolForKey:@"WOContactSNS"])
114 [[self logger] logWithFormat:@"SNS support disabled."];
116 NSDateClass = [NSDate class];
117 WOTemplateClass = [WOTemplate class];
119 rapidTurnAroundPath = [[ud stringForKey:@"WOProjectDirectory"] copy];
121 lm = [NGLoggerManager defaultLoggerManager];
122 perfLogger = [lm loggerForDefaultKey:@"WOProfileApplication"];
127 /* old license checks */
129 - (NSCalendarDate *)appExpireDate {
130 // TODO: can we remove that?
133 - (BOOL)isLicenseExpired {
134 // TODO: can we remove that?
140 - (NSString *)_lookupAppPath {
141 static NSString *suffix = nil;
142 static BOOL appPathMissing = NO;
151 ud = [NSUserDefaults standardUserDefaults];
153 // Check if appPath has been forced
154 result = [ud stringForKey:@"WOProjectDirectory"];
159 suffix = [ud stringForKey:@"WOApplicationSuffix"];
161 fm = [NSFileManager defaultManager];
162 cwd = [fm currentDirectoryPath];
164 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
165 result = [[NGBundle mainBundle] bundlePath];
166 //NSLog(@"%s: check path '%@'", __PRETTY_FUNCTION__, result);
171 if ([result hasSuffix:suffix]) {
172 /* started app inside of .woa directory */
173 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
174 result = [[NGBundle mainBundle] bundlePath];
180 NSString *wrapperName;
182 wrapperName = [self->name stringByAppendingString:suffix];
184 /* take a look whether ./AppName.woa exists */
185 result = [result stringByAppendingPathComponent:wrapperName];
186 if (![fm fileExistsAtPath:result]) {
187 /* lookup in process-path */
193 pi = [NSProcessInfo processInfo];
194 env = [pi environment];
195 if ([env objectForKey:@"GNUSTEP_SYSTEM_ROOT"] != nil) {
196 isFlattened = [[[env objectForKey:@"GNUSTEP_FLATTENED"]
197 lowercaseString] isEqualToString:@"yes"];
199 else /* default to flattened if no GNUstep runtime is set */
202 ppath = [[pi arguments] objectAtIndex:0];
203 ppath = [ppath stringByDeletingLastPathComponent]; // del exe-name
206 ppath = [ppath stringByDeletingLastPathComponent]; // lib-combo
207 ppath = [ppath stringByDeletingLastPathComponent]; // os
208 ppath = [ppath stringByDeletingLastPathComponent]; // cpu
210 if ([ppath hasSuffix:suffix])
215 if (![fm fileExistsAtPath:result]) {
216 [self debugWithFormat:@"%s: missing path '%@'",
217 __PRETTY_FUNCTION__, result];
218 appPathMissing = YES;
225 + (NSString *)defaultRequestHandlerClassName {
226 return @"WOComponentRequestHandler";
229 - (void)_logDefaults {
235 ud = [NSUserDefaults standardUserDefaults];
236 keys = [[ud dictionaryRepresentation] allKeys];
237 keys = [keys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
239 e = [keys objectEnumerator];
240 while((key = [e nextObject]) != nil) {
241 if ([key hasPrefix:@"WO"] || [key isEqualToString:@"NSProjectSearchPath"])
242 [self logWithFormat:@"[default]: %@ = %@",
244 [[ud objectForKey:key] description]];
248 - (id)initWithName:(NSString *)_name {
249 [WOApplication _initializeWOApp];
251 if ((self = [super init])) {
253 WORequestHandler *rh;
256 self->name = [_name copy];
258 ud = [NSUserDefaults standardUserDefaults];
260 [self setPageCacheSize:[ud integerForKey:@"WOPageCacheSize"]];
261 [self setPermanentPageCacheSize:
262 [ud integerForKey:@"WOPermanentPageCacheSize"]];
264 [self setPageRefreshOnBacktrackEnabled:
265 [[ud objectForKey:@"WOPageRefreshOnBacktrack"] boolValue]];
267 [self setCachingEnabled:[WOApplication isCachingEnabled]];
269 /* setup request handlers */
271 self->defaultRequestHandler =
272 [[NSClassFromString([[self class] defaultRequestHandlerClassName])
275 self->requestHandlerRegistry =
276 NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 8);
278 if ((rk = [WOApplication componentRequestHandlerKey]) == nil) {
280 @"WARNING: no component request handler key is specified, "
281 @"this probably means that share/ngobjweb/Defaults.plist "
282 @"could not get loaded (permissions?)"];
284 rh = [[NSClassFromString(defaultCompRqHandlerClassName) alloc] init];
285 if ([rk length] > 0 && (rh != nil))
286 [self registerRequestHandler:rh forKey:rk];
287 [rh release]; rh = nil;
289 rk = [WOApplication directActionRequestHandlerKey];
290 rh = [[NSClassFromString(@"WODirectActionRequestHandler") alloc] init];
291 if ([rk length] > 0 && rh != nil)
292 [self registerRequestHandler:rh forKey:rk];
293 [rh release]; rh = nil;
295 if ((rh = [[NSClassFromString(@"WOResourceRequestHandler") alloc] init])) {
296 rk = [WOApplication resourceRequestHandlerKey];
298 [self registerRequestHandler:rh forKey:rk];
299 [self registerRequestHandler:rh forKey:@"WebServerResources"];
301 [self registerRequestHandler:rh forKey:@"Resources"];
303 [rh release]; rh = nil;
306 /* setup session store */
308 self->iSessionStore =
309 [[NSClassFromString([self sessionStoreClassName]) alloc] init];
311 /* setup statistics store */
313 self->iStatisticsStore = [[WOStatisticsStore alloc] init];
315 /* register timers */
316 self->expirationTimer =
317 [[NSTimer scheduledTimerWithTimeInterval:
318 [[ud objectForKey:@"WOExpirationTimeInterval"] intValue]
320 selector:@selector(performExpirationCheck:)
325 if ([ud boolForKey:@"WOLogDefaultsOnStartup"])
328 [[NSNotificationCenter defaultCenter]
329 postNotificationName:
330 WOApplicationWillFinishLaunchingNotification
337 return [self initWithName:[[[NSProcessInfo processInfo]
339 stringByDeletingPathExtension]];
343 [[NSNotificationCenter defaultCenter] removeObserver:self];
345 [self->expirationTimer invalidate];
347 if (self->requestHandlerRegistry)
348 NSFreeMapTable(self->requestHandlerRegistry);
350 [self->expirationTimer release];
351 [self->resourceManager release];
352 [self->iSessionStore release];
353 [self->defaultRequestHandler release];
354 [self->path release];
355 [self->name release];
356 [self->instanceNumber release];
360 - (void)processHupSignal:(int)_signal {
361 /* this isn't called immediatly */
362 [self logWithFormat:@"terminating on SIGHUP ..."];
371 - (BOOL)monitoringEnabled {
375 static BOOL missingPath = NO;
379 if (self->path == nil) {
380 if ((self->path = [[self _lookupAppPath] copy]) == nil) {
381 [self debugWithFormat:@"could not find wrapper of application !"];
389 - (NSString *)number {
390 if (self->instanceNumber == nil) {
393 if ((num = [[NSUserDefaults standardUserDefaults] objectForKey:@"n"])) {
394 self->instanceNumber = [[num stringValue] copy];
398 #if defined(__MINGW32__)
399 pid = (unsigned)GetCurrentProcessId();
401 pid = (unsigned)getpid();
403 self->instanceNumber = [[NSString alloc] initWithFormat:@"%d", pid];
406 return self->instanceNumber;
409 - (void)_setCurrentContext:(WOContext *)_ctx {
410 NSMutableDictionary *info;
412 info = [[NSThread currentThread] threadDictionary];
414 [info setObject:_ctx forKey:@"WOContext"];
416 [info removeObjectForKey:@"WOContext"];
418 - (WOContext *)context {
423 if ((t = [NSThread currentThread]) == nil) {
424 [self errorWithFormat:@"missing current thread !!!"];
427 if ((td = [t threadDictionary]) == nil) {
428 [self errorWithFormat:
429 @"missing current thread's dictionary (thread=%@) !!!",
434 return [td objectForKey:@"WOContext"];
437 /* request handlers */
439 - (void)registerRequestHandler:(WORequestHandler *)_hdl
440 forKey:(NSString *)_key
443 NSMapInsert(self->requestHandlerRegistry, _key, _hdl);
446 - (void)removeRequestHandlerForKey:(NSString *)_key {
447 if (_key == nil) return;
449 NSMapRemove(self->requestHandlerRegistry, _key);
453 - (void)setDefaultRequestHandler:(WORequestHandler *)_hdl {
455 ASSIGN(self->defaultRequestHandler, _hdl);
458 - (WORequestHandler *)defaultRequestHandler {
459 return self->defaultRequestHandler;
461 - (WORequestHandler *)requestHandlerForKey:(NSString *)_key {
462 WORequestHandler *handler;
465 handler = [(id)NSMapGet(self->requestHandlerRegistry, _key) retain];
467 handler = [[self defaultRequestHandler] retain];
470 return [handler autorelease];
473 - (NSArray *)registeredRequestHandlerKeys {
474 NSMutableArray *array = [NSMutableArray arrayWithCapacity:16];
477 WORequestHandler *handler;
480 e = NSEnumerateMapTable(self->requestHandlerRegistry);
481 while (NSNextMapEnumeratorPair(&e, (void**)&key, (void**)&handler))
482 [array addObject:key];
485 return [[array copy] autorelease];
488 - (WORequestHandler *)handlerForRequest:(WORequest *)_request {
489 WORequestHandler *handler;
492 if ((key = [_request requestHandlerKey]) == nil)
493 return [self defaultRequestHandler];
495 handler = NSMapGet(self->requestHandlerRegistry, key);
496 return (handler != nil) ? handler : [self defaultRequestHandler];
501 - (WOSession *)_initializeSessionInContext:(WOContext *)_ctx {
504 sn = [self createSessionForRequest:[_ctx request]];
505 [_ctx setSession:sn];
507 if ([sn respondsToSelector:@selector(prepare)]) {
509 [self debugWithFormat:@"calling -prepare on session .."];
511 [sn performSelector:@selector(prepare)];
514 [sn _awakeWithContext:_ctx];
516 [[NSNotificationCenter defaultCenter]
517 postNotificationName:WOSessionDidCreateNotification
519 return [sn autorelease];
522 - (NSString *)sessionIDFromRequest:(WORequest *)_request {
525 if (_request == nil) return nil;
527 /* first look into form values */
528 if ((sessionId = [_request formValueForKey:WORequestValueSessionID])!=nil) {
529 if ([sessionId length] > 0)
533 /* now look into the cookies */
534 if ((sessionId = [_request cookieValueForKey:[self name]]) != nil) {
535 if ([sessionId respondsToSelector:@selector(objectEnumerator)]) {
538 e = [(id)sessionId objectEnumerator];
539 while ((sessionId = [e nextObject]) != nil) {
540 if ([sessionId length] > 0 && ![sessionId isEqual:@"nil"])
545 if ([sessionId length] > 0 && ![sessionId isEqual:@"nil"])
553 - (NSString *)createSessionIDForSession:(WOSession *)_session {
554 /* session id must be 18 chars long for snsd to work ! */
555 static unsigned int sessionCount = 0;
557 unsigned char buf[20];
560 sprintf((char *)buf, "%04X%04X%02X%08X",
561 [[self number] intValue], getpid(), sessionCount,
562 (unsigned int)time(NULL));
563 wosid = [NSString stringWithCString:(char *)buf];
567 - (id)createSessionForRequest:(WORequest *)_request {
568 if ([self respondsToSelector:@selector(createSession)]) {
569 /* call deprecated method */
570 [self warnWithFormat:@"calling deprecated -createSession .."];
571 return [self createSession];
576 if ((snClass = NSClassFromString(@"Session")) == Nil)
577 snClass = [WOSession class];
579 return [[snClass alloc] init];
583 - (id)restoreSessionWithID:(NSString *)_sid inContext:(WOContext *)_ctx {
588 if ([self respondsToSelector:@selector(restoreSession)]) {
589 /* call deprecated method */
590 [self warnWithFormat:@"calling deprecated -restoreSession .."];
591 return [self restoreSession];
595 WOSessionStore *store;
597 if ((store = [self sessionStore]) == nil) {
598 [self errorWithFormat:@"missing session store ..."];
601 session = [store restoreSessionWithID:_sid request:[_ctx request]];
603 [_ctx setSession:session];
604 [session _awakeWithContext:_ctx];
607 [self debugWithFormat:@"did not find a session for sid '%@'", _sid];
614 [[NSNotificationCenter defaultCenter]
615 postNotificationName:WOSessionDidRestoreNotification
619 if ([_sid hasPrefix:@"("]) {
622 sid = [_sid propertyList];
624 if ([sid respondsToSelector:@selector(objectEnumerator)]) {
627 [self errorWithFormat:@"got multiple session IDs !"];
629 e = [sid objectEnumerator];
630 while ((_sid = [e nextObject])) {
631 if ([_sid isEqualToString:@"nil"])
634 if ((session = [self restoreSessionWithID:_sid inContext:_ctx]))
637 //[self warnWithFormat:@"did not find session for sid %@", _sid);
644 - (void)saveSessionForContext:(WOContext *)_ctx {
645 NSTimeInterval startSave = 0.0;
648 startSave = [[NSDateClass date] timeIntervalSince1970];
650 if ([self respondsToSelector:@selector(saveSession:)]) {
651 /* call deprecated method */
652 [self warnWithFormat:@"calling deprecated -saveSession: .."];
653 [self saveSession:[_ctx session]];
659 NSTimeInterval startSnSleep = 0.0, startStore = 0.0;
664 startSnSleep = [[NSDateClass date] timeIntervalSince1970];
666 /* put session to sleep */
667 [sn _sleepWithContext:_ctx];
671 rt = [[NSDateClass date] timeIntervalSince1970] - startSnSleep;
672 [perfLogger logWithFormat:@"[woapp]: session -sleep took %4.3fs.",
673 rt < 0.0 ? -1.0 : rt];
676 if ([sn isTerminating]) {
677 [[NSNotificationCenter defaultCenter]
678 postNotificationName:
679 WOSessionDidTerminateNotification
684 startStore = [[NSDateClass date] timeIntervalSince1970];
686 [[self sessionStore] saveSessionForContext:_ctx];
690 rt = [[NSDateClass date] timeIntervalSince1970] - startStore;
691 [perfLogger logWithFormat:@"[woapp]: storing sn in store took %4.3fs.",
692 rt < 0.0 ? -1.0 : rt];
699 rt = [[NSDateClass date] timeIntervalSince1970] - startSave;
700 [perfLogger logWithFormat:@"[woapp]: saveSessionForContext took %4.3fs.",
701 rt < 0.0 ? -1.0 : rt];
705 - (void)refuseNewSessions:(BOOL)_flag {
706 self->appFlags.doesRefuseNewSessions = _flag ? 1 : 0;
708 - (BOOL)isRefusingNewSessions {
709 return self->appFlags.doesRefuseNewSessions;
711 - (int)activeSessionsCount {
712 return [[self sessionStore] activeSessionsCount];
715 - (void)setSessionStore:(WOSessionStore *)_store {
716 ASSIGN(self->iSessionStore, _store);
718 - (NSString *)sessionStoreClassName {
719 return [[NSUserDefaults standardUserDefaults] stringForKey:@"WOSessionStore"];
721 - (WOSessionStore *)sessionStore {
722 return self->iSessionStore;
725 - (void)setMinimumActiveSessionsCount:(int)_minimum {
726 self->minimumActiveSessionsCount = _minimum;
728 - (int)minimumActiveSessionsCount {
729 return self->minimumActiveSessionsCount;
732 - (void)performExpirationCheck:(NSTimer *)_timer {
735 /* let session store check for expiration ... */
737 ss = [self sessionStore];
738 if ([ss respondsToSelector:@selector(performExpirationCheck:)])
739 [ss performExpirationCheck:_timer];
741 /* check whether application should terminate ... */
743 if ([self isRefusingNewSessions] &&
744 ([self activeSessionsCount] < [self minimumActiveSessionsCount])) {
745 /* check whether the application instance is still valid .. */
746 [self debugWithFormat:
747 @"application terminates because it refuses new sessions and "
748 @"the active session count (%i) is below the minimum (%i).",
749 [self activeSessionsCount], [self minimumActiveSessionsCount]];
755 return [[self context] session];
758 - (WOResponse *)handleSessionCreationErrorInContext:(WOContext *)_ctx {
759 WOResponse *response = [_ctx response];
763 pid = GetCurrentProcessId();
768 if ([self respondsToSelector:@selector(handleSessionCreationError)]) {
769 [self warnWithFormat:@"called deprecated -handleSessionCreationError method"];
770 return [self handleSessionCreationError];
773 [self errorWithFormat:@"could not create session for context %@", _ctx];
775 [response setStatus:200];
776 [response appendContentString:@"<h4>Session Creation Error</h4>\n<pre>"];
777 [response appendContentString:
778 @"Application Instance failed to create session."];
779 [response appendContentHTMLString:
780 [NSString stringWithFormat:
781 @" application: %@\n"
788 [[_ctx request] adaptorPrefix],
792 [[_ctx request] description]]];
793 [response appendContentString:@"</pre>"];
797 - (WOResponse *)handleSessionRestorationErrorInContext:(WOContext *)_ctx {
798 if ([self respondsToSelector:@selector(handleSessionRestorationError)]) {
799 [self warnWithFormat:@"calling deprecated "
800 @"-handleSessionRestorationError method"];
801 return [self handleSessionRestorationError];
804 // TODO: is it correct to return nil?
805 // TODO: we should return a page saying sorry with a cookie + redirect
806 [self errorWithFormat:@"could not restore session for context %@", _ctx];
812 - (void)setStatisticsStore:(WOStatisticsStore *)_statStore {
813 ASSIGN(self->iStatisticsStore, _statStore);
815 - (WOStatisticsStore *)statisticsStore {
816 return self->iStatisticsStore;
819 - (bycopy NSDictionary *)statistics {
820 return [[self statisticsStore] statistics];
825 - (void)_setupDefaultResourceManager {
830 ud = [NSUserDefaults standardUserDefaults];
831 p = [ud stringForKey:@"WODefaultResourceManager"];
832 rmClass = ([p length] == 0)
833 ? [WOResourceManager class]
834 : NSClassFromString(p);
836 if (rmClass == Nil) {
837 [self errorWithFormat:
838 @"failed to locate class of resource manager: '%@'", p];
842 if ([rmClass instancesRespondToSelector:@selector(initWithPath:)])
843 self->resourceManager = [[rmClass alloc] init];
845 self->resourceManager =
846 [(WOResourceManager *)[rmClass alloc] initWithPath:[self path]];
850 - (void)setResourceManager:(WOResourceManager *)_manager {
851 ASSIGN(self->resourceManager, _manager);
853 - (WOResourceManager *)resourceManager {
854 if (self->resourceManager == nil)
855 [self _setupDefaultResourceManager];
857 return self->resourceManager;
862 WOContext *ctx = [self context];
864 n = [[ctx request] applicationName];
865 n = [@"/" stringByAppendingString:n ? n : [self name]];
867 return [NSURL URLWithString:n relativeToURL:[ctx baseURL]];
870 - (NSString *)pathForResourceNamed:(NSString *)_name ofType:(NSString *)_type {
872 return [[self resourceManager] pathForResourceNamed:_name ofType:_type];
875 - (NSString *)stringForKey:(NSString *)_key
876 inTableNamed:(NSString *)_tableName
877 withDefaultValue:(NSString *)_default
880 return [[self resourceManager] stringForKey:_key
881 inTableNamed:_tableName
882 withDefaultValue:_default
884 [(WOSession *)[self session] languages]];
892 #if DEBUG && PRINT_NSSTRING_STATISTICS
893 if ([NSString respondsToSelector:@selector(printStatistics)])
894 [NSString printStatistics];
897 #if DEBUG && PRINT_OBJC_STATISTICS
898 extern int __objc_selector_max_index;
899 printf("nbuckets=%i, nindices=%i, narrays=%i, idxsize=%i\n",
900 nbuckets, nindices, narrays, idxsize);
901 printf("maxsel=%i\n", __objc_selector_max_index);
907 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
908 if ([_ctx hasSession])
909 [[_ctx session] takeValuesFromRequest:_req inContext:_ctx];
913 if ((page = [_ctx page])) {
914 WOContext_enterComponent(_ctx, page, nil);
915 [page takeValuesFromRequest:_req inContext:_ctx];
916 WOContext_leaveComponent(_ctx, page);
921 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
924 if ([_ctx hasSession])
925 result = [[_ctx session] invokeActionForRequest:_rq inContext:_ctx];
929 if ((page = [_ctx page])) {
930 WOContext_enterComponent(_ctx, page, nil);
931 result = [[_ctx page] invokeActionForRequest:_rq inContext:_ctx];
932 WOContext_leaveComponent(_ctx, page);
940 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
941 if ([_ctx hasSession])
942 [[_ctx session] appendToResponse:_response inContext:_ctx];
946 if ((page = [_ctx page])) {
947 WOContext_enterComponent(_ctx, page, nil);
948 [page appendToResponse:_response inContext:_ctx];
949 WOContext_leaveComponent(_ctx, page);
953 if(rapidTurnAroundPath != nil) {
956 if((page = [_ctx page])) {
959 template = [page _woComponentTemplate];
960 if([template isKindOfClass:WOTemplateClass]) {
963 _path = [[(WOTemplate *)template url] path];
964 [_response setHeader:_path
965 forKey:@"x-sope-template-path"];
974 - (WOElement *)dynamicElementWithName:(NSString *)_name
975 associations:(NSDictionary *)_associations
976 template:(WOElement *)_template
977 languages:(NSArray *)_languages
979 WOElement *element = nil;
980 Class dynamicElementClass = NSClassFromString(_name);
982 if (dynamicElementClass == Nil) {
983 [self warnWithFormat:@"did not find dynamic element class %@ !", _name];
986 if (![dynamicElementClass isDynamicElement]) {
987 [self warnWithFormat:@"class %@ is not a dynamic element class !", _name];
991 element = [[dynamicElementClass allocWithZone:[_template zone]]
993 associations:_associations
997 - (WOElement *)dynamicElementWithName:(NSString *)_name
998 associations:(NSDictionary *)_associations
999 template:(WOElement *)_template
1001 return [self dynamicElementWithName:_name
1002 associations:_associations
1004 languages:[(WOSession *)[self session] languages]];
1009 - (void)setPageRefreshOnBacktrackEnabled:(BOOL)_flag {
1010 self->appFlags.isPageRefreshOnBacktrackEnabled = _flag ? 1 : 0;
1012 - (BOOL)isPageRefreshOnBacktrackEnabled {
1013 return self->appFlags.isPageRefreshOnBacktrackEnabled ? YES : NO;
1016 - (void)setCachingEnabled:(BOOL)_flag {
1017 self->appFlags.isCachingEnabled = _flag ? 1 : 0;
1019 - (BOOL)isCachingEnabled {
1020 // component definition caching
1021 return self->appFlags.isCachingEnabled ? YES : NO;
1024 - (void)setPageCacheSize:(int)_size {
1025 self->pageCacheSize = _size;
1027 - (int)pageCacheSize {
1028 return self->pageCacheSize;
1030 - (void)setPermanentPageCacheSize:(int)_size {
1031 self->permanentPageCacheSize = _size;
1033 - (int)permanentPageCacheSize {
1034 return self->permanentPageCacheSize;
1037 - (id)pageWithName:(NSString *)_name {
1038 // deprecated in WO4
1039 return [self pageWithName:_name inContext:[self context]];
1042 - (WOComponent *)_pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1044 OSX profiling: 3.4% of dispatchRequest?
1045 3.0% rm -pageWithName..
1046 1.5% def instantiate
1047 1.3% initWithName:...
1048 0.76% initWithContent:.. (0.43 addobserver)
1049 0.76% rm defForComp (0.43% touch)
1051 0.11% ctx -component
1056 NSAutoreleasePool *pool;
1057 WOResourceManager *rm;
1060 NSDictionary *start, *stop;
1061 start = [self memoryStatistics];
1064 pool = [[NSAutoreleasePool alloc] init];
1066 languages = [_ctx resourceLookupLanguages];
1068 if ((rm = [[_ctx component] resourceManager]) == nil)
1069 rm = [self resourceManager];
1072 * the following ignores the fact that the passed context may be different
1073 * from that of WOApplication. During the course of template instantiation
1074 * WOApplication's current context gets attached to page which is definitely
1075 * wrong. We workaround this problem by using the private API of WOComponent
1076 * to explicitly set it. However all accompanied methods should be
1077 * extended to pass the correct context where needed.
1079 page = [rm pageWithName:(_name != nil ? _name : @"Main")
1080 languages:languages];
1081 [page _setContext:_ctx];
1082 [page ensureAwakeInContext:_ctx];
1084 page = [page retain];
1089 int rss, vmsize, lib;
1090 stop = [self memoryStatistics];
1091 rss = [[stop objectForKey:@"VmRSS"] intValue] -
1092 [[start objectForKey:@"VmRSS"] intValue];
1093 vmsize = [[stop objectForKey:@"VmSize"] intValue] -
1094 [[start objectForKey:@"VmSize"] intValue];
1095 lib = [[stop objectForKey:@"VmLib"] intValue] -
1096 [[start objectForKey:@"VmLib"] intValue];
1097 [self debugWithFormat:@"loaded component %@; rss=%i vm=%i lib=%i.",
1098 _name, rss,vmsize,lib];
1102 return [page autorelease];
1104 - (id)pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1105 return [self _pageWithName:_name inContext:_ctx];
1107 - (id)pageWithName:(NSString *)_name forRequest:(WORequest *)_req {
1108 WOResourceManager *rm;
1110 if ((rm = [self resourceManager]) == nil)
1113 return [rm pageWithName:(_name != nil) ? _name : @"Main"
1114 languages:[_req browserLanguages]];
1117 - (void)savePage:(WOComponent *)_page {
1119 [[[self context] session] savePage:_page];
1121 - (id)restorePageForContextID:(NSString *)_ctxId {
1123 return [[[self context] session] restorePageForContextID:_ctxId];
1126 - (WOResponse *)handlePageRestorationErrorInContext:(WOContext *)_ctx {
1127 [self errorWithFormat:
1128 @"could not restore page for context-id %@\n in context %@",
1129 [_ctx currentElementID], _ctx];
1131 /* return main page ... */
1132 return [[self pageWithName:nil inContext:_ctx] generateResponse];
1134 - (WOResponse *)handlePageRestorationError {
1136 return [self handlePageRestorationErrorInContext:[self context]];
1141 - (WOResponse *)handleException:(NSException *)_exc
1142 inContext:(WOContext *)_ctx
1144 WORequest *rq = [_ctx request];
1145 WOResponse *r = nil;
1147 if ([self respondsToSelector:@selector(handleException:)]) {
1148 [self warnWithFormat:@"calling deprecated -handleException method !"];
1149 return [self handleException:_exc];
1154 static int doCore = -1;
1156 doCore = [[NSUserDefaults standardUserDefaults]
1157 boolForKey:@"WOCoreOnApplicationException"]
1161 [self fatalWithFormat:@"%@: caught (ctx=%@):\n %@.",
1169 [self fatalWithFormat:@"%@: caught (without context):\n %@.",
1173 else if (rq == nil) {
1174 [self fatalWithFormat:@"%@: caught (without request):\n %@.",
1179 static NSString *pageFormat =
1180 @"Application Server caught exception:\n\n"
1189 @" backtrace:\n%@\n";
1190 NSString *str = nil;
1193 [self errorWithFormat:@"%@: caught:\n %@\nin context:\n %@.",
1196 #if LIB_FOUNDATION_LIBRARY
1197 if ([NSException respondsToSelector:@selector(backtrace)])
1198 bt = [NSException backtrace];
1201 if ((r = [WOResponse responseWithRequest:rq]) == nil)
1202 [self errorWithFormat:@"could not create response !"];
1204 [r setHeader:@"text/html" forKey:@"content-type"];
1205 [r setHeader:@"no-cache" forKey:@"cache-control"];
1206 if(rapidTurnAroundPath != nil) {
1209 templateURL = [[_exc userInfo] objectForKey:@"templateURL"];
1210 if(templateURL != nil)
1211 [r setHeader:[templateURL path] forKey:@"x-sope-template-path"];
1214 str = [NSString stringWithFormat:pageFormat,
1216 ? [[_ctx session] sessionID]
1221 NSStringFromClass([_exc class]),
1224 [[_exc userInfo] description],
1227 [r appendContentString:@"<html><head><title>Caught exception</title></head><body><pre>\n"];
1228 [r appendContentHTMLString:str];
1229 [r appendContentString:@"</pre></body></html>\n"];
1236 - (BOOL)shouldTerminate {
1237 if (![self isRefusingNewSessions])
1239 if ([self activeSessionsCount] >= [self minimumActiveSessionsCount])
1242 /* check whether the application instance is still valid .. */
1243 [self debugWithFormat:
1244 @"application terminates because it refuses new sessions and "
1245 @"the active session count (%i) is below the minimum (%i).",
1246 [self activeSessionsCount], [self minimumActiveSessionsCount]];
1251 [self debugWithFormat:
1252 @"application terminates:\n"
1253 @" %i active sessions\n"
1254 @" %i minimum active sessions\n"
1255 @" refuses new session: %s",
1256 [self activeSessionsCount],
1257 [self minimumActiveSessionsCount],
1258 [self isRefusingNewSessions] ? "yes" : "no"];
1264 - (BOOL)isDebuggingEnabled {
1267 - (NSString *)loggingPrefix {
1268 return [NSString stringWithFormat:@"|%@%@|",
1270 [self isTerminating] ? @" terminating" : @""];
1275 #if !LIB_FOUNDATION_LIBRARY
1276 - (id)valueForUndefinedKey:(NSString *)_key {
1277 [self warnWithFormat:@"tried to access undefined KVC key: '%@'",
1285 + (Class)eoEditingContextClass {
1286 static Class eoEditingContextClass = Nil;
1287 static BOOL lookedUpForEOEditingContextClass = NO;
1289 if (!lookedUpForEOEditingContextClass) {
1290 if ((eoEditingContextClass = NSClassFromString(@"EOEditingContext")) ==nil)
1291 eoEditingContextClass = NSClassFromString(@"NSManagedObjectContext");
1292 lookedUpForEOEditingContextClass = YES;
1294 return eoEditingContextClass;
1297 + (BOOL)implementsEditingContexts {
1298 return [self eoEditingContextClass] != NULL ? YES : NO;
1303 - (NSString *)description {
1304 return [NSString stringWithFormat:@"<%@[0x%08X]: name=%@%@>",
1305 NSStringFromClass([self class]), self,
1307 [self isTerminating] ? @" terminating" : @""
1311 @end /* WOApplication */