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