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 #import <EOControl/EOControl.h>
40 @interface WOApplication(PrivateMethods)
42 - (id)_loadComponentDefinitionWithName:(NSString *)_name
43 language:(NSArray *)_langs;
44 - (NSDictionary *)memoryStatistics;
47 static NSRecursiveLock *classLock = nil;
48 static NGLogger *perfLogger = nil;
49 static Class NSDateClass = Nil;
50 static Class WOTemplateClass = Nil;
51 static BOOL debugOn = NO;
52 static NSString *rapidTurnAroundPath = nil;
54 @interface WOSessionStore(SnStore)
55 - (void)performExpirationCheck:(NSTimer *)_timer;
58 @implementation WOApplication
61 return [super version] + 5 /* v6 */;
68 clazz = NSClassFromString(@"SNSConnection");
69 c = [(id<NSObject>)clazz performSelector:@selector(defaultSNSConnection)];
72 [[self logger] fatalWithFormat:@"could not connect SNS, exiting .."];
76 [[self logger] logWithFormat:@"SNS enabled"];
79 + (void)_initializeWOApp {
80 static BOOL isInitialized = NO;
81 NSAutoreleasePool *pool;
85 if (isInitialized) return;
89 pool = [[NSAutoreleasePool alloc] init];
90 debugOn = [WOApplication isDebuggingEnabled];
92 [[self logger] setLogLevel:NGLogLevelInfo];
94 NSLog(@"Note: WOApplication debugging is enabled.");
96 if (classLock == nil) classLock = [[NSRecursiveLock alloc] init];
97 ud = [NSUserDefaults standardUserDefaults];
99 /* setup SNSConnection */
101 if ([ud boolForKey:@"WOContactSNS"])
104 [[self logger] logWithFormat:@"SNS support disabled."];
106 NSDateClass = [NSDate class];
107 WOTemplateClass = [WOTemplate class];
109 rapidTurnAroundPath = [[ud stringForKey:@"WOProjectDirectory"] copy];
111 lm = [NGLoggerManager defaultLoggerManager];
112 perfLogger = [lm loggerForDefaultKey:@"WOProfileApplication"];
117 /* old license checks */
119 - (NSCalendarDate *)appExpireDate {
120 // TODO: can we remove that?
123 - (BOOL)isLicenseExpired {
124 // TODO: can we remove that?
130 - (NSString *)_lookupAppPath {
131 static NSString *suffix = nil;
132 static BOOL appPathMissing = NO;
141 ud = [NSUserDefaults standardUserDefaults];
143 // Check if appPath has been forced
144 result = [ud stringForKey:@"WOProjectDirectory"];
149 suffix = [ud stringForKey:@"WOApplicationSuffix"];
151 fm = [NSFileManager defaultManager];
152 cwd = [fm currentDirectoryPath];
154 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
155 result = [[NGBundle mainBundle] bundlePath];
156 //NSLog(@"%s: check path '%@'", __PRETTY_FUNCTION__, result);
161 if ([result hasSuffix:suffix]) {
162 /* started app inside of .woa directory */
163 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
164 result = [[NGBundle mainBundle] bundlePath];
170 NSString *wrapperName;
172 wrapperName = [self->name stringByAppendingString:suffix];
174 /* take a look whether ./AppName.woa exists */
175 result = [result stringByAppendingPathComponent:wrapperName];
176 if (![fm fileExistsAtPath:result]) {
177 /* lookup in process-path */
183 pi = [NSProcessInfo processInfo];
184 env = [pi environment];
185 if ([env objectForKey:@"GNUSTEP_SYSTEM_ROOT"] != nil) {
186 isFlattened = [[[env objectForKey:@"GNUSTEP_FLATTENED"]
187 lowercaseString] isEqualToString:@"yes"];
189 else /* default to flattened if no GNUstep runtime is set */
192 ppath = [[pi arguments] objectAtIndex:0];
193 ppath = [ppath stringByDeletingLastPathComponent]; // del exe-name
196 ppath = [ppath stringByDeletingLastPathComponent]; // lib-combo
197 ppath = [ppath stringByDeletingLastPathComponent]; // os
198 ppath = [ppath stringByDeletingLastPathComponent]; // cpu
200 if ([ppath hasSuffix:suffix])
205 if (![fm fileExistsAtPath:result]) {
206 [self debugWithFormat:@"%s: missing path '%@'",
207 __PRETTY_FUNCTION__, result];
208 appPathMissing = YES;
215 + (NSString *)defaultRequestHandlerClassName {
216 return @"WOComponentRequestHandler";
219 - (void)_logDefaults {
225 ud = [NSUserDefaults standardUserDefaults];
226 keys = [[ud dictionaryRepresentation] allKeys];
227 keys = [keys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
229 e = [keys objectEnumerator];
230 while((key = [e nextObject]) != nil) {
231 if ([key hasPrefix:@"WO"] || [key isEqualToString:@"NSProjectSearchPath"])
232 [self logWithFormat:@"[default]: %@ = %@",
234 [[ud objectForKey:key] description]];
238 - (id)initWithName:(NSString *)_name {
239 [WOApplication _initializeWOApp];
241 if ((self = [super init])) {
243 WORequestHandler *rh;
246 self->name = [_name copy];
248 ud = [NSUserDefaults standardUserDefaults];
250 [self setPageCacheSize:[ud integerForKey:@"WOPageCacheSize"]];
251 [self setPermanentPageCacheSize:
252 [ud integerForKey:@"WOPermanentPageCacheSize"]];
254 [self setPageRefreshOnBacktrackEnabled:
255 [[ud objectForKey:@"WOPageRefreshOnBacktrack"] boolValue]];
257 [self setCachingEnabled:[WOApplication isCachingEnabled]];
259 /* setup request handlers */
261 self->defaultRequestHandler =
262 [[NSClassFromString([[self class] defaultRequestHandlerClassName])
265 self->requestHandlerRegistry =
266 NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 8);
268 rk = [WOApplication componentRequestHandlerKey];
270 rh = [[NSClassFromString(@"OWViewRequestHandler") alloc] init];
272 rh = [[NSClassFromString(@"WOComponentRequestHandler") alloc] init];
274 if ([rk length] > 0 && (rh != nil))
275 [self registerRequestHandler:rh forKey:rk];
276 [rh release]; rh = nil;
278 rk = [WOApplication directActionRequestHandlerKey];
279 rh = [[NSClassFromString(@"WODirectActionRequestHandler") alloc] init];
280 if ([rk length] > 0 && rh != nil)
281 [self registerRequestHandler:rh forKey:rk];
282 [rh release]; rh = nil;
284 if ((rh = [[NSClassFromString(@"WOResourceRequestHandler") alloc] init])) {
285 rk = [WOApplication resourceRequestHandlerKey];
287 [self registerRequestHandler:rh forKey:rk];
288 [self registerRequestHandler:rh forKey:@"WebServerResources"];
290 [self registerRequestHandler:rh forKey:@"Resources"];
292 [rh release]; rh = nil;
295 /* setup session store */
297 self->iSessionStore =
298 [[NSClassFromString([self sessionStoreClassName]) alloc] init];
300 /* setup statistics store */
302 self->iStatisticsStore = [[WOStatisticsStore alloc] init];
304 /* register timers */
305 self->expirationTimer =
306 [[NSTimer scheduledTimerWithTimeInterval:
307 [[ud objectForKey:@"WOExpirationTimeInterval"] intValue]
309 selector:@selector(performExpirationCheck:)
314 if ([ud boolForKey:@"WOLogDefaultsOnStartup"])
317 [[NSNotificationCenter defaultCenter]
318 postNotificationName:
319 WOApplicationWillFinishLaunchingNotification
326 return [self initWithName:[[[NSProcessInfo processInfo]
328 stringByDeletingPathExtension]];
332 [[NSNotificationCenter defaultCenter] removeObserver:self];
334 [self->expirationTimer invalidate];
336 if (self->requestHandlerRegistry)
337 NSFreeMapTable(self->requestHandlerRegistry);
339 [self->expirationTimer release];
340 [self->resourceManager release];
341 [self->iSessionStore release];
342 [self->defaultRequestHandler release];
343 [self->path release];
344 [self->name release];
345 [self->instanceNumber release];
349 - (void)processHupSignal:(int)_signal {
350 /* this isn't called immediatly */
351 [self logWithFormat:@"terminating on SIGHUP ..."];
360 - (BOOL)monitoringEnabled {
364 static BOOL missingPath = NO;
368 if (self->path == nil) {
369 if ((self->path = [[self _lookupAppPath] copy]) == nil) {
370 [self debugWithFormat:@"could not find wrapper of application !"];
378 - (NSString *)number {
379 if (self->instanceNumber == nil) {
382 if ((num = [[NSUserDefaults standardUserDefaults] objectForKey:@"n"])) {
383 self->instanceNumber = [[num stringValue] copy];
387 #if defined(__MINGW32__)
388 pid = (unsigned)GetCurrentProcessId();
390 pid = (unsigned)getpid();
392 self->instanceNumber = [[NSString alloc] initWithFormat:@"%d", pid];
395 return self->instanceNumber;
398 - (void)_setCurrentContext:(WOContext *)_ctx {
399 NSMutableDictionary *info;
401 info = [[NSThread currentThread] threadDictionary];
403 [info setObject:_ctx forKey:@"WOContext"];
405 [info removeObjectForKey:@"WOContext"];
407 - (WOContext *)context {
412 if ((t = [NSThread currentThread]) == nil) {
413 [self errorWithFormat:@"missing current thread !!!"];
416 if ((td = [t threadDictionary]) == nil) {
417 [self errorWithFormat:
418 @"missing current thread's dictionary (thread=%@) !!!",
423 return [td objectForKey:@"WOContext"];
426 /* request handlers */
428 - (void)registerRequestHandler:(WORequestHandler *)_hdl
429 forKey:(NSString *)_key
432 NSMapInsert(self->requestHandlerRegistry, _key, _hdl);
435 - (void)removeRequestHandlerForKey:(NSString *)_key {
436 if (_key == nil) return;
438 NSMapRemove(self->requestHandlerRegistry, _key);
442 - (void)setDefaultRequestHandler:(WORequestHandler *)_hdl {
444 ASSIGN(self->defaultRequestHandler, _hdl);
447 - (WORequestHandler *)defaultRequestHandler {
448 return self->defaultRequestHandler;
450 - (WORequestHandler *)requestHandlerForKey:(NSString *)_key {
451 WORequestHandler *handler;
454 handler = [(id)NSMapGet(self->requestHandlerRegistry, _key) retain];
456 handler = [[self defaultRequestHandler] retain];
459 return [handler autorelease];
462 - (NSArray *)registeredRequestHandlerKeys {
463 NSMutableArray *array = [NSMutableArray arrayWithCapacity:16];
466 WORequestHandler *handler;
469 e = NSEnumerateMapTable(self->requestHandlerRegistry);
470 while (NSNextMapEnumeratorPair(&e, (void**)&key, (void**)&handler))
471 [array addObject:key];
474 return [[array copy] autorelease];
477 - (WORequestHandler *)handlerForRequest:(WORequest *)_request {
478 WORequestHandler *handler;
481 if ((key = [_request requestHandlerKey]) == nil)
482 return [self defaultRequestHandler];
484 handler = NSMapGet(self->requestHandlerRegistry, key);
485 return (handler != nil) ? handler : [self defaultRequestHandler];
490 - (WOSession *)_initializeSessionInContext:(WOContext *)_ctx {
493 sn = [self createSessionForRequest:[_ctx request]];
494 [_ctx setSession:sn];
496 if ([sn respondsToSelector:@selector(prepare)]) {
498 [self debugWithFormat:@"calling -prepare on session .."];
500 [sn performSelector:@selector(prepare)];
503 [sn _awakeWithContext:_ctx];
505 [[NSNotificationCenter defaultCenter]
506 postNotificationName:WOSessionDidCreateNotification
508 return [sn autorelease];
511 - (NSString *)sessionIDFromRequest:(WORequest *)_request {
514 if (_request == nil) return nil;
516 /* first look into form values */
517 if ((sessionId = [_request formValueForKey:WORequestValueSessionID])!=nil) {
518 if ([sessionId length] > 0)
522 /* now look into the cookies */
523 if ((sessionId = [_request cookieValueForKey:[self name]]) != nil) {
524 if ([sessionId respondsToSelector:@selector(objectEnumerator)]) {
527 e = [(id)sessionId objectEnumerator];
528 while ((sessionId = [e nextObject]) != nil) {
529 if ([sessionId length] > 0 && ![sessionId isEqual:@"nil"])
534 if ([sessionId length] > 0 && ![sessionId isEqual:@"nil"])
542 - (NSString *)createSessionIDForSession:(WOSession *)_session {
543 /* session id must be 18 chars long for snsd to work ! */
544 static unsigned int sessionCount = 0;
546 unsigned char buf[20];
549 sprintf(buf, "%04X%04X%02X%08X",
550 [[self number] intValue], getpid(), sessionCount,
551 (unsigned int)time(NULL));
552 wosid = [NSString stringWithCString:buf];
556 - (WOSession *)createSessionForRequest:(WORequest *)_request {
557 if ([self respondsToSelector:@selector(createSession)]) {
558 /* call deprecated method */
559 [self warnWithFormat:@"calling deprecated -createSession .."];
560 return [self createSession];
565 if ((snClass = NSClassFromString(@"Session")) == Nil)
566 snClass = [WOSession class];
568 return [[snClass alloc] init];
572 - (WOSession *)restoreSessionWithID:(NSString *)_sid
573 inContext:(WOContext *)_ctx
579 if ([self respondsToSelector:@selector(restoreSession)]) {
580 /* call deprecated method */
581 [self warnWithFormat:@"calling deprecated -restoreSession .."];
582 return [self restoreSession];
586 WOSessionStore *store;
588 if ((store = [self sessionStore]) == nil) {
589 [self errorWithFormat:@"missing session store ..."];
592 session = [store restoreSessionWithID:_sid request:[_ctx request]];
594 [_ctx setSession:session];
595 [session _awakeWithContext:_ctx];
598 [self debugWithFormat:@"did not find a session for sid '%@'", _sid];
605 [[NSNotificationCenter defaultCenter]
606 postNotificationName:WOSessionDidRestoreNotification
610 if ([_sid hasPrefix:@"("]) {
613 sid = [_sid propertyList];
615 if ([sid respondsToSelector:@selector(objectEnumerator)]) {
618 [self errorWithFormat:@"got multiple session IDs !"];
620 e = [sid objectEnumerator];
621 while ((_sid = [e nextObject])) {
622 if ([_sid isEqualToString:@"nil"])
625 if ((session = [self restoreSessionWithID:_sid inContext:_ctx]))
628 //[self warnWithFormat:@"did not find session for sid %@", _sid);
635 - (void)saveSessionForContext:(WOContext *)_ctx {
636 NSTimeInterval startSave = 0.0;
639 startSave = [[NSDateClass date] timeIntervalSince1970];
641 if ([self respondsToSelector:@selector(saveSession:)]) {
642 /* call deprecated method */
643 [self warnWithFormat:@"calling deprecated -saveSession: .."];
644 [self saveSession:[_ctx session]];
650 NSTimeInterval startSnSleep = 0.0, startStore = 0.0;
655 startSnSleep = [[NSDateClass date] timeIntervalSince1970];
657 /* put session to sleep */
658 [sn _sleepWithContext:_ctx];
662 rt = [[NSDateClass date] timeIntervalSince1970] - startSnSleep;
663 [perfLogger logWithFormat:@"[woapp]: session -sleep took %4.3fs.",
664 rt < 0.0 ? -1.0 : rt];
667 if ([sn isTerminating]) {
668 [[NSNotificationCenter defaultCenter]
669 postNotificationName:
670 WOSessionDidTerminateNotification
675 startStore = [[NSDateClass date] timeIntervalSince1970];
677 [[self sessionStore] saveSessionForContext:_ctx];
681 rt = [[NSDateClass date] timeIntervalSince1970] - startStore;
682 [perfLogger logWithFormat:@"[woapp]: storing sn in store took %4.3fs.",
683 rt < 0.0 ? -1.0 : rt];
690 rt = [[NSDateClass date] timeIntervalSince1970] - startSave;
691 [perfLogger logWithFormat:@"[woapp]: saveSessionForContext took %4.3fs.",
692 rt < 0.0 ? -1.0 : rt];
696 - (void)refuseNewSessions:(BOOL)_flag {
697 self->appFlags.doesRefuseNewSessions = _flag ? 1 : 0;
699 - (BOOL)isRefusingNewSessions {
700 return self->appFlags.doesRefuseNewSessions;
702 - (int)activeSessionsCount {
703 return [[self sessionStore] activeSessionsCount];
706 - (void)setSessionStore:(WOSessionStore *)_store {
707 ASSIGN(self->iSessionStore, _store);
709 - (NSString *)sessionStoreClassName {
710 return [[NSUserDefaults standardUserDefaults] stringForKey:@"WOSessionStore"];
712 - (WOSessionStore *)sessionStore {
713 return self->iSessionStore;
716 - (void)setMinimumActiveSessionsCount:(int)_minimum {
717 self->minimumActiveSessionsCount = _minimum;
719 - (int)minimumActiveSessionsCount {
720 return self->minimumActiveSessionsCount;
723 - (void)performExpirationCheck:(NSTimer *)_timer {
726 /* let session store check for expiration ... */
728 ss = [self sessionStore];
729 if ([ss respondsToSelector:@selector(performExpirationCheck:)])
730 [ss performExpirationCheck:_timer];
732 /* check whether application should terminate ... */
734 if ([self isRefusingNewSessions] &&
735 ([self activeSessionsCount] < [self minimumActiveSessionsCount])) {
736 /* check whether the application instance is still valid .. */
737 [self debugWithFormat:
738 @"application terminates because it refuses new sessions and "
739 @"the active session count (%i) is below the minimum (%i).",
740 [self activeSessionsCount], [self minimumActiveSessionsCount]];
745 - (WOSession *)session {
746 return [[self context] session];
749 - (WOResponse *)handleSessionCreationErrorInContext:(WOContext *)_ctx {
750 WOResponse *response = [_ctx response];
754 pid = GetCurrentProcessId();
759 if ([self respondsToSelector:@selector(handleSessionCreationError)]) {
760 [self warnWithFormat:@"called deprecated -handleSessionCreationError method"];
761 return [self handleSessionCreationError];
764 [self errorWithFormat:@"could not create session for context %@", _ctx];
766 [response setStatus:200];
767 [response appendContentString:@"<h4>Session Creation Error</h4>\n<pre>"];
768 [response appendContentString:
769 @"Application Instance failed to create session."];
770 [response appendContentHTMLString:
771 [NSString stringWithFormat:
772 @" application: %@\n"
779 [[_ctx request] adaptorPrefix],
783 [[_ctx request] description]]];
784 [response appendContentString:@"</pre>"];
788 - (WOResponse *)handleSessionRestorationErrorInContext:(WOContext *)_ctx {
789 if ([self respondsToSelector:@selector(handleSessionRestorationError)]) {
790 [self warnWithFormat:@"calling deprecated "
791 @"-handleSessionRestorationError method"];
792 return [self handleSessionRestorationError];
795 [self errorWithFormat:@"could not restore session for context %@", _ctx];
801 - (void)setStatisticsStore:(WOStatisticsStore *)_statStore {
802 ASSIGN(self->iStatisticsStore, _statStore);
804 - (WOStatisticsStore *)statisticsStore {
805 return self->iStatisticsStore;
808 - (bycopy NSDictionary *)statistics {
809 return [[self statisticsStore] statistics];
814 - (void)setResourceManager:(WOResourceManager *)_manager {
815 ASSIGN(self->resourceManager, _manager);
817 - (WOResourceManager *)resourceManager {
818 if (self->resourceManager == nil) {
822 if ([(p = [self path]) length] > 0)
823 [self debugWithFormat:@"setup WOResourceManager at path '%@' ...", p];
828 self->resourceManager =
829 [(WOResourceManager *)[WOResourceManager alloc] initWithPath:p];
831 return self->resourceManager;
836 WOContext *ctx = [self context];
838 n = [[ctx request] applicationName];
839 n = [@"/" stringByAppendingString:n ? n : [self name]];
841 return [NSURL URLWithString:n relativeToURL:[ctx baseURL]];
844 - (NSString *)pathForResourceNamed:(NSString *)_name ofType:(NSString *)_type {
846 return [[self resourceManager] pathForResourceNamed:_name ofType:_type];
849 - (NSString *)stringForKey:(NSString *)_key
850 inTableNamed:(NSString *)_tableName
851 withDefaultValue:(NSString *)_default
854 return [[self resourceManager] stringForKey:_key
855 inTableNamed:_tableName
856 withDefaultValue:_default
858 [(WOSession *)[self session] languages]];
870 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
871 if ([_ctx hasSession])
872 [[_ctx session] takeValuesFromRequest:_req inContext:_ctx];
876 if ((page = [_ctx page])) {
877 WOContext_enterComponent(_ctx, page, nil);
878 [page takeValuesFromRequest:_req inContext:_ctx];
879 WOContext_leaveComponent(_ctx, page);
884 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
887 if ([_ctx hasSession])
888 result = [[_ctx session] invokeActionForRequest:_rq inContext:_ctx];
892 if ((page = [_ctx page])) {
893 WOContext_enterComponent(_ctx, page, nil);
894 result = [[_ctx page] invokeActionForRequest:_rq inContext:_ctx];
895 WOContext_leaveComponent(_ctx, page);
903 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
904 if ([_ctx hasSession])
905 [[_ctx session] appendToResponse:_response inContext:_ctx];
909 if ((page = [_ctx page])) {
910 WOContext_enterComponent(_ctx, page, nil);
911 [page appendToResponse:_response inContext:_ctx];
912 WOContext_leaveComponent(_ctx, page);
916 if(rapidTurnAroundPath != nil) {
919 if((page = [_ctx page])) {
922 template = [page _woComponentTemplate];
923 if([template isKindOfClass:WOTemplateClass]) {
926 _path = [[(WOTemplate *)template url] path];
927 [_response setHeader:_path
928 forKey:@"x-sope-template-path"];
937 - (WOElement *)dynamicElementWithName:(NSString *)_name
938 associations:(NSDictionary *)_associations
939 template:(WOElement *)_template
940 languages:(NSArray *)_languages
942 WOElement *element = nil;
943 Class dynamicElementClass = NSClassFromString(_name);
945 if (dynamicElementClass == Nil) {
946 [self warnWithFormat:@"did not find dynamic element class %@ !", _name];
949 if (![dynamicElementClass isDynamicElement]) {
950 [self warnWithFormat:@"class %@ is not a dynamic element class !", _name];
954 element = [[dynamicElementClass allocWithZone:[_template zone]]
956 associations:_associations
960 - (WOElement *)dynamicElementWithName:(NSString *)_name
961 associations:(NSDictionary *)_associations
962 template:(WOElement *)_template
964 return [self dynamicElementWithName:_name
965 associations:_associations
967 languages:[(WOSession *)[self session] languages]];
972 - (void)setPageRefreshOnBacktrackEnabled:(BOOL)_flag {
973 self->appFlags.isPageRefreshOnBacktrackEnabled = _flag ? 1 : 0;
975 - (BOOL)isPageRefreshOnBacktrackEnabled {
976 return self->appFlags.isPageRefreshOnBacktrackEnabled ? YES : NO;
979 - (void)setCachingEnabled:(BOOL)_flag {
980 self->appFlags.isCachingEnabled = _flag ? 1 : 0;
982 - (BOOL)isCachingEnabled {
983 // component definition caching
984 return self->appFlags.isCachingEnabled ? YES : NO;
987 - (void)setPageCacheSize:(int)_size {
988 self->pageCacheSize = _size;
990 - (int)pageCacheSize {
991 return self->pageCacheSize;
993 - (void)setPermanentPageCacheSize:(int)_size {
994 self->permanentPageCacheSize = _size;
996 - (int)permanentPageCacheSize {
997 return self->permanentPageCacheSize;
1000 - (WOComponent *)pageWithName:(NSString *)_name {
1001 // deprecated in WO4
1002 return [self pageWithName:_name inContext:[self context]];
1005 - (WOComponent *)_pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1007 OSX profiling: 3.4% of dispatchRequest?
1008 3.0% rm -pageWithName..
1009 1.5% def instantiate
1010 1.3% initWithName:...
1011 0.76% initWithContent:.. (0.43 addobserver)
1012 0.76% rm defForComp (0.43% touch)
1014 0.11% ctx -component
1019 NSAutoreleasePool *pool;
1020 WOResourceManager *rm;
1023 NSDictionary *start, *stop;
1024 start = [self memoryStatistics];
1027 pool = [[NSAutoreleasePool alloc] init];
1029 languages = [_ctx hasSession]
1030 ? [(WOSession *)[_ctx session] languages]
1031 : [[_ctx request] browserLanguages];
1033 if ((rm = [[_ctx component] resourceManager]) == nil)
1034 rm = [self resourceManager];
1036 page = [rm pageWithName:(_name != nil ? _name : @"Main")
1037 languages:languages];
1038 [page ensureAwakeInContext:_ctx];
1040 page = [page retain];
1045 int rss, vmsize, lib;
1046 stop = [self memoryStatistics];
1047 rss = [[stop objectForKey:@"VmRSS"] intValue] -
1048 [[start objectForKey:@"VmRSS"] intValue];
1049 vmsize = [[stop objectForKey:@"VmSize"] intValue] -
1050 [[start objectForKey:@"VmSize"] intValue];
1051 lib = [[stop objectForKey:@"VmLib"] intValue] -
1052 [[start objectForKey:@"VmLib"] intValue];
1053 [self debugWithFormat:@"loaded component %@; rss=%i vm=%i lib=%i.",
1054 _name, rss,vmsize,lib];
1058 return [page autorelease];
1060 - (WOComponent *)pageWithName:(NSString *)_name inContext:(WOContext *)_ctx {
1061 return [self _pageWithName:_name inContext:_ctx];
1063 - (WOComponent *)pageWithName:(NSString *)_name forRequest:(WORequest *)_req {
1064 WOResourceManager *rm;
1066 if ((rm = [self resourceManager]) == nil)
1069 return [rm pageWithName:(_name != nil) ? _name : @"Main"
1070 languages:[_req browserLanguages]];
1073 - (void)savePage:(WOComponent *)_page {
1075 [[[self context] session] savePage:_page];
1077 - (id)restorePageForContextID:(NSString *)_ctxId {
1079 return [[[self context] session] restorePageForContextID:_ctxId];
1082 - (WOResponse *)handlePageRestorationErrorInContext:(WOContext *)_ctx {
1083 [self errorWithFormat:
1084 @"could not restore page for context-id %@\n in context %@",
1085 [_ctx currentElementID], _ctx];
1087 /* return main page ... */
1088 return [[self pageWithName:nil inContext:_ctx] generateResponse];
1090 - (WOResponse *)handlePageRestorationError {
1092 return [self handlePageRestorationErrorInContext:[self context]];
1097 - (WOResponse *)handleException:(NSException *)_exc
1098 inContext:(WOContext *)_ctx
1100 WORequest *rq = [_ctx request];
1101 WOResponse *r = nil;
1103 if ([self respondsToSelector:@selector(handleException:)]) {
1104 [self warnWithFormat:@"calling deprecated -handleException method !"];
1105 return [self handleException:_exc];
1110 static int doCore = -1;
1112 doCore = [[NSUserDefaults standardUserDefaults]
1113 boolForKey:@"WOCoreOnApplicationException"]
1117 [self fatalWithFormat:@"%@: caught (ctx=%@):\n %@.",
1125 [self fatalWithFormat:@"%@: caught (without context):\n %@.",
1129 else if (rq == nil) {
1130 [self fatalWithFormat:@"%@: caught (without request):\n %@.",
1135 static NSString *pageFormat =
1136 @"Application Server caught exception:\n\n"
1145 @" backtrace:\n%@\n";
1146 NSString *str = nil;
1149 [self errorWithFormat:@"%@: caught:\n %@\nin context:\n %@.",
1152 #if LIB_FOUNDATION_LIBRARY
1153 if ([NSException respondsToSelector:@selector(backtrace)])
1154 bt = [NSException backtrace];
1157 if ((r = [WOResponse responseWithRequest:rq]) == nil)
1158 [self errorWithFormat:@"could not create response !"];
1160 [r setHeader:@"text/html" forKey:@"content-type"];
1161 [r setHeader:@"no-cache" forKey:@"cache-control"];
1162 if(rapidTurnAroundPath != nil) {
1165 templateURL = [[_exc userInfo] objectForKey:@"templateURL"];
1166 if(templateURL != nil)
1167 [r setHeader:[templateURL path] forKey:@"x-sope-template-path"];
1170 str = [NSString stringWithFormat:pageFormat,
1172 ? [[_ctx session] sessionID]
1177 NSStringFromClass([_exc class]),
1180 [[_exc userInfo] description],
1183 [r appendContentString:@"<html><head><title>Caught exception</title></head><body><pre>\n"];
1184 [r appendContentHTMLString:str];
1185 [r appendContentString:@"</pre></body></html>\n"];
1192 - (BOOL)shouldTerminate {
1193 if (![self isRefusingNewSessions])
1195 if ([self activeSessionsCount] >= [self minimumActiveSessionsCount])
1198 /* check whether the application instance is still valid .. */
1199 [self debugWithFormat:
1200 @"application terminates because it refuses new sessions and "
1201 @"the active session count (%i) is below the minimum (%i).",
1202 [self activeSessionsCount], [self minimumActiveSessionsCount]];
1207 [self debugWithFormat:
1208 @"application terminates:\n"
1209 @" %i active sessions\n"
1210 @" %i minimum active sessions\n"
1211 @" refuses new session: %s",
1212 [self activeSessionsCount],
1213 [self minimumActiveSessionsCount],
1214 [self isRefusingNewSessions] ? "yes" : "no"];
1220 - (BOOL)isDebuggingEnabled {
1223 - (NSString *)loggingPrefix {
1224 return [NSString stringWithFormat:@"|%@%@|",
1226 [self isTerminating] ? @" terminating" : @""];
1231 #if !LIB_FOUNDATION_LIBRARY
1232 - (id)valueForUndefinedKey:(NSString *)_key {
1233 [self warnWithFormat:@"tried to access undefined KVC key: '%@'",
1241 + (Class)eoEditingContextClass {
1242 static Class eoEditingContextClass = Nil;
1243 static BOOL lookedUpForEOEditingContextClass = NO;
1245 if (!lookedUpForEOEditingContextClass) {
1246 eoEditingContextClass = NSClassFromString(@"EOEditingContext");
1247 lookedUpForEOEditingContextClass = YES;
1249 return eoEditingContextClass;
1252 + (BOOL)implementsEditingContexts {
1253 return [self eoEditingContextClass] != NULL ? YES : NO;
1258 - (NSString *)description {
1259 return [NSString stringWithFormat:@"<%@[0x%08X]: name=%@%@>",
1260 NSStringFromClass([self class]), self,
1262 [self isTerminating] ? @" terminating" : @""
1266 @end /* WOApplication */