2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
21 // $Id: SNSConnection.m 14 2004-08-20 21:07:18Z helge $
23 #include "SNSConnection.h"
24 #include <NGObjWeb/WOApplication.h>
25 #include <NGObjWeb/WOAdaptor.h>
26 #include <NGObjWeb/WOSession.h>
27 #include <NGStreams/NGStreams.h>
28 #include <NGStreams/NGNet.h>
31 #if !LIB_FOUNDATION_LIBRARY
32 # include <NGExtensions/NSRunLoop+FileObjects.h>
35 // TODO: NEED TO FIX FOR Exception-less IO !!! (safeWrite...)
38 SNSUnregisterInstance = 0,
39 SNSRegisterInstance = 1,
40 SNSRegisterSession = 2,
42 SNSTerminateSession = 4,
43 SNSLookupSession = 50,
44 SNSInstanceAlive = 100
47 @interface SNSConnection(PrivateMethods)
49 - (void)initWithApplication:(WOApplication *)_application;
51 - (BOOL)registerWithSessionNameService;
52 - (void)disconnectFromSessionNameService;
56 @interface WOAdaptor(SocketAddress)
57 - (id<NGSocketAddress>)socketAddress;
60 @implementation SNSConnection
63 static BOOL isInitialized = NO;
65 NSDictionary *snsDefaults = nil;
69 snsDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
70 @"/tmp/.snsd", @"SNSPort",
71 @"60", @"SNSPingInterval",
72 @"NO", @"SNSLogActivity",
74 [[NSUserDefaults standardUserDefaults]
75 registerDefaults:snsDefaults];
79 + (SNSConnection *)defaultSNSConnection {
80 static SNSConnection *connection = nil;
81 if (connection) return connection;
82 connection = [[self alloc] init];
86 - (void)initWithApplication:(WOApplication *)_application {
88 id<NGSocketAddress> sns = nil;
91 ud = [NSUserDefaults standardUserDefaults];
93 self->loggingEnabled = [[ud objectForKey:@"SNSLogActivity"] boolValue];
95 sns = NGSocketAddressFromString([ud stringForKey:@"SNSPort"]);
97 [self errorWithFormat:
98 @"(%s): Could not create socket address for snsd(port=%@).",
99 __PRETTY_FUNCTION__, sns];
105 self->socket = [NGActiveSocket socketInDomain:[sns domain]];
108 [self logWithFormat:@"waiting %i seconds for snsd to come up ...",
113 if (![self->socket connectToAddress:sns]) {
114 [self logWithFormat:@" connect failed: %@",
115 [self->socket lastException]];
119 while (![self->socket isConnected] && (waitCnt < 5));
122 self->socket = [NGActiveSocket socketConnectedToAddress:sns];
130 if (![self->socket isConnected]) {
131 [self errorWithFormat:@"Could not connect socket %@ to snsd (port=%@), "
133 self->socket, sns, [self->socket lastException]];
134 ASSIGN(self->socket, (id)nil);
136 [[WOApplication application] terminate];
139 self->socket = RETAIN(self->socket);
141 self->io = [NGBufferedStream filterWithSource:self->socket];
142 self->io = RETAIN(self->io);
144 self->application = _application;
147 [self registerWithSessionNameService];
150 RELEASE(self->socket); self->socket = nil;
154 if (self->socket == nil) {
155 [self errorWithFormat:@"Could not register with snsd (port=%@).", sns];
159 [[NSNotificationCenter defaultCenter]
160 addObserver:self selector:@selector(receiveMessage:)
161 name:NSFileObjectBecameActiveNotificationName
162 object:self->socket];
164 int interval = [[ud objectForKey:@"SNSPingInterval"] intValue];
167 [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)interval
168 target:self selector:@selector(pingSNS:)
169 userInfo:nil repeats:YES];
170 self->pingTimer = RETAIN(self->pingTimer);
175 NSNotificationCenter *nc;
177 self->loggingEnabled = [[[NSUserDefaults standardUserDefaults]
178 objectForKey:@"SNSLogActivity"]
181 nc = [NSNotificationCenter defaultCenter];
183 [nc addObserver:self selector:@selector(appDidFinishLaunching:)
184 name:WOApplicationDidFinishLaunchingNotification object:nil];
185 [nc addObserver:self selector:@selector(appWillTerminate:)
186 name:WOApplicationWillTerminateNotification object:nil];
188 [nc addObserver:self selector:@selector(sessionDidCreate:)
189 name:WOSessionDidCreateNotification object:nil];
190 [nc addObserver:self selector:@selector(sessionDidTimeOut:)
191 name:WOSessionDidTimeOutNotification object:nil];
192 [nc addObserver:self selector:@selector(sessionDidTerminate:)
193 name:WOSessionDidTerminateNotification object:nil];
199 [self disconnectFromSessionNameService];
200 [self->pingTimer invalidate];
201 [[NSNotificationCenter defaultCenter] removeObserver:self];
202 [self disconnectFromSessionNameService];
203 RELEASE(self->pingTimer);
205 RELEASE(self->socket);
211 - (void)appDidFinishLaunching:(NSNotification *)_notification {
212 WOApplication *app = [_notification object];
213 [self initWithApplication:app];
215 - (void)appWillTerminate:(NSNotification *)_notification {
216 [self disconnectFromSessionNameService];
219 - (void)sessionDidCreate:(NSNotification *)_notification {
220 WOSession *sn = [_notification object];
221 if (sn) [self applicationCreatedSession:[sn sessionID]];
223 - (void)sessionDidTimeOut:(NSNotification *)_notification {
224 NSString *sn = [_notification object];
225 if (sn) [self sessionExpired:sn];
227 - (void)sessionDidTerminate:(NSNotification *)_notification {
228 WOSession *sn = [_notification object];
229 if (sn) [self sessionTerminated:[sn sessionID]];
234 - (BOOL)registerWithSessionNameService {
235 id<NGSocketAddress> port = nil;
237 NSArray *adaptors = [self->application adaptors];
238 WOAdaptor *adaptor = nil;
240 NSAssert([adaptors count] > 0, @"no adaptors registered for application");
241 *(&adaptor) = [adaptors objectAtIndex:0];
243 if ([adaptor respondsToSelector:@selector(socketAddress)])
244 *(&port) = [adaptor socketAddress];
250 if (self->loggingEnabled)
251 NSLog(@"register instance with snsd.");
258 unsigned char c = SNSRegisterInstance;
260 if (![self->io safeWriteBytes:&c count:sizeof(c)])
261 [[self->io lastException] raise];
263 tmp = [self->application name];
264 len = [tmp cStringLength];
265 NSAssert1(len <= 2000, @"application name to long (%i bytes)..", len);
266 [tmp getCString:buf maxLength:2000];
267 if (![self->io safeWriteBytes:&len count:sizeof(len)])
268 [[self->io lastException] raise];
269 if (![self->io safeWriteBytes:buf count:len])
270 [[self->io lastException] raise];
272 tmp = [self->application path];
273 len = [tmp cStringLength];
274 NSAssert1(len <= 2000, @"bundle name to long (%i bytes) ..", len);
275 [tmp getCString:buf maxLength:2000];
276 if (![self->io safeWriteBytes:&len count:sizeof(len)])
277 [[self->io lastException] raise];
278 if (![self->io safeWriteBytes:buf count:len])
279 [[self->io lastException] raise];
282 if (![self->io safeWriteBytes:&i count:sizeof(i)])
283 [[self->io lastException] raise];
285 { // encode port info
288 data = [NSArchiver archivedDataWithRootObject:port];
290 NSAssert1(len <= 2000, @"socket name to long (%i bytes) ..", len);
291 if (![self->io safeWriteBytes:&len count:sizeof(len)])
292 [[self->io lastException] raise];
293 if (![self->io safeWriteBytes:[data bytes] count:len])
294 [[self->io lastException] raise];
297 if (![self->io flush])
298 [[self->io lastException] raise];
301 if (self->loggingEnabled)
302 NSLog(@"registered instance with snsd: %s", result ? "YES" : "NO");
307 - (void)disconnectFromSessionNameService {
309 if (self->loggingEnabled)
310 NSLog(@"disconnecting instance from snsd ..");
313 unsigned char c = SNSUnregisterInstance;
315 (void)[self->socket safeWriteBytes:&c count:sizeof(c)];
316 (void)[self->socket flush];
318 if ([self->socket respondsToSelector:@selector(shutdownSendChannel)])
319 (void)[(NGActiveSocket *)self->socket shutdownSendChannel];
325 (void)[self->socket shutdown];
330 RELEASE(self->socket); self->socket = nil;
334 - (void)sendMessage:(unsigned char)_msg {
335 if (self->loggingEnabled)
336 NSLog(@"send msg %i", _msg);
337 if (![self->io safeWriteBytes:&_msg count:1])
338 [[self->io lastException] raise];
339 if (![self->io flush])
340 [[self->io lastException] raise];
342 - (void)sendMessage:(unsigned char)_msg sessionID:(NSString *)_sessionID {
346 len = [_sessionID cStringLength];
347 NSAssert1((len < 2000) && (len > 0), @"Invalid session id (%i bytes).", len);
348 [_sessionID getCString:buf maxLength:2000];
350 if (self->loggingEnabled)
351 NSLog(@"send msg %i with sessionID %@ (len=%i)", _msg, _sessionID, len);
353 if (![self->io safeWriteBytes:&_msg count:1])
354 [[self->io lastException] raise];
355 if (![self->io safeWriteBytes:&len count:sizeof(len)])
356 [[self->io lastException] raise];
357 if (![self->io safeWriteBytes:buf count:len])
358 [[self->io lastException] raise];
359 if (![self->io flush])
360 [[self->io lastException] raise];
363 - (void)lostConnectionToNameServer:(NSException *)_exception {
364 [self errorWithFormat:@"application lost connection to snsd: %@", _exception];
365 [[WOApplication application] terminate];
367 - (void)lostConnectionToNameServer {
368 [self lostConnectionToNameServer:nil];
371 - (void)applicationCreatedSession:(NSString *)_sessionID {
373 [self sendMessage:SNSRegisterSession sessionID:_sessionID];
376 [self lostConnectionToNameServer:localException];
381 - (void)sessionTerminated:(NSString *)_sessionID {
383 [self sendMessage:SNSTerminateSession sessionID:_sessionID];
386 [self lostConnectionToNameServer:localException];
390 - (void)sessionExpired:(NSString *)_sessionID {
391 NSLog(@"%s: expired: %@", __PRETTY_FUNCTION__, _sessionID);
393 [self sendMessage:SNSExpireSession sessionID:_sessionID];
396 [self lostConnectionToNameServer:localException];
401 - (void)pingSNS:(NSNotification *)_notification {
403 [self sendMessage:SNSInstanceAlive];
406 [self lostConnectionToNameServer:localException];
411 // back link from SNS
413 - (void)receiveMessage:(NSNotification *)_notification {
414 unsigned char msgCode;
416 if ((id)[_notification object] != (id)self->socket)
419 NSLog(@"SNS: receive message from snsd ..");
420 [self->io safeReadBytes:&msgCode count:1];