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(buf, "%04X%04X%02X%08X",
561 [[self number] intValue], getpid(), sessionCount,
562 (unsigned int)time(NULL));
563 wosid = [NSString stringWithCString:buf];
567 - (WOSession *)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 - (WOSession *)restoreSessionWithID:(NSString *)_sid
584 inContext:(WOContext *)_ctx
590 if ([self respondsToSelector:@selector(restoreSession)]) {
591 /* call deprecated method */
592 [self warnWithFormat:@"calling deprecated -restoreSession .."];
593 return [self restoreSession];
597 WOSessionStore *store;
599 if ((store = [self sessionStore]) == nil) {
600 [self errorWithFormat:@"missing session store ..."];
603 session = [store restoreSessionWithID:_sid request:[_ctx request]];
605 [_ctx setSession:session];
606 [session _awakeWithContext:_ctx];
609 [self debugWithFormat:@"did not find a session for sid '%@'", _sid];
616 [[NSNotificationCenter defaultCenter]
617 postNotificationName:WOSessionDidRestoreNotification
621 if ([_sid hasPrefix:@"("]) {
624 sid = [_sid propertyList];
626 if ([sid respondsToSelector:@selector(objectEnumerator)]) {
629 [self errorWithFormat:@"got multiple session IDs !"];
631 e = [sid objectEnumerator];
632 while ((_sid = [e nextObject])) {
633 if ([_sid isEqualToString:@"nil"])
636 if ((session = [self restoreSessionWithID:_sid inContext:_ctx]))
639 //[self warnWithFormat:@"did not find session for sid %@", _sid);
646 - (void)saveSessionForContext:(WOContext *)_ctx {
647 NSTimeInterval startSave = 0.0;
650 startSave = [[NSDateClass date] timeIntervalSince1970];
652 if ([self respondsToSelector:@selector(saveSession:)]) {
653 /* call deprecated method */
654 [self warnWithFormat:@"calling deprecated -saveSession: .."];
655 [self saveSession:[_ctx session]];
661 NSTimeInterval startSnSleep = 0.0, startStore = 0.0;
666 startSnSleep = [[NSDateClass date] timeIntervalSince1970];
668 /* put session to sleep */
669 [sn _sleepWithContext:_ctx];
673 rt = [[NSDateClass date] timeIntervalSince1970] - startSnSleep;
674 [perfLogger logWithFormat:@"[woapp]: session -sleep took %4.3fs.",
675 rt < 0.0 ? -1.0 : rt];
678 if ([sn isTerminating]) {
679 [[NSNotificationCenter defaultCenter]
680 postNotificationName:
681 WOSessionDidTerminateNotification
686 startStore = [[NSDateClass date] timeIntervalSince1970];
688 [[self sessionStore] saveSessionForContext:_ctx];
692 rt = [[NSDateClass date] timeIntervalSince1970] - startStore;
693 [perfLogger logWithFormat:@"[woapp]: storing sn in store took %4.3fs.",
694 rt < 0.0 ? -1.0 : rt];
701 rt = [[NSDateClass date] timeIntervalSince1970] - startSave;
702 [perfLogger logWithFormat:@"[woapp]: saveSessionForContext took %4.3fs.",
703 rt < 0.0 ? -1.0 : rt];
707 - (void)refuseNewSessions:(BOOL)_flag {
708 self->appFlags.doesRefuseNewSessions = _flag ? 1 : 0;
710 - (BOOL)isRefusingNewSessions {
711 return self->appFlags.doesRefuseNewSessions;
713 - (int)activeSessionsCount {
714 return [[self sessionStore] activeSessionsCount];
717 - (void)setSessionStore:(WOSessionStore *)_store {
718 ASSIGN(self->iSessionStore, _store);
720 - (NSString *)sessionStoreClassName {
721 return [[NSUserDefaults standardUserDefaults] stringForKey:@"WOSessionStore"];
723 - (WOSessionStore *)sessionStore {
724 return self->iSessionStore;
727 - (void)setMinimumActiveSessionsCount:(int)_minimum {
728 self->minimumActiveSessionsCount = _minimum;
730 - (int)minimumActiveSessionsCount {
731 return self->minimumActiveSessionsCount;
734 - (void)performExpirationCheck:(NSTimer *)_timer {
737 /* let session store check for expiration ... */
739 ss = [self sessionStore];
740 if ([ss respondsToSelector:@selector(performExpirationCheck:)])
741 [ss performExpirationCheck:_timer];
743 /* check whether application should terminate ... */
745 if ([self isRefusingNewSessions] &&
746 ([self activeSessionsCount] < [self minimumActiveSessionsCount])) {
747 /* check whether the application instance is still valid .. */
748 [self debugWithFormat:
749 @"application terminates because it refuses new sessions and "
750 @"the active session count (%i) is below the minimum (%i).",
751 [self activeSessionsCount], [self minimumActiveSessionsCount]];
756 - (WOSession *)session {
757 return [[self context] session];
760 - (WOResponse *)handleSessionCreationErrorInContext:(WOContext *)_ctx {
761 WOResponse *response = [_ctx response];
765 pid = GetCurrentProcessId();
770 if ([self respondsToSelector:@selector(handleSessionCreationError)]) {
771 [self warnWithFormat:@"called deprecated -handleSessionCreationError method"];
772 return [self handleSessionCreationError];
775 [self errorWithFormat:@"could not create session for context %@", _ctx];
777 [response setStatus:200];
778 [response appendContentString:@"<h4>Session Creation Error</h4>\n<pre>"];
779 [response appendContentString:
780 @"Application Instance failed to create session."];
781 [response appendContentHTMLString:
782 [NSString stringWithFormat:
783 @" application: %@\n"
790 [[_ctx request] adaptorPrefix],
794 [[_ctx request] description]]];
795 [response appendContentString:@"</pre>"];
799 - (WOResponse *)handleSessionRestorationErrorInContext:(WOContext *)_ctx {
800 if ([self respondsToSelector:@selector(handleSessionRestorationError)]) {
801 [self warnWithFormat:@"calling deprecated "
802 @"-handleSessionRestorationError method"];
803 return [self handleSessionRestorationError];
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 - (WOComponent *)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];
1071 page = [rm pageWithName:(_name != nil ? _name : @"Main")
1072 languages:languages];
1073 [page ensureAwakeInContext:_ctx];
1075 page = [page retain];
1080 int rss, vmsize, lib;
1081 stop = [self memoryStatistics];
1082 rss = [[stop objectForKey:@"VmRSS"] intValue] -
1083 [[start objectForKey:@"VmRSS"] intValue];
1084 vmsize = [[stop objectForKey:@"VmSize"] intValue] -
1085 [[start objectForKey:@"VmSize"] intValue];
1086 lib = [[stop objectForKey:@"VmLib"] intValue] -
1087 [[start objectForKey:@"VmLib"] intValue];
1088 [self debugWithFormat:@"loaded component %@; rss=%i vm=%i lib=%i.",
1089 _name, rss,vmsize,lib];
1093 return [page autorelease];
1095 - (WOComponent *)pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1096 return [self _pageWithName:_name inContext:_ctx];
1098 - (WOComponent *)pageWithName:(NSString *)_name forRequest:(WORequest *)_req {
1099 WOResourceManager *rm;
1101 if ((rm = [self resourceManager]) == nil)
1104 return [rm pageWithName:(_name != nil) ? _name : @"Main"
1105 languages:[_req browserLanguages]];
1108 - (void)savePage:(WOComponent *)_page {
1110 [[[self context] session] savePage:_page];
1112 - (id)restorePageForContextID:(NSString *)_ctxId {
1114 return [[[self context] session] restorePageForContextID:_ctxId];
1117 - (WOResponse *)handlePageRestorationErrorInContext:(WOContext *)_ctx {
1118 [self errorWithFormat:
1119 @"could not restore page for context-id %@\n in context %@",
1120 [_ctx currentElementID], _ctx];
1122 /* return main page ... */
1123 return [[self pageWithName:nil inContext:_ctx] generateResponse];
1125 - (WOResponse *)handlePageRestorationError {
1127 return [self handlePageRestorationErrorInContext:[self context]];
1132 - (WOResponse *)handleException:(NSException *)_exc
1133 inContext:(WOContext *)_ctx
1135 WORequest *rq = [_ctx request];
1136 WOResponse *r = nil;
1138 if ([self respondsToSelector:@selector(handleException:)]) {
1139 [self warnWithFormat:@"calling deprecated -handleException method !"];
1140 return [self handleException:_exc];
1145 static int doCore = -1;
1147 doCore = [[NSUserDefaults standardUserDefaults]
1148 boolForKey:@"WOCoreOnApplicationException"]
1152 [self fatalWithFormat:@"%@: caught (ctx=%@):\n %@.",
1160 [self fatalWithFormat:@"%@: caught (without context):\n %@.",
1164 else if (rq == nil) {
1165 [self fatalWithFormat:@"%@: caught (without request):\n %@.",
1170 static NSString *pageFormat =
1171 @"Application Server caught exception:\n\n"
1180 @" backtrace:\n%@\n";
1181 NSString *str = nil;
1184 [self errorWithFormat:@"%@: caught:\n %@\nin context:\n %@.",
1187 #if LIB_FOUNDATION_LIBRARY
1188 if ([NSException respondsToSelector:@selector(backtrace)])
1189 bt = [NSException backtrace];
1192 if ((r = [WOResponse responseWithRequest:rq]) == nil)
1193 [self errorWithFormat:@"could not create response !"];
1195 [r setHeader:@"text/html" forKey:@"content-type"];
1196 [r setHeader:@"no-cache" forKey:@"cache-control"];
1197 if(rapidTurnAroundPath != nil) {
1200 templateURL = [[_exc userInfo] objectForKey:@"templateURL"];
1201 if(templateURL != nil)
1202 [r setHeader:[templateURL path] forKey:@"x-sope-template-path"];
1205 str = [NSString stringWithFormat:pageFormat,
1207 ? [[_ctx session] sessionID]
1212 NSStringFromClass([_exc class]),
1215 [[_exc userInfo] description],
1218 [r appendContentString:@"<html><head><title>Caught exception</title></head><body><pre>\n"];
1219 [r appendContentHTMLString:str];
1220 [r appendContentString:@"</pre></body></html>\n"];
1227 - (BOOL)shouldTerminate {
1228 if (![self isRefusingNewSessions])
1230 if ([self activeSessionsCount] >= [self minimumActiveSessionsCount])
1233 /* check whether the application instance is still valid .. */
1234 [self debugWithFormat:
1235 @"application terminates because it refuses new sessions and "
1236 @"the active session count (%i) is below the minimum (%i).",
1237 [self activeSessionsCount], [self minimumActiveSessionsCount]];
1242 [self debugWithFormat:
1243 @"application terminates:\n"
1244 @" %i active sessions\n"
1245 @" %i minimum active sessions\n"
1246 @" refuses new session: %s",
1247 [self activeSessionsCount],
1248 [self minimumActiveSessionsCount],
1249 [self isRefusingNewSessions] ? "yes" : "no"];
1255 - (BOOL)isDebuggingEnabled {
1258 - (NSString *)loggingPrefix {
1259 return [NSString stringWithFormat:@"|%@%@|",
1261 [self isTerminating] ? @" terminating" : @""];
1266 #if !LIB_FOUNDATION_LIBRARY
1267 - (id)valueForUndefinedKey:(NSString *)_key {
1268 [self warnWithFormat:@"tried to access undefined KVC key: '%@'",
1276 + (Class)eoEditingContextClass {
1277 static Class eoEditingContextClass = Nil;
1278 static BOOL lookedUpForEOEditingContextClass = NO;
1280 if (!lookedUpForEOEditingContextClass) {
1281 eoEditingContextClass = NSClassFromString(@"EOEditingContext");
1282 lookedUpForEOEditingContextClass = YES;
1284 return eoEditingContextClass;
1287 + (BOOL)implementsEditingContexts {
1288 return [self eoEditingContextClass] != NULL ? YES : NO;
1293 - (NSString *)description {
1294 return [NSString stringWithFormat:@"<%@[0x%08X]: name=%@%@>",
1295 NSStringFromClass([self class]), self,
1297 [self isTerminating] ? @" terminating" : @""
1301 @end /* WOApplication */