2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "SNSConnection.h"
23 #include <NGObjWeb/WOApplication.h>
24 #include <NGObjWeb/WOAdaptor.h>
25 #include <NGObjWeb/WOSession.h>
26 #include <NGStreams/NGStreams.h>
27 #include <NGStreams/NGNet.h>
30 #if !LIB_FOUNDATION_LIBRARY
31 # include <NGExtensions/NSRunLoop+FileObjects.h>
34 // TODO: NEED TO FIX FOR Exception-less IO !!! (safeWrite...)
37 SNSUnregisterInstance = 0,
38 SNSRegisterInstance = 1,
39 SNSRegisterSession = 2,
41 SNSTerminateSession = 4,
42 SNSLookupSession = 50,
43 SNSInstanceAlive = 100
46 @interface SNSConnection(PrivateMethods)
48 - (void)initWithApplication:(WOApplication *)_application;
50 - (BOOL)registerWithSessionNameService;
51 - (void)disconnectFromSessionNameService;
55 @interface WOAdaptor(SocketAddress)
56 - (id<NGSocketAddress>)socketAddress;
59 @implementation SNSConnection
62 static BOOL isInitialized = NO;
64 NSDictionary *snsDefaults = nil;
68 snsDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
69 @"/tmp/.snsd", @"SNSPort",
70 @"60", @"SNSPingInterval",
71 @"NO", @"SNSLogActivity",
73 [[NSUserDefaults standardUserDefaults]
74 registerDefaults:snsDefaults];
78 + (SNSConnection *)defaultSNSConnection {
79 static SNSConnection *connection = nil;
80 if (connection) return connection;
81 connection = [[self alloc] init];
85 - (void)initWithApplication:(WOApplication *)_application {
87 id<NGSocketAddress> sns = nil;
90 ud = [NSUserDefaults standardUserDefaults];
92 self->loggingEnabled = [[ud objectForKey:@"SNSLogActivity"] boolValue];
94 sns = NGSocketAddressFromString([ud stringForKey:@"SNSPort"]);
96 [self errorWithFormat:
97 @"(%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 [self errorWithFormat:@"Could not connect socket %@ to snsd (port=%@), "
132 self->socket, sns, [self->socket lastException]];
133 ASSIGN(self->socket, (id)nil);
135 [[WOApplication application] terminate];
138 self->socket = RETAIN(self->socket);
140 self->io = [NGBufferedStream filterWithSource:self->socket];
141 self->io = RETAIN(self->io);
143 self->application = _application;
146 [self registerWithSessionNameService];
149 RELEASE(self->socket); self->socket = nil;
153 if (self->socket == nil) {
154 [self errorWithFormat:@"Could not register with snsd (port=%@).", sns];
158 [[NSNotificationCenter defaultCenter]
159 addObserver:self selector:@selector(receiveMessage:)
160 name:NSFileObjectBecameActiveNotificationName
161 object:self->socket];
163 int interval = [[ud objectForKey:@"SNSPingInterval"] intValue];
166 [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)interval
167 target:self selector:@selector(pingSNS:)
168 userInfo:nil repeats:YES];
169 self->pingTimer = RETAIN(self->pingTimer);
174 NSNotificationCenter *nc;
176 self->loggingEnabled = [[[NSUserDefaults standardUserDefaults]
177 objectForKey:@"SNSLogActivity"]
180 nc = [NSNotificationCenter defaultCenter];
182 [nc addObserver:self selector:@selector(appDidFinishLaunching:)
183 name:WOApplicationDidFinishLaunchingNotification object:nil];
184 [nc addObserver:self selector:@selector(appWillTerminate:)
185 name:WOApplicationWillTerminateNotification object:nil];
187 [nc addObserver:self selector:@selector(sessionDidCreate:)
188 name:WOSessionDidCreateNotification object:nil];
189 [nc addObserver:self selector:@selector(sessionDidTimeOut:)
190 name:WOSessionDidTimeOutNotification object:nil];
191 [nc addObserver:self selector:@selector(sessionDidTerminate:)
192 name:WOSessionDidTerminateNotification object:nil];
198 [self disconnectFromSessionNameService];
199 [self->pingTimer invalidate];
200 [[NSNotificationCenter defaultCenter] removeObserver:self];
201 [self disconnectFromSessionNameService];
202 RELEASE(self->pingTimer);
204 RELEASE(self->socket);
210 - (void)appDidFinishLaunching:(NSNotification *)_notification {
211 WOApplication *app = [_notification object];
212 [self initWithApplication:app];
214 - (void)appWillTerminate:(NSNotification *)_notification {
215 [self disconnectFromSessionNameService];
218 - (void)sessionDidCreate:(NSNotification *)_notification {
219 WOSession *sn = [_notification object];
220 if (sn) [self applicationCreatedSession:[sn sessionID]];
222 - (void)sessionDidTimeOut:(NSNotification *)_notification {
223 NSString *sn = [_notification object];
224 if (sn) [self sessionExpired:sn];
226 - (void)sessionDidTerminate:(NSNotification *)_notification {
227 WOSession *sn = [_notification object];
228 if (sn) [self sessionTerminated:[sn sessionID]];
233 - (BOOL)registerWithSessionNameService {
234 id<NGSocketAddress> port = nil;
236 NSArray *adaptors = [self->application adaptors];
237 WOAdaptor *adaptor = nil;
239 NSAssert([adaptors count] > 0, @"no adaptors registered for application");
240 *(&adaptor) = [adaptors objectAtIndex:0];
242 if ([adaptor respondsToSelector:@selector(socketAddress)])
243 *(&port) = [adaptor socketAddress];
249 if (self->loggingEnabled)
250 NSLog(@"register instance with snsd.");
257 unsigned char c = SNSRegisterInstance;
259 if (![self->io safeWriteBytes:&c count:sizeof(c)])
260 [[self->io lastException] raise];
262 tmp = [self->application name];
263 len = [tmp cStringLength];
264 NSAssert1(len <= 2000, @"application name to long (%i bytes)..", len);
265 [tmp getCString:buf maxLength:2000];
266 if (![self->io safeWriteBytes:&len count:sizeof(len)])
267 [[self->io lastException] raise];
268 if (![self->io safeWriteBytes:buf count:len])
269 [[self->io lastException] raise];
271 tmp = [self->application path];
272 len = [tmp cStringLength];
273 NSAssert1(len <= 2000, @"bundle name to long (%i bytes) ..", len);
274 [tmp getCString:buf maxLength:2000];
275 if (![self->io safeWriteBytes:&len count:sizeof(len)])
276 [[self->io lastException] raise];
277 if (![self->io safeWriteBytes:buf count:len])
278 [[self->io lastException] raise];
281 if (![self->io safeWriteBytes:&i count:sizeof(i)])
282 [[self->io lastException] raise];
284 { // encode port info
287 data = [NSArchiver archivedDataWithRootObject:port];
289 NSAssert1(len <= 2000, @"socket name to long (%i bytes) ..", len);
290 if (![self->io safeWriteBytes:&len count:sizeof(len)])
291 [[self->io lastException] raise];
292 if (![self->io safeWriteBytes:[data bytes] count:len])
293 [[self->io lastException] raise];
296 if (![self->io flush])
297 [[self->io lastException] raise];
300 if (self->loggingEnabled)
301 NSLog(@"registered instance with snsd: %s", result ? "YES" : "NO");
306 - (void)disconnectFromSessionNameService {
308 if (self->loggingEnabled)
309 NSLog(@"disconnecting instance from snsd ..");
312 unsigned char c = SNSUnregisterInstance;
314 (void)[self->socket safeWriteBytes:&c count:sizeof(c)];
315 (void)[self->socket flush];
317 if ([self->socket respondsToSelector:@selector(shutdownSendChannel)])
318 (void)[(NGActiveSocket *)self->socket shutdownSendChannel];
324 (void)[self->socket shutdown];
329 RELEASE(self->socket); self->socket = nil;
333 - (void)sendMessage:(unsigned char)_msg {
334 if (self->loggingEnabled)
335 NSLog(@"send msg %i", _msg);
336 if (![self->io safeWriteBytes:&_msg count:1])
337 [[self->io lastException] raise];
338 if (![self->io flush])
339 [[self->io lastException] raise];
341 - (void)sendMessage:(unsigned char)_msg sessionID:(NSString *)_sessionID {
345 len = [_sessionID cStringLength];
346 NSAssert1((len < 2000) && (len > 0), @"Invalid session id (%i bytes).", len);
347 [_sessionID getCString:buf maxLength:2000];
349 if (self->loggingEnabled)
350 NSLog(@"send msg %i with sessionID %@ (len=%i)", _msg, _sessionID, len);
352 if (![self->io safeWriteBytes:&_msg count:1])
353 [[self->io lastException] raise];
354 if (![self->io safeWriteBytes:&len count:sizeof(len)])
355 [[self->io lastException] raise];
356 if (![self->io safeWriteBytes:buf count:len])
357 [[self->io lastException] raise];
358 if (![self->io flush])
359 [[self->io lastException] raise];
362 - (void)lostConnectionToNameServer:(NSException *)_exception {
363 [self errorWithFormat:@"application lost connection to snsd: %@", _exception];
364 [[WOApplication application] terminate];
366 - (void)lostConnectionToNameServer {
367 [self lostConnectionToNameServer:nil];
370 - (void)applicationCreatedSession:(NSString *)_sessionID {
372 [self sendMessage:SNSRegisterSession sessionID:_sessionID];
375 [self lostConnectionToNameServer:localException];
380 - (void)sessionTerminated:(NSString *)_sessionID {
382 [self sendMessage:SNSTerminateSession sessionID:_sessionID];
385 [self lostConnectionToNameServer:localException];
389 - (void)sessionExpired:(NSString *)_sessionID {
390 NSLog(@"%s: expired: %@", __PRETTY_FUNCTION__, _sessionID);
392 [self sendMessage:SNSExpireSession sessionID:_sessionID];
395 [self lostConnectionToNameServer:localException];
400 - (void)pingSNS:(NSNotification *)_notification {
402 [self sendMessage:SNSInstanceAlive];
405 [self lostConnectionToNameServer:localException];
410 // back link from SNS
412 - (void)receiveMessage:(NSNotification *)_notification {
413 unsigned char msgCode;
415 if ((id)[_notification object] != (id)self->socket)
418 NSLog(@"SNS: receive message from snsd ..");
419 [self->io safeReadBytes:&msgCode count:1];