Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosAccountNode.m @ 9990166e

History | View | Annotate | Download (19.8 kB)

1
//
2
//  PithosAccountNode.m
3
//  pithos-macos
4
//
5
// Copyright 2011-2012 GRNET S.A. All rights reserved.
6
//
7
// Redistribution and use in source and binary forms, with or
8
// without modification, are permitted provided that the following
9
// conditions are met:
10
// 
11
//   1. Redistributions of source code must retain the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer.
14
// 
15
//   2. Redistributions in binary form must reproduce the above
16
//      copyright notice, this list of conditions and the following
17
//      disclaimer in the documentation and/or other materials
18
//      provided with the distribution.
19
// 
20
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
// POSSIBILITY OF SUCH DAMAGE.
32
// 
33
// The views and conclusions contained in the software and
34
// documentation are those of the authors and should not be
35
// interpreted as representing official policies, either expressed
36
// or implied, of GRNET S.A.
37

    
38
#import "PithosAccountNode.h"
39
#import "PithosContainerNode.h"
40
#import "ASIPithos.h"
41
#import "ASIPithosAccountRequest.h"
42
#import "ASIPithosAccount.h"
43
#import "ASIPithosContainer.h"
44
#import "ASIDownloadCache.h"
45
#import "PithosUtilities.h"
46
#import "PithosActivityFacility.h"
47

    
48
static NSImage *sharedIcon = nil;
49

    
50
@implementation PithosAccountNode
51
@synthesize pithos, pithosAccount;
52

    
53
+ (void)initialize {
54
	if (self == [PithosAccountNode class])
55
        sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)] retain];
56
}
57

    
58
#pragma mark -
59
#pragma mark Object Lifecycle
60

    
61
- (id)initWithPithos:(ASIPithos *)aPithos {
62
    if ((self = [super init])) {
63
        pithos = [aPithos retain];
64
    }
65
    return self;
66
}
67

    
68
- (void)dealloc {
69
    [accountRequest clearDelegatesAndCancel];
70
    [accountRequest release];
71
    [refreshMetadataAccountRequest clearDelegatesAndCancel];
72
    [refreshMetadataAccountRequest release];
73
    [applyMetadataAccountRequest clearDelegatesAndCancel];
74
    [applyMetadataAccountRequest release];
75
    [containers release];
76
    [pithosAccount release];
77
    [pithos release];
78
    [super dealloc];
79
}
80

    
81
#pragma mark -
82
#pragma mark Properties
83

    
84
- (void)setPithos:(ASIPithos *)aPithos {
85
    if (aPithos && ![aPithos isEqualTo:pithos]) {
86
        [pithos release];
87
        pithos = [aPithos retain];
88
        [url release];
89
        url = nil;
90
        [accountRequest clearDelegatesAndCancel];
91
        [accountRequest release];
92
        accountRequest = nil;
93
        [refreshMetadataAccountRequest clearDelegatesAndCancel];
94
        [refreshMetadataAccountRequest release];
95
        refreshMetadataAccountRequest = nil;
96
        [applyMetadataAccountRequest clearDelegatesAndCancel];
97
        [applyMetadataAccountRequest release];
98
        applyMetadataAccountRequest = nil;
99
        reset = YES;
100
    }
101
}
102

    
103
- (NSString *)url {
104
    if (url == nil)
105
        url = [[NSString alloc] initWithFormat:@"%@%@", 
106
               (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
107
               (shared ? @"?shared" : @"")];
108
    return url;
109
}
110

    
111
- (NSArray *)children {
112
    @synchronized(self) {
113
        if (reset) {
114
            [accountRequest clearDelegatesAndCancel];
115
            [accountRequest release];
116
            accountRequest = nil;
117
            [refreshMetadataAccountRequest clearDelegatesAndCancel];
118
            [refreshMetadataAccountRequest release];
119
            refreshMetadataAccountRequest = nil;
120
            [applyMetadataAccountRequest clearDelegatesAndCancel];
121
            [applyMetadataAccountRequest release];
122
            applyMetadataAccountRequest = nil;
123
            [children release];
124
            children = nil;
125
            [newChildren release];
126
            newChildren = nil;
127
            self.pithosAccount = nil;
128
            freshness = PithosNodeStateRefreshNeeded;
129
            forcedRefresh = YES;
130
            reset = NO;
131
            [self postChildrenUpdatedNotificationName];
132
        }
133
        switch (freshness) {
134
            case PithosNodeStateFresh:
135
                break;
136
            case PithosNodeStateRefreshNeeded:
137
                freshness = PithosNodeStateRefreshing;
138
                accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
139
                                                                                     limit:0 
140
                                                                                    marker:nil 
141
                                                                                    shared:shared 
142
                                                                                     until:nil] retain];
143
                if (sharingAccount)
144
                    [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
145
                else if (!forcedRefresh)
146
                    accountRequest.downloadCache = [ASIDownloadCache sharedCache];
147
                accountRequest.delegate = self;
148
                accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
149
                accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
150
                accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
151
                                           [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
152
                                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
153
                                           NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", 
154
                                           NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", 
155
                                           nil];
156
                [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
157
                break;
158
            case PithosNodeStateRefreshing:
159
                break;
160
            case PithosNodeStateRefreshFinished:
161
                if (newChildren) {
162
                    [children release];
163
                    children = newChildren;
164
                    newChildren = nil;
165
                }
166
                freshness = PithosNodeStateFresh;
167
            default:
168
                break;
169
        }
170
        return children;
171
    }
172
}
173

    
174
- (NSString *)displayName {
175
    if (displayName == nil)
176
        return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
177
    return [[displayName copy] autorelease];
178
}
179

    
180
- (NSImage *)icon {
181
    if (icon == nil)
182
        icon = [sharedIcon retain];
183
    return icon;
184
}
185

    
186
#pragma mark -
187
#pragma mark ASIHTTPRequestDelegate
188

    
189
- (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
190
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
191
    NSString *message;
192
    NSError *error = [accountRequest error];
193
    if (error)
194
        message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]];
195
    else
196
        message = [NSString stringWithFormat:@"Account listing %@ failed: (%d) %@", 
197
                   accountRequest.url, accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
198
    dispatch_async(dispatch_get_main_queue(), ^{
199
        [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
200
    });
201
    NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
202
    if (retries > 0) {
203
        ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest];
204
        [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
205
        [accountRequest release];
206
        accountRequest = newAccountRequest;
207
        [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
208
    } else {
209
        [newChildren release];
210
        newChildren = nil;
211
        [accountRequest release];
212
        accountRequest = nil;
213
        [containers release];
214
        containers = nil;
215
        forcedRefresh = NO;
216
        @synchronized(self) {
217
            freshness = PithosNodeStateRefreshNeeded;
218
        }
219
    }
220
    [pool drain];
221
}
222

    
223
- (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
224
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
225
    DLog(@"List account finished: %@", [accountRequest url]);
226
    DLog(@"Cached: %d", [accountRequest didUseCachedResponse]);
227
    if (accountRequest.responseStatusCode == 200) {
228
        self.pithosAccount = [accountRequest account];
229
        
230
        NSArray *someContainers = [accountRequest containers];
231
        if (containers == nil) {
232
            containers = [[NSMutableArray alloc] initWithArray:someContainers];
233
        } else {
234
            [containers addObjectsFromArray:someContainers];
235
        }
236
        if ([someContainers count] < 10000) {
237
            if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) {
238
                // Save new children
239
                DLog(@"using newChildren");
240
                newChildren = [[NSMutableArray alloc] init];
241
                NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
242
                for (ASIPithosContainer *container in containers) {
243
                    PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithos:pithos pithosContainer:container] autorelease];
244
                    node.parent = self;
245
                    node.shared = shared;
246
                    node.sharingAccount = sharingAccount;
247
                    node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
248
                    if (children) {
249
                        NSUInteger oldIndex = [children indexOfObject:node];
250
                        if (oldIndex != NSNotFound) {
251
                            // Use the same pointer value, if possible
252
                            node = [children objectAtIndex:oldIndex];
253
//                            node.pithosContainer = container;
254
                            [node setLimitedPithosContainer:container];
255
                            [keptNodes addIndex:oldIndex];
256
                        }
257
                    }
258
                    [newChildren addObject:node];
259
                }
260
                [[children objectsAtIndexes:
261
                  [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
262
                    if ([keptNodes containsIndex:idx])
263
                        return NO;
264
                    return YES;
265
                }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
266
            }
267
            // Else cache was used and all results were fetched during this request, so existing children can be reused
268
            [accountRequest release];
269
            accountRequest = nil;
270
            [containers release];
271
            containers = nil;
272
            forcedRefresh = NO;
273
            @synchronized(self) {
274
                freshness = PithosNodeStateRefreshFinished;
275
            }
276
            [self postChildrenUpdatedNotificationName];
277
        } else {
278
            [accountRequest release];
279
            // Do an additional request to fetch more objects
280
            accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
281
                                                                                 limit:0 
282
                                                                                marker:[[someContainers lastObject] name] 
283
                                                                                shared:shared 
284
                                                                                 until:nil] retain];
285
            if (sharingAccount)
286
                [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
287
            else if (!forcedRefresh)
288
                accountRequest.downloadCache = [ASIDownloadCache sharedCache];
289
            accountRequest.delegate = self;
290
            accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
291
            accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
292
            accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
293
                                       [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
294
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
295
                                       NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", 
296
                                       NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", 
297
                                       nil];
298
            [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
299
        }
300
    } else if (accountRequest.responseStatusCode == 304) {
301
        // Account is not modified, so existing children can be reused
302
        [accountRequest release];
303
        accountRequest = nil;
304
        [containers release];
305
        containers = nil;
306
        forcedRefresh = NO;
307
        @synchronized(self) {
308
            freshness = PithosNodeStateRefreshFinished;
309
        }
310
        [self postChildrenUpdatedNotificationName];
311
    } else {
312
        [self accountRequestFailed:accountRequest];
313
    }
314
    [pool drain];
315
}
316

    
317
- (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
318
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
319
    DLog(@"URL: %@", [request url]);
320
    DLog(@"cached: %d", [request didUseCachedResponse]);
321
    
322
    if ([request isEqualTo:applyMetadataAccountRequest]) {
323
        @synchronized(self) {
324
            [applyMetadataAccountRequest release];
325
            applyMetadataAccountRequest = nil;
326
        }
327
        [self refreshInfo];
328
    } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
329
        self.pithosAccount = [refreshMetadataAccountRequest account];
330
        @synchronized(self) {
331
            [refreshMetadataAccountRequest release];
332
            refreshMetadataAccountRequest = nil;
333
        }
334
    }
335
    [pool drain];
336
}
337

    
338
- (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
339
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
340
    NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
341
    if (retries > 0) {
342
        ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:request];
343
        [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
344
        if ([request isEqualTo:applyMetadataAccountRequest]) {
345
            @synchronized(self) {
346
                [applyMetadataAccountRequest release];
347
                applyMetadataAccountRequest = newRequest;
348
            }
349
        } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
350
            @synchronized(self) {
351
                [refreshMetadataAccountRequest release];
352
                refreshMetadataAccountRequest = newRequest;
353
            }
354
        }
355
        [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
356
    } else {
357
        if ([request isEqualTo:applyMetadataAccountRequest]) {
358
            [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
359
            @synchronized(self) {
360
                [applyMetadataAccountRequest release];
361
                applyMetadataAccountRequest = nil;
362
            }
363
        } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
364
            [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
365
            @synchronized(self) {
366
                [refreshMetadataAccountRequest release];
367
                refreshMetadataAccountRequest = nil;
368
            }
369
        }
370
    }
371
    [pool drain];
372
}
373

    
374
#pragma mark -
375
#pragma mark Info
376

    
377
- (void)applyInfo {
378
    @synchronized(self) {
379
        if (applyMetadataAccountRequest == nil) {
380
            NSMutableDictionary *groups = pithosAccount.groups;
381
            if ([groups count] == 0)
382
                groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
383
            applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos 
384
                                                                                                    groups:groups 
385
                                                                                                  metadata:pithosAccount.metadata 
386
                                                                                                    update:NO] retain];
387
            applyMetadataAccountRequest.delegate = self;
388
            applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
389
            applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
390
            applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
391
                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
392
                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
393
                                                    NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
394
                                                    NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
395
                                                    nil];
396
            [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
397
        }
398
    }
399
}
400

    
401
- (void)refreshInfo {
402
    @synchronized(self) {
403
        if (refreshMetadataAccountRequest == nil) {
404
            refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
405
            refreshMetadataAccountRequest.delegate = self;
406
            refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
407
            refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
408
            refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
409
                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
410
                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
411
                                                      NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
412
                                                      NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
413
                                                      nil];
414
            if (!sharingAccount)
415
                refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
416
            [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
417
        }
418
    }
419
}
420

    
421
@end