5 // Copyright 2011 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"
40 #import "ASIPithosAccountRequest.h"
41 #import "ASIPithosAccount.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIDownloadCache.h"
44 #import "PithosUtilities.h"
45 #import "PithosActivityFacility.h"
47 static NSImage *sharedIcon = nil;
49 @implementation PithosAccountNode
50 @synthesize pithosAccount;
53 if (self == [PithosAccountNode class])
54 sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)] retain];
58 #pragma mark Object Lifecycle
61 [accountRequest clearDelegatesAndCancel];
62 [accountRequest release];
63 [refreshMetadataAccountRequest clearDelegatesAndCancel];
64 [refreshMetadataAccountRequest release];
65 [applyMetadataAccountRequest clearDelegatesAndCancel];
66 [applyMetadataAccountRequest release];
68 [pithosAccount release];
73 #pragma mark Properties
77 url = [[NSString alloc] initWithFormat:@"%@%@",
78 (sharingAccount ? [ASIPithosRequest storageURLWithAuthUser:sharingAccount] : [ASIPithosRequest storageURL]),
79 (shared ? @"?shared" : @"")];
83 - (NSArray *)children {
86 case PithosNodeStateFresh:
88 case PithosNodeStateRefreshNeeded:
89 freshness = PithosNodeStateRefreshing;
90 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithLimit:0
95 [accountRequest setRequestUserFromDefaultTo:sharingAccount];
96 accountRequest.delegate = self;
97 accountRequest.didFinishSelector = @selector(accountRequestFinished:);
98 accountRequest.didFailSelector = @selector(accountRequestFailed:);
99 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
100 [NSNumber numberWithUnsignedInteger:10], @"retries",
103 accountRequest.downloadCache = [ASIDownloadCache sharedCache];
104 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
106 case PithosNodeStateRefreshing:
108 case PithosNodeStateRefreshFinished:
111 children = newChildren;
114 freshness = PithosNodeStateFresh;
122 - (NSString *)displayName {
123 if (displayName == nil)
124 return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
125 return [[displayName copy] autorelease];
130 icon = [sharedIcon retain];
135 #pragma mark ASIHTTPRequestDelegate
137 - (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
138 NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
140 ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest];
141 [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
142 [accountRequest release];
143 accountRequest = newAccountRequest;
144 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
147 NSError *error = [accountRequest error];
149 message = [NSString stringWithFormat:@"Account listing failed: %@", error];
151 message = [NSString stringWithFormat:@"Account listing failed: (%d) %@",
152 accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
153 [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
154 [newChildren release];
156 [accountRequest release];
157 accountRequest = nil;
158 [containers release];
161 @synchronized(self) {
162 freshness = PithosNodeStateRefreshNeeded;
167 - (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
168 NSLog(@"List account finished: %@", [accountRequest url]);
169 NSLog(@"Cached: %d", [accountRequest didUseCachedResponse]);
170 if (accountRequest.responseStatusCode == 200) {
171 self.pithosAccount = [accountRequest account];
173 NSArray *someContainers = [accountRequest containers];
174 if (containers == nil) {
175 containers = [[NSMutableArray alloc] initWithArray:someContainers];
177 [containers addObjectsFromArray:someContainers];
179 if ([someContainers count] < 10000) {
180 if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) {
182 NSLog(@"using newChildren");
183 newChildren = [[NSMutableArray alloc] init];
184 NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
185 for (ASIPithosContainer *container in containers) {
186 PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithosContainer:container] autorelease];
188 node.shared = shared;
189 node.sharingAccount = sharingAccount;
191 NSUInteger oldIndex = [children indexOfObject:node];
192 if (oldIndex != NSNotFound) {
193 // Use the same pointer value, if possible
194 node = [children objectAtIndex:oldIndex];
195 node.pithosContainer = container;
196 [keptNodes addIndex:oldIndex];
199 [newChildren addObject:node];
201 [[children objectsAtIndexes:
202 [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
203 if ([keptNodes containsIndex:idx])
206 }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
208 // Else cache was used and all results were fetched during this request, so existing children can be reused
209 [accountRequest release];
210 accountRequest = nil;
211 [containers release];
214 @synchronized(self) {
215 freshness = PithosNodeStateRefreshFinished;
217 // Notify observers that children are updated
218 [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosAccountNodeChildrenUpdated" object:self];
220 [accountRequest release];
221 // Do an additional request to fetch more objects
222 accountRequest = [[ASIPithosAccountRequest listContainersRequestWithLimit:0
223 marker:[[someContainers lastObject] name]
227 [accountRequest setRequestUserFromDefaultTo:sharingAccount];
228 accountRequest.delegate = self;
229 accountRequest.didFinishSelector = @selector(accountRequestFinished:);
230 accountRequest.didFailSelector = @selector(accountRequestFailed:);
231 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
232 [NSNumber numberWithUnsignedInteger:10], @"retries",
235 accountRequest.downloadCache = [ASIDownloadCache sharedCache];
236 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
238 } else if (accountRequest.responseStatusCode == 304) {
239 // Account is not modified, so existing children can be reused
240 [accountRequest release];
241 accountRequest = nil;
242 [containers release];
245 @synchronized(self) {
246 freshness = PithosNodeStateRefreshFinished;
248 // Notify observers that children are updated
249 [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosAccountNodeChildrenUpdated" object:self];
251 [self accountRequestFailed:accountRequest];
255 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
256 NSLog(@"URL: %@", [request url]);
257 NSLog(@"cached: %d", [request didUseCachedResponse]);
259 if ([request isEqualTo:applyMetadataAccountRequest]) {
260 @synchronized(self) {
261 [applyMetadataAccountRequest release];
262 applyMetadataAccountRequest = nil;
265 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
266 self.pithosAccount = [refreshMetadataAccountRequest account];
267 @synchronized(self) {
268 [refreshMetadataAccountRequest release];
269 refreshMetadataAccountRequest = nil;
274 - (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
275 if ([request isEqualTo:applyMetadataAccountRequest]) {
276 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
277 @synchronized(self) {
278 [applyMetadataAccountRequest release];
279 applyMetadataAccountRequest = nil;
281 } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
282 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
283 @synchronized(self) {
284 [refreshMetadataAccountRequest release];
285 refreshMetadataAccountRequest = nil;
294 @synchronized(self) {
295 if (applyMetadataAccountRequest == nil) {
296 NSMutableDictionary *groups = pithosAccount.groups;
297 if ([groups count] == 0)
298 groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
299 applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithGroups:groups
300 metadata:pithosAccount.metadata
302 applyMetadataAccountRequest.delegate = self;
303 applyMetadataAccountRequest.didFinishSelector = @selector(accountMetadataRequestFinished:);
304 applyMetadataAccountRequest.didFailSelector = @selector(accountMetadataRequestFailed:);
305 [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
310 - (void)refreshInfo {
311 @synchronized(self) {
312 if (refreshMetadataAccountRequest == nil) {
313 refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequest] retain];
314 refreshMetadataAccountRequest.delegate = self;
315 refreshMetadataAccountRequest.didFinishSelector = @selector(accountMetadataRequestFinished:);
316 refreshMetadataAccountRequest.didFailSelector = @selector(accountMetadataRequestFailed:);
317 refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
318 [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];