Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosAccountNode.m @ cb6abe72

History | View | Annotate | Download (18.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)];
56
}
57

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

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

    
68
- (void)dealloc {
69
    [accountRequest clearDelegatesAndCancel];
70
    [refreshMetadataAccountRequest clearDelegatesAndCancel];
71
    [applyMetadataAccountRequest clearDelegatesAndCancel];
72
}
73

    
74
#pragma mark -
75
#pragma mark Properties
76

    
77
- (void)setPithos:(ASIPithos *)aPithos {
78
    if (aPithos && ![aPithos isEqualTo:pithos]) {
79
        pithos = aPithos;
80
        url = nil;
81
        [accountRequest clearDelegatesAndCancel];
82
        accountRequest = nil;
83
        [refreshMetadataAccountRequest clearDelegatesAndCancel];
84
        refreshMetadataAccountRequest = nil;
85
        [applyMetadataAccountRequest clearDelegatesAndCancel];
86
        applyMetadataAccountRequest = nil;
87
        reset = YES;
88
    }
89
}
90

    
91
- (NSString *)url {
92
    if (url == nil)
93
        url = [[NSString alloc] initWithFormat:@"%@%@", 
94
               (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
95
               (shared ? @"?shared" : @"")];
96
    return url;
97
}
98

    
99
- (NSArray *)children {
100
    @synchronized(self) {
101
        if (reset) {
102
            [accountRequest clearDelegatesAndCancel];
103
            accountRequest = nil;
104
            [refreshMetadataAccountRequest clearDelegatesAndCancel];
105
            refreshMetadataAccountRequest = nil;
106
            [applyMetadataAccountRequest clearDelegatesAndCancel];
107
            applyMetadataAccountRequest = nil;
108
            children = nil;
109
            newChildren = nil;
110
            self.pithosAccount = nil;
111
            freshness = PithosNodeStateRefreshNeeded;
112
            forcedRefresh = YES;
113
            reset = NO;
114
            [self postChildrenUpdatedNotificationName];
115
        }
116
        switch (freshness) {
117
            case PithosNodeStateFresh:
118
                break;
119
            case PithosNodeStateRefreshNeeded:
120
                freshness = PithosNodeStateRefreshing;
121
                accountRequest = [ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
122
                                                                                     limit:0 
123
                                                                                    marker:nil 
124
                                                                                    shared:shared 
125
                                                                                     until:nil];
126
                if (sharingAccount)
127
                    [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
128
                else if (!forcedRefresh)
129
                    accountRequest.downloadCache = [ASIDownloadCache sharedCache];
130
                accountRequest.delegate = self;
131
                accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
132
                accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
133
                accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
134
                                           [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
135
                                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
136
                                           NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", 
137
                                           NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", 
138
                                           nil];
139
                [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
140
                break;
141
            case PithosNodeStateRefreshing:
142
                break;
143
            case PithosNodeStateRefreshFinished:
144
                if (newChildren) {
145
                    children = newChildren;
146
                    newChildren = nil;
147
                }
148
                freshness = PithosNodeStateFresh;
149
            default:
150
                break;
151
        }
152
        return children;
153
    }
154
}
155

    
156
- (NSString *)displayName {
157
    if (displayName == nil)
158
        return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
159
    return [displayName copy];
160
}
161

    
162
- (NSImage *)icon {
163
    if (icon == nil)
164
        icon = sharedIcon;
165
    return icon;
166
}
167

    
168
#pragma mark -
169
#pragma mark ASIHTTPRequestDelegate
170

    
171
- (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
172
    @autoreleasepool {
173
        NSString *message;
174
        NSError *error = [accountRequest error];
175
        if (error)
176
            message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]];
177
        else
178
            message = [NSString stringWithFormat:@"Account listing %@ failed: (%d) %@", 
179
                       accountRequest.url, accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
180
        dispatch_async(dispatch_get_main_queue(), ^{
181
            [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
182
        });
183
        NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
184
        if (retries > 0) {
185
            ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest];
186
            [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
187
            accountRequest = newAccountRequest;
188
            [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
189
        } else {
190
            newChildren = nil;
191
            accountRequest = nil;
192
            containers = nil;
193
            forcedRefresh = NO;
194
            @synchronized(self) {
195
                freshness = PithosNodeStateRefreshNeeded;
196
            }
197
        }
198
    }
199
}
200

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

    
290
- (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
291
    @autoreleasepool {
292
        DLog(@"URL: %@", [request url]);
293
        DLog(@"cached: %d", [request didUseCachedResponse]);
294
        
295
        if ([request isEqualTo:applyMetadataAccountRequest]) {
296
            @synchronized(self) {
297
                applyMetadataAccountRequest = nil;
298
            }
299
            [self refreshInfo];
300
        } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
301
            self.pithosAccount = [refreshMetadataAccountRequest account];
302
            @synchronized(self) {
303
                refreshMetadataAccountRequest = nil;
304
            }
305
        }
306
    }
307
}
308

    
309
- (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
310
    @autoreleasepool {
311
        NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
312
        if (retries > 0) {
313
            ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:request];
314
            [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
315
            if ([request isEqualTo:applyMetadataAccountRequest]) {
316
                @synchronized(self) {
317
                    applyMetadataAccountRequest = newRequest;
318
                }
319
            } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
320
                @synchronized(self) {
321
                    refreshMetadataAccountRequest = newRequest;
322
                }
323
            }
324
            [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
325
        } else {
326
            if ([request isEqualTo:applyMetadataAccountRequest]) {
327
                [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
328
                @synchronized(self) {
329
                    applyMetadataAccountRequest = nil;
330
                }
331
            } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
332
                [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
333
                @synchronized(self) {
334
                    refreshMetadataAccountRequest = nil;
335
                }
336
            }
337
        }
338
    }
339
}
340

    
341
#pragma mark -
342
#pragma mark Info
343

    
344
- (void)applyInfo {
345
    @synchronized(self) {
346
        if (applyMetadataAccountRequest == nil) {
347
            NSMutableDictionary *groups = pithosAccount.groups;
348
            if ([groups count] == 0)
349
                groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
350
            applyMetadataAccountRequest = [ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos 
351
                                                                                                    groups:groups 
352
                                                                                                  metadata:pithosAccount.metadata 
353
                                                                                                    update:NO];
354
            applyMetadataAccountRequest.delegate = self;
355
            applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
356
            applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
357
            applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
358
                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
359
                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
360
                                                    NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
361
                                                    NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
362
                                                    nil];
363
            [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
364
        }
365
    }
366
}
367

    
368
- (void)refreshInfo {
369
    @synchronized(self) {
370
        if (refreshMetadataAccountRequest == nil) {
371
            refreshMetadataAccountRequest = [ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos];
372
            refreshMetadataAccountRequest.delegate = self;
373
            refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
374
            refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
375
            refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
376
                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
377
                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
378
                                                      NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
379
                                                      NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
380
                                                      nil];
381
            if (!sharingAccount)
382
                refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
383
            [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
384
        }
385
    }
386
}
387

    
388
@end