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