/core/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. #import "GTMTransientRootProxy.h"
  19. #import "GTMObjC2Runtime.h"
  20. // Private methods on NSMethodSignature that we need to call. This method has
  21. // been available since 10.0, but Apple didn't add it to the headers until 10.5
  22. #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
  23. @interface NSMethodSignature (UndeclaredMethods)
  24. + (NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
  25. @end
  26. #endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
  27. @interface GTMTransientRootProxy (PrivateMethods)
  28. // Returns an NSConnection for NSMacPorts. This method is broken out to allow
  29. // subclasses to override it to generate different types of NSConnections.
  30. - (NSConnection *)makeConnection;
  31. // Returns the "real" proxy (stored in the realProxy_ ivar) associated with this
  32. // instance. If realProxy_ is nil, then an attempt is made to make a connection
  33. // to create the realProxy_.
  34. //
  35. - (NSDistantObject *)realProxy;
  36. // "Releases" the realProxy_ ivar, and removes |self| as an observer from
  37. // the NSNotificationCenter.
  38. //
  39. - (void)releaseRealProxy;
  40. // Notification that a connection has died.
  41. - (void)connectionDidDie:(NSNotification *)notification;
  42. @end
  43. @implementation GTMTransientRootProxy
  44. + (id)rootProxyWithRegisteredName:(NSString *)name
  45. host:(NSString *)host
  46. protocol:(Protocol *)protocol
  47. requestTimeout:(NSTimeInterval)requestTimeout
  48. replyTimeout:(NSTimeInterval)replyTimeout {
  49. return [[[self alloc] initWithRegisteredName:name
  50. host:host
  51. protocol:protocol
  52. requestTimeout:requestTimeout
  53. replyTimeout:replyTimeout] autorelease];
  54. }
  55. - (id)initWithRegisteredName:(NSString *)name
  56. host:(NSString *)host
  57. protocol:(Protocol *)protocol
  58. requestTimeout:(NSTimeInterval)requestTimeout
  59. replyTimeout:(NSTimeInterval)replyTimeout {
  60. if (!name || !protocol) {
  61. [self release];
  62. return nil;
  63. }
  64. requestTimeout_ = requestTimeout;
  65. replyTimeout_ = replyTimeout;
  66. registeredName_ = [name copy];
  67. host_ = [host copy];
  68. protocol_ = protocol; // Protocols can't be retained
  69. return self;
  70. }
  71. - (id)init {
  72. return [self initWithRegisteredName:nil
  73. host:nil
  74. protocol:nil
  75. requestTimeout:0.0
  76. replyTimeout:0.0];
  77. }
  78. - (void)dealloc {
  79. [self releaseRealProxy];
  80. [registeredName_ release];
  81. [host_ release];
  82. [super dealloc];
  83. }
  84. - (BOOL)isConnected {
  85. BOOL result = NO;
  86. @synchronized (self) {
  87. result = [[[self realProxy] connectionForProxy] isValid];
  88. }
  89. return result;
  90. }
  91. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
  92. struct objc_method_description mdesc;
  93. mdesc = protocol_getMethodDescription(protocol_, selector, YES, YES);
  94. NSMethodSignature *returnValue = nil;
  95. if (mdesc.types == NULL) {
  96. // COV_NF_START
  97. _GTMDevLog(@"Unable to get the protocol method description. Returning "
  98. @"nil.");
  99. // COV_NF_END
  100. } else {
  101. returnValue = [NSMethodSignature signatureWithObjCTypes:mdesc.types];
  102. }
  103. return returnValue;
  104. }
  105. - (void)forwardInvocation:(NSInvocation *)invocation {
  106. @try {
  107. NSDistantObject *target = [self realProxy];
  108. [invocation invokeWithTarget:target];
  109. // We need to catch NSException* here rather than "id" because we need to
  110. // treat |ex| as an NSException when using the -name method. Also, we're
  111. // only looking to catch a few types of exception here, all of which are
  112. // NSException types; the rest we just rethrow.
  113. } @catch (NSException *ex) {
  114. NSString *exName = [ex name];
  115. // If we catch an exception who's name matches any of the following types,
  116. // it's because the DO connection probably went down. So, we'll just
  117. // release our realProxy_, and attempt to reconnect on the next call.
  118. if ([exName isEqualToString:NSPortTimeoutException]
  119. || [exName isEqualToString:NSInvalidSendPortException]
  120. || [exName isEqualToString:NSInvalidReceivePortException]
  121. || [exName isEqualToString:NSFailedAuthenticationException]
  122. || [exName isEqualToString:NSPortSendException]
  123. || [exName isEqualToString:NSPortReceiveException]) {
  124. [self releaseRealProxy]; // COV_NF_LINE
  125. } else {
  126. // If the exception was any other type (commonly
  127. // NSInvalidArgumentException) then we'll just re-throw it to the caller.
  128. @throw;
  129. }
  130. } // COV_NF_LINE
  131. }
  132. @end
  133. @implementation GTMTransientRootProxy (PrivateMethods)
  134. - (NSConnection *)makeConnection {
  135. return [NSConnection connectionWithRegisteredName:registeredName_ host:host_];
  136. }
  137. - (NSDistantObject *)realProxy {
  138. NSDistantObject *returnProxy = nil;
  139. @synchronized (self) {
  140. // No change so no notification
  141. if (realProxy_) return realProxy_;
  142. NSConnection *conn = [self makeConnection];
  143. [conn setRequestTimeout:requestTimeout_];
  144. [conn setReplyTimeout:replyTimeout_];
  145. @try {
  146. // Try to get the root proxy for this connection's vended object.
  147. realProxy_ = [conn rootProxy];
  148. } @catch (id ex) {
  149. // We may fail here if we can't get the root proxy in the amount of time
  150. // specified by the timeout above. This may happen, for example, if the
  151. // server process is stopped (via SIGSTOP). We'll just ignore this, and
  152. // try again at the next message.
  153. [conn invalidate];
  154. return nil;
  155. }
  156. if (!realProxy_) {
  157. [conn invalidate];
  158. // Again, no change in connection status
  159. return nil;
  160. }
  161. [realProxy_ retain];
  162. [realProxy_ setProtocolForProxy:protocol_];
  163. NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  164. [nc addObserver:self
  165. selector:@selector(connectionDidDie:)
  166. name:NSConnectionDidDieNotification
  167. object:conn];
  168. // Retain/autorelease so it lives at least the duration of this synchronize
  169. returnProxy = [[realProxy_ retain] autorelease];
  170. } // @synchronized (self)
  171. return returnProxy;
  172. }
  173. - (void)connectionDidDie:(NSNotification *)notification {
  174. [self releaseRealProxy];
  175. }
  176. - (void)releaseRealProxy {
  177. @synchronized (self) {
  178. [[NSNotificationCenter defaultCenter] removeObserver:self];
  179. // Only trigger if we had a proxy before
  180. if (realProxy_) {
  181. [realProxy_ release];
  182. realProxy_ = nil;
  183. }
  184. }
  185. }
  186. @end
  187. @implementation GTMRootProxyCatchAll
  188. - (void)forwardInvocation:(NSInvocation *)invocation {
  189. @try {
  190. [super forwardInvocation:invocation];
  191. }
  192. @catch (id ex) {
  193. // Log for developers, but basically ignore it.
  194. _GTMDevLog(@"Proxy for invoking %@ has caught and is ignoring exception: %@",
  195. NSStringFromSelector([invocation selector]), ex);
  196. }
  197. }
  198. @end