Support multiple accounts. Bug fixes. Improve concurrency.
[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 #pragma mark -
135 #pragma mark ASIHTTPRequestDelegate
136
137 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
138     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
139     NSLog(@"URL: %@", [request url]);
140     NSLog(@"cached: %d", [request didUseCachedResponse]);
141     
142     if ([request isEqualTo:applyMetadataObjectRequest]) {
143         int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
144         if (responseStatusCode != 202)
145             [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
146         @synchronized(self) {
147             [applyMetadataObjectRequest release];
148             applyMetadataObjectRequest = nil;
149         }
150         if (responseStatusCode == 202)
151             [self refreshInfo];
152     } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
153         [[pithosNodeInfoController window] makeFirstResponder:nil];
154         self.pithosObject = [refreshMetadataObjectRequest object];
155         @synchronized(self) {
156             [refreshMetadataObjectRequest release];
157             refreshMetadataObjectRequest = nil;
158         }
159     }
160     [pool drain];
161 }
162
163 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
164     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
165     NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
166     if (retries > 0) {
167         ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request];
168         [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
169         if ([request isEqualTo:applyMetadataObjectRequest]) {
170             @synchronized(self) {
171                 [applyMetadataObjectRequest release];
172                 applyMetadataObjectRequest = newRequest;
173             }
174         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
175             @synchronized(self) {
176                 [refreshMetadataObjectRequest release];
177                 refreshMetadataObjectRequest = newRequest;
178             }
179         }
180         [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
181     } else {
182         if ([request isEqualTo:applyMetadataObjectRequest]) {
183             dispatch_async(dispatch_get_main_queue(), ^{
184                 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
185             });
186             @synchronized(self) {
187                 [applyMetadataObjectRequest release];
188                 applyMetadataObjectRequest = nil;
189             }
190         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
191             dispatch_async(dispatch_get_main_queue(), ^{
192                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
193             });
194             @synchronized(self) {
195                 [refreshMetadataObjectRequest release];
196                 refreshMetadataObjectRequest = nil;
197             }
198         }
199     }
200     [pool drain];
201 }
202
203 #pragma mark -
204 #pragma mark Info
205
206 - (void)applyInfo {
207     @synchronized(self) {
208         if (applyMetadataObjectRequest == nil) {
209             [[pithosNodeInfoController window] makeFirstResponder:nil];
210             if (sharingAccount) {
211                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
212                                                                                               containerName:pithosContainer.name 
213                                                                                                  objectName:pithosObject.name 
214                                                                                             contentEncoding:nil 
215                                                                                          contentDisposition:nil 
216                                                                                                    manifest:nil 
217                                                                                                     sharing:nil
218                                                                                                    isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
219                                                                                                    metadata:pithosObject.metadata
220                                                                                                      update:NO] retain];
221                 [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
222             } else {
223                 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
224                                                                                               containerName:pithosContainer.name 
225                                                                                                  objectName:pithosObject.name 
226                                                                                             contentEncoding:pithosObject.contentEncoding 
227                                                                                          contentDisposition:pithosObject.contentDisposition 
228                                                                                                    manifest:pithosObject.manifest 
229                                                                                                     sharing:(pithosObject.sharing ? pithosObject.sharing : @"")
230                                                                                                    isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
231                                                                                                    metadata:pithosObject.metadata
232                                                                                                      update:NO] retain];
233             }
234             applyMetadataObjectRequest.delegate = self;
235             applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
236             applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
237             applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
238                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
239                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
240                                                    NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
241                                                    NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
242                                                    nil];
243             [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
244         }
245     }
246 }
247
248 - (void)refreshInfo {
249     @synchronized(self) {
250         if (refreshMetadataObjectRequest == nil) {
251             refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
252                                                                                       containerName:pithosContainer.name 
253                                                                                          objectName:pithosObject.name] retain];
254             if (sharingAccount)
255                 [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
256             refreshMetadataObjectRequest.delegate = self;
257             refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
258             refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
259             refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
260                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
261                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
262                                                      NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
263                                                      NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
264                                                      nil];
265             refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
266             [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
267         }
268     }
269 }
270
271 #pragma mark -
272 #pragma mark Actions
273
274 - (void)showPithosNodeInfo:(id)sender {
275     if (!pithosNodeInfoController)
276         pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
277     [pithosNodeInfoController showWindow:sender];
278     [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
279     [NSApp activateIgnoringOtherApps:YES];
280 }
281
282 @end