]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SNSConnection.m
fixed various warnings
[sope] / sope-appserver / NGObjWeb / SNSConnection.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 // $Id: SNSConnection.m 14 2004-08-20 21:07:18Z helge $
22
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>
29 #include "common.h"
30
31 #if !LIB_FOUNDATION_LIBRARY
32 #  include <NGExtensions/NSRunLoop+FileObjects.h>
33 #endif
34
35 // TODO: NEED TO FIX FOR Exception-less IO !!! (safeWrite...)
36
37 typedef enum {
38   SNSUnregisterInstance = 0,
39   SNSRegisterInstance   = 1,
40   SNSRegisterSession    = 2,
41   SNSExpireSession      = 3,
42   SNSTerminateSession   = 4,
43   SNSLookupSession      = 50,
44   SNSInstanceAlive      = 100
45 } SNSMessageCode;
46
47 @interface SNSConnection(PrivateMethods)
48
49 - (void)initWithApplication:(WOApplication *)_application;
50
51 - (BOOL)registerWithSessionNameService;
52 - (void)disconnectFromSessionNameService;
53
54 @end
55
56 @interface WOAdaptor(SocketAddress)
57 - (id<NGSocketAddress>)socketAddress;
58 @end
59
60 @implementation SNSConnection
61
62 + (void)initialize {
63   static BOOL isInitialized = NO;
64   if (!isInitialized) {
65     NSDictionary *snsDefaults = nil;
66     
67     isInitialized = YES;
68
69     snsDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
70                                   @"/tmp/.snsd", @"SNSPort",
71                                   @"60",         @"SNSPingInterval",
72                                   @"NO",         @"SNSLogActivity",
73                                   nil];
74     [[NSUserDefaults standardUserDefaults]
75                      registerDefaults:snsDefaults];
76   }
77 }
78
79 + (SNSConnection *)defaultSNSConnection {
80   static SNSConnection *connection = nil;
81   if (connection) return connection;
82   connection = [[self alloc] init];
83   return connection;
84 }
85
86 - (void)initWithApplication:(WOApplication *)_application {
87   NSUserDefaults *ud;
88   id<NGSocketAddress> sns = nil;
89   int waitCnt = 0;
90   
91   ud = [NSUserDefaults standardUserDefaults];
92     
93   self->loggingEnabled = [[ud objectForKey:@"SNSLogActivity"] boolValue];
94   
95   sns = NGSocketAddressFromString([ud stringForKey:@"SNSPort"]);
96   if (sns == nil) {
97     [self errorWithFormat:
98             @"(%s): Could not create socket address for snsd(port=%@).",
99             __PRETTY_FUNCTION__, sns];
100     RELEASE(self);
101     return;
102   }
103
104 #if 1
105   self->socket = [NGActiveSocket socketInDomain:[sns domain]];
106   do {
107     if (waitCnt > 0) {
108       [self logWithFormat:@"waiting %i seconds for snsd to come up ...",
109               waitCnt];
110       sleep(waitCnt);
111     }
112     
113     if (![self->socket connectToAddress:sns]) {
114       [self logWithFormat:@"  connect failed: %@",
115               [self->socket lastException]];
116     }
117     waitCnt++;
118   }
119   while (![self->socket isConnected] && (waitCnt < 5));
120 #else
121   NS_DURING {
122     self->socket = [NGActiveSocket socketConnectedToAddress:sns];
123   }
124   NS_HANDLER {
125     self->socket = nil;
126   }
127   NS_ENDHANDLER;
128 #endif
129   
130   if (![self->socket isConnected]) {
131     [self errorWithFormat:@"Could not connect socket %@ to snsd (port=%@), "
132             @"terminating: %@",
133             self->socket, sns, [self->socket lastException]];
134     ASSIGN(self->socket, (id)nil);
135     RELEASE(self);
136     [[WOApplication application] terminate];
137     return;
138   }
139   self->socket = RETAIN(self->socket);
140
141   self->io = [NGBufferedStream filterWithSource:self->socket];
142   self->io = RETAIN(self->io);
143     
144   self->application = _application;
145
146   NS_DURING {
147     [self registerWithSessionNameService];
148   }
149   NS_HANDLER {
150     RELEASE(self->socket); self->socket = nil;
151   }
152   NS_ENDHANDLER;
153   
154   if (self->socket == nil) {
155     [self errorWithFormat:@"Could not register with snsd (port=%@).", sns];
156     RELEASE(self);
157     return;
158   }
159     [[NSNotificationCenter defaultCenter]
160                            addObserver:self selector:@selector(receiveMessage:)
161                            name:NSFileObjectBecameActiveNotificationName
162                            object:self->socket];
163     { // ping
164       int interval = [[ud objectForKey:@"SNSPingInterval"] intValue];
165       if (interval > 0) {
166         self->pingTimer =
167           [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)interval
168                    target:self selector:@selector(pingSNS:)
169                    userInfo:nil repeats:YES];
170         self->pingTimer = RETAIN(self->pingTimer);
171       }
172     }
173 }
174 - (id)init {
175   NSNotificationCenter *nc;
176   
177   self->loggingEnabled = [[[NSUserDefaults standardUserDefaults]
178                                            objectForKey:@"SNSLogActivity"]
179                                            boolValue];
180
181   nc = [NSNotificationCenter defaultCenter];
182
183   [nc addObserver:self selector:@selector(appDidFinishLaunching:)
184       name:WOApplicationDidFinishLaunchingNotification object:nil];
185   [nc addObserver:self selector:@selector(appWillTerminate:)
186       name:WOApplicationWillTerminateNotification object:nil];
187   
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];
194
195   return self;
196 }
197
198 - (void)dealloc {
199   [self disconnectFromSessionNameService];
200   [self->pingTimer invalidate];
201   [[NSNotificationCenter defaultCenter] removeObserver:self];
202   [self disconnectFromSessionNameService];
203   RELEASE(self->pingTimer);
204   RELEASE(self->io);
205   RELEASE(self->socket);
206   [super dealloc];
207 }
208
209 /* notifications */
210
211 - (void)appDidFinishLaunching:(NSNotification *)_notification {
212   WOApplication *app = [_notification object];
213   [self initWithApplication:app];
214 }
215 - (void)appWillTerminate:(NSNotification *)_notification {
216   [self disconnectFromSessionNameService];
217 }
218
219 - (void)sessionDidCreate:(NSNotification *)_notification {
220   WOSession *sn = [_notification object];
221   if (sn) [self applicationCreatedSession:[sn sessionID]];
222 }
223 - (void)sessionDidTimeOut:(NSNotification *)_notification {
224   NSString *sn = [_notification object];
225   if (sn) [self sessionExpired:sn];
226 }
227 - (void)sessionDidTerminate:(NSNotification *)_notification {
228   WOSession *sn = [_notification object];
229   if (sn) [self sessionTerminated:[sn sessionID]];
230 }
231
232 /* connection */
233
234 - (BOOL)registerWithSessionNameService {
235   id<NGSocketAddress> port = nil;
236   BOOL      result    = NO;
237   NSArray   *adaptors = [self->application adaptors];
238   WOAdaptor *adaptor  = nil;
239
240   NSAssert([adaptors count] > 0, @"no adaptors registered for application");
241   *(&adaptor) = [adaptors objectAtIndex:0];
242
243   if ([adaptor respondsToSelector:@selector(socketAddress)])
244     *(&port) = [adaptor socketAddress];
245   if (port == nil)
246     return NO;
247
248   *(&result) = YES;
249
250   if (self->loggingEnabled)
251     NSLog(@"register instance with snsd.");
252   
253   {
254     NSString      *tmp;
255     int           len;
256     char          buf[2048];
257     unsigned int  i;
258     unsigned char c = SNSRegisterInstance;
259
260     if (![self->io safeWriteBytes:&c count:sizeof(c)])
261       [[self->io lastException] raise];
262
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];
271
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];
280
281     i = getpid();
282     if (![self->io safeWriteBytes:&i count:sizeof(i)])
283       [[self->io lastException] raise];
284     
285     { // encode port info
286       NSData *data;
287
288       data = [NSArchiver archivedDataWithRootObject:port];
289       len = [data length];
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];
295     }
296
297     if (![self->io flush])
298       [[self->io lastException] raise];
299   }
300   
301   if (self->loggingEnabled)
302     NSLog(@"registered instance with snsd: %s", result ? "YES" : "NO");
303   
304   return result;
305 }
306
307 - (void)disconnectFromSessionNameService {
308   if (self->socket) {
309     if (self->loggingEnabled)
310       NSLog(@"disconnecting instance from snsd ..");
311   
312     NS_DURING {
313       unsigned char c = SNSUnregisterInstance;
314       
315       (void)[self->socket safeWriteBytes:&c count:sizeof(c)];
316       (void)[self->socket flush];
317       
318       if ([self->socket respondsToSelector:@selector(shutdownSendChannel)])
319         (void)[(NGActiveSocket *)self->socket shutdownSendChannel];
320     }
321     NS_HANDLER {}
322     NS_ENDHANDLER;
323     
324     NS_DURING {
325       (void)[self->socket shutdown];
326     }
327     NS_HANDLER {}
328     NS_ENDHANDLER;
329
330     RELEASE(self->socket); self->socket = nil;
331   }
332 }
333
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];
341 }
342 - (void)sendMessage:(unsigned char)_msg sessionID:(NSString *)_sessionID {
343   int  len;
344   char buf[2048];
345
346   len = [_sessionID cStringLength];
347   NSAssert1((len < 2000) && (len > 0), @"Invalid session id (%i bytes).", len);
348   [_sessionID getCString:buf maxLength:2000];
349
350   if (self->loggingEnabled)
351     NSLog(@"send msg %i with sessionID %@ (len=%i)", _msg, _sessionID, len);
352
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];
361 }
362
363 - (void)lostConnectionToNameServer:(NSException *)_exception {
364   [self errorWithFormat:@"application lost connection to snsd: %@", _exception];
365   [[WOApplication application] terminate];
366 }
367 - (void)lostConnectionToNameServer {
368   [self lostConnectionToNameServer:nil];
369 }
370
371 - (void)applicationCreatedSession:(NSString *)_sessionID {
372   NS_DURING {
373     [self sendMessage:SNSRegisterSession sessionID:_sessionID];
374   }
375   NS_HANDLER {
376     [self lostConnectionToNameServer:localException];
377   }
378   NS_ENDHANDLER;
379 }
380
381 - (void)sessionTerminated:(NSString *)_sessionID {
382   NS_DURING {
383     [self sendMessage:SNSTerminateSession sessionID:_sessionID];
384   }
385   NS_HANDLER {
386     [self lostConnectionToNameServer:localException];
387   }
388   NS_ENDHANDLER;
389 }
390 - (void)sessionExpired:(NSString *)_sessionID {
391   NSLog(@"%s: expired: %@", __PRETTY_FUNCTION__, _sessionID);
392   NS_DURING {
393     [self sendMessage:SNSExpireSession sessionID:_sessionID];
394   }
395   NS_HANDLER {
396     [self lostConnectionToNameServer:localException];
397   }
398   NS_ENDHANDLER;
399 }
400
401 - (void)pingSNS:(NSNotification *)_notification {
402   NS_DURING {
403     [self sendMessage:SNSInstanceAlive];
404   }
405   NS_HANDLER {
406     [self lostConnectionToNameServer:localException];
407   }
408   NS_ENDHANDLER;
409 }
410
411 // back link from SNS
412
413 - (void)receiveMessage:(NSNotification *)_notification {
414   unsigned char msgCode;
415   
416   if ((id)[_notification object] != (id)self->socket)
417     return;
418
419   NSLog(@"SNS: receive message from snsd ..");
420   [self->io safeReadBytes:&msgCode count:1];
421 }
422
423 @end