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;
52 static NSRecursiveLock *classLock = nil;
53 static NGLogger *perfLogger = nil;
54 static Class NSDateClass = Nil;
55 static Class WOTemplateClass = Nil;
56 static BOOL debugOn = NO;
57 static NSString *rapidTurnAroundPath = nil;
59 @interface WOSessionStore(SnStore)
60 - (void)performExpirationCheck:(NSTimer *)_timer;
63 @implementation WOApplication
65 #if 1 // TODO: why is that? why isn't that set by a default?
66 static NSString *defaultCompRqHandlerClassName = @"OWViewRequestHandler";
68 static NSString *defaultCompRqHandlerClassName = @"WOComponentRequestHandler";
72 return [super version] + 5 /* v6 */;
79 clazz = NSClassFromString(@"SNSConnection");
80 c = [(id<NSObject>)clazz performSelector:@selector(defaultSNSConnection)];
83 [[self logger] fatalWithFormat:@"could not connect SNS, exiting .."];
87 [[self logger] logWithFormat:@"SNS enabled"];
90 + (void)_initializeWOApp {
91 static BOOL isInitialized = NO;
92 NSAutoreleasePool *pool;
96 if (isInitialized) return;
100 pool = [[NSAutoreleasePool alloc] init];
101 debugOn = [WOApplication isDebuggingEnabled];
103 [[self logger] setLogLevel:NGLogLevelInfo];
105 NSLog(@"Note: WOApplication debugging is enabled.");
107 if (classLock == nil) classLock = [[NSRecursiveLock alloc] init];
108 ud = [NSUserDefaults standardUserDefaults];
110 /* setup SNSConnection */
112 if ([ud boolForKey:@"WOContactSNS"])
115 [[self logger] logWithFormat:@"SNS support disabled."];
117 NSDateClass = [NSDate class];
118 WOTemplateClass = [WOTemplate class];
120 rapidTurnAroundPath = [[ud stringForKey:@"WOProjectDirectory"] copy];
122 lm = [NGLoggerManager defaultLoggerManager];
123 perfLogger = [lm loggerForDefaultKey:@"WOProfileApplication"];
128 /* old license checks */
130 - (NSCalendarDate *)appExpireDate {
131 // TODO: can we remove that?
134 - (BOOL)isLicenseExpired {
135 // TODO: can we remove that?
141 - (NSString *)_lookupAppPath {
142 static NSString *suffix = nil;
143 static BOOL appPathMissing = NO;
152 ud = [NSUserDefaults standardUserDefaults];
154 // Check if appPath has been forced
155 result = [ud stringForKey:@"WOProjectDirectory"];
160 suffix = [ud stringForKey:@"WOApplicationSuffix"];
162 fm = [NSFileManager defaultManager];
163 cwd = [fm currentDirectoryPath];
165 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
166 result = [[NGBundle mainBundle] bundlePath];
167 //NSLog(@"%s: check path '%@'", __PRETTY_FUNCTION__, result);
172 if ([result hasSuffix:suffix]) {
173 /* started app inside of .woa directory */
174 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
175 result = [[NGBundle mainBundle] bundlePath];
181 NSString *wrapperName;
183 wrapperName = [self->name stringByAppendingString:suffix];
185 /* take a look whether ./AppName.woa exists */
186 result = [result stringByAppendingPathComponent:wrapperName];
187 if (![fm fileExistsAtPath:result]) {
188 /* lookup in process-path */
194 pi = [NSProcessInfo processInfo];
195 env = [pi environment];
196 if ([[env objectForKey:@"GNUSTEP_SYSTEM_ROOT"] isNotNull]) {
197 isFlattened = [[[env objectForKey:@"GNUSTEP_FLATTENED"]
198 lowercaseString] isEqualToString:@"yes"];
200 else /* default to flattened if no GNUstep runtime is set */
203 ppath = [[pi arguments] objectAtIndex:0];
204 ppath = [ppath stringByDeletingLastPathComponent]; // del exe-name
207 ppath = [ppath stringByDeletingLastPathComponent]; // lib-combo
208 ppath = [ppath stringByDeletingLastPathComponent]; // os
209 ppath = [ppath stringByDeletingLastPathComponent]; // cpu
211 if ([ppath hasSuffix:suffix])
216 if (![fm fileExistsAtPath:result]) {
217 [self debugWithFormat:@"%s: missing path '%@'",
218 __PRETTY_FUNCTION__, result];
219 appPathMissing = YES;
226 + (NSString *)defaultRequestHandlerClassName {
227 return @"WOComponentRequestHandler";
230 - (void)_logDefaults {
236 ud = [NSUserDefaults standardUserDefaults];
237 keys = [[ud dictionaryRepresentation] allKeys];
238 keys = [keys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
240 e = [keys objectEnumerator];
241 while((key = [e nextObject]) != nil) {
242 if ([key hasPrefix:@"WO"] || [key isEqualToString:@"NSProjectSearchPath"])
243 [self logWithFormat:@"[default]: %@ = %@",
245 [[ud objectForKey:key] description]];
249 - (id)initWithName:(NSString *)_name {
250 [WOApplication _initializeWOApp];
252 if ((self = [super init]) != nil) {
254 WORequestHandler *rh;
257 self->name = [_name copy];
259 ud = [NSUserDefaults standardUserDefaults];
261 [self setPageCacheSize:[ud integerForKey:@"WOPageCacheSize"]];
262 [self setPermanentPageCacheSize:
263 [ud integerForKey:@"WOPermanentPageCacheSize"]];
265 [self setPageRefreshOnBacktrackEnabled:
266 [[ud objectForKey:@"WOPageRefreshOnBacktrack"] boolValue]];
268 [self setCachingEnabled:[WOApplication isCachingEnabled]];
270 /* setup request handlers */
272 self->defaultRequestHandler =
273 [[NSClassFromString([[self class] defaultRequestHandlerClassName])
276 self->requestHandlerRegistry =
277 NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 8);
279 if ((rk = [WOApplication componentRequestHandlerKey]) == nil) {
281 @"WARNING: no component request handler key is specified, "
282 @"this probably means that share/ngobjweb/Defaults.plist "
283 @"could not get loaded (permissions?)"];
285 rh = [[NSClassFromString(defaultCompRqHandlerClassName) alloc] init];
286 if ([rk isNotEmpty] && [rh isNotNull])
287 [self registerRequestHandler:rh forKey:rk];
288 [rh release]; rh = nil;
290 rk = [WOApplication directActionRequestHandlerKey];
291 rh = [[NSClassFromString(@"WODirectActionRequestHandler") alloc] init];
292 if ([rk isNotEmpty] && [rh isNotNull])
293 [self registerRequestHandler:rh forKey:rk];
294 [rh release]; rh = nil;
296 if ((rh = [[NSClassFromString(@"WOResourceRequestHandler") alloc] init])) {
297 rk = [WOApplication resourceRequestHandlerKey];
299 [self registerRequestHandler:rh forKey:rk];
300 [self registerRequestHandler:rh forKey:@"WebServerResources"];
302 [self registerRequestHandler:rh forKey:@"Resources"];
304 [rh release]; rh = nil;
307 /* setup session store */
309 self->iSessionStore =
310 [[NSClassFromString([self sessionStoreClassName]) alloc] init];
312 /* setup statistics store */
314 self->iStatisticsStore = [[WOStatisticsStore alloc] init];
316 /* register timers */
317 self->expirationTimer =
318 [[NSTimer scheduledTimerWithTimeInterval:
319 [[ud objectForKey:@"WOExpirationTimeInterval"] intValue]
321 selector:@selector(performExpirationCheck:)
326 if ([ud boolForKey:@"WOLogDefaultsOnStartup"])
329 [[NSNotificationCenter defaultCenter]
330 postNotificationName:
331 WOApplicationWillFinishLaunchingNotification
338 return [self initWithName:[[[NSProcessInfo processInfo]
340 stringByDeletingPathExtension]];
344 [[NSNotificationCenter defaultCenter] removeObserver:self];
346 [self->expirationTimer invalidate];
348 if (self->requestHandlerRegistry)
349 NSFreeMapTable(self->requestHandlerRegistry);
351 [self->expirationTimer release];
352 [self->resourceManager release];
353 [self->iSessionStore release];
354 [self->defaultRequestHandler release];
355 [self->path release];
356 [self->name release];
357 [self->instanceNumber release];
361 - (void)processHupSignal:(int)_signal {
362 /* this isn't called immediatly */
363 [self logWithFormat:@"terminating on SIGHUP ..."];
372 - (BOOL)monitoringEnabled {
376 static BOOL missingPath = NO;
380 if (self->path == nil) {
381 if ((self->path = [[self _lookupAppPath] copy]) == nil) {
382 [self debugWithFormat:@"could not find wrapper of application !"];
390 - (NSString *)number {
391 if (self->instanceNumber == nil) {
394 if ((num = [[NSUserDefaults standardUserDefaults] objectForKey:@"n"])) {
395 self->instanceNumber = [[num stringValue] copy];
399 #if defined(__MINGW32__)
400 pid = (unsigned)GetCurrentProcessId();
402 pid = (unsigned)getpid();
404 self->instanceNumber = [[NSString alloc] initWithFormat:@"%d", pid];
407 return self->instanceNumber;
410 - (void)_setCurrentContext:(WOContext *)_ctx {
411 NSMutableDictionary *info;
413 info = [[NSThread currentThread] threadDictionary];
415 [info setObject:_ctx forKey:@"WOContext"];
417 [info removeObjectForKey:@"WOContext"];
419 - (WOContext *)context {
424 if ((t = [NSThread currentThread]) == nil) {
425 [self errorWithFormat:@"missing current thread !!!"];
428 if ((td = [t threadDictionary]) == nil) {
429 [self errorWithFormat:
430 @"missing current thread's dictionary (thread=%@) !!!",
435 return [td objectForKey:@"WOContext"];
438 /* request handlers */
440 - (void)registerRequestHandler:(WORequestHandler *)_hdl
441 forKey:(NSString *)_key
444 NSMapInsert(self->requestHandlerRegistry, _key, _hdl);
447 - (void)removeRequestHandlerForKey:(NSString *)_key {
448 if (_key == nil) return;
450 NSMapRemove(self->requestHandlerRegistry, _key);
454 - (void)setDefaultRequestHandler:(WORequestHandler *)_hdl {
456 ASSIGN(self->defaultRequestHandler, _hdl);
459 - (WORequestHandler *)defaultRequestHandler {
460 return self->defaultRequestHandler;
462 - (WORequestHandler *)requestHandlerForKey:(NSString *)_key {
463 WORequestHandler *handler;
466 handler = [(id)NSMapGet(self->requestHandlerRegistry, _key) retain];
468 handler = [[self defaultRequestHandler] retain];
471 return [handler autorelease];
474 - (NSArray *)registeredRequestHandlerKeys {
475 NSMutableArray *array = [NSMutableArray arrayWithCapacity:16];
478 WORequestHandler *handler;
481 e = NSEnumerateMapTable(self->requestHandlerRegistry);
482 while (NSNextMapEnumeratorPair(&e, (void**)&key, (void**)&handler))
483 [array addObject:key];
486 return [[array copy] autorelease];
489 - (WORequestHandler *)handlerForRequest:(WORequest *)_request {
490 WORequestHandler *handler;
493 if ((key = [_request requestHandlerKey]) == nil)
494 return [self defaultRequestHandler];
496 handler = NSMapGet(self->requestHandlerRegistry, key);
497 return (handler != nil) ? handler : [self defaultRequestHandler];
502 - (WOSession *)_initializeSessionInContext:(WOContext *)_ctx {
505 sn = [self createSessionForRequest:[_ctx request]];
506 [_ctx setNewSession:sn];
508 if ([sn respondsToSelector:@selector(prepare)]) {
510 [self debugWithFormat:@"calling -prepare on session .."];
512 [sn performSelector:@selector(prepare)];
515 [sn _awakeWithContext:_ctx];
517 [[NSNotificationCenter defaultCenter]
518 postNotificationName:WOSessionDidCreateNotification
520 return [sn autorelease];
523 - (NSString *)sessionIDFromRequest:(WORequest *)_request {
526 if (_request == nil) return nil;
528 /* first look into form values */
529 if ((sessionId = [_request formValueForKey:WORequestValueSessionID])!=nil) {
530 if ([sessionId isNotEmpty])
534 /* now look into the cookies */
535 if ((sessionId = [_request cookieValueForKey:[self name]]) != nil) {
536 if ([sessionId respondsToSelector:@selector(objectEnumerator)]) {
539 e = [(id)sessionId objectEnumerator];
540 while ((sessionId = [e nextObject]) != nil) {
541 if ([sessionId isNotEmpty] && ![sessionId isEqual:@"nil"])
546 if ([sessionId isNotEmpty] && ![sessionId isEqual:@"nil"])
554 - (NSString *)createSessionIDForSession:(WOSession *)_session {
555 /* session id must be 18 chars long for snsd to work ! */
556 static unsigned int sessionCount = 0;
558 unsigned char buf[20];
561 sprintf((char *)buf, "%04X%04X%02X%08X",
562 [[self number] intValue], getpid(), sessionCount,
563 (unsigned int)time(NULL));
564 wosid = [NSString stringWithCString:(char *)buf];
568 - (id)createSessionForRequest:(WORequest *)_request {
569 if ([self respondsToSelector:@selector(createSession)]) {
570 /* call deprecated method */
571 [self warnWithFormat:@"calling deprecated -createSession .."];
572 return [self createSession];
577 if ((snClass = NSClassFromString(@"Session")) == Nil)
578 snClass = [WOSession class];
580 return [[snClass alloc] init];
584 - (id)restoreSessionWithID:(NSString *)_sid inContext:(WOContext *)_ctx {
589 if ([self respondsToSelector:@selector(restoreSession)]) {
590 /* call deprecated method */
591 [self warnWithFormat:@"calling deprecated -restoreSession .."];
592 return [self restoreSession];
596 WOSessionStore *store;
598 if ((store = [self sessionStore]) == nil) {
599 [self errorWithFormat:@"missing session store ..."];
602 session = [store restoreSessionWithID:_sid request:[_ctx request]];
603 if ([session isNotNull]) {
604 [_ctx setSession:session];
605 [session _awakeWithContext:_ctx];
608 [self debugWithFormat:@"did not find a session for sid '%@'", _sid];
615 [[NSNotificationCenter defaultCenter]
616 postNotificationName:WOSessionDidRestoreNotification
620 if ([_sid hasPrefix:@"("]) {
623 sid = [_sid propertyList];
625 if ([sid respondsToSelector:@selector(objectEnumerator)]) {
628 [self errorWithFormat:@"got multiple session IDs !"];
630 e = [sid objectEnumerator];
631 while ((_sid = [e nextObject])) {
632 if ([_sid isEqualToString:@"nil"])
635 if ((session = [self restoreSessionWithID:_sid inContext:_ctx]))
638 //[self warnWithFormat:@"did not find session for sid %@", _sid);
645 - (void)saveSessionForContext:(WOContext *)_ctx {
646 NSTimeInterval startSave = 0.0;
649 startSave = [[NSDateClass date] timeIntervalSince1970];
651 if ([self respondsToSelector:@selector(saveSession:)]) {
652 /* call deprecated method */
653 [self warnWithFormat:@"calling deprecated -saveSession: .."];
654 [self saveSession:[_ctx session]];
660 NSTimeInterval startSnSleep = 0.0, startStore = 0.0;
665 startSnSleep = [[NSDateClass date] timeIntervalSince1970];
667 /* put session to sleep */
668 [sn _sleepWithContext:_ctx];
672 rt = [[NSDateClass date] timeIntervalSince1970] - startSnSleep;
673 [perfLogger logWithFormat:@"[woapp]: session -sleep took %4.3fs.",
674 rt < 0.0 ? -1.0 : rt];
677 if ([sn isTerminating]) {
678 [[NSNotificationCenter defaultCenter]
679 postNotificationName:
680 WOSessionDidTerminateNotification
685 startStore = [[NSDateClass date] timeIntervalSince1970];
687 [[self sessionStore] saveSessionForContext:_ctx];
691 rt = [[NSDateClass date] timeIntervalSince1970] - startStore;
692 [perfLogger logWithFormat:@"[woapp]: storing sn in store took %4.3fs.",
693 rt < 0.0 ? -1.0 : rt];
700 rt = [[NSDateClass date] timeIntervalSince1970] - startSave;
701 [perfLogger logWithFormat:@"[woapp]: saveSessionForContext took %4.3fs.",
702 rt < 0.0 ? -1.0 : rt];
706 - (void)refuseNewSessions:(BOOL)_flag {
707 self->appFlags.doesRefuseNewSessions = _flag ? 1 : 0;
709 - (BOOL)isRefusingNewSessions {
710 return self->appFlags.doesRefuseNewSessions;
712 - (int)activeSessionsCount {
713 return [[self sessionStore] activeSessionsCount];
716 - (void)setSessionStore:(WOSessionStore *)_store {
717 ASSIGN(self->iSessionStore, _store);
719 - (NSString *)sessionStoreClassName {
720 return [[NSUserDefaults standardUserDefaults] stringForKey:@"WOSessionStore"];
722 - (WOSessionStore *)sessionStore {
723 return self->iSessionStore;
726 - (void)setMinimumActiveSessionsCount:(int)_minimum {
727 self->minimumActiveSessionsCount = _minimum;
729 - (int)minimumActiveSessionsCount {
730 return self->minimumActiveSessionsCount;
733 - (void)performExpirationCheck:(NSTimer *)_timer {
736 /* let session store check for expiration ... */
738 ss = [self sessionStore];
739 if ([ss respondsToSelector:@selector(performExpirationCheck:)])
740 [ss performExpirationCheck:_timer];
742 /* check whether application should terminate ... */
744 if ([self isRefusingNewSessions] &&
745 ([self activeSessionsCount] < [self minimumActiveSessionsCount])) {
746 /* check whether the application instance is still valid .. */
747 [self debugWithFormat:
748 @"application terminates because it refuses new sessions and "
749 @"the active session count (%i) is below the minimum (%i).",
750 [self activeSessionsCount], [self minimumActiveSessionsCount]];
756 return [[self context] session];
759 - (WOResponse *)handleSessionCreationErrorInContext:(WOContext *)_ctx {
760 WOResponse *response = [_ctx response];
764 pid = GetCurrentProcessId();
769 if ([self respondsToSelector:@selector(handleSessionCreationError)]) {
770 [self warnWithFormat:@"called deprecated -handleSessionCreationError method"];
771 return [self handleSessionCreationError];
774 [self errorWithFormat:@"could not create session for context %@", _ctx];
776 [response setStatus:200];
777 [response appendContentString:@"<h4>Session Creation Error</h4>\n<pre>"];
778 [response appendContentString:
779 @"Application Instance failed to create session."];
780 [response appendContentHTMLString:
781 [NSString stringWithFormat:
782 @" application: %@\n"
789 [[_ctx request] adaptorPrefix],
793 [[_ctx request] description]]];
794 [response appendContentString:@"</pre>"];
798 - (WOResponse *)handleSessionRestorationErrorInContext:(WOContext *)_ctx {
799 if ([self respondsToSelector:@selector(handleSessionRestorationError)]) {
800 [self warnWithFormat:@"calling deprecated "
801 @"-handleSessionRestorationError method"];
802 return [self handleSessionRestorationError];
805 // TODO: is it correct to return nil?
806 // TODO: we should return a page saying sorry with a cookie + redirect
807 [self errorWithFormat:@"could not restore session for context %@", _ctx];
813 - (void)setStatisticsStore:(WOStatisticsStore *)_statStore {
814 ASSIGN(self->iStatisticsStore, _statStore);
816 - (WOStatisticsStore *)statisticsStore {
817 return self->iStatisticsStore;
820 - (bycopy NSDictionary *)statistics {
821 return [[self statisticsStore] statistics];
826 - (void)_setupDefaultResourceManager {
831 ud = [NSUserDefaults standardUserDefaults];
832 p = [ud stringForKey:@"WODefaultResourceManager"];
833 rmClass = [p isNotEmpty]
834 ? NSClassFromString(p)
835 : [WOResourceManager class];
837 if (rmClass == Nil) {
838 [self errorWithFormat:
839 @"failed to locate class of resource manager: '%@'", p];
843 if ([rmClass instancesRespondToSelector:@selector(initWithPath:)])
844 self->resourceManager = [[rmClass alloc] init];
846 self->resourceManager =
847 [(WOResourceManager *)[rmClass alloc] initWithPath:[self path]];
851 - (void)setResourceManager:(WOResourceManager *)_manager {
852 ASSIGN(self->resourceManager, _manager);
854 - (WOResourceManager *)resourceManager {
855 if (self->resourceManager == nil)
856 [self _setupDefaultResourceManager];
858 return self->resourceManager;
863 WOContext *ctx = [self context];
865 n = [[ctx request] applicationName];
866 n = [@"/" stringByAppendingString:n ? n : [self name]];
868 return [NSURL URLWithString:n relativeToURL:[ctx baseURL]];
871 - (NSString *)pathForResourceNamed:(NSString *)_name ofType:(NSString *)_type {
873 return [[self resourceManager] pathForResourceNamed:_name ofType:_type];
876 - (NSString *)stringForKey:(NSString *)_key
877 inTableNamed:(NSString *)_tableName
878 withDefaultValue:(NSString *)_default
881 return [[self resourceManager] stringForKey:_key
882 inTableNamed:_tableName
883 withDefaultValue:_default
885 [(WOSession *)[self session] languages]];
893 #if DEBUG && PRINT_NSSTRING_STATISTICS
894 if ([NSString respondsToSelector:@selector(printStatistics)])
895 [NSString printStatistics];
898 #if DEBUG && PRINT_OBJC_STATISTICS
899 extern int __objc_selector_max_index;
900 printf("nbuckets=%i, nindices=%i, narrays=%i, idxsize=%i\n",
901 nbuckets, nindices, narrays, idxsize);
902 printf("maxsel=%i\n", __objc_selector_max_index);
908 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
909 if ([_ctx hasSession])
910 [[_ctx session] takeValuesFromRequest:_req inContext:_ctx];
914 if ((page = [_ctx page]) != nil) {
915 WOContext_enterComponent(_ctx, page, nil);
916 [page takeValuesFromRequest:_req inContext:_ctx];
917 WOContext_leaveComponent(_ctx, page);
922 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
925 if ([_ctx hasSession])
926 result = [[_ctx session] invokeActionForRequest:_rq inContext:_ctx];
930 if ((page = [_ctx page])) {
931 WOContext_enterComponent(_ctx, page, nil);
932 result = [[_ctx page] invokeActionForRequest:_rq inContext:_ctx];
933 WOContext_leaveComponent(_ctx, page);
941 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
942 if ([_ctx hasSession])
943 [[_ctx session] appendToResponse:_response inContext:_ctx];
947 if ((page = [_ctx page])) {
948 WOContext_enterComponent(_ctx, page, nil);
949 [page appendToResponse:_response inContext:_ctx];
950 WOContext_leaveComponent(_ctx, page);
954 if(rapidTurnAroundPath != nil) {
957 if((page = [_ctx page])) {
960 template = [page _woComponentTemplate];
961 if([template isKindOfClass:WOTemplateClass]) {
964 _path = [[(WOTemplate *)template url] path];
965 [_response setHeader:_path
966 forKey:@"x-sope-template-path"];
975 - (WOElement *)dynamicElementWithName:(NSString *)_name
976 associations:(NSDictionary *)_associations
977 template:(WOElement *)_template
978 languages:(NSArray *)_languages
980 WOElement *element = nil;
981 Class dynamicElementClass = NSClassFromString(_name);
983 if (dynamicElementClass == Nil) {
984 [self warnWithFormat:@"did not find dynamic element class %@ !", _name];
987 if (![dynamicElementClass isDynamicElement]) {
988 [self warnWithFormat:@"class %@ is not a dynamic element class !", _name];
992 element = [[dynamicElementClass allocWithZone:[_template zone]]
994 associations:_associations
998 - (WOElement *)dynamicElementWithName:(NSString *)_name
999 associations:(NSDictionary *)_associations
1000 template:(WOElement *)_template
1002 return [self dynamicElementWithName:_name
1003 associations:_associations
1005 languages:[(WOSession *)[self session] languages]];
1010 - (void)setPageRefreshOnBacktrackEnabled:(BOOL)_flag {
1011 self->appFlags.isPageRefreshOnBacktrackEnabled = _flag ? 1 : 0;
1013 - (BOOL)isPageRefreshOnBacktrackEnabled {
1014 return self->appFlags.isPageRefreshOnBacktrackEnabled ? YES : NO;
1017 - (void)setCachingEnabled:(BOOL)_flag {
1018 self->appFlags.isCachingEnabled = _flag ? 1 : 0;
1020 - (BOOL)isCachingEnabled {
1021 // component definition caching
1022 return self->appFlags.isCachingEnabled ? YES : NO;
1025 - (void)setPageCacheSize:(int)_size {
1026 self->pageCacheSize = _size;
1028 - (int)pageCacheSize {
1029 return self->pageCacheSize;
1031 - (void)setPermanentPageCacheSize:(int)_size {
1032 self->permanentPageCacheSize = _size;
1034 - (int)permanentPageCacheSize {
1035 return self->permanentPageCacheSize;
1038 - (id)pageWithName:(NSString *)_name {
1039 // deprecated in WO4
1040 return [self pageWithName:_name inContext:[self context]];
1043 - (WOComponent *)_pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1045 OSX profiling: 3.4% of dispatchRequest?
1046 3.0% rm -pageWithName..
1047 1.5% def instantiate
1048 1.3% initWithName:...
1049 0.76% initWithContent:.. (0.43 addobserver)
1050 0.76% rm defForComp (0.43% touch)
1052 0.11% ctx -component
1057 NSAutoreleasePool *pool;
1058 WOResourceManager *rm;
1061 NSDictionary *start, *stop;
1062 start = [self memoryStatistics];
1065 pool = [[NSAutoreleasePool alloc] init];
1067 languages = [_ctx resourceLookupLanguages];
1069 if ((rm = [[_ctx component] resourceManager]) == nil)
1070 rm = [self resourceManager];
1073 * the following ignores the fact that the passed context may be different
1074 * from that of WOApplication. During the course of template instantiation
1075 * WOApplication's current context gets attached to page which is definitely
1076 * wrong. We workaround this problem by using the private API of WOComponent
1077 * to explicitly set it. However all accompanied methods should be
1078 * extended to pass the correct context where needed.
1080 page = [rm pageWithName:(_name != nil ? _name : @"Main")
1081 languages:languages];
1082 [page _setContext:_ctx];
1083 [page ensureAwakeInContext:_ctx];
1085 page = [page retain];
1090 int rss, vmsize, lib;
1091 stop = [self memoryStatistics];
1092 rss = [[stop objectForKey:@"VmRSS"] intValue] -
1093 [[start objectForKey:@"VmRSS"] intValue];
1094 vmsize = [[stop objectForKey:@"VmSize"] intValue] -
1095 [[start objectForKey:@"VmSize"] intValue];
1096 lib = [[stop objectForKey:@"VmLib"] intValue] -
1097 [[start objectForKey:@"VmLib"] intValue];
1098 [self debugWithFormat:@"loaded component %@; rss=%i vm=%i lib=%i.",
1099 _name, rss,vmsize,lib];
1103 return [page autorelease];
1105 - (id)pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1106 return [self _pageWithName:_name inContext:_ctx];
1108 - (id)pageWithName:(NSString *)_name forRequest:(WORequest *)_req {
1109 WOResourceManager *rm;
1111 if ((rm = [self resourceManager]) == nil)
1114 return [rm pageWithName:(_name != nil) ? _name : @"Main"
1115 languages:[_req browserLanguages]];
1118 - (void)savePage:(WOComponent *)_page {
1120 [[[self context] session] savePage:_page];
1122 - (id)restorePageForContextID:(NSString *)_ctxId {
1124 return [[[self context] session] restorePageForContextID:_ctxId];
1127 - (WOResponse *)handlePageRestorationErrorInContext:(WOContext *)_ctx {
1128 [self errorWithFormat:
1129 @"could not restore page for context-id %@\n in context %@",
1130 [_ctx currentElementID], _ctx];
1132 /* return main page ... */
1133 return [[self pageWithName:nil inContext:_ctx] generateResponse];
1135 - (WOResponse *)handlePageRestorationError {
1137 return [self handlePageRestorationErrorInContext:[self context]];
1142 - (WOResponse *)handleException:(NSException *)_exc
1143 inContext:(WOContext *)_ctx
1145 WORequest *rq = [_ctx request];
1146 WOResponse *r = nil;
1148 if ([self respondsToSelector:@selector(handleException:)]) {
1149 [self warnWithFormat:@"calling deprecated -handleException method !"];
1150 return [self handleException:_exc];
1155 static int doCore = -1;
1157 doCore = [[NSUserDefaults standardUserDefaults]
1158 boolForKey:@"WOCoreOnApplicationException"]
1162 [self fatalWithFormat:@"%@: caught (ctx=%@):\n %@.",
1170 [self fatalWithFormat:@"%@: caught (without context):\n %@.",
1174 else if (rq == nil) {
1175 [self fatalWithFormat:@"%@: caught (without request):\n %@.",
1180 static NSString *pageFormat =
1181 @"Application Server caught exception:\n\n"
1190 @" backtrace:\n%@\n";
1191 NSString *str = nil;
1194 [self errorWithFormat:@"%@: caught:\n %@\nin context:\n %@.",
1197 #if LIB_FOUNDATION_LIBRARY
1198 if ([NSException respondsToSelector:@selector(backtrace)])
1199 bt = [NSException backtrace];
1202 if ((r = [WOResponse responseWithRequest:rq]) == nil)
1203 [self errorWithFormat:@"could not create response !"];
1205 [r setHeader:@"text/html" forKey:@"content-type"];
1206 [r setHeader:@"no-cache" forKey:@"cache-control"];
1207 if (rapidTurnAroundPath != nil) {
1210 templateURL = [[_exc userInfo] objectForKey:@"templateURL"];
1211 if(templateURL != nil)
1212 [r setHeader:[templateURL path] forKey:@"x-sope-template-path"];
1215 str = [NSString stringWithFormat:pageFormat,
1217 ? [[_ctx session] sessionID]
1222 NSStringFromClass([_exc class]),
1225 [[_exc userInfo] description],
1228 [r appendContentString:@"<html><head><title>Caught exception</title></head><body><pre>\n"];
1229 [r appendContentHTMLString:str];
1230 [r appendContentString:@"</pre></body></html>\n"];
1237 - (BOOL)shouldTerminate {
1238 if (![self isRefusingNewSessions])
1240 if ([self activeSessionsCount] >= [self minimumActiveSessionsCount])
1243 /* check whether the application instance is still valid .. */
1244 [self debugWithFormat:
1245 @"application terminates because it refuses new sessions and "
1246 @"the active session count (%i) is below the minimum (%i).",
1247 [self activeSessionsCount], [self minimumActiveSessionsCount]];
1252 [self debugWithFormat:
1253 @"application terminates:\n"
1254 @" %i active sessions\n"
1255 @" %i minimum active sessions\n"
1256 @" refuses new session: %s",
1257 [self activeSessionsCount],
1258 [self minimumActiveSessionsCount],
1259 [self isRefusingNewSessions] ? "yes" : "no"];
1265 - (BOOL)isDebuggingEnabled {
1268 - (NSString *)loggingPrefix {
1269 return [NSString stringWithFormat:@"|%@%@|",
1271 [self isTerminating] ? @" terminating" : @""];
1276 #if !LIB_FOUNDATION_LIBRARY
1277 - (id)valueForUndefinedKey:(NSString *)_key {
1278 [self warnWithFormat:@"tried to access undefined KVC key: '%@'",
1286 + (Class)eoEditingContextClass {
1287 static Class eoEditingContextClass = Nil;
1288 static BOOL lookedUpForEOEditingContextClass = NO;
1290 if (!lookedUpForEOEditingContextClass) {
1291 if ((eoEditingContextClass = NSClassFromString(@"EOEditingContext")) ==nil)
1292 eoEditingContextClass = NSClassFromString(@"NSManagedObjectContext");
1293 lookedUpForEOEditingContextClass = YES;
1295 return eoEditingContextClass;
1298 + (BOOL)implementsEditingContexts {
1299 return [self eoEditingContextClass] != NULL ? YES : NO;
1304 - (NSString *)description {
1305 return [NSString stringWithFormat:@"<%@[0x%08X]: name=%@%@>",
1306 NSStringFromClass([self class]), self,
1308 [self isTerminating] ? @" terminating" : @""
1312 @end /* WOApplication */