2 // PithosContainerNode.m
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 "PithosContainerNode.h"
39 #import "PithosObjectNode.h"
40 #import "PithosSubdirNode.h"
41 #import "ASIPithosContainerRequest.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIPithosObject.h"
44 #import "ASIDownloadCache.h"
45 #import "PithosUtilities.h"
46 #import "PithosContainerNodeInfoController.h"
48 static NSImage *sharedIcon = nil;
50 @implementation PithosContainerNode
51 @synthesize pithosContainer, prefix;
52 @synthesize policyVersioning, policyQuota;
55 if (self == [PithosContainerNode class])
56 sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericHardDiskIcon)] retain];
60 #pragma mark Object Lifecycle
62 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer icon:(NSImage *)anIcon {
63 if ((self = [super init])) {
64 self.pithosContainer = aPithosContainer;
67 childrenUpdatedNotificationName = @"PithosContainerNodeChildrenUpdated";
72 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer {
73 return [self initWithPithosContainer:aPithosContainer icon:nil];
76 - (id)initWithContainerName:(NSString *)aContainerName icon:(NSImage *)anIcon {
77 ASIPithosContainer *container = [ASIPithosContainer container];
78 container.name = aContainerName;
79 return [self initWithPithosContainer:container icon:anIcon];
82 - (id)initWithContainerName:(NSString *)aContainerName {
83 return [self initWithContainerName:aContainerName icon:nil];
87 [containerRequest clearDelegatesAndCancel];
88 [containerRequest release];
89 [refreshMetadataContainerRequest clearDelegatesAndCancel];
90 [refreshMetadataContainerRequest release];
91 [applyMetadataContainerRequest clearDelegatesAndCancel];
92 [applyMetadataContainerRequest release];
93 [policyQuota release];
94 [policyVersioning release];
95 [childrenUpdatedNotificationName release];
98 [pithosContainer release];
103 #pragma mark Properties
107 url = [[NSString alloc] initWithFormat:@"%@/%@%@",
108 (sharingAccount ? [ASIPithosRequest storageURLWithAuthUser:sharingAccount] : [ASIPithosRequest storageURL]),
109 pithosContainer.name,
110 (shared ? @"?shared" : @"")];
114 - (NSArray *)children {
115 @synchronized(self) {
117 case PithosNodeStateFresh:
119 case PithosNodeStateRefreshNeeded:
120 freshness = PithosNodeStateRefreshing;
121 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name
131 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
132 containerRequest.delegate = self;
133 containerRequest.didFinishSelector = @selector(containerRequestFinished:);
134 containerRequest.didFailSelector = @selector(containerRequestFailed:);
136 containerRequest.downloadCache = [ASIDownloadCache sharedCache];
137 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
139 case PithosNodeStateRefreshing:
141 case PithosNodeStateRefreshFinished:
144 children = newChildren;
147 freshness = PithosNodeStateFresh;
155 - (NSString *)displayName {
156 return [[pithosContainer.name copy] autorelease];
159 - (void)setDisplayName:(NSString *)aDisplayName {
164 if ([pithosContainer.name isEqualToString:@"pithos"])
165 icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)] retain];
166 else if ([pithosContainer.name isEqualToString:@"trash"])
167 icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)] retain];
169 icon = [sharedIcon retain];
174 - (void)setPithosContainer:(ASIPithosContainer *)aPithosContainer {
175 if (![pithosContainer isEqualTo:aPithosContainer]) {
176 [pithosContainer release];
177 pithosContainer = [aPithosContainer retain];
179 if (pithosContainer.policy) {
180 self.policyVersioning = [pithosContainer.policy objectForKey:@"versioning"];
181 self.policyQuota = [NSNumber numberWithLongLong:[[pithosContainer.policy objectForKey:@"quota"] longLongValue]];
183 self.policyVersioning = @"manual";
184 self.policyQuota = [NSNumber numberWithLongLong:0];
189 #pragma mark ASIHTTPRequestDelegate
191 - (void)containerRequestFinished:(ASIPithosContainerRequest *)request {
192 NSLog(@"URL: %@", [containerRequest url]);
193 NSLog(@"cached: %d", [containerRequest didUseCachedResponse]);
195 if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) {
196 pithosContainer.blockHash = [containerRequest blockHash];
197 pithosContainer.blockSize = [containerRequest blockSize];
200 NSArray *someObjects = [containerRequest objects];
201 if (objects == nil) {
202 objects = [[NSMutableArray alloc] initWithArray:someObjects];
204 [objects addObjectsFromArray:someObjects];
206 if ([someObjects count] < 10000) {
207 if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) {
209 NSLog(@"using newChildren");
210 newChildren = [[NSMutableArray alloc] init];
211 NSArray *objectNames = [objects valueForKey:@"name"];
212 NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
213 BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]);
214 for (ASIPithosObject *object in objects) {
216 [object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]]) {
217 // The check above removes false objects due to trailing slash or same prefix
219 NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]];
220 if ((sameNameObjectIndex == NSNotFound) ||
221 ![[[objects objectAtIndex:sameNameObjectIndex] contentType] isEqualToString:@"application/directory"]) {
222 PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
224 node.shared = shared;
225 node.sharingAccount = sharingAccount;
227 NSUInteger oldIndex = [children indexOfObject:node];
228 if (oldIndex != NSNotFound) {
229 // Use the same pointer value, if possible
230 node = [children objectAtIndex:oldIndex];
231 node.pithosContainer = pithosContainer;
232 node.pithosObject = object;
233 [keptNodes addIndex:oldIndex];
237 node.pithosObject.allowedTo = [NSString stringWithString:@"read"];
238 [newChildren addObject:node];
240 } else if ([object.contentType isEqualToString:@"application/directory"]) {
241 PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
243 node.shared = shared;
244 node.sharingAccount = sharingAccount;
246 NSUInteger oldIndex = [children indexOfObject:node];
247 if (oldIndex != NSNotFound) {
248 // Use the same pointer value, if possible
249 node = [children objectAtIndex:oldIndex];
250 node.pithosContainer = pithosContainer;
251 node.pithosObject = object;
252 [keptNodes addIndex:oldIndex];
255 [newChildren addObject:node];
257 PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
259 node.shared = shared;
260 node.sharingAccount = sharingAccount;
262 NSUInteger oldIndex = [children indexOfObject:node];
263 if (oldIndex != NSNotFound) {
264 // Use the same pointer value, if possible
265 node = [children objectAtIndex:oldIndex];
266 node.pithosContainer = pithosContainer;
267 node.pithosObject = object;
268 [keptNodes addIndex:oldIndex];
271 [newChildren addObject:node];
275 [[children objectsAtIndexes:
276 [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
277 if ([keptNodes containsIndex:idx])
280 }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
282 // Else cache was used and all results were fetched during this request, so existing children can be reused
283 [containerRequest release];
284 containerRequest = nil;
288 @synchronized(self) {
289 freshness = PithosNodeStateRefreshFinished;
291 // Notify observers that children are updated
292 [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
294 [containerRequest release];
295 // Do an additional request to fetch more objects
296 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name
298 marker:[[someObjects lastObject] name]
306 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
307 containerRequest.delegate = self;
309 containerRequest.downloadCache = [ASIDownloadCache sharedCache];
310 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
314 - (void)containerRequestFailed:(ASIPithosContainerRequest *)request {
315 [PithosUtilities httpRequestErrorAlertWithRequest:request];
316 [newChildren release];
318 [containerRequest release];
319 containerRequest = nil;
323 @synchronized(self) {
324 freshness = PithosNodeStateRefreshNeeded;
328 - (void)containerMetadataRequestFinished:(ASIPithosContainerRequest *)request {
329 NSLog(@"URL: %@", [request url]);
330 NSLog(@"cached: %d", [request didUseCachedResponse]);
332 if ([request isEqualTo:applyMetadataContainerRequest]) {
333 @synchronized(self) {
334 [applyMetadataContainerRequest release];
335 applyMetadataContainerRequest = nil;
338 } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
339 [[pithosNodeInfoController window] makeFirstResponder:nil];
340 self.pithosContainer = [refreshMetadataContainerRequest container];
341 @synchronized(self) {
342 [refreshMetadataContainerRequest release];
343 refreshMetadataContainerRequest = nil;
348 - (void)containerMetadataRequestFailed:(ASIPithosContainerRequest *)request {
349 if ([request isEqualTo:applyMetadataContainerRequest]) {
350 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataContainerRequest];
351 @synchronized(self) {
352 [applyMetadataContainerRequest release];
353 applyMetadataContainerRequest = nil;
355 } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
356 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataContainerRequest];
357 @synchronized(self) {
358 [refreshMetadataContainerRequest release];
359 refreshMetadataContainerRequest = nil;
368 @synchronized(self) {
369 if (applyMetadataContainerRequest == nil) {
370 [[pithosNodeInfoController window] makeFirstResponder:nil];
371 applyMetadataContainerRequest = [[ASIPithosContainerRequest updateContainerMetadataRequestWithContainerName:pithosContainer.name
372 policy:[NSDictionary dictionaryWithObjectsAndKeys:
373 policyVersioning, @"versioning",
374 [policyQuota stringValue], @"quota",
376 metadata:pithosContainer.metadata
378 applyMetadataContainerRequest.delegate = self;
379 applyMetadataContainerRequest.didFinishSelector = @selector(containerMetadataRequestFinished:);
380 applyMetadataContainerRequest.didFailSelector = @selector(containerMetadataRequestFailed:);
381 [[PithosUtilities prepareRequest:applyMetadataContainerRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
386 - (void)refreshInfo {
387 @synchronized(self) {
388 if (refreshMetadataContainerRequest == nil) {
389 refreshMetadataContainerRequest = [[ASIPithosContainerRequest containerMetadataRequestWithContainerName:pithosContainer.name] retain];
390 refreshMetadataContainerRequest.delegate = self;
391 refreshMetadataContainerRequest.didFinishSelector = @selector(containerMetadataRequestFinished:);
392 refreshMetadataContainerRequest.didFailSelector = @selector(containerMetadataRequestFailed:);
393 refreshMetadataContainerRequest.downloadCache = [ASIDownloadCache sharedCache];
394 [[PithosUtilities prepareRequest:refreshMetadataContainerRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
402 - (void)showPithosNodeInfo:(id)sender {
403 if (!pithosNodeInfoController)
404 pithosNodeInfoController = [[PithosContainerNodeInfoController alloc] initWithPithosNode:self];
405 [pithosNodeInfoController showWindow:sender];
406 [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
407 [NSApp activateIgnoringOtherApps:YES];