2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include <NGObjWeb/WOCoreApplication.h>
24 #include <NGObjWeb/WOAdaptor.h>
25 #include <NGObjWeb/WORequest.h>
26 #include <NGObjWeb/WORequestHandler.h>
27 #include <NGObjWeb/WOResponse.h>
28 #include <NGObjWeb/WOContext.h>
29 #include <EOControl/EOControl.h>
30 #include "WORunLoop.h"
33 #if LIB_FOUNDATION_LIBRARY
34 # import <Foundation/UnixSignalHandler.h>
36 # include "UnixSignalHandler.h"
39 NGObjWeb_DECLARE NSString *WOApplicationDidFinishLaunchingNotification =
40 @"WOApplicationDidFinishLaunching";
41 NGObjWeb_DECLARE NSString *WOApplicationWillFinishLaunchingNotification =
42 @"WOApplicationWillFinishLaunching";
43 NGObjWeb_DECLARE NSString *WOApplicationWillTerminateNotification =
44 @"WOApplicationWillTerminate";
45 NGObjWeb_DECLARE NSString *WOApplicationDidTerminateNotification =
46 @"WOApplicationDidTerminate";
48 @interface WOCoreApplication(PrivateMethods)
49 + (void)_initDefaults;
50 - (NSDictionary *)memoryStatistics;
51 - (WOResponse *)handleException:(NSException *)_exc;
54 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
55 @interface NSObject(KVCWarn)
56 + (void)suppressCapitalizedKeyWarning;
57 - (void)notImplemented:(SEL)cmd;
61 @implementation WOCoreApplication
63 static BOOL perflog = NO;
64 static BOOL outputValidateOn = NO;
65 static Class NSDateClass = Nil;
71 NGObjWeb_DECLARE id WOApp = nil;
72 static NSMutableArray *activeApps = nil; // THREAD
76 NSLog(@"WARNING(%s): some code called +application without "
77 @"an active app !", __PRETTY_FUNCTION__);
79 # warning REMOVE THAT ABORT IN PRODUCTION CODE !!!
85 - (void)activateApplication {
87 if (activeApps == nil)
88 activeApps = [[NSMutableArray alloc] init];
89 [activeApps addObject:WOApp];
93 - (void)deactivateApplication {
98 @"tried to deactivate inactive application !\n"
107 if ((idx = [activeApps count]) > 0) {
109 WOApp = [[activeApps objectAtIndex:idx] retain];
110 [activeApps removeObjectAtIndex:idx];
114 + (void)_initializeClass {
116 this must be called in -init, since the environment is not setup
117 properly if this is called first.
119 static BOOL didInit = NO;
123 [self _initDefaults];
125 ud = [NSUserDefaults standardUserDefaults];
126 perflog = [ud boolForKey:@"WOProfileApplication"];
127 outputValidateOn = [ud boolForKey:@"WOOutputValidationEnabled"];
128 NSDateClass = [NSDate class];
132 [[self class] _initializeClass];
133 #if COCOA_Foundation_LIBRARY
135 NSKeyBinding Warning: <Application 0xc1f70> was accessed using a capitalized key
136 'NSFileSubject'. Keys should normally start with a lowercase character. A
137 typographical error like this could cause a crash or an infinite loop. Use
138 +[NSKeyBinding suppressCapitalizedKeyWarning] to suppress this warning.
140 [NSClassFromString(@"NSKeyBinding") suppressCapitalizedKeyWarning];
143 if ((self = [super init])) {
144 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
146 [self activateApplication];
148 if ([[ud objectForKey:@"WORunMultithreaded"] boolValue]) {
149 self->lock = [[NSRecursiveLock alloc] init];
150 self->requestLock = [[NSLock alloc] init];
154 #if !defined(__MINGW32__) && !defined(NeXT_Foundation_LIBRARY)
156 UnixSignalHandler *us = [UnixSignalHandler sharedHandler];
158 [us addObserver:self selector:@selector(terminateOnSignal:)
159 forSignal:SIGTERM immediatelyNotifyOnSignal:YES];
160 [us addObserver:self selector:@selector(terminateOnSignal:)
161 forSignal:SIGINT immediatelyNotifyOnSignal:YES];
162 [us addObserver:self selector:@selector(terminateOnSignal:)
163 forSignal:SIGQUIT immediatelyNotifyOnSignal:YES];
164 [us addObserver:self selector:@selector(terminateOnSignal:)
165 forSignal:SIGILL immediatelyNotifyOnSignal:YES];
167 [us addObserver:self selector:@selector(processHupSignal:)
168 forSignal:SIGHUP immediatelyNotifyOnSignal:NO];
176 #if !defined(__MINGW32__) && !defined(NeXT_Foundation_LIBRARY)
177 [[UnixSignalHandler sharedHandler] removeObserver:self];
179 [[NSNotificationCenter defaultCenter] removeObserver:self];
180 [self->adaptors release];
181 [self->requestLock release];
182 [self->lock release];
188 - (void)processHupSignal:(int)_signal {
189 /* this isn't called immediatly */
192 - (void)terminateOnSignal:(int)_signal {
193 /* STDIO is forbidden in signal handlers !!! no malloc !!! */
195 self->cappFlags.isTerminating = 1;
197 static int termCount = 0;
201 pid = (unsigned)GetCurrentProcessId();
203 pid = (unsigned)getpid();
206 if ([self isTerminating]) {
212 fprintf(stderr, "%d: forcing termination because of signal %i\n",
220 fprintf(stderr, "%i: terminating because of signal %i\n", pid, _signal);
229 - (NSArray *)adaptors {
230 return self->adaptors;
232 - (WOAdaptor *)adaptorWithName:(NSString *)_name
233 arguments:(NSDictionary *)_args
235 Class adaptorClass = Nil;
236 WOAdaptor *adaptor = nil;
238 adaptorClass = NSClassFromString(_name);
239 if (adaptorClass == Nil) {
240 [self logWithFormat:@"ERROR: did not find adaptor class %@", _name];
244 adaptor = [[adaptorClass allocWithZone:[self zone]]
249 return [adaptor autorelease];
252 - (BOOL)allowsConcurrentRequestHandling {
255 - (BOOL)adaptorsDispatchRequestsConcurrently {
259 /* request recording */
261 - (void)setRecordingPath:(NSString *)_path {
262 [self notImplemented:_cmd];
264 - (NSString *)recordingPath {
265 static NSString *rp = nil;
267 rp = [[[NSUserDefaults standardUserDefaults]
268 stringForKey:@"WORecordingPath"]
277 return NSStringFromClass([self class]);
280 - (WOResponse *)handleException:(NSException *)_exc
281 inContext:(WOContext *)_ctx
283 WORequest *rq = [_ctx request];
286 if ([self respondsToSelector:@selector(handleException:)]) {
287 NSLog(@"WARNING: calling deprecated -handleException method !");
288 return [self handleException:_exc];
292 [self logWithFormat:@"%@: caught (without context):\n %@.", self, _exc];
297 [self logWithFormat:@"%@: caught (without context):\n %@.", self, _exc];
300 else if (rq == nil) {
301 [self logWithFormat:@"%@: caught (without request):\n %@.", self, _exc];
305 [self logWithFormat:@"%@: caught:\n %@\nin context:\n %@.",
310 if ((r = [WOResponse responseWithRequest:rq]) == nil)
311 [self logWithFormat:@"could not create response !"];
326 return [self->lock tryLock];
329 - (void)lockRequestHandling {
330 [self->requestLock lock];
332 - (void)unlockRequestHandling {
333 [self->requestLock unlock];
345 - (void)_loadAdaptors {
346 NSMutableArray *ads = nil;
350 args = [[NSProcessInfo processInfo] arguments];
352 for (i = 0, count = [args count]; i < count; i++) {
355 arg = [args objectAtIndex:i];
357 if ([arg isEqualToString:@"-a"] && ((i + 1) < count)) {
359 NSString *adaptorName = nil;
360 NSMutableDictionary *arguments = nil;
361 WOAdaptor *adaptor = nil;
363 i++; // skip '-a' option
364 adaptorName = [args objectAtIndex:i];
365 i++; // skip adaptor name
367 if (i < count) { // search for arguments
370 arguments = [NSMutableDictionary dictionaryWithCapacity:10];
371 for (; i < count; i++) {
372 arg = [args objectAtIndex:i];
373 if ([arg isEqualToString:@"-a"] ||
374 [arg isEqualToString:@"-c"] ||
375 [arg isEqualToString:@"-d"]) {
382 [arguments setObject:arg forKey:key];
388 adaptor = [self adaptorWithName:adaptorName
389 arguments:[[arguments copy] autorelease]];
391 if (ads == nil) ads = [[NSMutableArray alloc] initWithCapacity:8];
392 [ads addObject:adaptor];
397 self->adaptors = [ads copy];
398 [ads release]; ads = nil;
400 if ([self->adaptors count] == 0) {
402 NSArray *moreAdaptors;
404 defaultAdaptor = [[self class] adaptor];
405 defaultAdaptor = [self adaptorWithName:defaultAdaptor arguments:nil];
406 if (defaultAdaptor) {
407 self->adaptors = [[NSArray alloc] initWithObjects:defaultAdaptor, nil];
410 moreAdaptors = [[self class] additionalAdaptors];
411 if ([moreAdaptors count] > 0) {
413 NSMutableArray *newArray;
417 for (i = 0, count = [moreAdaptors count]; i < count; i++) {
420 adaptor = [self adaptorWithName:[moreAdaptors objectAtIndex:i]
422 if (adaptor == nil) {
423 [self logWithFormat:@"could not find WOAdaptor '%@' !",
424 [moreAdaptors objectAtIndex:i]];
428 if (newArray == nil) {
429 newArray = [self->adaptors mutableCopy];
430 [newArray addObject:adaptor];
433 [self->adaptors release];
434 self->adaptors = [newArray shallowCopy];
435 [newArray release]; newArray = nil;
440 - (void)_setupAdaptors {
445 if ([self->adaptors count] == 0)
446 [self _loadAdaptors];
448 ads = [self->adaptors objectEnumerator];
449 while ((adaptor = [ads nextObject]))
450 [adaptor registerForEvents];
452 - (void)_tearDownAdaptors {
453 // unregister adaptors
457 ads = [self->adaptors objectEnumerator];
458 while ((adaptor = [ads nextObject]))
459 [adaptor unregisterForEvents];
461 [self->adaptors release]; self->adaptors = nil;
464 - (NSRunLoop *)runLoop { // deprecated in WO4
466 return [self mainThreadRunLoop];
468 - (NSRunLoop *)mainThreadRunLoop {
469 // wrong, should remove main thread runloop
470 return [WORunLoop currentRunLoop];
473 - (BOOL)shouldTerminate {
477 [self activateApplication];
479 [self _setupAdaptors];
481 [[NSNotificationCenter defaultCenter]
482 postNotificationName:
483 WOApplicationDidFinishLaunchingNotification
486 [self deactivateApplication];
488 while (![self isTerminating]) {
489 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
491 if ([self shouldTerminate]) {
492 /* check whether we should still process requests */
497 NSDate *limitDate = nil;
499 loop = [self mainThreadRunLoop];
501 limitDate = [loop limitDateForMode:NSDefaultRunLoopMode];
503 if ([self isTerminating])
506 [self activateApplication];
507 [loop runMode:NSDefaultRunLoopMode beforeDate:limitDate];
508 [self deactivateApplication];
514 [self debugWithFormat:@"application finished runloop."];
516 [self activateApplication];
518 [[NSNotificationCenter defaultCenter]
519 postNotificationName:
520 WOApplicationWillTerminateNotification
523 [self _tearDownAdaptors];
525 self->cappFlags.isTerminating = 1;
527 [[NSNotificationCenter defaultCenter]
528 postNotificationName:
529 WOApplicationDidTerminateNotification
532 [self deactivateApplication];
536 self->cappFlags.isTerminating = 1;
538 - (BOOL)isTerminating {
539 return self->cappFlags.isTerminating ? YES : NO;
542 - (void)_terminateNow:(id)_dummy {
545 - (void)terminateAfterTimeInterval:(NSTimeInterval)_interval {
546 [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(_terminateNow:)
547 target:self argument:nil];
548 [self performSelector:@selector(_terminateNow:) withObject:nil
549 afterDelay:_interval];
552 /* output validation */
554 - (void)setPrintsHTMLParserDiagnostics:(BOOL)_flag {
555 [[NSUserDefaults standardUserDefaults] setBool:_flag
556 forKey:@"WOOutputValidationEnabled"];
557 outputValidateOn = _flag;
559 - (BOOL)printsHTMLParserDiagnostics {
560 return outputValidateOn;
563 - (void)_logWarningOnOutputValidation {
564 static BOOL didWarn = NO;
568 @"WARNING: output validation is enabled, this will "
569 @"slow down request processing!"];
574 - (BOOL)hideValidationIssue:(NSException *)_issue {
575 /* to deal with some non-standard HTML ... */
579 - (void)validateOutputOfResponse:(WOResponse *)_response {
584 [self _logWarningOnOutputValidation];
586 if (_response == nil) {
587 [self logWithFormat:@"validate-output: no response returned by handler."];
591 if (![_response respondsToSelector:@selector(validateContent)]) {
592 [self logWithFormat:@"response does not support content validation!"];
595 if ((issues = [_response validateContent]) == nil)
598 e = [issues objectEnumerator];
599 while ((issue = [e nextObject])) {
600 if ([issue isKindOfClass:[NSException class]]) {
601 if ([self hideValidationIssue:issue])
603 [self logWithFormat:@"validate-output[%@]: %@",
604 [(NSException *)issue name], [issue reason]];
608 [self logWithFormat:@"validate-output: %@", [issue stringValue]];
612 /* request handling */
614 - (WORequestHandler *)handlerForRequest:(WORequest *)_request {
618 - (WOResponse *)dispatchRequest:(WORequest *)_request
619 usingHandler:(WORequestHandler *)handler
621 WOResponse *response = nil;
622 NSTimeInterval startDispatch = 0.0;
625 startDispatch = [[NSDateClass date] timeIntervalSince1970];
627 /* let request handler process the request */
629 NSTimeInterval startDispatch = 0.0;
632 startDispatch = [[NSDateClass date] timeIntervalSince1970];
635 response = [handler handleRequest:_request];
639 rt = [[NSDateClass date] timeIntervalSince1970] - startDispatch;
640 NSLog(@" [woapp-rq]: request handler took %4.3fs.",
641 rt < 0.0 ? -1.0 : rt);
645 response = [[response retain] autorelease];
647 if (outputValidateOn)
648 [self validateOutputOfResponse:response];
652 rt = [[NSDateClass date] timeIntervalSince1970] - startDispatch;
653 NSLog(@"[woapp]: dispatchRequest took %4.3fs.",
654 rt < 0.0 ? -1.0 : rt);
659 - (WOResponse *)dispatchRequest:(WORequest *)_request {
660 WORequestHandler *handler;
662 if ([self respondsToSelector:@selector(handleRequest:)]) {
664 @"WARNING: calling deprecated -handleRequest: method .."];
665 return [self handleRequest:_request];
668 /* find request handler for request */
669 if ((handler = [self handlerForRequest:_request]) == nil) {
670 [self logWithFormat:@"ERROR: got no request handler for request: %@ !",
675 return [self dispatchRequest:_request usingHandler:handler];
680 - (NSString *)description {
681 return [NSString stringWithFormat:@"<%@[0x%08X]: %@>",
682 NSStringFromClass([self class]), self,
683 [self isTerminating] ? @" terminating" : @""
687 @end /* WOCoreApplication */
689 @implementation WOCoreApplication(Defaults)
691 + (void)_initDefaults {
692 static BOOL didInit = NO;
693 #if !COMPILE_AS_FRAMEWORK
700 NSDictionary *owDefaults = nil;
706 #if COMPILE_AS_FRAMEWORK
707 bundle = [NSBundle bundleForClass:[WOCoreApplication class]];
708 apath = [bundle pathForResource:@"Defaults" ofType:@"plist"];
711 NSLog(@"ERROR: cannot find Defaults.plist "
712 @"resource of NGObjWeb framework (%@) !", bundle);
715 fm = [NSFileManager defaultManager];
716 env = [[NSProcessInfo processInfo] environment];
719 TODO: the following is a dirty hack because GNUstep people decided
720 to change the directory structure. SIGH!
722 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY || GNUSTEP_BASE_LIBRARY
723 relPath = @"Library/Libraries";
725 relPath = @"Libraries";
727 relPath = [relPath stringByAppendingPathComponent:@"Resources"];
728 relPath = [relPath stringByAppendingPathComponent:@"NGObjWeb"];
729 relPath = [relPath stringByAppendingPathComponent:@"Defaults.plist"];
731 apath = [env objectForKey:@"GNUSTEP_USER_ROOT"];
732 apath = [apath stringByAppendingPathComponent:relPath];
733 if (![fm fileExistsAtPath:apath]) {
734 apath = [env objectForKey:@"GNUSTEP_LOCAL_ROOT"];
735 apath = [apath stringByAppendingPathComponent:relPath];
737 if (![fm fileExistsAtPath:apath]) {
738 apath = [env objectForKey:@"GNUSTEP_SYSTEM_ROOT"];
739 apath = [apath stringByAppendingPathComponent:relPath];
741 if (![fm fileExistsAtPath:apath]) {
744 if (![fm fileExistsAtPath:apath]) {
745 NSLog(@"ERROR: cannot find Defaults.plist "
746 @"resource of NGObjWeb library !");
749 owDefaults = [NSDictionary dictionaryWithContentsOfFile:apath];
752 [[NSUserDefaults standardUserDefaults] registerDefaults:owDefaults];
754 [self logWithFormat:@"did register NGObjWeb defaults: %@\n%@",
759 [self logWithFormat:@"ERROR: could not load NGObjWeb defaults: '%@'",
764 + (NSUserDefaults *)userDefaults {
765 return [NSUserDefaults standardUserDefaults];
770 + (void)setAdaptor:(NSString *)_key {
771 [[self userDefaults] setObject:_key forKey:@"WOAdaptor"];
773 + (NSString *)adaptor {
774 return [[self userDefaults] stringForKey:@"WOAdaptor"];
777 + (void)setAdditionalAdaptors:(NSArray *)_names {
778 [[self userDefaults] setObject:_names forKey:@"WOAdditionalAdaptors"];
780 + (NSArray *)additionalAdaptors {
781 return [[self userDefaults] arrayForKey:@"WOAdditionalAdaptors"];
786 + (void)setPort:(NSNumber *)_port {
787 [[self userDefaults] setObject:_port forKey:@"WOPort"];
793 woport = [[self userDefaults] objectForKey:@"WOPort"];
794 if ([woport isKindOfClass:[NSNumber class]])
797 woport = [woport stringValue];
798 addr = NGSocketAddressFromString(woport);
800 if ([addr isKindOfClass:[NGInternetSocketAddress class]])
801 return [NSNumber numberWithInt:[(NGInternetSocketAddress *)addr port]];
806 /* WOWorkerThreadCount */
808 + (NSNumber *)workerThreadCount {
809 static NSNumber *s = nil;
812 i = [[self userDefaults] integerForKey:@"WOWorkerThreadCount"];
813 s = [[NSNumber numberWithInt:i] retain];
818 /* WOListenQueueSize */
820 + (NSNumber *)listenQueueSize {
821 static NSNumber *s = nil;
824 i = [[self userDefaults] integerForKey:@"WOListenQueueSize"];
825 s = [[NSNumber numberWithInt:i] retain];
830 @end /* WOCoreApplication(Defaults) */