Support changes in container and object metadata.
[pithos-macos] / pithos-macos / PithosObjectNode.m
1 //
2 //  PithosObjectNode.m
3 //  pithos-macos
4 //
5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
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.
19 // 
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.
32 // 
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.
37
38 #import "PithosObjectNode.h"
39 #import "ASIPithosRequest.h"
40 #import "ASIPithos.h"
41 #import "ASIPithosObjectRequest.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIPithosObject.h"
44 #import "ASIDownloadCache.h"
45 #import "PithosUtilities.h"
46 #import "PithosObjectNodeInfoController.h"
47
48 @implementation PithosObjectNode
49 @synthesize pithos, pithosContainer, pithosObject;
50 @synthesize isPublic;
51
52 #pragma mark -
53 #pragma mark Object Lifecycle
54
55 - (id)initWithPithos:(ASIPithos *)aPithos 
56      pithosContainer:(ASIPithosContainer *)aPithosContainer 
57         pithosObject:(ASIPithosObject *)aPithosObject {
58     if ((self = [super init])) {
59         self.pithos = aPithos;
60         self.pithosContainer = aPithosContainer;
61         self.pithosObject = aPithosObject;
62         isLeafItem = YES;
63     }
64     return self;
65 }
66
67 - (void)dealloc {
68     [refreshMetadataObjectRequest clearDelegatesAndCancel];
69     [refreshMetadataObjectRequest release];
70     [applyMetadataObjectRequest clearDelegatesAndCancel];
71     [applyMetadataObjectRequest release];
72     [pithosObject release];
73     [pithosContainer release];
74     [pithos release];
75     [super dealloc];
76 }
77
78 #pragma mark -
79 #pragma mark Properties
80
81 - (void)setPithos:(ASIPithos *)aPithos {
82     if (aPithos && ![aPithos isEqualTo:pithos]) {
83         [pithos release];
84         pithos = [aPithos retain];
85         [url release];
86         url = nil;
87     }
88 }
89
90 - (NSString *)url {
91     if (url == nil)
92         url = [[NSString alloc] initWithFormat:@"object %@/%@/%@%@", 
93                (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
94                pithosContainer.name, 
95                pithosObject.name, 
96                (shared ? @"?shared" : @"")];
97     return url;
98 }
99
100 - (NSArray *)children {
101     return nil;
102 }
103
104 - (NSString *)displayName {
105     if (displayName == nil) {
106         displayName = [pithosObject.name lastPathComponent];
107         if([pithosObject.name hasSuffix:@"/"])
108             displayName = [displayName stringByAppendingString:@"/"];
109         [displayName retain];
110     }
111     return [[displayName copy] autorelease];
112 }
113
114 - (void)setDisplayName:(NSString *)aDisplayName {    
115 }
116
117 - (NSImage *)icon {
118     if (icon == nil)
119         icon = [[[NSWorkspace sharedWorkspace] iconForFileType:[pithosObject.name pathExtension]] retain];
120     return icon;
121 }
122
123 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
124     if (![pithosObject isEqualTo:aPithosObject]) {
125         [pithosObject release];
126         pithosObject = [aPithosObject retain];
127     }
128     self.isPublic = (pithosObject.publicURI != nil);
129     // Refresh browser if the object is in my shared and is no longer shared
130     if (shared && !pithosObject.sharing)
131         [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self];
132 }
133
134 - (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
135     if (![pithosObject isEqualTo:aPithosObject]) {
136         self.pithosObject.subdir = aPithosObject.subdir;
137         self.pithosObject.name = aPithosObject.name;
138         self.pithosObject.hash = aPithosObject.hash;
139         self.pithosObject.objectHash = aPithosObject.objectHash;
140         self.pithosObject.UUID = aPithosObject.UUID;
141         self.pithosObject.bytes = aPithosObject.bytes;
142         self.pithosObject.contentType = aPithosObject.contentType;
143         self.pithosObject.lastModified = aPithosObject.lastModified;
144         self.pithosObject.version = aPithosObject.version;
145         self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
146         self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
147         self.pithosObject.sharedBy = aPithosObject.sharedBy;
148         self.pithosObject.allowedTo = aPithosObject.allowedTo;
149         if (!pithosNodeInfoController) {
150             self.pithosObject.sharing = aPithosObject.sharing;
151             self.pithosObject.publicURI = aPithosObject.publicURI;
152             self.pithosObject = pithosObject;
153         }
154     }
155 }
156
157 #pragma mark -
158 #pragma mark ASIHTTPRequestDelegate
159
160 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
161     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
162     NSLog(@"URL: %@", [request url]);
163     NSLog(@"cached: %d", [request didUseCachedResponse]);
164     
165     if ([request isEqualTo:applyMetadataObjectRequest]) {
166         int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
167         if (responseStatusCode != 202)
168             [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
169         @synchronized(self) {
170             [applyMetadataObjectRequest release];
171             applyMetadataObjectRequest = nil;
172         }
173         if (responseStatusCode == 202)
174             [self refreshInfo];
175     } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
176         [[pithosNodeInfoController window] makeFirstResponder:nil];
177         self.pithosObject = [refreshMetadataObjectRequest object];
178         @synchronized(self) {
179             [refreshMetadataObjectRequest release];
180             refreshMetadataObjectRequest = nil;
181         }
182     }
183     [pool drain];
184 }
185
186 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
187     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
188     NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
189     if (retries > 0) {
190         ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request];
191         [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
192         if ([request isEqualTo:applyMetadataObjectRequest]) {
193             @synchronized(self) {
194                 [applyMetadataObjectRequest release];
195                 applyMetadataObjectRequest = newRequest;
196             }
197         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
198             @synchronized(self) {
199                 [refreshMetadataObjectRequest release];
200                 refreshMetadataObjectRequest = newRequest;
201             }
202         }
203         [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
204     } else {
205         if ([request isEqualTo:applyMetadataObjectRequest]) {
206             dispatch_async(dispatch_get_main_queue(), ^{
207                 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
208             });
209             @synchronized(self) {
210                 [applyMetadataObjectRequest release];
211                 applyMetadataObjectRequest = nil;
212             }
213         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
214             dispatch_async(dispatch_get_main_queue(), ^{
215                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
216             });
217             @synchronized(self) {
218                 [refreshMetadataObjectRequest release];
219                 refreshMetadataObjectRequest = nil;
220             }
221         }
222     }
223     [pool drain];
224 }
225
226 #pragma mark -
227 #pragma mark Info
228
229 - (void)applyInfo {
230     @synchronized(self) {
231         if (applyMetadataObjectRequest == nil) {
232             [[pithosNodeInfoController window] makeFirstResponder:nil];
233             if (sharingAccount) {
234                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
235                                                                                               containerName:pithosContainer.name 
236                                                                                                  objectName:pithosObject.name 
237                                                                                             contentEncoding:nil 
238                                                                                          contentDisposition:nil 
239                                                                                                    manifest:nil 
240                                                                                                     sharing:nil
241                                                                                                    isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
242                                                                                                    metadata:pithosObject.metadata
243                                                                                                      update:NO] retain];
244                 [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
245             } else {
246                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
247                                                                                               containerName:pithosContainer.name 
248                                                                                                  objectName:pithosObject.name 
249                                                                                             contentEncoding:pithosObject.contentEncoding 
250                                                                                          contentDisposition:pithosObject.contentDisposition 
251                                                                                                    manifest:pithosObject.manifest 
252                                                                                                     sharing:(pithosObject.sharing ? pithosObject.sharing : @"")
253                                                                                                    isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
254                                                                                                    metadata:pithosObject.metadata
255                                                                                                      update:NO] retain];
256             }
257             applyMetadataObjectRequest.delegate = self;
258             applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
259             applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
260             applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
261                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
262                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
263                                                    NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
264                                                    NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
265                                                    nil];
266             [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
267         }
268     }
269 }
270
271 - (void)refreshInfo {
272     @synchronized(self) {
273         if (refreshMetadataObjectRequest == nil) {
274             refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
275                                                                                       containerName:pithosContainer.name 
276                                                                                          objectName:pithosObject.name] retain];
277             if (sharingAccount)
278                 [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
279             refreshMetadataObjectRequest.delegate = self;
280             refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
281             refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
282             refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
283                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
284                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
285                                                      NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
286                                                      NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
287                                                      nil];
288             refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
289             [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
290         }
291     }
292 }
293
294 #pragma mark -
295 #pragma mark Actions
296
297 - (void)showPithosNodeInfo:(id)sender {
298     if (!pithosNodeInfoController) {
299         pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
300         [self refreshInfo];
301     }
302     [pithosNodeInfoController showWindow:sender];
303     [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
304     [NSApp activateIgnoringOtherApps:YES];
305 }
306
307 @end