21
21
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
23
23
<title>NSDistributedNotificationCenter class reference</title>
24
$Date: 2002/03/10 08:08:11 $ $Revision: 1.21 $
24
$Date: 2005/02/22 11:22:44 $ $Revision: 1.36 $
28
#include <base/preface.h>
29
#include <Foundation/NSObject.h>
30
#include <Foundation/NSConnection.h>
31
#include <Foundation/NSDistantObject.h>
32
#include <Foundation/NSException.h>
33
#include <Foundation/NSArchiver.h>
34
#include <Foundation/NSNotification.h>
35
#include <Foundation/NSDate.h>
36
#include <Foundation/NSPathUtilities.h>
37
#include <Foundation/NSRunLoop.h>
38
#include <Foundation/NSTask.h>
39
#include <Foundation/NSDistributedNotificationCenter.h>
40
#include <Foundation/NSUserDefaults.h>
41
#include <Foundation/NSHost.h>
28
#include "GNUstepBase/preface.h"
29
#include "Foundation/NSObject.h"
30
#include "Foundation/NSConnection.h"
31
#include "Foundation/NSDistantObject.h"
32
#include "Foundation/NSException.h"
33
#include "Foundation/NSArchiver.h"
34
#include "Foundation/NSNotification.h"
35
#include "Foundation/NSDate.h"
36
#include "Foundation/NSPathUtilities.h"
37
#include "Foundation/NSRunLoop.h"
38
#include "Foundation/NSTask.h"
39
#include "Foundation/NSDistributedNotificationCenter.h"
40
#include "Foundation/NSUserDefaults.h"
41
#include "Foundation/NSHost.h"
42
#include "Foundation/NSPortNameServer.h"
43
44
#include "../Tools/gdnc.h"
46
* Macros to build text to start name server and to give an error
47
* message about it - they include installation path information.
49
#define MAKE_GDNC_CMD [GSSystemRootDirectory() \
50
stringByAppendingPathComponent: @"Tools/gdnc"]
51
#define MAKE_GDNC_ERR [NSString stringWithFormat: \
52
@"check that %@/Tools/gdnc is running", \
53
GSSystemRootDirectory()]
56
* Global variables for distributed notification center types.
58
NSString *NSLocalNotificationCenterType =
59
@"NSLocalNotificationCenterType";
62
47
@interface NSDistributedNotificationCenter (Private)
69
54
to: (unsigned long)observer;
58
* <p>The <code>NSDistributedNotificationCenter</code> provides a versatile yet
59
* simple mechanism for objects in different processes to communicate
60
* effectively while knowing very little about each others' internals.<br />
61
* A distributed notification center acts much like a normal
62
* notification center, but it handles notifications on a machine-wide
63
* (or local network wide) basis rather than just notifications within
64
* a single process. Objects are able to register themselves as
65
* observers for particular notification names and objects, and they
66
* will then receive notifications (including optional user information
67
* consisting of a dictionary of property-list objects) as they are posted.
69
* <p>Since posting of distributed notifications involves inter-process
70
* (and sometimes inter-host) communication, it is fundamentally slower
71
* than normal notifications, and should be used relatively sparingly.
72
* In order to help with this, the <code>NSDistributedNotificationCenter</code>
73
* provides a notion of 'suspension', whereby a center can be suspended
74
* causing notifications for observers in the process where the center
75
* was suspended to cease receiving notifications. Observers can
76
* specify how notifications are to be handled in this case (queued
77
* or discarded) and posters can specify that particular notifications
78
* are to be delivered immediately irrespective of suspension.
80
* <p>Distributed notifications are mediated by a server process which
81
* handles all notifications for a particular center type. In GNUstep
82
* this process is the <code>gdnc</code> tool, and when started without special
83
* options, a gdnc process acts as the local centre for the host it is
84
* running on. When started with the <code>GSNetwork</code> user default set
85
* to YES, the <code>gdnc</code> tool acts as a local network wide server (you
86
* should only run one copy of <code>gdnc</code> like this on your LAN).<br />
87
* The <code>gdnc</code> process should be started at machine boot time, but
88
* GNUstep will attempt to start it automatically if it can't find it.
90
* <p>MacOS-X currently defines only a notification center for the
91
* local host. GNUstep also defines a local network center which can
92
* be used from multiple hosts. By default the system sends this to
93
* any gdnc process it can find which is configured as a network-wide
94
* server, but the <code>GDNCHost</code> user default may be used to specify a
95
* particular host to be contacted ... this may be of use where you
96
* wish to have logically separate clusters of machines on a shared LAN.
72
99
@implementation NSDistributedNotificationCenter
74
static NSDistributedNotificationCenter *defCenter = nil;
101
static NSDistributedNotificationCenter *locCenter = nil;
102
static NSDistributedNotificationCenter *netCenter = nil;
104
+ (id) allocWithZone: (NSZone*)z
106
[NSException raise: NSInternalInconsistencyException
107
format: @"Should not call +alloc for NSDistributedNotificationCenter"];
112
* Returns the default notification center ... a shared notification
113
* center for the local host. This is simply a convenience method
114
* equivalent to calling +notificationCenterForType: with
115
* <code>NSLocalNotificationCenterType</code> as its argument.
76
117
+ (id) defaultCenter
78
119
return [self notificationCenterForType: NSLocalNotificationCenterType];
123
* Returns a notification center of the specified type.<br />
124
* The <code>NSLocalNotificationCenterType</code> provides a shared access to
125
* a notificatiuon center used by processes on the local host.<br />
126
* The <code>GSNetworkNotificationCenterType</code> provides a shared access to
127
* a notificatiuon center used by processes on the local network.<br />
128
* MacOS-X supports only <code>NSLocalNotificationCenterType</code>.
81
130
+ (id) notificationCenterForType: (NSString*)type
83
NSAssert([type isEqual: NSLocalNotificationCenterType],
84
NSInvalidArgumentException);
87
[gnustep_global_lock lock];
94
tmp = NSAllocateObject(self, 0, NSDefaultMallocZone());
95
defCenter = (NSDistributedNotificationCenter*)[tmp init];
99
[gnustep_global_lock unlock];
100
[localException raise];
104
[gnustep_global_lock unlock];
132
if ([type isEqual: NSLocalNotificationCenterType] == YES)
134
if (locCenter == nil)
136
[gnustep_global_lock lock];
137
if (locCenter == nil)
141
NSDistributedNotificationCenter *tmp;
143
tmp = (NSDistributedNotificationCenter*)
144
NSAllocateObject(self, 0, NSDefaultMallocZone());
145
tmp->_centerLock = [NSRecursiveLock new];
146
tmp->_type = RETAIN(NSLocalNotificationCenterType);
151
[gnustep_global_lock unlock];
152
[localException raise];
156
[gnustep_global_lock unlock];
160
else if ([type isEqual: GSNetworkNotificationCenterType] == YES)
162
if (netCenter == nil)
164
[gnustep_global_lock lock];
165
if (netCenter == nil)
169
NSDistributedNotificationCenter *tmp;
171
tmp = (NSDistributedNotificationCenter*)
172
NSAllocateObject(self, 0, NSDefaultMallocZone());
173
tmp->_centerLock = [NSRecursiveLock new];
174
tmp->_type = RETAIN(GSNetworkNotificationCenterType);
179
[gnustep_global_lock unlock];
180
[localException raise];
184
[gnustep_global_lock unlock];
190
[NSException raise: NSInvalidArgumentException
191
format: @"Unknown center type (%@)", type];
192
return nil; /* NOT REACHED */
135
232
suspensionBehavior: NSNotificationSuspensionBehaviorCoalesce];
236
* Adds an observer to the receiver.<br />
237
* When a notification matching notificationName and anObject is
238
* sent to the center, the object anObserver is sent the message
239
* aSelector with the notification info dictionary as its argument.<br />
240
* The suspensionBehavior governs how the center deals with notifications
241
* when the process to which the notification should be delivered is
244
* <term><code>NSNotificationSuspensionBehaviorDrop</code></term>
246
* Discards the notification if the observing process is suspended.
248
* <term><code>NSNotificationSuspensionBehaviorCoalesce</code></term>
250
* Discards previously queued notifications when the observing process
251
* is suspended, leaving only the last notification posted in the queue.
252
* Delivers this single notification when the process becomes unsuspended.
254
* <term><code>NSNotificationSuspensionBehaviorHold</code></term>
256
* Queues notifications when the observing process is suspended,
257
* delivering all the queued notifications when the process becomes
260
* <term><code>NSNotificationSuspensionBehaviorDeliverImmediately</code></term>
262
* Deliver the notification immediately, even if the destination
263
* process is suspended.
138
267
- (void) addObserver: (id)anObserver
139
268
selector: (SEL)aSelector
140
269
name: (NSString*)notificationName
479
* The following dummy class is here solely as a workaround for pre 3.3
480
* versions of gcc where protocols didn't work properly unless implemented
481
* in the source where the '@protocol()' directive is used.
483
@interface NSDistributedNotificationCenterDummy : NSObject <GDNCProtocol>
484
- (void) addObserver: (unsigned long)anObserver
485
selector: (NSString*)aSelector
486
name: (NSString*)notificationname
487
object: (NSString*)anObject
488
suspensionBehavior: (NSNotificationSuspensionBehavior)suspensionBehavior
489
for: (id<GDNCClient>)client;
490
- (oneway void) postNotificationName: (NSString*)notificationName
491
object: (NSString*)anObject
493
deliverImmediately: (BOOL)deliverImmediately
494
for: (id<GDNCClient>)client;
495
- (void) registerClient: (id<GDNCClient>)client;
496
- (void) removeObserver: (unsigned long)anObserver
497
name: (NSString*)notificationname
498
object: (NSString*)anObject
499
for: (id<GDNCClient>)client;
500
- (void) setSuspended: (BOOL)flag
501
for: (id<GDNCClient>)client;
502
- (void) unregisterClient: (id<GDNCClient>)client;
505
@implementation NSDistributedNotificationCenterDummy
506
- (void) addObserver: (unsigned long)anObserver
507
selector: (NSString*)aSelector
508
name: (NSString*)notificationname
509
object: (NSString*)anObject
510
suspensionBehavior: (NSNotificationSuspensionBehavior)suspensionBehavior
511
for: (id<GDNCClient>)client
514
- (oneway void) postNotificationName: (NSString*)notificationName
515
object: (NSString*)anObject
517
deliverImmediately: (BOOL)deliverImmediately
518
for: (id<GDNCClient>)client
521
- (void) registerClient: (id<GDNCClient>)client
524
- (void) removeObserver: (unsigned long)anObserver
525
name: (NSString*)notificationname
526
object: (NSString*)anObject
527
for: (id<GDNCClient>)client
530
- (void) setSuspended: (BOOL)flag
531
for: (id<GDNCClient>)client
534
- (void) unregisterClient: (id<GDNCClient>)client
316
539
@implementation NSDistributedNotificationCenter (Private)
542
* Establish a connection to the server. This method should only be called
543
* when protected by the center's lock, so that it is thread-safe.
318
545
- (void) _connect
320
547
if (_remote == nil)
323
NSString *description;
326
* Connect to the NSDistributedNotificationCenter for this host.
328
host = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"];
549
NSString *host = nil;
550
NSString *service = nil;
551
NSString *description = nil;
553
if (_type == NSLocalNotificationCenterType)
338
* If we have a host specified, but it is the current host,
339
* we do not need to ask for a host by name (nameserver lookup
340
* can be faster) and the empty host name can be used to
341
* indicate that we may start a gdnc server locally.
556
* Connect to the NSDistributedNotificationCenter for this host.
343
h = [NSHost hostWithName: host];
346
NSLog(@"Unknown -NSHost '%@' ignored", host);
349
else if ([h isEqual: [NSHost currentHost]] == YES)
558
host = [[NSUserDefaults standardUserDefaults]
559
stringForKey: @"NSHost"];
562
//host = @"localhost";
359
if ([host length] == 0)
361
description = @"local host";
570
* If we have a host specified, but it is the current host,
571
* we do not need to ask for a host by name (nameserver lookup
572
* can be faster) and the empty host name can be used to
573
* indicate that we may start a <code>gdnc</code> server locally.
575
h = [NSHost hostWithName: host];
578
NSLog(@"Unknown -NSHost '%@' ignored", host);
581
else if ([h isEqual: [NSHost currentHost]] == YES)
590
if ([host length] == 0
591
|| [host isEqualToString: @"localhost"] == YES
592
|| [host isEqualToString: @"127.0.0.1"] == YES)
594
description = @"local host";
600
service = GDNC_SERVICE;
602
else if (_type == GSNetworkNotificationCenterType)
604
host = [[NSUserDefaults standardUserDefaults]
605
stringForKey: @"GDNCHost"];
365
606
description = host;
368
_remote = RETAIN([NSConnection rootProxyForConnectionWithRegisteredName:
369
GDNC_SERVICE host: host]);
370
if (_remote == nil && [host isEqual: @""] == NO)
372
_remote = [NSConnection rootProxyForConnectionWithRegisteredName:
373
[GDNC_SERVICE stringByAppendingFormat: @"-%@", host] host: @"*"];
610
description = @"network host";
612
service = GDNC_NETWORK;
616
[NSException raise: NSInternalInconsistencyException
617
format: @"Unknown center type - %@", _type];
620
if ([host isEqualToString: @"*"] == YES)
622
_remote = [NSConnection rootProxyForConnectionWithRegisteredName:
624
usingNameServer: [NSSocketPortNameServer sharedInstance]];
628
_remote = [NSConnection rootProxyForConnectionWithRegisteredName:
633
if (_type == NSLocalNotificationCenterType
634
&& _remote == nil && [host isEqual: @""] == NO)
636
_remote = [NSConnection rootProxyForConnectionWithRegisteredName:
637
[service stringByAppendingFormat: @"-%@", host] host: @"*"];
396
static BOOL recursion = NO;
666
static BOOL recursion = NO;
667
static NSString *cmd = nil;
668
static NSArray *args = nil;
398
670
if (recursion == NO)
400
static NSString *cmd = nil;
401
static NSArray *args = nil;
403
NSLog(@"\nI couldn't contact the notification server for %@ -\n"
404
@"so I'm attempting to to start one - which will take a few seconds.\n"
405
@"It is recommended that you start the notification server (gdnc) either at\n"
406
@"login or (better) when your computer is started up.\n", description);
411
674
cmd = RETAIN([[NSSearchPathForDirectoriesInDomains(
412
675
GSToolsDirectory, NSSystemDomainMask, YES) objectAtIndex: 0]
413
676
stringByAppendingPathComponent: @"gdnc"]);
417
if ([host length] > 0)
419
args = [[NSArray alloc] initWithObjects:
420
@"-NSHost", host, nil];
679
if (recursion == NO && cmd != nil)
681
NSLog(@"\nI couldn't contact the notification server for %@ -\n"
682
@"so I'm attempting to to start one - which will take a few seconds.\n"
683
@"Trying to launch gdnc from %@ or a machine/operating-system subdirectory.\n"
684
@"It is recommended that you start the notification server (gdnc) either at\n"
685
@"login or (better) when your computer is started up.\n", description,
686
[cmd stringByDeletingLastPathComponent]);
688
if (_type == GSNetworkNotificationCenterType)
690
args = [[NSArray alloc] initWithObjects:
691
@"-GSNetwork", @"YES", nil];
693
else if ([host length] > 0)
695
args = [[NSArray alloc] initWithObjects:
696
@"-NSHost", host, nil];
424
699
[NSTask launchedTaskWithLaunchPath: cmd arguments: args];