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 "PithosFileUtilities.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;
66 icon = [anIcon retain];
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 ASIHTTPRequestDelegate
105 - (void)containerRequestFinished:(ASIPithosContainerRequest *)request {
106 NSLog(@"URL: %@", [containerRequest url]);
107 NSLog(@"cached: %d", [containerRequest didUseCachedResponse]);
109 if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) {
110 pithosContainer.blockHash = [containerRequest blockHash];
111 pithosContainer.blockSize = [containerRequest blockSize];
114 NSArray *someObjects = [containerRequest objects];
115 if (objects == nil) {
116 objects = [[NSMutableArray alloc] initWithArray:someObjects];
118 [objects addObjectsFromArray:someObjects];
120 if ([someObjects count] < 10000) {
121 if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) {
123 NSLog(@"using newChildren");
124 newChildren = [[NSMutableArray alloc] init];
125 NSArray *objectNames = [objects valueForKey:@"name"];
126 NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
127 BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]);
128 for (ASIPithosObject *object in objects) {
130 [object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]]) {
131 // The check above removes false objects due to trailing slash or same prefix
133 NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]];
134 if ((sameNameObjectIndex == NSNotFound) ||
135 ![[[objects objectAtIndex:sameNameObjectIndex] contentType] isEqualToString:@"application/directory"]) {
136 PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
139 NSUInteger oldIndex = [children indexOfObject:node];
140 if (oldIndex != NSNotFound) {
141 // Use the same pointer value, if possible
142 node = [children objectAtIndex:oldIndex];
143 node.pithosContainer = pithosContainer;
144 node.pithosObject = object;
145 [keptNodes addIndex:oldIndex];
148 [newChildren addObject:node];
150 } else if ([object.contentType isEqualToString:@"application/directory"]) {
151 PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
154 NSUInteger oldIndex = [children indexOfObject:node];
155 if (oldIndex != NSNotFound) {
156 // Use the same pointer value, if possible
157 node = [children objectAtIndex:oldIndex];
158 node.pithosContainer = pithosContainer;
159 node.pithosObject = object;
160 [keptNodes addIndex:oldIndex];
163 [newChildren addObject:node];
165 PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
168 NSUInteger oldIndex = [children indexOfObject:node];
169 if (oldIndex != NSNotFound) {
170 // Use the same pointer value, if possible
171 node = [children objectAtIndex:oldIndex];
172 node.pithosContainer = pithosContainer;
173 node.pithosObject = object;
174 [keptNodes addIndex:oldIndex];
177 [newChildren addObject:node];
181 [[children objectsAtIndexes:
182 [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
183 if ([keptNodes containsIndex:idx])
186 }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
188 // Else cache was used and all results were fetched during this request, so existing children can be reused
189 [containerRequest release];
190 containerRequest = nil;
193 @synchronized(self) {
194 freshness = PithosNodeStateRefreshFinished;
196 // Notify observers that children are updated
197 [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
199 [containerRequest release];
200 // Do an additional request to fetch more objects
201 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name
203 marker:[[someObjects lastObject] name]
210 containerRequest.delegate = self;
211 containerRequest.downloadCache = [ASIDownloadCache sharedCache];
212 [containerRequest startAsynchronous];
216 - (void)containerRequestFailed:(ASIPithosContainerRequest *)request {
217 [PithosFileUtilities httpRequestErrorAlertWithRequest:request];
218 [newChildren release];
220 [containerRequest release];
221 containerRequest = nil;
224 @synchronized(self) {
225 freshness = PithosNodeStateRefreshNeeded;
229 - (void)containerMetadataRequestFinished:(ASIPithosContainerRequest *)request {
230 NSLog(@"URL: %@", [request url]);
231 NSLog(@"cached: %d", [request didUseCachedResponse]);
233 if ([request isEqualTo:applyMetadataContainerRequest]) {
234 @synchronized(self) {
235 [applyMetadataContainerRequest release];
236 applyMetadataContainerRequest = nil;
239 } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
240 [[pithosNodeInfoController window] makeFirstResponder:nil];
241 self.pithosContainer = [refreshMetadataContainerRequest container];
242 @synchronized(self) {
243 [refreshMetadataContainerRequest release];
244 refreshMetadataContainerRequest = nil;
249 - (void)containerMetadataRequestFailed:(ASIPithosContainerRequest *)request {
250 if ([request isEqualTo:applyMetadataContainerRequest]) {
251 [PithosFileUtilities httpRequestErrorAlertWithRequest:applyMetadataContainerRequest];
252 @synchronized(self) {
253 [applyMetadataContainerRequest release];
254 applyMetadataContainerRequest = nil;
256 } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
257 [PithosFileUtilities httpRequestErrorAlertWithRequest:refreshMetadataContainerRequest];
258 @synchronized(self) {
259 [refreshMetadataContainerRequest release];
260 refreshMetadataContainerRequest = nil;
266 #pragma mark Properties
270 url = [[NSString alloc] initWithFormat:@"%@/%@", [ASIPithosRequest storageURL], pithosContainer.name];
274 - (NSArray *)children {
275 @synchronized(self) {
277 case PithosNodeStateFresh:
279 case PithosNodeStateRefreshNeeded:
280 freshness = PithosNodeStateRefreshing;
281 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name
290 containerRequest.delegate = self;
291 containerRequest.didFinishSelector = @selector(containerRequestFinished:);
292 containerRequest.didFailSelector = @selector(containerRequestFailed:);
293 containerRequest.downloadCache = [ASIDownloadCache sharedCache];
294 [containerRequest startAsynchronous];
296 case PithosNodeStateRefreshing:
298 case PithosNodeStateRefreshFinished:
301 children = newChildren;
304 freshness = PithosNodeStateFresh;
312 - (NSString *)displayName {
313 return [[pithosContainer.name copy] autorelease];
322 - (void)setIcon:(NSImage *)anIcon {
324 icon = [anIcon retain];
327 - (void)setPithosContainer:(ASIPithosContainer *)aPithosContainer {
328 if (![pithosContainer isEqualTo:aPithosContainer]) {
329 [pithosContainer release];
330 pithosContainer = [aPithosContainer retain];
332 if (pithosContainer.policy) {
333 self.policyVersioning = [pithosContainer.policy objectForKey:@"versioning"];
334 self.policyQuota = [NSNumber numberWithLongLong:[[pithosContainer.policy objectForKey:@"quota"] longLongValue]];
336 self.policyVersioning = @"manual";
337 self.policyQuota = [NSNumber numberWithLongLong:0];
345 @synchronized(self) {
346 if (applyMetadataContainerRequest == nil) {
347 [[pithosNodeInfoController window] makeFirstResponder:nil];
348 applyMetadataContainerRequest = [[ASIPithosContainerRequest updateContainerMetadataRequestWithContainerName:pithosContainer.name
349 policy:[NSDictionary dictionaryWithObjectsAndKeys:
350 policyVersioning, @"versioning",
351 [policyQuota stringValue], @"quota",
353 metadata:pithosContainer.metadata
355 applyMetadataContainerRequest.delegate = self;
356 applyMetadataContainerRequest.didFinishSelector = @selector(containerMetadataRequestFinished:);
357 applyMetadataContainerRequest.didFailSelector = @selector(containerMetadataRequestFailed:);
358 [applyMetadataContainerRequest startAsynchronous];
363 - (void)refreshInfo {
364 @synchronized(self) {
365 if (refreshMetadataContainerRequest == nil) {
366 refreshMetadataContainerRequest = [[ASIPithosContainerRequest containerMetadataRequestWithContainerName:pithosContainer.name] retain];
367 refreshMetadataContainerRequest.delegate = self;
368 refreshMetadataContainerRequest.didFinishSelector = @selector(containerMetadataRequestFinished:);
369 refreshMetadataContainerRequest.didFailSelector = @selector(containerMetadataRequestFailed:);
370 refreshMetadataContainerRequest.downloadCache = [ASIDownloadCache sharedCache];
371 [refreshMetadataContainerRequest startAsynchronous];
379 - (void)showPithosNodeInfo:(id)sender {
380 if (!pithosNodeInfoController)
381 pithosNodeInfoController = [[PithosContainerNodeInfoController alloc] initWithPithosNode:self];
382 [pithosNodeInfoController showWindow:sender];
383 [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
384 [NSApp activateIgnoringOtherApps:YES];