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