PageRenderTime 49ms CodeModel.GetById 13ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/externals/google-toolbox-for-mac/Foundation/GTMTransientRootProxy.m

http://macfuse.googlecode.com/
Objective C | 230 lines | 148 code | 32 blank | 50 comment | 15 complexity | ce216a9972aee2a1e6ce96d61604b31c MD5 | raw file
  1//
  2//  GTMTransientRootProxy.m
  3//
  4//  Copyright 2006-2009 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import "GTMTransientRootProxy.h"
 20#import "GTMObjC2Runtime.h"
 21
 22// Private methods on NSMethodSignature that we need to call.  This method has
 23// been available since 10.0, but Apple didn't add it to the headers until 10.5
 24#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
 25@interface NSMethodSignature (UndeclaredMethods)
 26+ (NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
 27@end
 28#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
 29
 30@interface GTMTransientRootProxy (PrivateMethods)
 31// Returns an NSConnection for NSMacPorts.  This method is broken out to allow
 32// subclasses to override it to generate different types of NSConnections.
 33- (NSConnection *)makeConnection;
 34
 35// Returns the "real" proxy (stored in the realProxy_ ivar) associated with this
 36// instance.  If realProxy_ is nil, then an attempt is made to make a connection
 37// to create the realProxy_.
 38//
 39- (NSDistantObject *)realProxy;
 40
 41// "Releases" the realProxy_ ivar, and removes |self| as an observer from
 42// the NSNotificationCenter.
 43//
 44- (void)releaseRealProxy;
 45
 46// Notification that a connection has died.
 47- (void)connectionDidDie:(NSNotification *)notification;
 48@end
 49
 50@implementation GTMTransientRootProxy
 51
 52+ (id)rootProxyWithRegisteredName:(NSString *)name
 53                             host:(NSString *)host
 54                         protocol:(Protocol *)protocol
 55                   requestTimeout:(NSTimeInterval)requestTimeout
 56                     replyTimeout:(NSTimeInterval)replyTimeout {
 57  return [[[self alloc] initWithRegisteredName:name
 58                                          host:host
 59                                      protocol:protocol
 60                                requestTimeout:requestTimeout
 61                                  replyTimeout:replyTimeout] autorelease];
 62}
 63
 64- (id)initWithRegisteredName:(NSString *)name
 65                        host:(NSString *)host
 66                    protocol:(Protocol *)protocol
 67              requestTimeout:(NSTimeInterval)requestTimeout
 68                replyTimeout:(NSTimeInterval)replyTimeout {
 69  if (!name || !protocol) {
 70    [self release];
 71    return nil;
 72  }
 73
 74  requestTimeout_ = requestTimeout;
 75  replyTimeout_ = replyTimeout;
 76
 77  registeredName_ = [name copy];
 78  host_ = [host copy];
 79
 80  protocol_ = protocol;  // Protocols can't be retained
 81
 82  return self;
 83}
 84
 85- (id)init {
 86  return [self initWithRegisteredName:nil
 87                                 host:nil
 88                             protocol:nil
 89                       requestTimeout:0.0
 90                         replyTimeout:0.0];
 91}
 92
 93- (void)dealloc {
 94  [self releaseRealProxy];
 95  [registeredName_ release];
 96  [host_ release];
 97  [super dealloc];
 98}
 99
100- (BOOL)isConnected {
101  BOOL result = NO;
102  @synchronized (self) {
103    result = [[[self realProxy] connectionForProxy] isValid];
104  }
105  return result;
106}
107
108- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
109  struct objc_method_description mdesc;
110  mdesc = protocol_getMethodDescription(protocol_, selector, YES, YES);
111  NSMethodSignature *returnValue = nil;
112  if (mdesc.types == NULL) {
113    // COV_NF_START
114    _GTMDevLog(@"Unable to get the protocol method description.  Returning "
115               @"nil.");
116    // COV_NF_END
117  } else {
118    returnValue = [NSMethodSignature signatureWithObjCTypes:mdesc.types];
119  }
120  return returnValue;
121}
122
123- (void)forwardInvocation:(NSInvocation *)invocation {
124  @try {
125    NSDistantObject *target = [self realProxy];
126    [invocation invokeWithTarget:target];
127
128    // We need to catch NSException* here rather than "id" because we need to
129    // treat |ex| as an NSException when using the -name method.  Also, we're
130    // only looking to catch a few types of exception here, all of which are
131    // NSException types; the rest we just rethrow.
132  } @catch (NSException *ex) {
133    NSString *exName = [ex name];
134    // If we catch an exception who's name matches any of the following types,
135    // it's because the DO connection probably went down.  So, we'll just
136    // release our realProxy_, and attempt to reconnect on the next call.
137    if ([exName isEqualToString:NSPortTimeoutException]
138        || [exName isEqualToString:NSInvalidSendPortException]
139        || [exName isEqualToString:NSInvalidReceivePortException]
140        || [exName isEqualToString:NSFailedAuthenticationException]
141        || [exName isEqualToString:NSPortSendException]
142        || [exName isEqualToString:NSPortReceiveException]) {
143      [self releaseRealProxy];  // COV_NF_LINE
144    } else {
145      // If the exception was any other type (commonly
146      // NSInvalidArgumentException) then we'll just re-throw it to the caller.
147      @throw;
148    }
149  }  // COV_NF_LINE
150}
151
152@end
153
154@implementation GTMTransientRootProxy (PrivateMethods)
155
156- (NSConnection *)makeConnection {
157  return [NSConnection connectionWithRegisteredName:registeredName_ host:host_];
158}
159
160- (NSDistantObject *)realProxy {
161  NSDistantObject *returnProxy = nil;
162
163  @synchronized (self) {
164    // No change so no notification
165    if (realProxy_) return realProxy_;
166
167    NSConnection *conn = [self makeConnection];
168    [conn setRequestTimeout:requestTimeout_];
169    [conn setReplyTimeout:replyTimeout_];
170    @try {
171      // Try to get the root proxy for this connection's vended object.
172      realProxy_ = [conn rootProxy];
173    } @catch (id ex) {
174      // We may fail here if we can't get the root proxy in the amount of time
175      // specified by the timeout above.  This may happen, for example, if the
176      // server process is stopped (via SIGSTOP).  We'll just ignore this, and
177      // try again at the next message.
178      [conn invalidate];
179      return nil;
180    }
181    if (!realProxy_) {
182      [conn invalidate];
183      // Again, no change in connection status
184      return nil;
185    }
186    [realProxy_ retain];
187    [realProxy_ setProtocolForProxy:protocol_];
188    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
189    [nc addObserver:self
190           selector:@selector(connectionDidDie:)
191               name:NSConnectionDidDieNotification
192             object:conn];
193    // Retain/autorelease so it lives at least the duration of this synchronize
194    returnProxy = [[realProxy_ retain] autorelease];
195  }  // @synchronized (self)
196
197  return returnProxy;
198}
199
200- (void)connectionDidDie:(NSNotification *)notification {
201  [self releaseRealProxy];
202}
203
204- (void)releaseRealProxy {
205  @synchronized (self) {
206    [[NSNotificationCenter defaultCenter] removeObserver:self];
207    // Only trigger if we had a proxy before
208    if (realProxy_) {
209      [realProxy_ release];
210      realProxy_ = nil;
211    }
212  }
213}
214
215@end
216
217@implementation GTMRootProxyCatchAll
218
219- (void)forwardInvocation:(NSInvocation *)invocation {
220  @try {
221    [super forwardInvocation:invocation];
222  }
223  @catch (id ex) {
224    // Log for developers, but basically ignore it.
225    _GTMDevLog(@"Proxy for invoking %@ has caught and is ignoring exception: %@",
226               NSStringFromSelector([invocation selector]), ex);
227  }
228}
229
230@end