2 Copyright (C) 2000-2005 SKYRIX Software AG
3 Copyright (C) 2006 Helge Hess
5 This file is part of SOPE.
7 SOPE is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with SOPE; see the file COPYING. If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include <NGObjWeb/WOApplication.h>
24 #include "WOContext+private.h"
25 #include "WOElement+private.h"
26 #include "WOComponent+private.h"
27 #include <NGObjWeb/WOAdaptor.h>
28 #include <NGObjWeb/WORequest.h>
29 #include <NGObjWeb/WORequestHandler.h>
30 #include <NGObjWeb/WOResourceManager.h>
31 #include <NGObjWeb/WOResponse.h>
32 #include <NGObjWeb/WOSession.h>
33 #include <NGObjWeb/WOSessionStore.h>
34 #include <NGObjWeb/WOStatisticsStore.h>
35 #include <NGObjWeb/WODynamicElement.h>
36 #include <NGObjWeb/WOTemplate.h>
37 #import <EOControl/EOControl.h>
42 # include <objc/sarray.h>
45 @interface WOApplication(PrivateMethods)
47 - (id)_loadComponentDefinitionWithName:(NSString *)_name
48 language:(NSArray *)_langs;
49 - (NSDictionary *)memoryStatistics;
53 static NSRecursiveLock *classLock = nil;
54 static NGLogger *perfLogger = nil;
55 static Class NSDateClass = Nil;
56 static Class WOTemplateClass = Nil;
57 static BOOL debugOn = NO;
58 static NSString *rapidTurnAroundPath = nil;
60 @interface WOSessionStore(SnStore)
61 - (void)performExpirationCheck:(NSTimer *)_timer;
64 @implementation WOApplication
66 #if 1 // TODO: why is that? why isn't that set by a default?
67 static NSString *defaultCompRqHandlerClassName = @"OWViewRequestHandler";
69 static NSString *defaultCompRqHandlerClassName = @"WOComponentRequestHandler";
73 return [super version] + 5 /* v6 */;
80 clazz = NSClassFromString(@"SNSConnection");
81 c = [(id<NSObject>)clazz performSelector:@selector(defaultSNSConnection)];
84 [[self logger] fatalWithFormat:@"could not connect SNS, exiting .."];
88 [[self logger] logWithFormat:@"SNS enabled"];
91 /* old license checks */
93 - (NSCalendarDate *)appExpireDate {
94 // TODO: can we remove that?
97 - (BOOL)isLicenseExpired {
98 // TODO: can we remove that?
104 - (NSString *)_lookupAppPath {
105 static NSString *suffix = nil;
106 static BOOL appPathMissing = NO;
115 ud = [NSUserDefaults standardUserDefaults];
117 // Check if appPath has been forced
118 result = [ud stringForKey:@"WOProjectDirectory"];
123 suffix = [ud stringForKey:@"WOApplicationSuffix"];
125 fm = [NSFileManager defaultManager];
126 cwd = [fm currentDirectoryPath];
128 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
129 result = [[NGBundle mainBundle] bundlePath];
130 //NSLog(@"%s: check path '%@'", __PRETTY_FUNCTION__, result);
135 if ([result hasSuffix:suffix]) {
136 /* started app inside of .woa directory */
137 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
138 result = [[NGBundle mainBundle] bundlePath];
144 NSString *wrapperName;
146 wrapperName = [self->name stringByAppendingString:suffix];
148 /* take a look whether ./AppName.woa exists */
149 result = [result stringByAppendingPathComponent:wrapperName];
150 if (![fm fileExistsAtPath:result]) {
151 /* lookup in process-path */
157 pi = [NSProcessInfo processInfo];
158 env = [pi environment];
159 if ([[env objectForKey:@"GNUSTEP_SYSTEM_ROOT"] isNotNull]) {
160 isFlattened = [[[env objectForKey:@"GNUSTEP_FLATTENED"]
161 lowercaseString] isEqualToString:@"yes"];
163 else /* default to flattened if no GNUstep runtime is set */
166 ppath = [[pi arguments] objectAtIndex:0];
167 ppath = [ppath stringByDeletingLastPathComponent]; // del exe-name
170 ppath = [ppath stringByDeletingLastPathComponent]; // lib-combo
171 ppath = [ppath stringByDeletingLastPathComponent]; // os
172 ppath = [ppath stringByDeletingLastPathComponent]; // cpu
174 if ([ppath hasSuffix:suffix])
179 if (![fm fileExistsAtPath:result]) {
180 [self debugWithFormat:@"%s: missing path '%@'",
181 __PRETTY_FUNCTION__, result];
182 appPathMissing = YES;
189 + (NSString *)defaultRequestHandlerClassName {
190 return @"WOComponentRequestHandler";
193 - (void)_logDefaults {
199 ud = [NSUserDefaults standardUserDefaults];
200 keys = [[ud dictionaryRepresentation] allKeys];
201 keys = [keys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
203 e = [keys objectEnumerator];
204 while((key = [e nextObject]) != nil) {
205 if ([key hasPrefix:@"WO"] || [key isEqualToString:@"NSProjectSearchPath"])
206 [self logWithFormat:@"[default]: %@ = %@",
208 [[ud objectForKey:key] description]];
212 - (id)initWithName:(NSString *)_name {
213 if ((self = [super init]) != nil) {
216 WORequestHandler *rh;
219 self->name = [_name copy];
220 ud = [NSUserDefaults standardUserDefaults];
222 debugOn = [WOApplication isDebuggingEnabled];
224 [[self logger] setLogLevel:NGLogLevelInfo];
226 [[self logger] logWithFormat:@"WOApplication debugging is enabled."];
228 if (classLock == nil) classLock = [[NSRecursiveLock alloc] init];
230 /* setup SNSConnection */
232 if ([ud boolForKey:@"WOContactSNS"])
235 [[self logger] logWithFormat:@"SNS support disabled."];
237 NSDateClass = [NSDate class];
238 WOTemplateClass = [WOTemplate class];
240 rapidTurnAroundPath = [[ud stringForKey:@"WOProjectDirectory"] copy];
242 lm = [NGLoggerManager defaultLoggerManager];
243 perfLogger = [lm loggerForDefaultKey:@"WOProfileApplication"];
246 [self setPageCacheSize:[ud integerForKey:@"WOPageCacheSize"]];
247 [self setPermanentPageCacheSize:
248 [ud integerForKey:@"WOPermanentPageCacheSize"]];
250 [self setPageRefreshOnBacktrackEnabled:
251 [[ud objectForKey:@"WOPageRefreshOnBacktrack"] boolValue]];
253 [self setCachingEnabled:[WOApplication isCachingEnabled]];
255 /* setup request handlers */
257 self->defaultRequestHandler =
258 [[NSClassFromString([[self class] defaultRequestHandlerClassName])
261 self->requestHandlerRegistry =
262 NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 8);
264 if ((rk = [WOApplication componentRequestHandlerKey]) == nil) {
266 @"WARNING: no component request handler key is specified, "
267 @"this probably means that share/ngobjweb/Defaults.plist "
268 @"could not get loaded (permissions?)"];
270 rh = [[NSClassFromString(defaultCompRqHandlerClassName) alloc] init];
271 if ([rk isNotEmpty] && [rh isNotNull])
272 [self registerRequestHandler:rh forKey:rk];
273 [rh release]; rh = nil;
275 rk = [WOApplication directActionRequestHandlerKey];
276 rh = [[NSClassFromString(@"WODirectActionRequestHandler") alloc] init];
277 if ([rk isNotEmpty] && [rh isNotNull])
278 [self registerRequestHandler:rh forKey:rk];
279 [rh release]; rh = nil;
281 if ((rh = [[NSClassFromString(@"WOResourceRequestHandler") alloc] init])) {
282 rk = [WOApplication resourceRequestHandlerKey];
284 [self registerRequestHandler:rh forKey:rk];
285 [self registerRequestHandler:rh forKey:@"WebServerResources"];
287 [self registerRequestHandler:rh forKey:@"Resources"];
289 [rh release]; rh = nil;
292 /* setup session store */
294 self->iSessionStore =
295 [[NSClassFromString([self sessionStoreClassName]) alloc] init];
297 /* setup statistics store */
299 self->iStatisticsStore = [[WOStatisticsStore alloc] init];
301 /* register timers */
302 self->expirationTimer =
303 [[NSTimer scheduledTimerWithTimeInterval:
304 [[ud objectForKey:@"WOExpirationTimeInterval"] intValue]
306 selector:@selector(performExpirationCheck:)
311 if ([ud boolForKey:@"WOLogDefaultsOnStartup"])
314 [[NSNotificationCenter defaultCenter]
315 postNotificationName:
316 WOApplicationWillFinishLaunchingNotification
323 return [self initWithName:[[[NSProcessInfo processInfo]
325 stringByDeletingPathExtension]];
329 [[NSNotificationCenter defaultCenter] removeObserver:self];
331 [self->expirationTimer invalidate];
333 if (self->requestHandlerRegistry)
334 NSFreeMapTable(self->requestHandlerRegistry);
336 [self->expirationTimer release];
337 [self->resourceManager release];
338 [self->iSessionStore release];
339 [self->defaultRequestHandler release];
340 [self->path release];
341 [self->name release];
342 [self->instanceNumber release];
346 - (void)processHupSignal:(int)_signal {
347 /* this isn't called immediatly */
348 [self logWithFormat:@"terminating on SIGHUP ..."];
357 - (BOOL)monitoringEnabled {
361 static BOOL missingPath = NO;
365 if (self->path == nil) {
366 if ((self->path = [[self _lookupAppPath] copy]) == nil) {
367 [self debugWithFormat:@"could not find wrapper of application !"];
375 - (NSString *)number {
376 if (self->instanceNumber == nil) {
379 if ((num = [[NSUserDefaults standardUserDefaults] objectForKey:@"n"])) {
380 self->instanceNumber = [[num stringValue] copy];
384 #if defined(__MINGW32__)
385 pid = (unsigned)GetCurrentProcessId();
387 pid = (unsigned)getpid();
389 self->instanceNumber = [[NSString alloc] initWithFormat:@"%d", pid];
392 return self->instanceNumber;
395 - (void)_setCurrentContext:(WOContext *)_ctx {
396 NSMutableDictionary *info;
398 info = [[NSThread currentThread] threadDictionary];
400 [info setObject:_ctx forKey:@"WOContext"];
402 [info removeObjectForKey:@"WOContext"];
404 - (WOContext *)context {
409 if ((t = [NSThread currentThread]) == nil) {
410 [self errorWithFormat:@"missing current thread !!!"];
413 if ((td = [t threadDictionary]) == nil) {
414 [self errorWithFormat:
415 @"missing current thread's dictionary (thread=%@) !!!",
420 return [td objectForKey:@"WOContext"];
423 /* request handlers */
425 - (void)registerRequestHandler:(WORequestHandler *)_hdl
426 forKey:(NSString *)_key
429 NSMapInsert(self->requestHandlerRegistry, _key, _hdl);
432 - (void)removeRequestHandlerForKey:(NSString *)_key {
433 if (_key == nil) return;
435 NSMapRemove(self->requestHandlerRegistry, _key);
439 - (void)setDefaultRequestHandler:(WORequestHandler *)_hdl {
441 ASSIGN(self->defaultRequestHandler, _hdl);
444 - (WORequestHandler *)defaultRequestHandler {
445 return self->defaultRequestHandler;
447 - (WORequestHandler *)requestHandlerForKey:(NSString *)_key {
448 WORequestHandler *handler;
451 handler = [(id)NSMapGet(self->requestHandlerRegistry, _key) retain];
453 handler = [[self defaultRequestHandler] retain];
456 return [handler autorelease];
459 - (NSArray *)registeredRequestHandlerKeys {
460 NSMutableArray *array = [NSMutableArray arrayWithCapacity:16];
463 WORequestHandler *handler;
466 e = NSEnumerateMapTable(self->requestHandlerRegistry);
467 while (NSNextMapEnumeratorPair(&e, (void**)&key, (void**)&handler))
468 [array addObject:key];
471 return [[array copy] autorelease];
474 - (WORequestHandler *)handlerForRequest:(WORequest *)_request {
475 WORequestHandler *handler;
478 if ((key = [_request requestHandlerKey]) == nil)
479 return [self defaultRequestHandler];
481 handler = NSMapGet(self->requestHandlerRegistry, key);
482 return (handler != nil) ? handler : [self defaultRequestHandler];
487 - (WOSession *)_initializeSessionInContext:(WOContext *)_ctx {
490 sn = [self createSessionForRequest:[_ctx request]];
491 [_ctx setNewSession:sn];
493 if ([sn respondsToSelector:@selector(prepare)]) {
495 [self debugWithFormat:@"calling -prepare on session .."];
497 [sn performSelector:@selector(prepare)];
500 [sn _awakeWithContext:_ctx];
502 [[NSNotificationCenter defaultCenter]
503 postNotificationName:WOSessionDidCreateNotification
505 return [sn autorelease];
508 - (NSString *)sessionIDFromRequest:(WORequest *)_request {
511 if (_request == nil) return nil;
513 /* first look into form values */
514 if ((sessionId = [_request formValueForKey:WORequestValueSessionID])!=nil) {
515 if ([sessionId isNotEmpty])
519 /* now look into the cookies */
520 if ((sessionId = [_request cookieValueForKey:[self name]]) != nil) {
521 if ([sessionId respondsToSelector:@selector(objectEnumerator)]) {
524 e = [(id)sessionId objectEnumerator];
525 while ((sessionId = [e nextObject]) != nil) {
526 if ([sessionId isNotEmpty] && ![sessionId isEqual:@"nil"])
531 if ([sessionId isNotEmpty] && ![sessionId isEqual:@"nil"])
539 - (NSString *)createSessionIDForSession:(WOSession *)_session {
540 /* session id must be 18 chars long for snsd to work ! */
541 static unsigned int sessionCount = 0;
543 unsigned char buf[20];
546 sprintf((char *)buf, "%04X%04X%02X%08X",
547 [[self number] intValue], getpid(), sessionCount,
548 (unsigned int)time(NULL));
549 wosid = [NSString stringWithCString:(char *)buf];
553 - (id)createSessionForRequest:(WORequest *)_request {
554 if ([self respondsToSelector:@selector(createSession)]) {
555 /* call deprecated method */
556 [self warnWithFormat:@"calling deprecated -createSession .."];
557 return [self createSession];
562 if ((snClass = NSClassFromString(@"Session")) == Nil)
563 snClass = [WOSession class];
565 return [[snClass alloc] init];
569 - (id)restoreSessionWithID:(NSString *)_sid inContext:(WOContext *)_ctx {
574 if ([self respondsToSelector:@selector(restoreSession)]) {
575 /* call deprecated method */
576 [self warnWithFormat:@"calling deprecated -restoreSession .."];
577 return [self restoreSession];
581 WOSessionStore *store;
583 if ((store = [self sessionStore]) == nil) {
584 [self errorWithFormat:@"missing session store ..."];
587 session = [store restoreSessionWithID:_sid request:[_ctx request]];
588 if ([session isNotNull]) {
589 [_ctx setSession:session];
590 [session _awakeWithContext:_ctx];
593 [self debugWithFormat:@"did not find a session for sid '%@'", _sid];
600 [[NSNotificationCenter defaultCenter]
601 postNotificationName:WOSessionDidRestoreNotification
605 if ([_sid hasPrefix:@"("]) {
608 sid = [_sid propertyList];
610 if ([sid respondsToSelector:@selector(objectEnumerator)]) {
613 [self errorWithFormat:@"got multiple session IDs !"];
615 e = [sid objectEnumerator];
616 while ((_sid = [e nextObject])) {
617 if ([_sid isEqualToString:@"nil"])
620 if ((session = [self restoreSessionWithID:_sid inContext:_ctx]))
623 //[self warnWithFormat:@"did not find session for sid %@", _sid);
630 - (void)saveSessionForContext:(WOContext *)_ctx {
631 NSTimeInterval startSave = 0.0;
634 startSave = [[NSDateClass date] timeIntervalSince1970];
636 if ([self respondsToSelector:@selector(saveSession:)]) {
637 /* call deprecated method */
638 [self warnWithFormat:@"calling deprecated -saveSession: .."];
639 [self saveSession:[_ctx session]];
645 NSTimeInterval startSnSleep = 0.0, startStore = 0.0;
650 startSnSleep = [[NSDateClass date] timeIntervalSince1970];
652 /* put session to sleep */
653 [sn _sleepWithContext:_ctx];
657 rt = [[NSDateClass date] timeIntervalSince1970] - startSnSleep;
658 [perfLogger logWithFormat:@"[woapp]: session -sleep took %4.3fs.",
659 rt < 0.0 ? -1.0 : rt];
662 if ([sn isTerminating]) {
663 [[NSNotificationCenter defaultCenter]
664 postNotificationName:
665 WOSessionDidTerminateNotification
670 startStore = [[NSDateClass date] timeIntervalSince1970];
672 [[self sessionStore] saveSessionForContext:_ctx];
676 rt = [[NSDateClass date] timeIntervalSince1970] - startStore;
677 [perfLogger logWithFormat:@"[woapp]: storing sn in store took %4.3fs.",
678 rt < 0.0 ? -1.0 : rt];
685 rt = [[NSDateClass date] timeIntervalSince1970] - startSave;
686 [perfLogger logWithFormat:@"[woapp]: saveSessionForContext took %4.3fs.",
687 rt < 0.0 ? -1.0 : rt];
691 - (void)refuseNewSessions:(BOOL)_flag {
692 self->appFlags.doesRefuseNewSessions = _flag ? 1 : 0;
694 - (BOOL)isRefusingNewSessions {
695 return self->appFlags.doesRefuseNewSessions;
697 - (int)activeSessionsCount {
698 return [[self sessionStore] activeSessionsCount];
701 - (void)setSessionStore:(WOSessionStore *)_store {
702 ASSIGN(self->iSessionStore, _store);
704 - (NSString *)sessionStoreClassName {
705 return [[NSUserDefaults standardUserDefaults] stringForKey:@"WOSessionStore"];
707 - (WOSessionStore *)sessionStore {
708 return self->iSessionStore;
711 - (void)setMinimumActiveSessionsCount:(int)_minimum {
712 self->minimumActiveSessionsCount = _minimum;
714 - (int)minimumActiveSessionsCount {
715 return self->minimumActiveSessionsCount;
718 - (void)performExpirationCheck:(NSTimer *)_timer {
721 /* let session store check for expiration ... */
723 ss = [self sessionStore];
724 if ([ss respondsToSelector:@selector(performExpirationCheck:)])
725 [ss performExpirationCheck:_timer];
727 /* check whether application should terminate ... */
729 if ([self isRefusingNewSessions] &&
730 ([self activeSessionsCount] < [self minimumActiveSessionsCount])) {
731 /* check whether the application instance is still valid .. */
732 [self debugWithFormat:
733 @"application terminates because it refuses new sessions and "
734 @"the active session count (%i) is below the minimum (%i).",
735 [self activeSessionsCount], [self minimumActiveSessionsCount]];
741 return [[self context] session];
744 - (WOResponse *)handleSessionCreationErrorInContext:(WOContext *)_ctx {
745 WOResponse *response = [_ctx response];
749 pid = GetCurrentProcessId();
754 if ([self respondsToSelector:@selector(handleSessionCreationError)]) {
755 [self warnWithFormat:@"called deprecated -handleSessionCreationError method"];
756 return [self handleSessionCreationError];
759 [self errorWithFormat:@"could not create session for context %@", _ctx];
761 [response setStatus:200];
762 [response appendContentString:@"<h4>Session Creation Error</h4>\n<pre>"];
763 [response appendContentString:
764 @"Application Instance failed to create session."];
765 [response appendContentHTMLString:
766 [NSString stringWithFormat:
767 @" application: %@\n"
774 [[_ctx request] adaptorPrefix],
778 [[_ctx request] description]]];
779 [response appendContentString:@"</pre>"];
783 - (WOResponse *)handleSessionRestorationErrorInContext:(WOContext *)_ctx {
784 if ([self respondsToSelector:@selector(handleSessionRestorationError)]) {
785 [self warnWithFormat:@"calling deprecated "
786 @"-handleSessionRestorationError method"];
787 return [self handleSessionRestorationError];
790 // TODO: is it correct to return nil?
791 // TODO: we should return a page saying sorry with a cookie + redirect
792 [self errorWithFormat:@"could not restore session for context %@", _ctx];
798 - (void)setStatisticsStore:(WOStatisticsStore *)_statStore {
799 ASSIGN(self->iStatisticsStore, _statStore);
801 - (WOStatisticsStore *)statisticsStore {
802 return self->iStatisticsStore;
805 - (bycopy NSDictionary *)statistics {
806 return [[self statisticsStore] statistics];
811 - (void)_setupDefaultResourceManager {
816 ud = [NSUserDefaults standardUserDefaults];
817 p = [ud stringForKey:@"WODefaultResourceManager"];
818 rmClass = [p isNotEmpty]
819 ? NSClassFromString(p)
820 : [WOResourceManager class];
822 if (rmClass == Nil) {
823 [self errorWithFormat:
824 @"failed to locate class of resource manager: '%@'", p];
828 if ([rmClass instancesRespondToSelector:@selector(initWithPath:)])
829 self->resourceManager = [[rmClass alloc] init];
831 self->resourceManager =
832 [(WOResourceManager *)[rmClass alloc] initWithPath:[self path]];
836 - (void)setResourceManager:(WOResourceManager *)_manager {
837 ASSIGN(self->resourceManager, _manager);
839 - (WOResourceManager *)resourceManager {
840 if (self->resourceManager == nil)
841 [self _setupDefaultResourceManager];
843 return self->resourceManager;
848 WOContext *ctx = [self context];
850 n = [[ctx request] applicationName];
851 n = [@"/" stringByAppendingString:n ? n : [self name]];
853 return [NSURL URLWithString:n relativeToURL:[ctx baseURL]];
856 - (NSString *)pathForResourceNamed:(NSString *)_name ofType:(NSString *)_type {
858 return [[self resourceManager] pathForResourceNamed:_name ofType:_type];
861 - (NSString *)stringForKey:(NSString *)_key
862 inTableNamed:(NSString *)_tableName
863 withDefaultValue:(NSString *)_default
866 return [[self resourceManager] stringForKey:_key
867 inTableNamed:_tableName
868 withDefaultValue:_default
870 [(WOSession *)[self session] languages]];
878 #if DEBUG && PRINT_NSSTRING_STATISTICS
879 if ([NSString respondsToSelector:@selector(printStatistics)])
880 [NSString printStatistics];
883 #if DEBUG && PRINT_OBJC_STATISTICS
884 extern int __objc_selector_max_index;
885 printf("nbuckets=%i, nindices=%i, narrays=%i, idxsize=%i\n",
886 nbuckets, nindices, narrays, idxsize);
887 printf("maxsel=%i\n", __objc_selector_max_index);
893 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
894 if ([_ctx hasSession])
895 [[_ctx session] takeValuesFromRequest:_req inContext:_ctx];
899 if ((page = [_ctx page]) != nil) {
900 WOContext_enterComponent(_ctx, page, nil);
901 [page takeValuesFromRequest:_req inContext:_ctx];
902 WOContext_leaveComponent(_ctx, page);
907 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
910 if ([_ctx hasSession])
911 result = [[_ctx session] invokeActionForRequest:_rq inContext:_ctx];
915 if ((page = [_ctx page])) {
916 WOContext_enterComponent(_ctx, page, nil);
917 result = [[_ctx page] invokeActionForRequest:_rq inContext:_ctx];
918 WOContext_leaveComponent(_ctx, page);
926 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
927 if ([_ctx hasSession])
928 [[_ctx session] appendToResponse:_response inContext:_ctx];
932 if ((page = [_ctx page])) {
933 WOContext_enterComponent(_ctx, page, nil);
934 [page appendToResponse:_response inContext:_ctx];
935 WOContext_leaveComponent(_ctx, page);
939 if(rapidTurnAroundPath != nil) {
942 if((page = [_ctx page])) {
945 template = [page _woComponentTemplate];
946 if([template isKindOfClass:WOTemplateClass]) {
949 _path = [[(WOTemplate *)template url] path];
950 [_response setHeader:_path
951 forKey:@"x-sope-template-path"];
960 - (WOElement *)dynamicElementWithName:(NSString *)_name
961 associations:(NSDictionary *)_associations
962 template:(WOElement *)_template
963 languages:(NSArray *)_languages
965 WOElement *element = nil;
966 Class dynamicElementClass = NSClassFromString(_name);
968 if (dynamicElementClass == Nil) {
969 [self warnWithFormat:@"did not find dynamic element class %@ !", _name];
972 if (![dynamicElementClass isDynamicElement]) {
973 [self warnWithFormat:@"class %@ is not a dynamic element class !", _name];
977 element = [[dynamicElementClass allocWithZone:[_template zone]]
979 associations:_associations
983 - (WOElement *)dynamicElementWithName:(NSString *)_name
984 associations:(NSDictionary *)_associations
985 template:(WOElement *)_template
987 return [self dynamicElementWithName:_name
988 associations:_associations
990 languages:[(WOSession *)[self session] languages]];
995 - (void)setPageRefreshOnBacktrackEnabled:(BOOL)_flag {
996 self->appFlags.isPageRefreshOnBacktrackEnabled = _flag ? 1 : 0;
998 - (BOOL)isPageRefreshOnBacktrackEnabled {
999 return self->appFlags.isPageRefreshOnBacktrackEnabled ? YES : NO;
1002 - (void)setCachingEnabled:(BOOL)_flag {
1003 self->appFlags.isCachingEnabled = _flag ? 1 : 0;
1005 - (BOOL)isCachingEnabled {
1006 // component definition caching
1007 return self->appFlags.isCachingEnabled ? YES : NO;
1010 - (void)setPageCacheSize:(int)_size {
1011 self->pageCacheSize = _size;
1013 - (int)pageCacheSize {
1014 return self->pageCacheSize;
1016 - (void)setPermanentPageCacheSize:(int)_size {
1017 self->permanentPageCacheSize = _size;
1019 - (int)permanentPageCacheSize {
1020 return self->permanentPageCacheSize;
1023 - (id)pageWithName:(NSString *)_name {
1024 // deprecated in WO4
1025 return [self pageWithName:_name inContext:[self context]];
1028 - (WOComponent *)_pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1030 OSX profiling: 3.4% of dispatchRequest?
1031 3.0% rm -pageWithName..
1032 1.5% def instantiate
1033 1.3% initWithName:...
1034 0.76% initWithContent:.. (0.43 addobserver)
1035 0.76% rm defForComp (0.43% touch)
1037 0.11% ctx -component
1042 NSAutoreleasePool *pool;
1043 WOResourceManager *rm;
1046 NSDictionary *start, *stop;
1047 start = [self memoryStatistics];
1050 pool = [[NSAutoreleasePool alloc] init];
1052 languages = [_ctx resourceLookupLanguages];
1054 if ((rm = [[_ctx component] resourceManager]) == nil)
1055 rm = [self resourceManager];
1058 * the following ignores the fact that the passed context may be different
1059 * from that of WOApplication. During the course of template instantiation
1060 * WOApplication's current context gets attached to page which is definitely
1061 * wrong. We workaround this problem by using the private API of WOComponent
1062 * to explicitly set it. However all accompanied methods should be
1063 * extended to pass the correct context where needed.
1065 page = [rm pageWithName:(_name != nil ? _name : (NSString *)@"Main")
1066 languages:languages];
1067 [page _setContext:_ctx];
1068 [page ensureAwakeInContext:_ctx];
1070 page = [page retain];
1075 int rss, vmsize, lib;
1076 stop = [self memoryStatistics];
1077 rss = [[stop objectForKey:@"VmRSS"] intValue] -
1078 [[start objectForKey:@"VmRSS"] intValue];
1079 vmsize = [[stop objectForKey:@"VmSize"] intValue] -
1080 [[start objectForKey:@"VmSize"] intValue];
1081 lib = [[stop objectForKey:@"VmLib"] intValue] -
1082 [[start objectForKey:@"VmLib"] intValue];
1083 [self debugWithFormat:@"loaded component %@; rss=%i vm=%i lib=%i.",
1084 _name, rss,vmsize,lib];
1088 return [page autorelease];
1090 - (id)pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1091 return [self _pageWithName:_name inContext:_ctx];
1093 - (id)pageWithName:(NSString *)_name forRequest:(WORequest *)_req {
1094 WOResourceManager *rm;
1096 if ((rm = [self resourceManager]) == nil)
1099 return [rm pageWithName:(_name != nil) ? _name : (NSString *)@"Main"
1100 languages:[_req browserLanguages]];
1103 - (void)savePage:(WOComponent *)_page {
1105 [[[self context] session] savePage:_page];
1107 - (id)restorePageForContextID:(NSString *)_ctxId {
1109 return [[[self context] session] restorePageForContextID:_ctxId];
1112 - (WOResponse *)handlePageRestorationErrorInContext:(WOContext *)_ctx {
1113 [self errorWithFormat:
1114 @"could not restore page for context-id %@\n in context %@",
1115 [_ctx currentElementID], _ctx];
1117 /* return main page ... */
1118 return [[self pageWithName:nil inContext:_ctx] generateResponse];
1120 - (WOResponse *)handlePageRestorationError {
1122 return [self handlePageRestorationErrorInContext:[self context]];
1127 - (WOResponse *)handleException:(NSException *)_exc
1128 inContext:(WOContext *)_ctx
1130 WORequest *rq = [_ctx request];
1131 WOResponse *r = nil;
1133 if ([self respondsToSelector:@selector(handleException:)]) {
1134 [self warnWithFormat:@"calling deprecated -handleException method !"];
1135 return [self handleException:_exc];
1140 static int doCore = -1;
1142 doCore = [[NSUserDefaults standardUserDefaults]
1143 boolForKey:@"WOCoreOnApplicationException"]
1147 [self fatalWithFormat:@"%@: caught (ctx=%@):\n %@.",
1155 [self fatalWithFormat:@"%@: caught (without context):\n %@.",
1159 else if (rq == nil) {
1160 [self fatalWithFormat:@"%@: caught (without request):\n %@.",
1165 static NSString *pageFormat =
1166 @"Application Server caught exception:\n\n"
1175 @" backtrace:\n%@\n";
1176 NSString *str = nil;
1179 [self errorWithFormat:@"%@: caught:\n %@\nin context:\n %@.",
1182 #if LIB_FOUNDATION_LIBRARY
1183 if ([NSException respondsToSelector:@selector(backtrace)])
1184 bt = [NSException backtrace];
1187 if ((r = [WOResponse responseWithRequest:rq]) == nil)
1188 [self errorWithFormat:@"could not create response !"];
1190 [r setHeader:@"text/html" forKey:@"content-type"];
1191 [r setHeader:@"no-cache" forKey:@"cache-control"];
1192 if (rapidTurnAroundPath != nil) {
1195 templateURL = [[_exc userInfo] objectForKey:@"templateURL"];
1196 if(templateURL != nil)
1197 [r setHeader:[templateURL path] forKey:@"x-sope-template-path"];
1200 str = [NSString stringWithFormat:pageFormat,
1202 ? [[_ctx session] sessionID]
1203 : (NSString *)@"[no session]",
1207 NSStringFromClass([_exc class]),
1210 [[_exc userInfo] description],
1213 [r appendContentString:@"<html><head><title>Caught exception</title></head><body><pre>\n"];
1214 [r appendContentHTMLString:str];
1215 [r appendContentString:@"</pre></body></html>\n"];
1222 - (BOOL)shouldTerminate {
1223 if (![self isRefusingNewSessions])
1225 if ([self activeSessionsCount] >= [self minimumActiveSessionsCount])
1228 /* check whether the application instance is still valid .. */
1229 [self debugWithFormat:
1230 @"application terminates because it refuses new sessions and "
1231 @"the active session count (%i) is below the minimum (%i).",
1232 [self activeSessionsCount], [self minimumActiveSessionsCount]];
1237 [self debugWithFormat:
1238 @"application terminates:\n"
1239 @" %i active sessions\n"
1240 @" %i minimum active sessions\n"
1241 @" refuses new session: %s",
1242 [self activeSessionsCount],
1243 [self minimumActiveSessionsCount],
1244 [self isRefusingNewSessions] ? "yes" : "no"];
1250 - (BOOL)isDebuggingEnabled {
1253 - (NSString *)loggingPrefix {
1254 return [NSString stringWithFormat:@"|%@%@|",
1256 [self isTerminating] ? @" terminating" : @""];
1261 #if !LIB_FOUNDATION_LIBRARY
1262 - (id)valueForUndefinedKey:(NSString *)_key {
1263 [self warnWithFormat:@"tried to access undefined KVC key: '%@'",
1271 + (Class)eoEditingContextClass {
1272 static Class eoEditingContextClass = Nil;
1273 static BOOL lookedUpForEOEditingContextClass = NO;
1275 if (!lookedUpForEOEditingContextClass) {
1276 if ((eoEditingContextClass = NSClassFromString(@"EOEditingContext")) ==nil)
1277 eoEditingContextClass = NSClassFromString(@"NSManagedObjectContext");
1278 lookedUpForEOEditingContextClass = YES;
1280 return eoEditingContextClass;
1283 + (BOOL)implementsEditingContexts {
1284 return [self eoEditingContextClass] != NULL ? YES : NO;
1289 - (NSString *)description {
1290 return [NSString stringWithFormat:@"<%@[0x%p]: name=%@%@>",
1291 NSStringFromClass([self class]), self,
1293 [self isTerminating] ? @" terminating" : @""
1297 @end /* WOApplication */