]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SNSConnection.m
fixed makefiles to search FHS locations at the last resort
[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$
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     NSLog(@"ERROR(%s): Could not create socket address for snsd(port=%@).",
98           __PRETTY_FUNCTION__, sns);
99     RELEASE(self);
100     return;
101   }
102
103 #if 1
104   self->socket = [NGActiveSocket socketInDomain:[sns domain]];
105   do {
106     if (waitCnt > 0) {
107       [self logWithFormat:@"waiting %i seconds for snsd to come up ...",
108               waitCnt];
109       sleep(waitCnt);
110     }
111     
112     if (![self->socket connectToAddress:sns]) {
113       [self logWithFormat:@"  connect failed: %@",
114               [self->socket lastException]];
115     }
116     waitCnt++;
117   }
118   while (![self->socket isConnected] && (waitCnt < 5));
119 #else
120   NS_DURING {
121     self->socket = [NGActiveSocket socketConnectedToAddress:sns];
122   }
123   NS_HANDLER {
124     self->socket = nil;
125   }
126   NS_ENDHANDLER;
127 #endif
128   
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);
133     RELEASE(self);
134     [[WOApplication application] terminate];
135     return;
136   }
137   self->socket = RETAIN(self->socket);
138
139   self->io = [NGBufferedStream filterWithSource:self->socket];
140   self->io = RETAIN(self->io);
141     
142   self->application = _application;
143
144   NS_DURING {
145     [self registerWithSessionNameService];
146   }
147   NS_HANDLER {
148     RELEASE(self->socket); self->socket = nil;
149   }
150   NS_ENDHANDLER;
151   
152   if (self->socket == nil) {
153     NSLog(@"ERROR: Could not register with snsd (port=%@).", sns);
154     RELEASE(self);
155     return;
156   }
157     [[NSNotificationCenter defaultCenter]
158                            addObserver:self selector:@selector(receiveMessage:)
159                            name:NSFileObjectBecameActiveNotificationName
160                            object:self->socket];
161     { // ping
162       int interval = [[ud objectForKey:@"SNSPingInterval"] intValue];
163       if (interval > 0) {
164         self->pingTimer =
165           [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)interval
166                    target:self selector:@selector(pingSNS:)
167                    userInfo:nil repeats:YES];
168         self->pingTimer = RETAIN(self->pingTimer);
169       }
170     }
171 }
172 - (id)init {
173   NSNotificationCenter *nc;
174   
175   self->loggingEnabled = [[[NSUserDefaults standardUserDefaults]
176                                            objectForKey:@"SNSLogActivity"]
177                                            boolValue];
178
179   nc = [NSNotificationCenter defaultCenter];
180
181   [nc addObserver:self selector:@selector(appDidFinishLaunching:)
182       name:WOApplicationDidFinishLaunchingNotification object:nil];
183   [nc addObserver:self selector:@selector(appWillTerminate:)
184       name:WOApplicationWillTerminateNotification object:nil];
185   
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];
192
193   return self;
194 }
195
196 - (void)dealloc {
197   [self disconnectFromSessionNameService];
198   [self->pingTimer invalidate];
199   [[NSNotificationCenter defaultCenter] removeObserver:self];
200   [self disconnectFromSessionNameService];
201   RELEASE(self->pingTimer);
202   RELEASE(self->io);
203   RELEASE(self->socket);
204   [super dealloc];
205 }
206
207 /* notifications */
208
209 - (void)appDidFinishLaunching:(NSNotification *)_notification {
210   WOApplication *app = [_notification object];
211   [self initWithApplication:app];
212 }
213 - (void)appWillTerminate:(NSNotification *)_notification {
214   [self disconnectFromSessionNameService];
215 }
216
217 - (void)sessionDidCreate:(NSNotification *)_notification {
218   WOSession *sn = [_notification object];
219   if (sn) [self applicationCreatedSession:[sn sessionID]];
220 }
221 - (void)sessionDidTimeOut:(NSNotification *)_notification {
222   NSString *sn = [_notification object];
223   if (sn) [self sessionExpired:sn];
224 }
225 - (void)sessionDidTerminate:(NSNotification *)_notification {
226   WOSession *sn = [_notification object];
227   if (sn) [self sessionTerminated:[sn sessionID]];
228 }
229
230 /* connection */
231
232 - (BOOL)registerWithSessionNameService {
233   id<NGSocketAddress> port = nil;
234   BOOL      result    = NO;
235   NSArray   *adaptors = [self->application adaptors];
236   WOAdaptor *adaptor  = nil;
237
238   NSAssert([adaptors count] > 0, @"no adaptors registered for application");
239   *(&adaptor) = [adaptors objectAtIndex:0];
240
241   if ([adaptor respondsToSelector:@selector(socketAddress)])
242     *(&port) = [adaptor socketAddress];
243   if (port == nil)
244     return NO;
245
246   *(&result) = YES;
247
248   if (self->loggingEnabled)
249     NSLog(@"register instance with snsd.");
250   
251   {
252     NSString      *tmp;
253     int           len;
254     char          buf[2048];
255     unsigned int  i;
256     unsigned char c = SNSRegisterInstance;
257
258     if (![self->io safeWriteBytes:&c count:sizeof(c)])
259       [[self->io lastException] raise];
260
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];
269
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];
278
279     i = getpid();
280     if (![self->io safeWriteBytes:&i count:sizeof(i)])
281       [[self->io lastException] raise];
282     
283     { // encode port info
284       NSData *data;
285
286       data = [NSArchiver archivedDataWithRootObject:port];
287       len = [data length];
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];
293     }
294
295     if (![self->io flush])
296       [[self->io lastException] raise];
297   }
298   
299   if (self->loggingEnabled)
300     NSLog(@"registered instance with snsd: %s", result ? "YES" : "NO");
301   
302   return result;
303 }
304
305 - (void)disconnectFromSessionNameService {
306   if (self->socket) {
307     if (self->loggingEnabled)
308       NSLog(@"disconnecting instance from snsd ..");
309   
310     NS_DURING {
311       unsigned char c = SNSUnregisterInstance;
312       
313       (void)[self->socket safeWriteBytes:&c count:sizeof(c)];
314       (void)[self->socket flush];
315       
316       if ([self->socket respondsToSelector:@selector(shutdownSendChannel)])
317         (void)[(NGActiveSocket *)self->socket shutdownSendChannel];
318     }
319     NS_HANDLER {}
320     NS_ENDHANDLER;
321     
322     NS_DURING {
323       (void)[self->socket shutdown];
324     }
325     NS_HANDLER {}
326     NS_ENDHANDLER;
327
328     RELEASE(self->socket); self->socket = nil;
329   }
330 }
331
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];
339 }
340 - (void)sendMessage:(unsigned char)_msg sessionID:(NSString *)_sessionID {
341   int  len;
342   char buf[2048];
343
344   len = [_sessionID cStringLength];
345   NSAssert1((len < 2000) && (len > 0), @"Invalid session id (%i bytes).", len);
346   [_sessionID getCString:buf maxLength:2000];
347
348   if (self->loggingEnabled)
349     NSLog(@"send msg %i with sessionID %@ (len=%i)", _msg, _sessionID, len);
350
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];
359 }
360
361 - (void)lostConnectionToNameServer:(NSException *)_exception {
362   NSLog(@"ERROR: application lost connection to snsd: %@", _exception);
363   [[WOApplication application] terminate];
364 }
365 - (void)lostConnectionToNameServer {
366   [self lostConnectionToNameServer:nil];
367 }
368
369 - (void)applicationCreatedSession:(NSString *)_sessionID {
370   NS_DURING {
371     [self sendMessage:SNSRegisterSession sessionID:_sessionID];
372   }
373   NS_HANDLER {
374     [self lostConnectionToNameServer:localException];
375   }
376   NS_ENDHANDLER;
377 }
378
379 - (void)sessionTerminated:(NSString *)_sessionID {
380   NS_DURING {
381     [self sendMessage:SNSTerminateSession sessionID:_sessionID];
382   }
383   NS_HANDLER {
384     [self lostConnectionToNameServer:localException];
385   }
386   NS_ENDHANDLER;
387 }
388 - (void)sessionExpired:(NSString *)_sessionID {
389   NSLog(@"%s: expired: %@", __PRETTY_FUNCTION__, _sessionID);
390   NS_DURING {
391     [self sendMessage:SNSExpireSession sessionID:_sessionID];
392   }
393   NS_HANDLER {
394     [self lostConnectionToNameServer:localException];
395   }
396   NS_ENDHANDLER;
397 }
398
399 - (void)pingSNS:(NSNotification *)_notification {
400   NS_DURING {
401     [self sendMessage:SNSInstanceAlive];
402   }
403   NS_HANDLER {
404     [self lostConnectionToNameServer:localException];
405   }
406   NS_ENDHANDLER;
407 }
408
409 // back link from SNS
410
411 - (void)receiveMessage:(NSNotification *)_notification {
412   unsigned char msgCode;
413   
414   if ((id)[_notification object] != (id)self->socket)
415     return;
416
417   NSLog(@"SNS: receive message from snsd ..");
418   [self->io safeReadBytes:&msgCode count:1];
419 }
420
421 @end