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 self.pithos = aPithos;
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];
95 url = [[NSString alloc] initWithFormat:@"%@%@",
96 (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
97 (shared ? @"?shared" : @"")];
101 - (NSArray *)children {
102 @synchronized(self) {
104 case PithosNodeStateFresh:
106 case PithosNodeStateRefreshNeeded:
107 freshness = PithosNodeStateRefreshing;
108 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos
114 [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
115 else if (!forcedRefresh)
116 accountRequest.downloadCache = [ASIDownloadCache sharedCache];
117 accountRequest.delegate = self;
118 accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
119 accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
120 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
121 [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority",
122 [NSNumber numberWithUnsignedInteger:10], @"retries",
123 NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector",
124 NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector",
126 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
128 case PithosNodeStateRefreshing:
130 case PithosNodeStateRefreshFinished:
133 children = newChildren;
136 freshness = PithosNodeStateFresh;
144 - (NSString *)displayName {
145 if (displayName == nil)
146 return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
147 return [[displayName copy] autorelease];
152 icon = [sharedIcon retain];
157 #pragma mark ASIHTTPRequestDelegate
159 - (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
160 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
161 NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
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];
170 NSError *error = [accountRequest error];
172 message = [NSString stringWithFormat:@"Account listing failed: %@", error];
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];
179 [newChildren release];
181 [accountRequest release];
182 accountRequest = nil;
183 [containers release];
186 @synchronized(self) {
187 freshness = PithosNodeStateRefreshNeeded;
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];
200 NSArray *someContainers = [accountRequest containers];
201 if (containers == nil) {
202 containers = [[NSMutableArray alloc] initWithArray:someContainers];
204 [containers addObjectsFromArray:someContainers];
206 if ([someContainers count] < 10000) {
207 if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !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];
215 node.shared = shared;
216 node.sharingAccount = sharingAccount;
217 node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
219 NSUInteger oldIndex = [children indexOfObject:node];
220 if (oldIndex != NSNotFound) {
221 // Use the same pointer value, if possible
222 node = [children objectAtIndex:oldIndex];
223 // node.pithosContainer = container;
224 [node setLimitedPithosContainer:container];
225 [keptNodes addIndex:oldIndex];
228 [newChildren addObject:node];
230 [[children objectsAtIndexes:
231 [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
232 if ([keptNodes containsIndex:idx])
235 }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
237 // Else cache was used and all results were fetched during this request, so existing children can be reused
238 [accountRequest release];
239 accountRequest = nil;
240 [containers release];
243 @synchronized(self) {
244 freshness = PithosNodeStateRefreshFinished;
246 [self postChildrenUpdatedNotificationName];
248 [accountRequest release];
249 // Do an additional request to fetch more objects
250 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos
252 marker:[[someContainers lastObject] name]
256 [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
257 else if (!forcedRefresh)
258 accountRequest.downloadCache = [ASIDownloadCache sharedCache];
259 accountRequest.delegate = self;
260 accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
261 accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
262 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
263 [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority",
264 [NSNumber numberWithUnsignedInteger:10], @"retries",
265 NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector",
266 NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector",
268 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
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];
277 @synchronized(self) {
278 freshness = PithosNodeStateRefreshFinished;
280 [self postChildrenUpdatedNotificationName];
282 [self accountRequestFailed:accountRequest];
287 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
288 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
289 NSLog(@"URL: %@", [request url]);
290 NSLog(@"cached: %d", [request didUseCachedResponse]);
292 if ([request isEqualTo:applyMetadataAccountRequest]) {
293 @synchronized(self) {
294 [applyMetadataAccountRequest release];
295 applyMetadataAccountRequest = nil;
298 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
299 self.pithosAccount = [refreshMetadataAccountRequest account];
300 @synchronized(self) {
301 [refreshMetadataAccountRequest release];
302 refreshMetadataAccountRequest = nil;
308 - (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
309 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
310 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
312 ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:request];
313 [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
314 if ([request isEqualTo:applyMetadataAccountRequest]) {
315 @synchronized(self) {
316 [applyMetadataAccountRequest release];
317 applyMetadataAccountRequest = newRequest;
319 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
320 @synchronized(self) {
321 [refreshMetadataAccountRequest release];
322 refreshMetadataAccountRequest = newRequest;
325 [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
327 if ([request isEqualTo:applyMetadataAccountRequest]) {
328 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
329 @synchronized(self) {
330 [applyMetadataAccountRequest release];
331 applyMetadataAccountRequest = nil;
333 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
334 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
335 @synchronized(self) {
336 [refreshMetadataAccountRequest release];
337 refreshMetadataAccountRequest = nil;
348 @synchronized(self) {
349 if (applyMetadataAccountRequest == nil) {
350 NSMutableDictionary *groups = pithosAccount.groups;
351 if ([groups count] == 0)
352 groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
353 applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos
355 metadata:pithosAccount.metadata
357 applyMetadataAccountRequest.delegate = self;
358 applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
359 applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
360 applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
361 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
362 [NSNumber numberWithUnsignedInteger:10], @"retries",
363 NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector",
364 NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector",
366 [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
371 - (void)refreshInfo {
372 @synchronized(self) {
373 if (refreshMetadataAccountRequest == nil) {
374 refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
375 refreshMetadataAccountRequest.delegate = self;
376 refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
377 refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
378 refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
379 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
380 [NSNumber numberWithUnsignedInteger:10], @"retries",
381 NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector",
382 NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector",
385 refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
386 [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];