StropheCappuccino API 1.0.0
/tmp/tempDoc.doc/TNStropheConnection.j
Go to the documentation of this file.
00001 /*
00002  * TNStropheConnection.j
00003  *
00004  * Copyright (C) 2010  Antoine Mercadal <antoine.mercadal@inframonde.eu>
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 3.0 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 
00022 
00023 TNStropheConnectionStatusAuthenticatingNotification     = @"TNStropheConnectionStatusAuthenticatingNotification"
00024 TNStropheConnectionStatusAuthFailureNotification        = @"TNStropheConnectionStatusAuthFailureNotification";
00025 TNStropheConnectionStatusConnectedNotification          = @"TNStropheConnectionStatusConnectedNotification";
00026 TNStropheConnectionStatusConnectingNotification         = @"TNStropheConnectionStatusConnectingNotification";
00027 TNStropheConnectionStatusConnectionFailureNotification  = @"TNStropheConnectionStatusConnectionFailureNotification";
00028 TNStropheConnectionStatusDisconnectedNotification       = @"TNStropheConnectionStatusDisconnectedNotification";
00029 TNStropheConnectionStatusDisconnectingNotification      = @"TNStropheConnectionStatusDisconnectingNotification";
00030 TNStropheConnectionStatusErrorNotification              = @"TNStropheConnectionStatusErrorNotification";
00031 TNStropheConnectionStatusWillDisconnectNotification     = @"TNStropheConnectionStatusWillDisconnectNotification";
00032 
00033 
00034 var TNStropheTimerRunLoopMode = @"TNStropheTimerRunLoopMode";
00035 
00076 @implementation TNStropheConnection : CPObject
00077 {
00078     BOOL            _connected;
00079     CPString        _password;
00080     float           _giveupTimeout;
00081     id              _currentStatus;
00082     id              _delegate;
00083     int             _connectionTimeout;
00084     int             _maxConnections;
00085 
00086     CPArray         _registeredHandlers;
00087     CPArray         _registeredTimedHandlers;
00088     CPDictionary    _timersIds;
00089     CPString        _boshService;
00090     CPString        _userPresenceShow;
00091     CPString        _userPresenceStatus;
00092     CPTimer         _giveUpTimer;
00093     float           _stropheJSRunloopInterval;
00094     id              _connection;
00095     TNStropheJID    _JID;
00096 }
00097 
00098 #pragma mark -
00099 #pragma mark Class methods
00100 
00101 + (void)addNamespaceWithName:(CPString)aName value:(CPString)aValue
00102 {
00103     Strophe.addNamespace(aName, aValue);
00104 }
00105 
00112 + (TNStropheConnection)connectionWithService:(CPString)aService andDelegate:(id)aDelegate
00113 {
00114     return [[TNStropheConnection alloc] initWithService:aService andDelegate:aDelegate];
00115 }
00116 
00117 
00118 #pragma mark -
00119 #pragma mark Initialization
00120 
00125 - (id)initWithService:(CPString)aService andDelegate:(id)aDelegate
00126 {
00127     if (self = [super init])
00128     {
00129         var bundle = [CPBundle bundleForClass:[self class]];
00130         _registeredHandlers         = [CPArray array];
00131         _registeredTimedHandlers    = [CPArray array];
00132         _connected                  = NO;
00133         _maxConnections             = [bundle objectForInfoDictionaryKey:@"TNStropheConnectionMaxConnection"];
00134         _connectionTimeout          = [bundle objectForInfoDictionaryKey:@"TNStropheConnectionTimeout"];
00135         _giveupTimeout              = [bundle objectForInfoDictionaryKey:@"TNStropheConnectionGiveUpTimer"];
00136         _currentStatus              = Strophe.Status.DISCONNECTED;
00137         _boshService                = aService;
00138         _connection                 = new Strophe.Connection(_boshService);
00139         _delegate                   = aDelegate;
00140         _timersIds                  = [CPDictionary dictionary];
00141         _stropheJSRunloopInterval   = [bundle objectForInfoDictionaryKey:@"TNStropheJSRunLoopInterval"];
00142 
00143         if ([bundle objectForInfoDictionaryKey:@"TNStropheJSUseCappuccinoRunLoop"] == 1)
00144         {
00145             CPLog.info("StropheCappuccino has been compiled to use the Cappuccino runloop. unsing interval of " + _stropheJSRunloopInterval);
00146             Strophe.setTimeout = function(f, delay)
00147             {
00148                 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00149 
00150                 var timerID = [self getUniqueId],
00151                     timer = [CPTimer timerWithTimeInterval:_stropheJSRunloopInterval target:self selector:@selector(triggerStropheTimer:) userInfo:{"function": f, "id": timerID} repeats:NO];
00152 
00153                 [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
00154                 [_timersIds setObject:timer forKey:timerID];
00155                 return timerID;
00156             }
00157 
00158             Strophe.clearTimeout = function(tid)
00159             {
00160                 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00161 
00162                 var timer = [_timersIds objectForKey:tid];
00163                 [timer invalidate];
00164                 [_timersIds removeObjectForKey:tid];
00165             }
00166         }
00167     }
00168 
00169     return self;
00170 }
00171 
00172 #pragma mark -
00173 #pragma mark Cappuccino's stropheJS runloop
00174 
00178 - (void)triggerStropheTimer:(CPTimer)aTimer
00179 {
00180     [_timersIds removeObjectForKey:[aTimer userInfo]["id"]];
00181     [aTimer userInfo]["function"]();
00182     [aTimer invalidate];
00183 }
00184 
00185 
00186 #pragma mark -
00187 #pragma mark Connection
00188 
00189 - (TNStropheJID)JID
00190 {
00191     if ([_delegate respondsToSelector:@selector(JID)])
00192         return [_delegate JID];
00193     else
00194         return _JID;
00195 }
00196 
00199 - (void)connectWithJID:(TNStropheJID)aJID andPassword:(CPString)aPassword
00200 {
00201     if (_currentStatus !== Strophe.Status.DISCONNECTED)
00202         return;
00203 
00204     _JID = aJID;
00205 
00206     _connection.connect([aJID full], aPassword, function (status, errorCond)
00207     {
00208         [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00209 
00210         var selector,
00211             notificationName;
00212 
00213         _currentStatus = status;
00214 
00215         if (errorCond)
00216         {
00217             _currentStatus = Strophe.Status.DISCONNECTED;
00218 
00219             if ([_delegate respondsToSelector:@selector(connection:errorCondition:)])
00220                 [_delegate connection:self errorCondition:errorCond];
00221         }
00222         else
00223         {
00224             switch (status)
00225             {
00226                 case Strophe.Status.ERROR:
00227                     selector            = @selector(onStropheError:);
00228                     notificationName    = TNStropheConnectionStatusErrorNotification;
00229                     break;
00230                 case Strophe.Status.CONNECTING:
00231                     selector            = @selector(onStropheConnecting:);
00232                     notificationName    = TNStropheConnectionStatusConnectingNotification;
00233                     _giveUpTimer = [CPTimer scheduledTimerWithTimeInterval:_giveupTimeout callback:function(aTimer) {
00234                             _currentStatus  = Strophe.Status.DISCONNECTED;
00235                             _giveUpTimer    = nil;
00236                             [self reset]
00237                             if ((_currentStatus === Strophe.Status.CONNECTING) && ([_delegate respondsToSelector:@selector(connection:errorCondition:)]))
00238                                 [_delegate connection:self errorCondition:@"Cannot connect"];
00239                         } repeats:NO];
00240 
00241                     break;
00242                 case Strophe.Status.CONNFAIL:
00243                     selector            = @selector(onStropheConnectFail:);
00244                     notificationName    = TNStropheConnectionStatusConnectionFailureNotification;
00245                     _connected          = NO;
00246                     break;
00247                 case Strophe.Status.AUTHENTICATING:
00248                     selector            = @selector(onStropheAuthenticating:);
00249                     notificationName    = TNStropheConnectionStatusAuthenticatingNotification;
00250                     _connected          = NO;
00251                     break;
00252                 case Strophe.Status.AUTHFAIL:
00253                     selector            = @selector(onStropheAuthFail:);
00254                     notificationName    = TNStropheConnectionStatusAuthFailureNotification;
00255                     _connected          = NO;
00256                     break;
00257                 case Strophe.Status.DISCONNECTING:
00258                     selector            = @selector(onStropheDisconnecting:);
00259                     notificationName    = TNStropheConnectionStatusDisconnectingNotification;
00260                     _connected          = YES;
00261                     break;
00262                 case Strophe.Status.DISCONNECTED:
00263                     [self deleteAllRegisteredSelectors];
00264                     selector            = @selector(onStropheDisconnected:);
00265                     notificationName    = TNStropheConnectionStatusDisconnectedNotification;
00266                     _connected          = NO;
00267                     break;
00268                 case Strophe.Status.CONNECTED:
00269                     selector            = @selector(onStropheConnected:);
00270                     notificationName    = TNStropheConnectionStatusConnectedNotification;
00271                     _connected          = YES;
00272                     if (_giveUpTimer)
00273                         [_giveUpTimer invalidate];
00274                     break;
00275             }
00276         }
00277 
00278         if (selector && [_delegate respondsToSelector:selector])
00279             [_delegate performSelector:selector withObject:self];
00280 
00281         if (notificationName)
00282             [[CPNotificationCenter defaultCenter] postNotificationName:notificationName object:self];
00283 
00284     }, /* wait */ _connectionTimeout, /* hold */ _maxConnections);
00285 }
00286 
00289 - (void)disconnect
00290 {
00291     if (_currentStatus === Strophe.Status.DISCONNECTED)
00292         return;
00293 
00294     [[CPNotificationCenter defaultCenter] postNotificationName:TNStropheConnectionStatusWillDisconnectNotification object:self];
00295     _connection.disconnect();
00296 }
00297 
00300 - (void)reset
00301 {
00302     if (_connection)
00303         _connection.reset();
00304 }
00305 
00308 - (void)pause
00309 {
00310     if (_connection)
00311         _connection.pause();
00312 }
00313 
00316 - (void)resume
00317 {
00318     if (_connection)
00319         _connection.pause();
00320 }
00321 
00324 - (void)flush
00325 {
00326     _connection.flush();
00327 }
00328 
00329 - (TNStropheJID)JID
00330 {
00331     return [_delegate JID];
00332 }
00333 
00334 
00335 #pragma mark -
00336 #pragma mark Sending
00337 
00341 - (void)send:(TNStropheStanza)aStanza
00342 {
00343     if (_currentStatus == Strophe.Status.CONNECTED)
00344     {
00345         CPLog.trace("StropheCappuccino Stanza Send:")
00346         CPLog.trace(aStanza);
00347         [[CPRunLoop currentRunLoop] performSelector:@selector(performSend:) target:self argument:aStanza order:0 modes:[CPDefaultRunLoopMode]];
00348     }
00349 }
00350 
00351 - (void)performSend:(TNStropheStanza)aStanza
00352 {
00353     _connection.send([aStanza tree]);
00354 }
00355 
00358 - (CPString)getUniqueId
00359 {
00360     return [self getUniqueIdWithSuffix:null];
00361 }
00362 
00367 - (CPString)getUniqueIdWithSuffix:(CPString)suffix
00368 {
00369     return _connection.getUniqueId(suffix);
00370 }
00371 
00372 
00373 #pragma mark -
00374 #pragma mark Handlers
00375 
00397 - (id)registerSelector:(SEL)aSelector ofObject:(CPObject)anObject withDict:(id)aDict userInfo:(id)someUserInfo handlerDelegate:(id)aHandlerDelegate
00398 {
00399     var from = ([[aDict valueForKey:@"from"] isKindOfClass:CPString]) ? [aDict valueForKey:@"from"] : [[aDict valueForKey:@"from"] stringValue],
00400         handlerId = _connection.addHandler(function(stanza)
00401         {
00402             // seems to be a good practice to pump the runloop in async function
00403             [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00404 
00405             var stanzaObject = [TNStropheStanza stanzaWithStanza:stanza],
00406                 ret;
00407 
00408             if (someUserInfo)
00409                 ret = [anObject performSelector:aSelector withObject:stanzaObject withObject:someUserInfo];
00410             else
00411                 ret = [anObject performSelector:aSelector withObject:stanzaObject];
00412 
00413             CPLog.trace("StropheCappuccino stanza received that trigger selector : " + [anObject class] + "." + aSelector);
00414             CPLog.trace(stanzaObject);
00415 
00416             // experimental thing
00417             delete aDict.options;
00418             delete someUserInfo;
00419 
00420             if (aHandlerDelegate && [aHandlerDelegate respondsToSelector:@selector(stropheConnection:performedHandlerId:)])
00421                 [aHandlerDelegate stropheConnection:self performedHandlerId:handlerId]
00422 
00423             someUserInfo = nil;
00424 
00425             [_registeredHandlers removeObject:handlerId];
00426             return ret;
00427         },
00428         [aDict valueForKey:@"namespace"],
00429         [aDict valueForKey:@"name"],
00430         [aDict valueForKey:@"type"],
00431         [aDict valueForKey:@"id"],
00432         from,
00433         [aDict valueForKey:@"options"]);
00434 
00435     [_registeredHandlers addObject:handlerId];
00436 
00437     return handlerId;
00438 }
00439 
00460 - (id)registerSelector:(SEL)aSelector ofObject:(CPObject)anObject withDict:(id)aDict handlerDelegate:(id)aHandlerDelegate
00461 {
00462     return [self registerSelector:aSelector ofObject:anObject withDict:aDict userInfo:nil handlerDelegate:aHandlerDelegate];
00463 }
00464 
00485 - (id)registerSelector:(SEL)aSelector ofObject:(CPObject)anObject withDict:(id)aDict userInfo:(id)someUserInfo
00486 {
00487     return [self registerSelector:aSelector ofObject:anObject withDict:aDict userInfo:someUserInfo handlerDelegate:nil];
00488 }
00489 
00509 - (id)registerSelector:(SEL)aSelector ofObject:(CPObject)anObject withDict:(id)aDict
00510 {
00511     return [self registerSelector:aSelector ofObject:anObject withDict:aDict userInfo:nil handlerDelegate:nil];
00512 }
00513 
00524 - (id)registerTimeoutSelector:(SEL)aTimeoutSelector ofObject:(CPObject)anObject withDict:(id)aDict forTimeout:(float)aTimeout
00525 {
00526     var from = ([[aDict valueForKey:@"from"] isKindOfClass:CPString]) ? [aDict valueForKey:@"from"] : [[aDict valueForKey:@"from"] stringValue],
00527         handlerId =  _connection.addTimedHandler(aTimeout, function(stanza) {
00528                 if (!stanza)
00529                 {
00530                     // seems to be a good practice to pump the runloop in async function
00531                     [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00532 
00533                     var ret = [anObject performSelector:aTimeoutSelector];
00534 
00535                     CPLog.trace("StropheCappuccino stanza timeout that trigger selector : " + [anObject class] + "." + aTimeoutSelector);
00536 
00537                     // experimental thing
00538                     delete aDict.options;
00539                     return ret;
00540                 }
00541                 [_registeredTimedHandlers removeObject:handlerId];
00542                 return NO;
00543             },
00544             [aDict valueForKey:@"namespace"],
00545             [aDict valueForKey:@"name"],
00546             [aDict valueForKey:@"type"],
00547             [aDict valueForKey:@"id"],
00548             from,
00549             [aDict valueForKey:@"options"]);
00550 
00551     [_registeredTimedHandlers addObject:handlerId];
00552 
00553     return handlerId;
00554 }
00555 
00559 - (void)deleteRegisteredSelector:(id)aHandlerId
00560 {
00561     _connection.deleteHandler(aHandlerId);
00562     [_registeredHandlers removeObject:aHandlerId];
00563 }
00564 
00568 - (void)deleteRegisteredTimedSelector:(id)aTimedHandlerId
00569 {
00570     _connection.deleteTimedHandler(aTimedHandlerId);
00571     [_registeredTimedHandlers removeObject:aTimedHandlerId];
00572 }
00573 
00576 - (void)deleteAllRegisteredSelectors
00577 {
00578     for (var i = 0; i < [_registeredHandlers count]; i++)
00579         [self deleteRegisteredSelector:[_registeredHandlers objectAtIndex:i]];
00580     for (var i = 0; i < [_registeredTimedHandlers count]; i++)
00581         [self deleteRegisteredTimedSelector:[_registeredTimedHandlers objectAtIndex:i]];
00582 
00583     [_registeredHandlers removeAllObjects];
00584     [_registeredTimedHandlers removeAllObjects];
00585 }
00586 
00591 - (void)rawInputRegisterSelector:(SEL)aSelector ofObject:(id)anObject
00592 {
00593     _connection.xmlInput = function(elem) {
00594         [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00595         [anObject performSelector:aSelector withObject:[TNStropheStanza nodeWithXMLNode:elem]];
00596     }
00597 }
00598 
00601 - (void)removeRawInputSelector
00602 {
00603     _connection.xmlInput = function(elem){
00604         return;
00605     };
00606 }
00607 
00608 
00613 - (void)rawOutputRegisterSelector:(SEL)aSelector ofObject:(id)anObject
00614 {
00615     _connection.xmlOutput = function(elem) {
00616         [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00617         [anObject performSelector:aSelector withObject:[TNStropheStanza nodeWithXMLNode:elem]];
00618     }
00619 }
00620 
00623 - (void)removeRawOutputSelector
00624 {
00625     _connection.xmlOutput = function(elem){
00626         return;
00627     };
00628 }
00629 
00630 @end
00631 
00632 @implementation TNStropheConnection (CPCoding)
00633 
00634 - (id)initWithCoder:(CPCoder)aCoder
00635 {
00636     self = [super initWithCoder:aCoder];
00637 
00638     if (self)
00639     {
00640         _delegate                   = [aCoder decodeObjectForKey:@"_delegate"];
00641         _boshService                = [aCoder decodeObjectForKey:@"_boshService"];
00642         _connection                 = [aCoder decodeObjectForKey:@"_connection"];
00643         _registeredHandlers         = [aCoder decodeObjectForKey:@"_registeredHandlers"];
00644         _registeredTimedHandlers    = [aCoder decodeObjectForKey:@"_registeredTimedHandlers"];
00645     }
00646 
00647     return self;
00648 }
00649 
00650 - (void)encodeWithCoder:(CPCoder)aCoder
00651 {
00652     [aCoder encodeObject:_boshService forKey:@"_boshService"];
00653     [aCoder encodeObject:_connection forKey:@"_connection"];
00654     [aCoder encodeObject:_registeredHandlers forKey:@"_registeredHandlers"];
00655     [aCoder encodeObject:_registeredTimedHandlers forKey:@"_registeredTimedHandlers"];
00656 }
00657 
00658 @end
00659 
00660 @implementation TNStropheConnection (CPSynthesizedAccessors)
00661 
00665 - (BOOL)isConnected
00666 {
00667     return _connected;
00668 }
00669 
00673 - (CPString)password
00674 {
00675     return _password;
00676 }
00677 
00681 - (void)setPassword:(CPString)aValue
00682 {
00683     _password = aValue;
00684 }
00685 
00689 - (float)giveupTimeout
00690 {
00691     return _giveupTimeout;
00692 }
00693 
00697 - (void)setGiveupTimeout:(float)aValue
00698 {
00699     _giveupTimeout = aValue;
00700 }
00701 
00705 - (id)currentStatus
00706 {
00707     return _currentStatus;
00708 }
00709 
00713 - (id)delegate
00714 {
00715     return _delegate;
00716 }
00717 
00721 - (int)connectionTimeout
00722 {
00723     return _connectionTimeout;
00724 }
00725 
00729 - (void)setConnectionTimeout:(int)aValue
00730 {
00731     _connectionTimeout = aValue;
00732 }
00733 
00737 - (int)maxConnections
00738 {
00739     return _maxConnections;
00740 }
00741 
00745 - (void)setMaxConnections:(int)aValue
00746 {
00747     _maxConnections = aValue;
00748 }
00749 
00750 @end
 All Classes Namespaces Files Functions Variables