Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosAccountNode.m @ 46b46b83

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)] retain];
56
}
57

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

    
61
- (id)initWithPithos:(ASIPithos *)aPithos {
62
    if ((self = [super init])) {
63
        self.pithos = aPithos;
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
    }
91
}
92

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

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

    
144
- (NSString *)displayName {
145
    if (displayName == nil)
146
        return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
147
    return [[displayName copy] autorelease];
148
}
149

    
150
- (NSImage *)icon {
151
    if (icon == nil)
152
        icon = [sharedIcon retain];
153
    return icon;
154
}
155

    
156
#pragma mark -
157
#pragma mark ASIHTTPRequestDelegate
158

    
159
- (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
160
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
161
    NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
162
    if (retries > 0) {
163
        ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest];
164
        [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
165
        [accountRequest release];
166
        accountRequest = newAccountRequest;
167
        [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
168
    } else {
169
        NSString *message;
170
        NSError *error = [accountRequest error];
171
        if (error)
172
            message = [NSString stringWithFormat:@"Account listing failed: %@", error];
173
        else
174
            message = [NSString stringWithFormat:@"Account listing failed: (%d) %@", 
175
                       accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
176
        dispatch_async(dispatch_get_main_queue(), ^{
177
            [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
178
        });
179
        [newChildren release];
180
        newChildren = nil;
181
        [accountRequest release];
182
        accountRequest = nil;
183
        [containers release];
184
        containers = nil;
185
        forcedRefresh = NO;
186
        @synchronized(self) {
187
            freshness = PithosNodeStateRefreshNeeded;
188
        }
189
    }
190
    [pool drain];
191
}
192

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

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

    
309
- (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
310
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
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 release];
318
                applyMetadataAccountRequest = newRequest;
319
            }
320
        } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
321
            @synchronized(self) {
322
                [refreshMetadataAccountRequest release];
323
                refreshMetadataAccountRequest = newRequest;
324
            }
325
        }
326
        [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
327
    } else {
328
        if ([request isEqualTo:applyMetadataAccountRequest]) {
329
            dispatch_async(dispatch_get_main_queue(), ^{
330
                [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
331
            });
332
            @synchronized(self) {
333
                [applyMetadataAccountRequest release];
334
                applyMetadataAccountRequest = nil;
335
            }
336
        } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
337
            dispatch_async(dispatch_get_main_queue(), ^{
338
                [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
339
            });
340
            @synchronized(self) {
341
                [refreshMetadataAccountRequest release];
342
                refreshMetadataAccountRequest = nil;
343
            }
344
        }
345
    }
346
    [pool drain];
347
}
348

    
349
#pragma mark -
350
#pragma mark Info
351

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

    
376
- (void)refreshInfo {
377
    @synchronized(self) {
378
        if (refreshMetadataAccountRequest == nil) {
379
            refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
380
            refreshMetadataAccountRequest.delegate = self;
381
            refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
382
            refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
383
            refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
384
                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
385
                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
386
                                                      NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
387
                                                      NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
388
                                                      nil];
389
            refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
390
            [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
391
        }
392
    }
393
}
394

    
395
@end