Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosAccountNode.m @ 919cb043

History | View | Annotate | Download (17.3 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
- (NSString *)url {
85
    if (url == nil)
86
        url = [[NSString alloc] initWithFormat:@"%@%@", 
87
               (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
88
               (shared ? @"?shared" : @"")];
89
    return url;
90
}
91

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

    
135
- (NSString *)displayName {
136
    if (displayName == nil)
137
        return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
138
    return [[displayName copy] autorelease];
139
}
140

    
141
- (NSImage *)icon {
142
    if (icon == nil)
143
        icon = [sharedIcon retain];
144
    return icon;
145
}
146

    
147
#pragma mark -
148
#pragma mark ASIHTTPRequestDelegate
149

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

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

    
278
- (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
279
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
280
    NSLog(@"URL: %@", [request url]);
281
    NSLog(@"cached: %d", [request didUseCachedResponse]);
282
    
283
    if ([request isEqualTo:applyMetadataAccountRequest]) {
284
        @synchronized(self) {
285
            [applyMetadataAccountRequest release];
286
            applyMetadataAccountRequest = nil;
287
        }
288
        [self refreshInfo];
289
    } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
290
        self.pithosAccount = [refreshMetadataAccountRequest account];
291
        @synchronized(self) {
292
            [refreshMetadataAccountRequest release];
293
            refreshMetadataAccountRequest = nil;
294
        }
295
    }
296
    [pool drain];
297
}
298

    
299
- (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
300
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
301
    if ([request isEqualTo:applyMetadataAccountRequest]) {
302
        [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
303
        @synchronized(self) {
304
            [applyMetadataAccountRequest release];
305
            applyMetadataAccountRequest = nil;
306
        }
307
    } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
308
        [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
309
        @synchronized(self) {
310
            [refreshMetadataAccountRequest release];
311
            refreshMetadataAccountRequest = nil;
312
        }
313
    }
314
    [pool drain];
315
}
316

    
317
#pragma mark -
318
#pragma mark Info
319

    
320
- (void)applyInfo {
321
    @synchronized(self) {
322
        if (applyMetadataAccountRequest == nil) {
323
            NSMutableDictionary *groups = pithosAccount.groups;
324
            if ([groups count] == 0)
325
                groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
326
            applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos 
327
                                                                                                    groups:groups 
328
                                                                                                  metadata:pithosAccount.metadata 
329
                                                                                                    update:NO] retain];
330
            applyMetadataAccountRequest.delegate = self;
331
            applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
332
            applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
333
            applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
334
                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
335
                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
336
                                                    NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
337
                                                    NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
338
                                                    nil];
339
            [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
340
        }
341
    }
342
}
343

    
344
- (void)refreshInfo {
345
    @synchronized(self) {
346
        if (refreshMetadataAccountRequest == nil) {
347
            refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
348
            refreshMetadataAccountRequest.delegate = self;
349
            refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
350
            refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
351
            refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
352
                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
353
                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
354
                                                      NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
355
                                                      NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
356
                                                      nil];
357
            refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
358
            [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
359
        }
360
    }
361
}
362

    
363
@end