5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
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.
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.
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.
38 #import "PithosAccountNode.h"
39 #import "PithosContainerNode.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"
48 static NSImage *sharedIcon = nil;
50 @implementation PithosAccountNode
51 @synthesize pithos, pithosAccount;
54 if (self == [PithosAccountNode class])
55 sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)] retain];
59 #pragma mark Object Lifecycle
61 - (id)initWithPithos:(ASIPithos *)aPithos {
62 if ((self = [super init])) {
63 pithos = [aPithos retain];
69 [accountRequest clearDelegatesAndCancel];
70 [accountRequest release];
71 [refreshMetadataAccountRequest clearDelegatesAndCancel];
72 [refreshMetadataAccountRequest release];
73 [applyMetadataAccountRequest clearDelegatesAndCancel];
74 [applyMetadataAccountRequest release];
76 [pithosAccount release];
82 #pragma mark Properties
84 - (void)setPithos:(ASIPithos *)aPithos {
85 if (aPithos && ![aPithos isEqualTo:pithos]) {
87 pithos = [aPithos retain];
90 [accountRequest clearDelegatesAndCancel];
91 [accountRequest release];
93 [refreshMetadataAccountRequest clearDelegatesAndCancel];
94 [refreshMetadataAccountRequest release];
95 refreshMetadataAccountRequest = nil;
96 [applyMetadataAccountRequest clearDelegatesAndCancel];
97 [applyMetadataAccountRequest release];
98 applyMetadataAccountRequest = nil;
105 url = [[NSString alloc] initWithFormat:@"%@%@",
106 (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
107 (shared ? @"?shared" : @"")];
111 - (NSArray *)children {
112 @synchronized(self) {
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;
125 [newChildren release];
127 self.pithosAccount = nil;
128 freshness = PithosNodeStateRefreshNeeded;
131 [self postChildrenUpdatedNotificationName];
134 case PithosNodeStateFresh:
136 case PithosNodeStateRefreshNeeded:
137 freshness = PithosNodeStateRefreshing;
138 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos
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",
156 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
158 case PithosNodeStateRefreshing:
160 case PithosNodeStateRefreshFinished:
163 children = newChildren;
166 freshness = PithosNodeStateFresh;
174 - (NSString *)displayName {
175 if (displayName == nil)
176 return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
177 return [[displayName copy] autorelease];
182 icon = [sharedIcon retain];
187 #pragma mark ASIHTTPRequestDelegate
189 - (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
192 NSError *error = [accountRequest error];
194 message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]];
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];
201 NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
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];
209 [newChildren release];
211 [accountRequest release];
212 accountRequest = nil;
213 [containers release];
216 @synchronized(self) {
217 freshness = PithosNodeStateRefreshNeeded;
223 - (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
225 DLog(@"List account finished: %@", [accountRequest url]);
226 DLog(@"Cached: %d", [accountRequest didUseCachedResponse]);
227 if (accountRequest.responseStatusCode == 200) {
228 self.pithosAccount = [accountRequest account];
230 NSArray *someContainers = [accountRequest containers];
231 if (containers == nil) {
232 containers = [[NSMutableArray alloc] initWithArray:someContainers];
234 [containers addObjectsFromArray:someContainers];
236 if ([someContainers count] < 10000) {
237 if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !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];
245 node.shared = shared;
246 node.sharingAccount = sharingAccount;
247 node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
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];
258 [newChildren addObject:node];
260 [[children objectsAtIndexes:
261 [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
262 if ([keptNodes containsIndex:idx])
265 }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
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];
273 @synchronized(self) {
274 freshness = PithosNodeStateRefreshFinished;
276 [self postChildrenUpdatedNotificationName];
278 [accountRequest release];
279 // Do an additional request to fetch more objects
280 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos
282 marker:[[someContainers lastObject] name]
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",
298 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
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];
307 @synchronized(self) {
308 freshness = PithosNodeStateRefreshFinished;
310 [self postChildrenUpdatedNotificationName];
312 [self accountRequestFailed:accountRequest];
317 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
319 DLog(@"URL: %@", [request url]);
320 DLog(@"cached: %d", [request didUseCachedResponse]);
322 if ([request isEqualTo:applyMetadataAccountRequest]) {
323 @synchronized(self) {
324 [applyMetadataAccountRequest release];
325 applyMetadataAccountRequest = nil;
328 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
329 self.pithosAccount = [refreshMetadataAccountRequest account];
330 @synchronized(self) {
331 [refreshMetadataAccountRequest release];
332 refreshMetadataAccountRequest = nil;
338 - (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
340 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
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;
349 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
350 @synchronized(self) {
351 [refreshMetadataAccountRequest release];
352 refreshMetadataAccountRequest = newRequest;
355 [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
357 if ([request isEqualTo:applyMetadataAccountRequest]) {
358 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
359 @synchronized(self) {
360 [applyMetadataAccountRequest release];
361 applyMetadataAccountRequest = nil;
363 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
364 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
365 @synchronized(self) {
366 [refreshMetadataAccountRequest release];
367 refreshMetadataAccountRequest = nil;
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
385 metadata:pithosAccount.metadata
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",
396 [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
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",
415 refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
416 [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];