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