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
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 NSLog(@"ERROR(%s): Could not create socket address for snsd(port=%@).",
98 __PRETTY_FUNCTION__, sns);
104 self->socket = [NGActiveSocket socketInDomain:[sns domain]];
107 [self logWithFormat:@"waiting %i seconds for snsd to come up ...",
112 if (![self->socket connectToAddress:sns]) {
113 [self logWithFormat:@" connect failed: %@",
114 [self->socket lastException]];
118 while (![self->socket isConnected] && (waitCnt < 5));
121 self->socket = [NGActiveSocket socketConnectedToAddress:sns];
129 if (![self->socket isConnected]) {
130 NSLog(@"ERROR: Could not connect socket %@ to snsd (port=%@), "
131 @"terminating: %@", self->socket, sns, [self->socket lastException]);
132 ASSIGN(self->socket, (id)nil);
134 [[WOApplication application] terminate];
137 self->socket = RETAIN(self->socket);
139 self->io = [NGBufferedStream filterWithSource:self->socket];
140 self->io = RETAIN(self->io);
142 self->application = _application;
145 [self registerWithSessionNameService];
148 RELEASE(self->socket); self->socket = nil;
152 if (self->socket == nil) {
153 NSLog(@"ERROR: Could not register with snsd (port=%@).", sns);
157 [[NSNotificationCenter defaultCenter]
158 addObserver:self selector:@selector(receiveMessage:)
159 name:NSFileObjectBecameActiveNotificationName
160 object:self->socket];
162 int interval = [[ud objectForKey:@"SNSPingInterval"] intValue];
165 [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)interval
166 target:self selector:@selector(pingSNS:)
167 userInfo:nil repeats:YES];
168 self->pingTimer = RETAIN(self->pingTimer);
173 NSNotificationCenter *nc;
175 self->loggingEnabled = [[[NSUserDefaults standardUserDefaults]
176 objectForKey:@"SNSLogActivity"]
179 nc = [NSNotificationCenter defaultCenter];
181 [nc addObserver:self selector:@selector(appDidFinishLaunching:)
182 name:WOApplicationDidFinishLaunchingNotification object:nil];
183 [nc addObserver:self selector:@selector(appWillTerminate:)
184 name:WOApplicationWillTerminateNotification object:nil];
186 [nc addObserver:self selector:@selector(sessionDidCreate:)
187 name:WOSessionDidCreateNotification object:nil];
188 [nc addObserver:self selector:@selector(sessionDidTimeOut:)
189 name:WOSessionDidTimeOutNotification object:nil];
190 [nc addObserver:self selector:@selector(sessionDidTerminate:)
191 name:WOSessionDidTerminateNotification object:nil];
197 [self disconnectFromSessionNameService];
198 [self->pingTimer invalidate];
199 [[NSNotificationCenter defaultCenter] removeObserver:self];
200 [self disconnectFromSessionNameService];
201 RELEASE(self->pingTimer);
203 RELEASE(self->socket);
209 - (void)appDidFinishLaunching:(NSNotification *)_notification {
210 WOApplication *app = [_notification object];
211 [self initWithApplication:app];
213 - (void)appWillTerminate:(NSNotification *)_notification {
214 [self disconnectFromSessionNameService];
217 - (void)sessionDidCreate:(NSNotification *)_notification {
218 WOSession *sn = [_notification object];
219 if (sn) [self applicationCreatedSession:[sn sessionID]];
221 - (void)sessionDidTimeOut:(NSNotification *)_notification {
222 NSString *sn = [_notification object];
223 if (sn) [self sessionExpired:sn];
225 - (void)sessionDidTerminate:(NSNotification *)_notification {
226 WOSession *sn = [_notification object];
227 if (sn) [self sessionTerminated:[sn sessionID]];
232 - (BOOL)registerWithSessionNameService {
233 id<NGSocketAddress> port = nil;
235 NSArray *adaptors = [self->application adaptors];
236 WOAdaptor *adaptor = nil;
238 NSAssert([adaptors count] > 0, @"no adaptors registered for application");
239 *(&adaptor) = [adaptors objectAtIndex:0];
241 if ([adaptor respondsToSelector:@selector(socketAddress)])
242 *(&port) = [adaptor socketAddress];
248 if (self->loggingEnabled)
249 NSLog(@"register instance with snsd.");
256 unsigned char c = SNSRegisterInstance;
258 if (![self->io safeWriteBytes:&c count:sizeof(c)])
259 [[self->io lastException] raise];
261 tmp = [self->application name];
262 len = [tmp cStringLength];
263 NSAssert1(len <= 2000, @"application name to long (%i bytes)..", len);
264 [tmp getCString:buf maxLength:2000];
265 if (![self->io safeWriteBytes:&len count:sizeof(len)])
266 [[self->io lastException] raise];
267 if (![self->io safeWriteBytes:buf count:len])
268 [[self->io lastException] raise];
270 tmp = [self->application path];
271 len = [tmp cStringLength];
272 NSAssert1(len <= 2000, @"bundle name to long (%i bytes) ..", len);
273 [tmp getCString:buf maxLength:2000];
274 if (![self->io safeWriteBytes:&len count:sizeof(len)])
275 [[self->io lastException] raise];
276 if (![self->io safeWriteBytes:buf count:len])
277 [[self->io lastException] raise];
280 if (![self->io safeWriteBytes:&i count:sizeof(i)])
281 [[self->io lastException] raise];
283 { // encode port info
286 data = [NSArchiver archivedDataWithRootObject:port];
288 NSAssert1(len <= 2000, @"socket name to long (%i bytes) ..", len);
289 if (![self->io safeWriteBytes:&len count:sizeof(len)])
290 [[self->io lastException] raise];
291 if (![self->io safeWriteBytes:[data bytes] count:len])
292 [[self->io lastException] raise];
295 if (![self->io flush])
296 [[self->io lastException] raise];
299 if (self->loggingEnabled)
300 NSLog(@"registered instance with snsd: %s", result ? "YES" : "NO");
305 - (void)disconnectFromSessionNameService {
307 if (self->loggingEnabled)
308 NSLog(@"disconnecting instance from snsd ..");
311 unsigned char c = SNSUnregisterInstance;
313 (void)[self->socket safeWriteBytes:&c count:sizeof(c)];
314 (void)[self->socket flush];
316 if ([self->socket respondsToSelector:@selector(shutdownSendChannel)])
317 (void)[(NGActiveSocket *)self->socket shutdownSendChannel];
323 (void)[self->socket shutdown];
328 RELEASE(self->socket); self->socket = nil;
332 - (void)sendMessage:(unsigned char)_msg {
333 if (self->loggingEnabled)
334 NSLog(@"send msg %i", _msg);
335 if (![self->io safeWriteBytes:&_msg count:1])
336 [[self->io lastException] raise];
337 if (![self->io flush])
338 [[self->io lastException] raise];
340 - (void)sendMessage:(unsigned char)_msg sessionID:(NSString *)_sessionID {
344 len = [_sessionID cStringLength];
345 NSAssert1((len < 2000) && (len > 0), @"Invalid session id (%i bytes).", len);
346 [_sessionID getCString:buf maxLength:2000];
348 if (self->loggingEnabled)
349 NSLog(@"send msg %i with sessionID %@ (len=%i)", _msg, _sessionID, len);
351 if (![self->io safeWriteBytes:&_msg count:1])
352 [[self->io lastException] raise];
353 if (![self->io safeWriteBytes:&len count:sizeof(len)])
354 [[self->io lastException] raise];
355 if (![self->io safeWriteBytes:buf count:len])
356 [[self->io lastException] raise];
357 if (![self->io flush])
358 [[self->io lastException] raise];
361 - (void)lostConnectionToNameServer:(NSException *)_exception {
362 NSLog(@"ERROR: application lost connection to snsd: %@", _exception);
363 [[WOApplication application] terminate];
365 - (void)lostConnectionToNameServer {
366 [self lostConnectionToNameServer:nil];
369 - (void)applicationCreatedSession:(NSString *)_sessionID {
371 [self sendMessage:SNSRegisterSession sessionID:_sessionID];
374 [self lostConnectionToNameServer:localException];
379 - (void)sessionTerminated:(NSString *)_sessionID {
381 [self sendMessage:SNSTerminateSession sessionID:_sessionID];
384 [self lostConnectionToNameServer:localException];
388 - (void)sessionExpired:(NSString *)_sessionID {
389 NSLog(@"%s: expired: %@", __PRETTY_FUNCTION__, _sessionID);
391 [self sendMessage:SNSExpireSession sessionID:_sessionID];
394 [self lostConnectionToNameServer:localException];
399 - (void)pingSNS:(NSNotification *)_notification {
401 [self sendMessage:SNSInstanceAlive];
404 [self lostConnectionToNameServer:localException];
409 // back link from SNS
411 - (void)receiveMessage:(NSNotification *)_notification {
412 unsigned char msgCode;
414 if ((id)[_notification object] != (id)self->socket)
417 NSLog(@"SNS: receive message from snsd ..");
418 [self->io safeReadBytes:&msgCode count:1];