2 Copyright (C) 2002-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include "SoSubscriptionManager.h"
23 #include "SoSubscription.h"
25 #import <EOControl/EOControl.h>
28 @implementation SoSubscriptionManager
30 static BOOL debugOn = NO;
31 static NSTimeInterval defaultLifeTime = 3600;
32 static int expirationInterval = 10 * 60 /* every 10 minutes */;
34 static SoSubscriptionManager *sm = nil;
36 + (id)sharedSubscriptionManager {
38 debugOn = [[NSUserDefaults standardUserDefaults]
39 boolForKey:@"SoSubscriptionManagerDebugEnabled"];
41 sm = [[SoSubscriptionManager alloc] init];
47 if ((self = [super init])) {
48 self->idToSubscription = [[NSMutableDictionary alloc] init];
50 [[NSNotificationCenter defaultCenter]
51 addObserver:self selector:@selector(trackChange:)
52 name:@"SoObjectChanged" object:nil];
57 [[NSNotificationCenter defaultCenter] removeObserver:self];
58 [self->idToSubscription release];
64 - (NSString *)nextSubscriptionID {
67 return [NSString stringWithFormat:@"%i", i];
70 - (NSString *)subscribeURL:(NSURL *)_url forObserver:(NSURL *)_callback
71 type:(NSString *)_type delay:(NSTimeInterval)_delay
72 lifetime:(NSTimeInterval)_lifetime
74 /* subscribe httpu://127.0.0.1:14970/ for 3600s (type update, delay 30s) */
78 if (_lifetime == 0.0) _lifetime = defaultLifeTime;
79 sid = [self nextSubscriptionID];
82 [self debugWithFormat:@"subscribe url %@", _url];
83 [self debugWithFormat:@" observer: %@", _callback];
84 [self debugWithFormat:@" type: %@", _type];
85 [self debugWithFormat:@" delay: %i", (int)_delay];
86 [self debugWithFormat:@"SID: %@", sid];
89 s = [[SoSubscription alloc] initWithID:sid
90 url:_url observer:_callback type:_type
91 delay:_delay lifetime:_lifetime];
92 [self->idToSubscription setObject:s forKey:sid];
95 [self debugWithFormat:@"expires: %@", [s expirationDate]];
97 if (s != nil && self->timer == nil) {
98 self->timer = [[NSTimer scheduledTimerWithTimeInterval:expirationInterval
100 selector:@selector(performExpirationCheck:)
106 return [s subscriptionID];
109 - (NSString *)renewSubscription:(NSString *)_sid onURL:(NSURL *)_url {
113 [self debugWithFormat:@"renew subscription %@, url: %@", _sid, _url];
115 if ((s = [self->idToSubscription objectForKey:_sid]) == nil) {
117 @"attempt to renew non-existing subscription '%@'", _sid];
121 if (![s isValidForURL:_url]) {
123 @"mismatch between subscription-id and subscribed resource"];
127 if (![s renewSubscription])
133 - (BOOL)unsubscribeID:(NSString *)_sid onURL:(NSURL *)_url {
136 if (_sid == nil) return NO;
137 if (_url == nil) return NO;
139 if ((s = [self->idToSubscription objectForKey:_sid]) == nil) {
140 [self debugWithFormat:@"no subscription with id '%@'", _sid];
143 if (![s isValidForURL:_url]) {
145 @"mismatch between subscription-id and subscribed resource"];
149 [self->idToSubscription removeObjectForKey:_sid];
150 [self debugWithFormat:@"canceled subscription '%@'", _sid];
154 - (id)pollSubscriptions:(NSArray *)_sids onURL:(NSURL *)_url {
156 NSMutableArray *pending = nil;
157 NSMutableArray *inactive = nil;
160 e = [_sids objectEnumerator];
161 while ((sid = [e nextObject])) {
164 if ((s = [self->idToSubscription objectForKey:sid]) == nil) {
165 [self debugWithFormat:@"no subscription with id '%@'", sid];
168 if (![s isValidForURL:_url]) {
169 [self debugWithFormat:@"subscription '%@' is not valid for given URL",
174 [s renewSubscription]; /* renew subscription on access ... */
176 if ([s hasEventsPending]) {
177 [self debugWithFormat:@"events pending on sid '%@' ...", sid];
179 if (pending == nil) pending = [[NSMutableArray alloc] init];
180 [pending addObject:sid];
183 [self debugWithFormat:@"no events pending on sid '%@' ...", sid];
184 if (inactive == nil) inactive = [[NSMutableArray alloc] init];
185 [inactive addObject:sid];
189 if (pending == nil) pending = [NSArray array];
190 if (inactive == nil) inactive = [NSArray array];
191 return [NSDictionary dictionaryWithObjectsAndKeys:
193 inactive, @"inactive",
197 /* tracking changes */
199 - (void)trackChangedURL:(NSURL *)_url {
200 [self debugWithFormat:@"url was changed: %@", _url];
202 - (void)trackChangedPath:(NSString *)_path {
203 [self debugWithFormat:@"path was changed: %@", _path];
205 - (void)trackChangedObject:(id)_object {
208 if ((url = [_object baseURLInContext:nil]))
209 url = [NSURL URLWithString:url];
212 [self debugWithFormat:@"object with URL was changed: %@", _object];
213 [self trackChangedURL:url];
216 [self debugWithFormat:@"object without URL was changed: %@", _object];
219 - (void)trackChange:(NSNotification *)_notification {
222 if ((object = [_notification object]) == nil) {
223 [self logWithFormat:@"missing changed object in change notification ..."];
227 if ([object isKindOfClass:[NSURL class]])
228 [self trackChangedURL:object];
229 else if ([object isKindOfClass:[NSString class]])
230 [self trackChangedPath:object];
232 [self trackChangedObject:object];
235 /* expiration check */
237 - (void)performExpirationCheck:(NSTimer *)_timer {
238 NSAutoreleasePool *pool;
243 pool = [[NSAutoreleasePool alloc] init];
245 /* scan for expired subscriptions */
247 subs = [[self->idToSubscription allValues] shallowCopy];
249 [self debugWithFormat:@"perform expiration check (%i subscriptions) ...",
253 e = [subs objectEnumerator];
254 while ((s = [e nextObject])) {
257 [self debugWithFormat:@" expired: %@", [s subscriptionID]];
258 [self->idToSubscription removeObjectForKey:[s subscriptionID]];
262 /* remove timer if we have no subscriptions ... */
264 if ([self->idToSubscription count] == 0) {
265 [self debugWithFormat:@" no subscriptions left, removing timer .."];
266 [self->timer invalidate];
267 [self->timer release]; self->timer = nil;
275 - (BOOL)isDebuggingEnabled {
279 @end /* SoSubscriptionManager */