PageRenderTime 25ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/sope-appserver/NGObjWeb/WebDAV/SoSubscriptionManager.m

http://sope-appserver.googlecode.com/
Objective C | 279 lines | 200 code | 52 blank | 27 comment | 49 complexity | 6560bfc488dfab7ff66aaf460de56b10 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0
  1. /*
  2. Copyright (C) 2002-2005 SKYRIX Software AG
  3. This file is part of SOPE.
  4. SOPE is free software; you can redistribute it and/or modify it under
  5. the terms of the GNU Lesser General Public License as published by the
  6. Free Software Foundation; either version 2, or (at your option) any
  7. later version.
  8. SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
  9. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  11. License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with SOPE; see the file COPYING. If not, write to the
  14. Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  15. 02111-1307, USA.
  16. */
  17. #include "SoSubscriptionManager.h"
  18. #include "SoSubscription.h"
  19. #include "SoObject.h"
  20. #import <EOControl/EOControl.h>
  21. #include "common.h"
  22. @implementation SoSubscriptionManager
  23. static BOOL debugOn = NO;
  24. static NSTimeInterval defaultLifeTime = 3600;
  25. static int expirationInterval = 10 * 60 /* every 10 minutes */;
  26. static SoSubscriptionManager *sm = nil;
  27. + (id)sharedSubscriptionManager {
  28. if (sm == nil) {
  29. debugOn = [[NSUserDefaults standardUserDefaults]
  30. boolForKey:@"SoSubscriptionManagerDebugEnabled"];
  31. sm = [[SoSubscriptionManager alloc] init];
  32. }
  33. return sm;
  34. }
  35. - (id)init {
  36. if ((self = [super init])) {
  37. self->idToSubscription = [[NSMutableDictionary alloc] init];
  38. [[NSNotificationCenter defaultCenter]
  39. addObserver:self selector:@selector(trackChange:)
  40. name:@"SoObjectChanged" object:nil];
  41. }
  42. return self;
  43. }
  44. - (void)dealloc {
  45. [[NSNotificationCenter defaultCenter] removeObserver:self];
  46. [self->idToSubscription release];
  47. [super dealloc];
  48. }
  49. /* subscriptions */
  50. - (NSString *)nextSubscriptionID {
  51. static int i = 1000;
  52. i++;
  53. return [NSString stringWithFormat:@"%i", i];
  54. }
  55. - (NSString *)subscribeURL:(NSURL *)_url forObserver:(NSURL *)_callback
  56. type:(NSString *)_type delay:(NSTimeInterval)_delay
  57. lifetime:(NSTimeInterval)_lifetime
  58. {
  59. /* subscribe httpu://127.0.0.1:14970/ for 3600s (type update, delay 30s) */
  60. SoSubscription *s;
  61. NSString *sid;
  62. if (_lifetime == 0.0) _lifetime = defaultLifeTime;
  63. sid = [self nextSubscriptionID];
  64. if (debugOn) {
  65. [self debugWithFormat:@"subscribe url %@", _url];
  66. [self debugWithFormat:@" observer: %@", _callback];
  67. [self debugWithFormat:@" type: %@", _type];
  68. [self debugWithFormat:@" delay: %i", (int)_delay];
  69. [self debugWithFormat:@"SID: %@", sid];
  70. }
  71. s = [[SoSubscription alloc] initWithID:sid
  72. url:_url observer:_callback type:_type
  73. delay:_delay lifetime:_lifetime];
  74. [self->idToSubscription setObject:s forKey:sid];
  75. [s autorelease];
  76. [self debugWithFormat:@"expires: %@", [s expirationDate]];
  77. if (s != nil && self->timer == nil) {
  78. self->timer = [[NSTimer scheduledTimerWithTimeInterval:expirationInterval
  79. target:self
  80. selector:@selector(performExpirationCheck:)
  81. userInfo:nil
  82. repeats:YES]
  83. retain];
  84. }
  85. return [s subscriptionID];
  86. }
  87. - (NSString *)renewSubscription:(NSString *)_sid onURL:(NSURL *)_url {
  88. SoSubscription *s;
  89. if (debugOn)
  90. [self debugWithFormat:@"renew subscription %@, url: %@", _sid, _url];
  91. if ((s = [self->idToSubscription objectForKey:_sid]) == nil) {
  92. [self logWithFormat:
  93. @"attempt to renew non-existing subscription '%@'", _sid];
  94. return nil;
  95. }
  96. if (![s isValidForURL:_url]) {
  97. [self logWithFormat:
  98. @"mismatch between subscription-id and subscribed resource"];
  99. return nil;
  100. }
  101. if (![s renewSubscription])
  102. return _sid;
  103. return _sid;
  104. }
  105. - (BOOL)unsubscribeID:(NSString *)_sid onURL:(NSURL *)_url {
  106. SoSubscription *s;
  107. if (_sid == nil) return NO;
  108. if (_url == nil) return NO;
  109. if ((s = [self->idToSubscription objectForKey:_sid]) == nil) {
  110. [self debugWithFormat:@"no subscription with id '%@'", _sid];
  111. return NO;
  112. }
  113. if (![s isValidForURL:_url]) {
  114. [self logWithFormat:
  115. @"mismatch between subscription-id and subscribed resource"];
  116. return NO;
  117. }
  118. [self->idToSubscription removeObjectForKey:_sid];
  119. [self debugWithFormat:@"canceled subscription '%@'", _sid];
  120. return YES;
  121. }
  122. - (id)pollSubscriptions:(NSArray *)_sids onURL:(NSURL *)_url {
  123. NSEnumerator *e;
  124. NSMutableArray *pending = nil;
  125. NSMutableArray *inactive = nil;
  126. NSString *sid;
  127. e = [_sids objectEnumerator];
  128. while ((sid = [e nextObject])) {
  129. SoSubscription *s;
  130. if ((s = [self->idToSubscription objectForKey:sid]) == nil) {
  131. [self debugWithFormat:@"no subscription with id '%@'", sid];
  132. continue;
  133. }
  134. if (![s isValidForURL:_url]) {
  135. [self debugWithFormat:@"subscription '%@' is not valid for given URL",
  136. sid];
  137. continue;
  138. }
  139. [s renewSubscription]; /* renew subscription on access ... */
  140. if ([s hasEventsPending]) {
  141. [self debugWithFormat:@"events pending on sid '%@' ...", sid];
  142. [s resetEvents];
  143. if (pending == nil) pending = [[NSMutableArray alloc] init];
  144. [pending addObject:sid];
  145. }
  146. else {
  147. [self debugWithFormat:@"no events pending on sid '%@' ...", sid];
  148. if (inactive == nil) inactive = [[NSMutableArray alloc] init];
  149. [inactive addObject:sid];
  150. }
  151. }
  152. if (pending == nil) pending = [NSArray array];
  153. if (inactive == nil) inactive = [NSArray array];
  154. return [NSDictionary dictionaryWithObjectsAndKeys:
  155. pending, @"pending",
  156. inactive, @"inactive",
  157. nil];
  158. }
  159. /* tracking changes */
  160. - (void)trackChangedURL:(NSURL *)_url {
  161. [self debugWithFormat:@"url was changed: %@", _url];
  162. }
  163. - (void)trackChangedPath:(NSString *)_path {
  164. [self debugWithFormat:@"path was changed: %@", _path];
  165. }
  166. - (void)trackChangedObject:(id)_object {
  167. id url = nil;
  168. if ((url = [_object baseURLInContext:nil]))
  169. url = [NSURL URLWithString:url];
  170. if (url) {
  171. [self debugWithFormat:@"object with URL was changed: %@", _object];
  172. [self trackChangedURL:url];
  173. }
  174. else
  175. [self debugWithFormat:@"object without URL was changed: %@", _object];
  176. }
  177. - (void)trackChange:(NSNotification *)_notification {
  178. id object;
  179. if ((object = [_notification object]) == nil) {
  180. [self logWithFormat:@"missing changed object in change notification ..."];
  181. return;
  182. }
  183. if ([object isKindOfClass:[NSURL class]])
  184. [self trackChangedURL:object];
  185. else if ([object isKindOfClass:[NSString class]])
  186. [self trackChangedPath:object];
  187. else
  188. [self trackChangedObject:object];
  189. }
  190. /* expiration check */
  191. - (void)performExpirationCheck:(NSTimer *)_timer {
  192. NSAutoreleasePool *pool;
  193. NSArray *subs;
  194. NSEnumerator *e;
  195. SoSubscription *s;
  196. pool = [[NSAutoreleasePool alloc] init];
  197. /* scan for expired subscriptions */
  198. subs = [[self->idToSubscription allValues] shallowCopy];
  199. if (debugOn) {
  200. [self debugWithFormat:@"perform expiration check (%i subscriptions) ...",
  201. [subs count]];
  202. }
  203. e = [subs objectEnumerator];
  204. while ((s = [e nextObject])) {
  205. if ([s isExpired]) {
  206. if (debugOn)
  207. [self debugWithFormat:@" expired: %@", [s subscriptionID]];
  208. [self->idToSubscription removeObjectForKey:[s subscriptionID]];
  209. }
  210. }
  211. /* remove timer if we have no subscriptions ... */
  212. if ([self->idToSubscription count] == 0) {
  213. [self debugWithFormat:@" no subscriptions left, removing timer .."];
  214. [self->timer invalidate];
  215. [self->timer release]; self->timer = nil;
  216. }
  217. [pool release];
  218. }
  219. /* debugging */
  220. - (BOOL)isDebuggingEnabled {
  221. return debugOn;
  222. }
  223. @end /* SoSubscriptionManager */