]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WOCoreApplication.m
7db7e352bd26a07ebac127c57b92fc0fb9fda9de
[sope] / sope-appserver / NGObjWeb / WOCoreApplication.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include <NGObjWeb/WOCoreApplication.h>
23 #include <NGObjWeb/WOAdaptor.h>
24 #include <NGObjWeb/WORequest.h>
25 #include <NGObjWeb/WORequestHandler.h>
26 #include <NGObjWeb/WOResponse.h>
27 #include <NGObjWeb/WOContext.h>
28 #include <EOControl/EOControl.h>
29 #include <NGStreams/NGStreams.h>
30 #include <NGStreams/NGNet.h>
31 #include <NGExtensions/NGResourceLocator.h>
32 #include "WORunLoop.h"
33 #include "common.h"
34
35 #if LIB_FOUNDATION_LIBRARY
36 #  import <Foundation/UnixSignalHandler.h>
37 #else
38 #  include "UnixSignalHandler.h"
39 #endif
40
41 NGObjWeb_DECLARE NSString *WOApplicationDidFinishLaunchingNotification =
42   @"WOApplicationDidFinishLaunching";
43 NGObjWeb_DECLARE NSString *WOApplicationWillFinishLaunchingNotification =
44   @"WOApplicationWillFinishLaunching";
45 NGObjWeb_DECLARE NSString *WOApplicationWillTerminateNotification =
46   @"WOApplicationWillTerminate";
47 NGObjWeb_DECLARE NSString *WOApplicationDidTerminateNotification =
48   @"WOApplicationDidTerminate";
49
50 @interface WOCoreApplication(PrivateMethods)
51 + (void)_initDefaults;
52 + (id)logger;
53 - (NSDictionary *)memoryStatistics;
54 - (WOResponse *)handleException:(NSException *)_exc;
55 @end
56
57 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
58 @interface NSObject(KVCWarn)
59 + (void)suppressCapitalizedKeyWarning;
60 - (void)notImplemented:(SEL)cmd;
61 @end
62 #endif
63
64 @implementation WOCoreApplication
65
66 static BOOL     outputValidateOn = NO;
67 static Class    NSDateClass      = Nil;
68 static NGLogger *logger          = nil;
69 static NGLogger *perfLogger      = nil;
70
71 + (int)version {
72   return 1;
73 }
74
75 NGObjWeb_DECLARE id WOApp = nil;
76 static NSMutableArray *activeApps = nil; // THREAD
77
78 + (id)application {
79   if (WOApp == nil) {
80     [self warnWithFormat:@"%s: some code called +application without an "
81             @"active app !", __PRETTY_FUNCTION__];
82 #if DEBUG && 0
83 #  warning REMOVE THAT ABORT IN PRODUCTION CODE !!!
84     abort();
85 #endif
86   }
87   return WOApp;
88 }
89 - (void)activateApplication {
90   if (WOApp) {
91     if (activeApps == nil)
92       activeApps = [[NSMutableArray alloc] init];
93     [activeApps addObject:WOApp];
94   }
95   ASSIGN(WOApp, self);
96 }
97 - (void)deactivateApplication {
98   unsigned idx;
99   
100   if (WOApp != self) {
101     [self warnWithFormat:
102             @"tried to deactivate inactive application !\n"
103             @"  self:   %@\n"
104             @"  active: %@",
105             self, WOApp];
106     return;
107   }
108   [self autorelease];
109   WOApp = nil;
110   
111   if ((idx = [activeApps count]) > 0) {
112     idx--;
113     WOApp = [[activeApps objectAtIndex:idx] retain];
114     [activeApps removeObjectAtIndex:idx];
115   }
116 }
117
118 + (void)_initializeClass {
119   /*
120     this must be called in -init, since the environment is not setup
121     properly if this is called first.
122   */
123   static BOOL didInit = NO;
124   NSUserDefaults  *ud;
125   NGLoggerManager *lm;
126   if (didInit) return;
127   didInit = YES;
128   [self _initDefaults];
129
130   lm         = [NGLoggerManager defaultLoggerManager];
131   logger     = [lm loggerForClass:self];
132   perfLogger = [lm loggerForDefaultKey:@"WOProfileApplication"];
133
134   ud               = [NSUserDefaults standardUserDefaults];
135   outputValidateOn = [ud boolForKey:@"WOOutputValidationEnabled"];
136   NSDateClass      = [NSDate class];
137 }
138
139 - (id)init {
140   [[self class] _initializeClass];
141 #if COCOA_Foundation_LIBRARY
142   /*
143     NSKeyBinding Warning: <Application 0xc1f70> was accessed using a capitalized key
144     'NSFileSubject'.  Keys should normally start with a lowercase character.  A
145     typographical error like this could cause a crash or an infinite loop.  Use
146     +[NSKeyBinding suppressCapitalizedKeyWarning] to suppress this warning.
147   */
148   [NSClassFromString(@"NSKeyBinding") suppressCapitalizedKeyWarning];
149 #endif
150   
151   if ((self = [super init])) {
152     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
153
154     [self activateApplication];
155     
156     if ([[ud objectForKey:@"WORunMultithreaded"] boolValue]) {
157       self->lock        = [[NSRecursiveLock alloc] init];
158       self->requestLock = [[NSLock alloc] init];
159     }
160     
161     /* handle signals */
162 #if !defined(__MINGW32__) && !defined(NeXT_Foundation_LIBRARY)
163     {
164       UnixSignalHandler *us = [UnixSignalHandler sharedHandler];
165       
166       [us addObserver:self selector:@selector(terminateOnSignal:)
167           forSignal:SIGTERM immediatelyNotifyOnSignal:YES];
168       [us addObserver:self selector:@selector(terminateOnSignal:)
169           forSignal:SIGINT immediatelyNotifyOnSignal:YES];
170       [us addObserver:self selector:@selector(terminateOnSignal:)
171           forSignal:SIGQUIT immediatelyNotifyOnSignal:YES];
172       [us addObserver:self selector:@selector(terminateOnSignal:)
173           forSignal:SIGILL immediatelyNotifyOnSignal:YES];
174       
175       [us addObserver:self selector:@selector(processHupSignal:)
176           forSignal:SIGHUP immediatelyNotifyOnSignal:NO];
177     }
178 #endif
179   }
180   return self;
181 }
182
183 - (void)dealloc {
184 #if !defined(__MINGW32__) && !defined(NeXT_Foundation_LIBRARY)
185   [[UnixSignalHandler sharedHandler] removeObserver:self];
186 #endif
187   [[NSNotificationCenter defaultCenter] removeObserver:self];
188   [self->adaptors    release];
189   [self->requestLock release];
190   [self->lock        release];
191   [super dealloc];
192 }
193
194 /* NGLogging */
195
196 + (id)logger {
197   return logger;
198 }
199
200 - (id)logger {
201   return logger;
202 }
203
204 /* signals */
205
206 - (void)processHupSignal:(int)_signal {
207   /* this isn't called immediatly */
208 }
209
210 - (void)terminateOnSignal:(int)_signal {
211   /* STDIO is forbidden in signal handlers !!! no malloc !!! */
212 #if 1
213   self->cappFlags.isTerminating = 1;
214 #else
215   static int termCount = 0;
216   unsigned pid;
217   
218 #ifdef __MINGW32__
219   pid = (unsigned)GetCurrentProcessId();
220 #else
221   pid = (unsigned)getpid();
222 #endif
223   
224   if ([self isTerminating]) {
225 #if 0
226     termCount++;
227     if (termCount > 2);
228 #endif
229     fflush(stderr);
230     fprintf(stderr, "%d: forcing termination because of signal %i\n",
231             pid, _signal);
232     fflush(stderr);
233     exit(20);
234   }
235   termCount = 0;
236   
237   fflush(stderr);
238   fprintf(stderr, "%i: terminating because of signal %i\n", pid, _signal);
239   fflush(stderr);
240   
241   [self terminate];
242 #endif
243 }
244
245 /* adaptors */
246
247 - (NSArray *)adaptors {
248   return self->adaptors;
249 }
250 - (WOAdaptor *)adaptorWithName:(NSString *)_name
251   arguments:(NSDictionary *)_args
252 {
253   Class     adaptorClass = Nil;
254   WOAdaptor *adaptor     = nil;
255
256   adaptorClass = NSClassFromString(_name);
257   if (adaptorClass == Nil) {
258     [self errorWithFormat:@"did not find adaptor class %@", _name];
259     return nil;
260   }
261
262   adaptor = [[adaptorClass allocWithZone:[self zone]]
263                            initWithName:_name
264                            arguments:_args
265                            application:self];
266   
267   return [adaptor autorelease];
268 }
269
270 - (BOOL)allowsConcurrentRequestHandling {
271   return NO;
272 }
273 - (BOOL)adaptorsDispatchRequestsConcurrently {
274   return NO;
275 }
276
277 /* request recording */
278
279 - (void)setRecordingPath:(NSString *)_path {
280   [self notImplemented:_cmd];
281 }
282 - (NSString *)recordingPath {
283   static NSString *rp = nil;
284   if (rp == nil) {
285     rp = [[[NSUserDefaults standardUserDefaults]
286                            stringForKey:@"WORecordingPath"]
287                            copy];
288   }
289   return rp;
290 }
291
292 /* exceptions */
293
294 - (NSString *)name {
295   return NSStringFromClass([self class]);
296 }
297
298 - (WOResponse *)handleException:(NSException *)_exc
299   inContext:(WOContext *)_ctx
300 {
301   WORequest  *rq = [_ctx request];
302   WOResponse *r  = nil;
303   
304   if ([self respondsToSelector:@selector(handleException:)]) {
305     [self warnWithFormat:@"calling deprecated -handleException method !"];
306     return [self handleException:_exc];
307   }
308   
309 #if 0 && DEBUG
310   [self errorWithFormat:@"%@: caught (without context):\n  %@.", self, _exc];
311   abort();
312 #endif
313   
314   if (_ctx == nil) {
315     [self errorWithFormat:@"%@: caught (without context):\n  %@.",
316       self, _exc];
317     [self terminate];
318   }
319   else if (rq == nil) {
320     [self errorWithFormat:@"%@: caught (without request):\n  %@.",
321       self, _exc];
322     [self terminate];
323   }
324   else {
325     [self logWithFormat:@"%@: caught:\n  %@\nin context:\n  %@.",
326             self, _exc, _ctx];
327     
328   }
329   
330   if ((r = [WOResponse responseWithRequest:rq]) == nil)
331     [self warnWithFormat:@"could not create response !"];
332     
333   [r setStatus:500];
334   return r;
335 }
336
337 /* multithreading */
338
339 - (void)lock {
340   [self->lock lock];
341 }
342 - (void)unlock {
343   [self->lock unlock];
344 }
345 - (BOOL)tryLock {
346   return [self->lock tryLock];
347 }
348
349 - (void)lockRequestHandling {
350   [self->requestLock lock];
351 }
352 - (void)unlockRequestHandling {
353   [self->requestLock unlock];
354 }
355
356 /* notifications */
357
358 - (void)awake {
359 }
360 - (void)sleep {
361 }
362
363 /* runloop */
364
365 - (void)_loadAdaptors {
366   NSMutableArray *ads = nil;
367   NSArray *args;
368   int     i, count;
369       
370   args = [[NSProcessInfo processInfo] arguments];
371       
372   for (i = 0, count = [args count]; i < count; i++) {
373     NSString *arg;
374         
375     arg = [args objectAtIndex:i];
376         
377     if ([arg isEqualToString:@"-a"] && ((i + 1) < count)) {
378       // found adaptor
379       NSString            *adaptorName = nil;
380       NSMutableDictionary *arguments   = nil;
381       WOAdaptor           *adaptor     = nil;
382
383       i++; // skip '-a' option
384       adaptorName = [args objectAtIndex:i];
385       i++; // skip adaptor name
386
387       if (i < count) { // search for arguments
388         NSString *key = nil;
389             
390         arguments = [NSMutableDictionary dictionaryWithCapacity:10];
391         for (; i < count; i++) {
392           arg = [args objectAtIndex:i];
393           if ([arg isEqualToString:@"-a"] ||
394               [arg isEqualToString:@"-c"] ||
395               [arg isEqualToString:@"-d"]) {
396             i--;
397             break;
398           }
399           if (key == nil)
400             key = arg;
401           else {
402             [arguments setObject:arg forKey:key];
403             key = nil;
404           }
405         }
406       }
407
408       adaptor = [self adaptorWithName:adaptorName
409                       arguments:[[arguments copy] autorelease]];
410       if (adaptor) {
411         if (ads == nil) ads = [[NSMutableArray alloc] initWithCapacity:8];
412         [ads addObject:adaptor];
413       }
414     }
415   }
416
417   self->adaptors = [ads copy];
418   [ads release]; ads = nil;
419       
420   if ([self->adaptors count] == 0) {
421     id      defaultAdaptor;
422     NSArray *moreAdaptors;
423         
424     defaultAdaptor = [[self class] adaptor];
425     defaultAdaptor = [self adaptorWithName:defaultAdaptor arguments:nil];
426     if (defaultAdaptor) {
427       self->adaptors = [[NSArray alloc] initWithObjects:defaultAdaptor, nil];
428     }
429
430     moreAdaptors = [[self class] additionalAdaptors];
431     if ([moreAdaptors count] > 0) {
432       unsigned i, count;
433       NSMutableArray *newArray;
434
435       newArray = nil;
436       
437       for (i = 0, count = [moreAdaptors count]; i < count; i++) {
438         WOAdaptor *adaptor;
439
440         adaptor = [self adaptorWithName:[moreAdaptors objectAtIndex:i]
441                         arguments:nil];
442         if (adaptor == nil) {
443           [self warnWithFormat:@"could not find WOAdaptor '%@' !",
444                 [moreAdaptors objectAtIndex:i]];
445           continue;
446         }
447
448         if (newArray == nil) {
449           newArray = [self->adaptors mutableCopy];
450           [newArray addObject:adaptor];
451         }
452       }
453       [self->adaptors release];
454       self->adaptors = [newArray shallowCopy];
455       [newArray release]; newArray = nil;
456     }
457   }
458 }
459
460 - (void)_setupAdaptors {
461   // register adaptors
462   NSEnumerator *ads;
463   WOAdaptor    *adaptor;
464
465   if ([self->adaptors count] == 0)
466     [self _loadAdaptors];
467   
468   ads = [self->adaptors objectEnumerator];
469   while ((adaptor = [ads nextObject]))
470     [adaptor registerForEvents];
471 }
472 - (void)_tearDownAdaptors {
473   // unregister adaptors
474   NSEnumerator *ads;
475   WOAdaptor    *adaptor;
476   
477   ads = [self->adaptors objectEnumerator];
478   while ((adaptor = [ads nextObject]))
479     [adaptor unregisterForEvents];
480   
481   [self->adaptors release]; self->adaptors = nil;
482 }
483
484 - (NSRunLoop *)runLoop { // deprecated in WO4
485   IS_DEPRECATED;
486   return [self mainThreadRunLoop];
487 }
488 - (NSRunLoop *)mainThreadRunLoop {
489   // wrong, should remove main thread runloop
490   return [WORunLoop currentRunLoop];
491 }
492
493 - (BOOL)shouldTerminate {
494   return NO;
495 }
496 - (void)run {
497   [self activateApplication];
498   {
499     [self _setupAdaptors];
500     
501     [[NSNotificationCenter defaultCenter]
502                            postNotificationName:
503                              WOApplicationDidFinishLaunchingNotification
504                            object:self];
505   }
506   [self deactivateApplication];
507   
508   while (![self isTerminating]) {
509     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
510     
511     if ([self shouldTerminate]) {
512       /* check whether we should still process requests */
513       [self terminate];
514     }
515     else {
516       NSRunLoop *loop;
517       NSDate *limitDate = nil;
518       
519       loop = [self mainThreadRunLoop];
520       
521       limitDate = [loop limitDateForMode:NSDefaultRunLoopMode];
522       
523       if ([self isTerminating])
524         break;
525       
526       [self activateApplication];
527       [loop runMode:NSDefaultRunLoopMode beforeDate:limitDate];
528       [self deactivateApplication];
529     }
530     
531     [pool release];
532   }
533
534   [self debugWithFormat:@"application finished runloop."];
535
536   [self activateApplication];
537   {
538     [[NSNotificationCenter defaultCenter]
539                            postNotificationName:
540                              WOApplicationWillTerminateNotification
541                            object:self];
542   
543     [self _tearDownAdaptors];
544   
545     self->cappFlags.isTerminating = 1;
546
547     [[NSNotificationCenter defaultCenter]
548                            postNotificationName:
549                              WOApplicationDidTerminateNotification
550                            object:self];
551   }
552   [self deactivateApplication];
553 }
554
555 - (void)terminate {
556   self->cappFlags.isTerminating = 1;
557 }
558 - (BOOL)isTerminating {
559   return self->cappFlags.isTerminating ? YES : NO;
560 }
561
562 - (void)_terminateNow:(id)_dummy {
563   [self terminate];
564 }
565 - (void)terminateAfterTimeInterval:(NSTimeInterval)_interval {
566   [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(_terminateNow:)
567                               target:self argument:nil];
568   [self performSelector:@selector(_terminateNow:) withObject:nil
569         afterDelay:_interval];
570 }
571
572 /* output validation */
573
574 - (void)setPrintsHTMLParserDiagnostics:(BOOL)_flag {
575   [[NSUserDefaults standardUserDefaults] setBool:_flag 
576                                          forKey:@"WOOutputValidationEnabled"];
577   outputValidateOn = _flag;
578 }
579 - (BOOL)printsHTMLParserDiagnostics {
580   return outputValidateOn;
581 }
582
583 - (void)_logWarningOnOutputValidation {
584   static BOOL didWarn = NO;
585
586   if (!didWarn) {
587     [self warnWithFormat:
588             @"output validation is enabled, this will "
589             @"slow down request processing!"];
590     didWarn = YES;
591   }
592 }
593
594 - (BOOL)hideValidationIssue:(NSException *)_issue {
595   /* to deal with some non-standard HTML ... */
596   return NO;
597 }
598
599 - (void)validateOutputOfResponse:(WOResponse *)_response {
600   NSArray      *issues;
601   NSEnumerator *e;
602   id           issue;
603   
604   [self _logWarningOnOutputValidation];
605   
606   if (_response == nil) {
607     [self logWithFormat:@"validate-output: no response returned by handler."];
608     return;
609   }
610   
611   if (![_response respondsToSelector:@selector(validateContent)]) {
612     [self logWithFormat:@"response does not support content validation!"];
613     return;
614   }
615   if ((issues = [_response validateContent]) == nil)
616     return;
617   
618   e = [issues objectEnumerator];
619   while ((issue = [e nextObject])) {
620     if ([issue isKindOfClass:[NSException class]]) {
621       if ([self hideValidationIssue:issue])
622         continue;
623       [self logWithFormat:@"validate-output[%@]: %@",
624               [(NSException *)issue name], [issue reason]];
625       continue;
626     }
627     
628     [self logWithFormat:@"validate-output: %@", [issue stringValue]];
629   }
630 }
631
632 /* request handling */
633
634 - (WORequestHandler *)handlerForRequest:(WORequest *)_request {
635   return nil;
636 }
637
638 - (WOResponse *)dispatchRequest:(WORequest *)_request
639   usingHandler:(WORequestHandler *)handler
640 {
641   WOResponse     *response = nil;
642   NSTimeInterval startDispatch = 0.0;
643   
644   if (perfLogger)
645     startDispatch = [[NSDateClass date] timeIntervalSince1970];
646   
647   /* let request handler process the request */
648   {
649     NSTimeInterval startDispatch = 0.0;
650     
651     if (perfLogger)
652       startDispatch = [[NSDateClass date] timeIntervalSince1970];
653     
654     /* the call ;-) */
655     response = [handler handleRequest:_request];
656     
657     if (perfLogger) {
658       NSTimeInterval rt;
659       rt = [[NSDateClass date] timeIntervalSince1970] - startDispatch;
660       [perfLogger logWithFormat:@"[woapp-rq]: request handler took %4.3fs.",
661                                     rt < 0.0 ? -1.0 : rt];
662     }
663   }
664   
665   response = [[response retain] autorelease];
666
667   if (outputValidateOn)
668     [self validateOutputOfResponse:response];
669   
670   if (perfLogger) {
671     NSTimeInterval rt;
672     rt = [[NSDateClass date] timeIntervalSince1970] - startDispatch;
673     [perfLogger logWithFormat:@"[woapp]: dispatchRequest took %4.3fs.",
674                   rt < 0.0 ? -1.0 : rt];
675   }
676   
677   return response;
678 }
679 - (WOResponse *)dispatchRequest:(WORequest *)_request {
680   WORequestHandler *handler;
681   
682   if ([self respondsToSelector:@selector(handleRequest:)]) {
683     [self warnWithFormat:
684             @"calling deprecated -handleRequest: method .."];
685     return [self handleRequest:_request];
686   }
687   
688   /* find request handler for request */
689   if ((handler = [self handlerForRequest:_request]) == nil) {
690     [self errorWithFormat:@"got no request handler for request: %@ !",
691             _request];
692     return nil;
693   }
694   
695   return [self dispatchRequest:_request usingHandler:handler];
696 }
697
698 /* description */
699
700 - (NSString *)description {
701   return [NSString stringWithFormat:@"<%@[0x%08X]: %@>",
702                      NSStringFromClass([self class]), self,
703                      [self isTerminating] ? @" terminating" : @""
704                    ];
705 }
706
707 /* defaults */
708
709 + (int)sopeMajorVersion {
710   return SOPE_MAJOR_VERSION;
711 }
712 + (int)sopeMinorVersion {
713   return SOPE_MINOR_VERSION;
714 }
715 + (NSString *)ngobjwebShareDirectorySubPath {
716   return [NSString stringWithFormat:@"share/sope-%i.%i/ngobjweb/",
717                      [self sopeMajorVersion], [self sopeMinorVersion]];
718 }
719 + (NGResourceLocator *)ngobjwebResourceLocator {
720   return [NGResourceLocator resourceLocatorForGNUstepPath:
721                               @"Library/Libraries/Resources/NGObjWeb"
722                             fhsPath:[self ngobjwebShareDirectorySubPath]];
723 }
724
725 + (NSArray *)resourcesSearchPathes {
726   // TODO: is this actually used somewhere?
727   return [[self ngobjwebResourceLocator] searchPathes];
728 }
729
730 + (NSString *)findNGObjWebResource:(NSString *)_name ofType:(NSString *)_ext {
731 #if COMPILE_AS_FRAMEWORK
732   NSBundle *bundle;
733   
734   if ((bundle = [NSBundle bundleForClass:[WOCoreApplication class]]) == nil) {
735     NSLog(@"ERROR(%s): did not find NGObjWeb framework bundle!",
736           __PRETTY_FUNCTION__);
737     return nil;
738   }
739   
740   return [bundle pathForResource:_name ofType:_ext];
741 #else
742   return [[self ngobjwebResourceLocator] lookupFileWithName:_name 
743                                          extension:_ext];
744 #endif
745 }
746
747 + (void)_initDefaults {
748   static BOOL didInit = NO;
749   NSDictionary *owDefaults = nil;
750   NSString     *apath;
751   
752   if (didInit) return;
753   didInit = YES;
754   
755   apath = [self findNGObjWebResource:@"Defaults" ofType:@"plist"];
756   if (apath == nil)
757     NSLog(@"ERROR: Cannot find Defaults.plist resource of NGObjWeb library!");
758 #if HEAVY_DEBUG
759   else
760     [self debugWithFormat:@"Note: loading default defaults: %@", apath];
761 #endif
762   
763   owDefaults = [NSDictionary dictionaryWithContentsOfFile:apath];
764   if (owDefaults) {
765     [[NSUserDefaults standardUserDefaults] registerDefaults:owDefaults];
766 #if HEAVY_DEBUG
767     [self debugWithFormat:@"did register NGObjWeb defaults: %@\n%@", 
768             apath, owDefaults];
769 #endif
770   }
771   else {
772     [self errorWithFormat:@"could not load NGObjWeb defaults: '%@'",
773             apath];
774   }
775 }
776
777 + (NSUserDefaults *)userDefaults {
778   return [NSUserDefaults standardUserDefaults];
779 }
780
781 /* WOAdaptor */
782
783 + (void)setAdaptor:(NSString *)_key {
784   [[self userDefaults] setObject:_key forKey:@"WOAdaptor"];
785 }
786 + (NSString *)adaptor {
787   return [[self userDefaults] stringForKey:@"WOAdaptor"];
788 }
789
790 + (void)setAdditionalAdaptors:(NSArray *)_names {
791   [[self userDefaults] setObject:_names forKey:@"WOAdditionalAdaptors"];
792 }
793 + (NSArray *)additionalAdaptors {
794   return [[self userDefaults] arrayForKey:@"WOAdditionalAdaptors"];
795 }
796
797 /* WOPort */
798
799 + (void)setPort:(NSNumber *)_port {
800   [[self userDefaults] setObject:_port forKey:@"WOPort"];
801 }
802 + (NSNumber *)port {
803   id woport;
804   id addr;
805   
806   woport = [[self userDefaults] objectForKey:@"WOPort"];
807   if ([woport isKindOfClass:[NSNumber class]])
808     return woport;
809   woport = [woport stringValue];
810   if ([woport length] > 0 && isdigit([woport characterAtIndex:0]))
811     return [NSNumber numberWithInt:[woport intValue]];
812   
813   addr   = NGSocketAddressFromString(woport);
814   
815   if ([addr isKindOfClass:[NGInternetSocketAddress class]])
816     return [NSNumber numberWithInt:[(NGInternetSocketAddress *)addr port]];
817
818   [self fatalWithFormat:@"GOT NO PORT FOR ADDR: %@ (%@)", addr, woport];
819   return nil;
820 }
821
822 /* WOWorkerThreadCount */
823
824 + (NSNumber *)workerThreadCount {
825   static NSNumber *s = nil;
826   if (s == nil) {
827     int i;
828     i = [[self userDefaults] integerForKey:@"WOWorkerThreadCount"];
829     s = [[NSNumber numberWithInt:i] retain];
830   }
831   return s;
832 }
833
834 /* WOListenQueueSize */
835
836 + (NSNumber *)listenQueueSize {
837   static NSNumber *s = nil;
838   if (s == nil) {
839     int i;
840     i = [[self userDefaults] integerForKey:@"WOListenQueueSize"];
841     s = [[NSNumber numberWithInt:i] retain];
842   }
843   return s;
844 }
845
846 @end /* WOCoreApplication */