/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
- //
- // GTMTransientRootProxy.m
- //
- // Copyright 2006-2009 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not
- // use this file except in compliance with the License. You may obtain a copy
- // of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations under
- // the License.
- //
- #import "GTMTransientRootProxy.h"
- #import "GTMObjC2Runtime.h"
- // Private methods on NSMethodSignature that we need to call. This method has
- // been available since 10.0, but Apple didn't add it to the headers until 10.5
- #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
- @interface NSMethodSignature (UndeclaredMethods)
- + (NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
- @end
- #endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
- @interface GTMTransientRootProxy (PrivateMethods)
- // Returns an NSConnection for NSMacPorts. This method is broken out to allow
- // subclasses to override it to generate different types of NSConnections.
- - (NSConnection *)makeConnection;
- // Returns the "real" proxy (stored in the realProxy_ ivar) associated with this
- // instance. If realProxy_ is nil, then an attempt is made to make a connection
- // to create the realProxy_.
- //
- - (NSDistantObject *)realProxy;
- // "Releases" the realProxy_ ivar, and removes |self| as an observer from
- // the NSNotificationCenter.
- //
- - (void)releaseRealProxy;
- // Notification that a connection has died.
- - (void)connectionDidDie:(NSNotification *)notification;
- @end
- @implementation GTMTransientRootProxy
- + (id)rootProxyWithRegisteredName:(NSString *)name
- host:(NSString *)host
- protocol:(Protocol *)protocol
- requestTimeout:(NSTimeInterval)requestTimeout
- replyTimeout:(NSTimeInterval)replyTimeout {
- return [[[self alloc] initWithRegisteredName:name
- host:host
- protocol:protocol
- requestTimeout:requestTimeout
- replyTimeout:replyTimeout] autorelease];
- }
- - (id)initWithRegisteredName:(NSString *)name
- host:(NSString *)host
- protocol:(Protocol *)protocol
- requestTimeout:(NSTimeInterval)requestTimeout
- replyTimeout:(NSTimeInterval)replyTimeout {
- if (!name || !protocol) {
- [self release];
- return nil;
- }
- requestTimeout_ = requestTimeout;
- replyTimeout_ = replyTimeout;
- registeredName_ = [name copy];
- host_ = [host copy];
- protocol_ = protocol; // Protocols can't be retained
- return self;
- }
- - (id)init {
- return [self initWithRegisteredName:nil
- host:nil
- protocol:nil
- requestTimeout:0.0
- replyTimeout:0.0];
- }
- - (void)dealloc {
- [self releaseRealProxy];
- [registeredName_ release];
- [host_ release];
- [super dealloc];
- }
- - (BOOL)isConnected {
- BOOL result = NO;
- @synchronized (self) {
- result = [[[self realProxy] connectionForProxy] isValid];
- }
- return result;
- }
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
- struct objc_method_description mdesc;
- mdesc = protocol_getMethodDescription(protocol_, selector, YES, YES);
- NSMethodSignature *returnValue = nil;
- if (mdesc.types == NULL) {
- // COV_NF_START
- _GTMDevLog(@"Unable to get the protocol method description. Returning "
- @"nil.");
- // COV_NF_END
- } else {
- returnValue = [NSMethodSignature signatureWithObjCTypes:mdesc.types];
- }
- return returnValue;
- }
- - (void)forwardInvocation:(NSInvocation *)invocation {
- @try {
- NSDistantObject *target = [self realProxy];
- [invocation invokeWithTarget:target];
- // We need to catch NSException* here rather than "id" because we need to
- // treat |ex| as an NSException when using the -name method. Also, we're
- // only looking to catch a few types of exception here, all of which are
- // NSException types; the rest we just rethrow.
- } @catch (NSException *ex) {
- NSString *exName = [ex name];
- // If we catch an exception who's name matches any of the following types,
- // it's because the DO connection probably went down. So, we'll just
- // release our realProxy_, and attempt to reconnect on the next call.
- if ([exName isEqualToString:NSPortTimeoutException]
- || [exName isEqualToString:NSInvalidSendPortException]
- || [exName isEqualToString:NSInvalidReceivePortException]
- || [exName isEqualToString:NSFailedAuthenticationException]
- || [exName isEqualToString:NSPortSendException]
- || [exName isEqualToString:NSPortReceiveException]) {
- [self releaseRealProxy]; // COV_NF_LINE
- } else {
- // If the exception was any other type (commonly
- // NSInvalidArgumentException) then we'll just re-throw it to the caller.
- @throw;
- }
- } // COV_NF_LINE
- }
- @end
- @implementation GTMTransientRootProxy (PrivateMethods)
- - (NSConnection *)makeConnection {
- return [NSConnection connectionWithRegisteredName:registeredName_ host:host_];
- }
- - (NSDistantObject *)realProxy {
- NSDistantObject *returnProxy = nil;
- @synchronized (self) {
- // No change so no notification
- if (realProxy_) return realProxy_;
- NSConnection *conn = [self makeConnection];
- [conn setRequestTimeout:requestTimeout_];
- [conn setReplyTimeout:replyTimeout_];
- @try {
- // Try to get the root proxy for this connection's vended object.
- realProxy_ = [conn rootProxy];
- } @catch (id ex) {
- // We may fail here if we can't get the root proxy in the amount of time
- // specified by the timeout above. This may happen, for example, if the
- // server process is stopped (via SIGSTOP). We'll just ignore this, and
- // try again at the next message.
- [conn invalidate];
- return nil;
- }
- if (!realProxy_) {
- [conn invalidate];
- // Again, no change in connection status
- return nil;
- }
- [realProxy_ retain];
- [realProxy_ setProtocolForProxy:protocol_];
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- [nc addObserver:self
- selector:@selector(connectionDidDie:)
- name:NSConnectionDidDieNotification
- object:conn];
- // Retain/autorelease so it lives at least the duration of this synchronize
- returnProxy = [[realProxy_ retain] autorelease];
- } // @synchronized (self)
- return returnProxy;
- }
- - (void)connectionDidDie:(NSNotification *)notification {
- [self releaseRealProxy];
- }
- - (void)releaseRealProxy {
- @synchronized (self) {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- // Only trigger if we had a proxy before
- if (realProxy_) {
- [realProxy_ release];
- realProxy_ = nil;
- }
- }
- }
- @end
- @implementation GTMRootProxyCatchAll
- - (void)forwardInvocation:(NSInvocation *)invocation {
- @try {
- [super forwardInvocation:invocation];
- }
- @catch (id ex) {
- // Log for developers, but basically ignore it.
- _GTMDevLog(@"Proxy for invoking %@ has caught and is ignoring exception: %@",
- NSStringFromSelector([invocation selector]), ex);
- }
- }
- @end