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;
64 self.childrenUpdatedNotificationName = [NSString stringWithString:@"PithosAccountNodeChildrenUpdated"];
70 [accountRequest clearDelegatesAndCancel];
71 [accountRequest release];
72 [refreshMetadataAccountRequest clearDelegatesAndCancel];
73 [refreshMetadataAccountRequest release];
74 [applyMetadataAccountRequest clearDelegatesAndCancel];
75 [applyMetadataAccountRequest release];
77 [pithosAccount release];
83 #pragma mark Properties
85 - (void)setPithos:(ASIPithos *)aPithos {
86 if (aPithos && ![aPithos isEqualTo:pithos]) {
88 pithos = [aPithos retain];
96 url = [[NSString alloc] initWithFormat:@"%@%@",
97 (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
98 (shared ? @"?shared" : @"")];
102 - (NSArray *)children {
103 @synchronized(self) {
105 case PithosNodeStateFresh:
107 case PithosNodeStateRefreshNeeded:
108 freshness = PithosNodeStateRefreshing;
109 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos
115 [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
116 accountRequest.delegate = self;
117 accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
118 accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
119 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
120 [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority",
121 [NSNumber numberWithUnsignedInteger:10], @"retries",
122 NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector",
123 NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector",
126 accountRequest.downloadCache = [ASIDownloadCache sharedCache];
127 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
129 case PithosNodeStateRefreshing:
131 case PithosNodeStateRefreshFinished:
134 children = newChildren;
137 freshness = PithosNodeStateFresh;
145 - (NSString *)displayName {
146 if (displayName == nil)
147 return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
148 return [[displayName copy] autorelease];
153 icon = [sharedIcon retain];
158 #pragma mark ASIHTTPRequestDelegate
160 - (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
161 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
162 NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
164 ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest];
165 [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
166 [accountRequest release];
167 accountRequest = newAccountRequest;
168 [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
171 NSError *error = [accountRequest error];
173 message = [NSString stringWithFormat:@"Account listing failed: %@", error];
175 message = [NSString stringWithFormat:@"Account listing failed: (%d) %@",
176 accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
177 dispatch_async(dispatch_get_main_queue(), ^{
178 [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
180 [newChildren release];
182 [accountRequest release];
183 accountRequest = nil;
184 [containers release];
187 @synchronized(self) {
188 freshness = PithosNodeStateRefreshNeeded;
194 - (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
195 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
196 NSLog(@"List account finished: %@", [accountRequest url]);
197 NSLog(@"Cached: %d", [accountRequest didUseCachedResponse]);
198 if (accountRequest.responseStatusCode == 200) {
199 self.pithosAccount = [accountRequest account];
201 NSArray *someContainers = [accountRequest containers];
202 if (containers == nil) {
203 containers = [[NSMutableArray alloc] initWithArray:someContainers];
205 [containers addObjectsFromArray:someContainers];
207 if ([someContainers count] < 10000) {
208 if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) {
210 NSLog(@"using newChildren");
211 newChildren = [[NSMutableArray alloc] init];
212 NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
213 for (ASIPithosContainer *container in containers) {
214 PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithos:pithos pithosContainer:container] autorelease];
216 node.shared = shared;
217 node.sharingAccount = sharingAccount;
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 if (childrenUpdatedNotificationName) {
247 // Notify observers that children are updated
248 [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
251 [accountRequest release];
252 // Do an additional request to fetch more objects
253 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos
255 marker:[[someContainers lastObject] name]
259 [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
260 accountRequest.delegate = self;
261 accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
262 accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
263 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
264 [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority",
265 [NSNumber numberWithUnsignedInteger:10], @"retries",
266 NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector",
267 NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector",
270 accountRequest.downloadCache = [ASIDownloadCache sharedCache];
271 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
273 } else if (accountRequest.responseStatusCode == 304) {
274 // Account is not modified, so existing children can be reused
275 [accountRequest release];
276 accountRequest = nil;
277 [containers release];
280 @synchronized(self) {
281 freshness = PithosNodeStateRefreshFinished;
283 if (childrenUpdatedNotificationName) {
284 // Notify observers that children are updated
285 [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
288 [self accountRequestFailed:accountRequest];
293 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
294 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
295 NSLog(@"URL: %@", [request url]);
296 NSLog(@"cached: %d", [request didUseCachedResponse]);
298 if ([request isEqualTo:applyMetadataAccountRequest]) {
299 @synchronized(self) {
300 [applyMetadataAccountRequest release];
301 applyMetadataAccountRequest = nil;
304 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
305 self.pithosAccount = [refreshMetadataAccountRequest account];
306 @synchronized(self) {
307 [refreshMetadataAccountRequest release];
308 refreshMetadataAccountRequest = nil;
314 - (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
315 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
316 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
318 ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[[PithosUtilities copyRequest:request] autorelease];
319 [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
320 if ([request isEqualTo:applyMetadataAccountRequest]) {
321 @synchronized(self) {
322 [applyMetadataAccountRequest release];
323 applyMetadataAccountRequest = newRequest;
325 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
326 @synchronized(self) {
327 [refreshMetadataAccountRequest release];
328 refreshMetadataAccountRequest = newRequest;
331 [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
333 if ([request isEqualTo:applyMetadataAccountRequest]) {
334 dispatch_async(dispatch_get_main_queue(), ^{
335 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
337 @synchronized(self) {
338 [applyMetadataAccountRequest release];
339 applyMetadataAccountRequest = nil;
341 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
342 dispatch_async(dispatch_get_main_queue(), ^{
343 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
345 @synchronized(self) {
346 [refreshMetadataAccountRequest release];
347 refreshMetadataAccountRequest = nil;
358 @synchronized(self) {
359 if (applyMetadataAccountRequest == nil) {
360 NSMutableDictionary *groups = pithosAccount.groups;
361 if ([groups count] == 0)
362 groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
363 applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos
365 metadata:pithosAccount.metadata
367 applyMetadataAccountRequest.delegate = self;
368 applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
369 applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
370 applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
371 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
372 [NSNumber numberWithUnsignedInteger:10], @"retries",
373 NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector",
374 NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector",
376 [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
381 - (void)refreshInfo {
382 @synchronized(self) {
383 if (refreshMetadataAccountRequest == nil) {
384 refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
385 refreshMetadataAccountRequest.delegate = self;
386 refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
387 refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
388 refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
389 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
390 [NSNumber numberWithUnsignedInteger:10], @"retries",
391 NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector",
392 NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector",
394 refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
395 [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];