THRIFT-280. Server-side Cocoa implementation.

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@796538 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_cocoa_generator.cc b/compiler/cpp/src/generate/t_cocoa_generator.cc
index b815ce0..9dadd2d 100644
--- a/compiler/cpp/src/generate/t_cocoa_generator.cc
+++ b/compiler/cpp/src/generate/t_cocoa_generator.cc
@@ -91,6 +91,7 @@
   void generate_cocoa_struct_description(std::ofstream& out, t_struct* tstruct);
 
   std::string function_result_helper_struct_type(t_function* tfunction);
+  std::string function_args_helper_struct_type(t_function* tfunction);
   void generate_function_helpers(t_function* tfunction);
 
   /**
@@ -100,6 +101,8 @@
   void generate_cocoa_service_protocol (std::ofstream& out, t_service* tservice);
   void generate_cocoa_service_client_interface (std::ofstream& out, t_service* tservice);
   void generate_cocoa_service_client_implementation (std::ofstream& out, t_service* tservice);
+  void generate_cocoa_service_server_interface (std::ofstream& out, t_service* tservice);
+  void generate_cocoa_service_server_implementation (std::ofstream& out, t_service* tservice);
   void generate_cocoa_service_helpers   (t_service* tservice);
   void generate_service_client    (t_service* tservice);
   void generate_service_server    (t_service* tservice);
@@ -265,6 +268,7 @@
     "#import <TProtocol.h>\n" +
     "#import <TApplicationException.h>\n" +
     "#import <TProtocolUtil.h>\n" +
+    "#import <TProcessor.h>\n" +
     "\n";
 
   // Include other Thrift includes
@@ -983,8 +987,10 @@
 void t_cocoa_generator::generate_service(t_service* tservice) {
   generate_cocoa_service_protocol(f_header_, tservice);
   generate_cocoa_service_client_interface(f_header_, tservice);
+  generate_cocoa_service_server_interface(f_header_, tservice);
   generate_cocoa_service_helpers(tservice);
   generate_cocoa_service_client_implementation(f_impl_, tservice);
+  generate_cocoa_service_server_implementation(f_impl_, tservice);
 }
 
 
@@ -997,12 +1003,20 @@
   vector<t_function*> functions = tservice->get_functions();
   vector<t_function*>::iterator f_iter;
   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    t_struct* ts = (*f_iter)->get_arglist();
+    generate_cocoa_struct_interface(f_impl_, ts, false);
+    generate_cocoa_struct_implementation(f_impl_, ts, false, false);  
     generate_function_helpers(*f_iter);
   }
 }
 
 string t_cocoa_generator::function_result_helper_struct_type(t_function* tfunction) {
-  return capitalize(tfunction->get_name()) + "Result_";
+  return capitalize(tfunction->get_name()) + "_result";
+}
+
+
+string t_cocoa_generator::function_args_helper_struct_type(t_function* tfunction) {
+  return tfunction->get_name() + "_args";
 }
 
 
@@ -1033,9 +1047,10 @@
 
   // generate the result struct
   generate_cocoa_struct_interface(f_impl_, &result, false);
-  generate_cocoa_struct_implementation(f_impl_, &result, false, true);
+  generate_cocoa_struct_implementation(f_impl_, &result, false, true);  
 }
 
+
 /**
  * Generates a service protocol definition.
  *
@@ -1084,6 +1099,28 @@
 
 
 /**
+ * Generates a service server interface definition. In other words, the TProcess implementation for the
+ * service definition.
+ *
+ * @param tservice The service to generate a client interface definition for
+ */
+void t_cocoa_generator::generate_cocoa_service_server_interface(ofstream& out,
+                                                                t_service* tservice) {
+  out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Processor : NSObject <TProcessor> ";
+  
+  scope_up(out);
+  out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() <<"> mService;" << endl;
+  out << indent() << "NSDictionary * mMethodMap;" << endl;
+  scope_down(out);
+  
+  out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service;" << endl;
+  out << "- (id<"<<cocoa_prefix_ << tservice->get_name() << ">) service;" << endl;
+
+  out << "@end" << endl << endl;
+}
+
+
+/**
  * Generates a service client implementation.
  *
  * @param tservice The service to generate an implementation for
@@ -1282,6 +1319,152 @@
 
 
 /**
+ * Generates a service server implementation.  In other words the actual TProcessor implementation
+ * for the service.
+ *
+ * @param tservice The service to generate an implementation for
+ */
+void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& out,
+                                                                     t_service* tservice) {
+  out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Processor" << endl;
+  indent_up();
+  
+  // initializer
+  out << endl;
+  out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service" << endl;
+  scope_up(out);
+  out << indent() << "self = [super init];" << endl;
+  out << indent() << "if (!self) {" << endl;
+  out << indent() << "  return nil;" << endl;
+  out << indent() << "}" << endl;
+  out << indent() << "mService = [service retain];" << endl;
+  out << indent() << "mMethodMap = [[NSMutableDictionary dictionary] retain];" << endl;
+  
+  // generate method map for routing incoming calls
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::const_iterator f_iter;
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    string funname = (*f_iter)->get_name();
+    scope_up(out);
+    out << indent() << "SEL s = @selector(process_" << funname << "_withSequenceID:inProtocol:outProtocol:);" << endl;
+    out << indent() << "NSMethodSignature * sig = [self methodSignatureForSelector: s];" << endl;
+    out << indent() << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl;
+    out << indent() << "[invocation setSelector: s];" << endl;
+    out << indent() << "[invocation retainArguments];" << endl;
+    out << indent() << "[mMethodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl;
+    scope_down(out);
+  }
+  out << indent() << "return self;" << endl;
+  scope_down(out);
+  
+  // implementation of the 'service' method which returns the service associated with this
+  // processor
+  out << endl;
+  out << indent() << "- (id<"<<cocoa_prefix_ << tservice->get_name() << ">) service" << endl;
+  out << indent() << "{" << endl;
+  out << indent() << "  return [[mService retain] autorelease];" << endl;
+  out << indent() << "}" << endl;
+  
+  // implementation of the TProcess method, which dispatches the incoming call using the method map
+  out << endl;
+  out << indent() << "- (BOOL) processOnInputProtocol: (id <TProtocol>) inProtocol" << endl;
+  out << indent() << "                 outputProtocol: (id <TProtocol>) outProtocol" <<endl;
+  out << indent() << "{" << endl;
+  out << indent() << "  NSString * messageName;" << endl;
+  out << indent() << "  int messageType;" << endl;
+  out << indent() << "  int seqID;" << endl;
+  out << indent() << "  [inProtocol readMessageBeginReturningName: &messageName" << endl;
+  out << indent() << "                                       type: &messageType" << endl;
+  out << indent() << "                                 sequenceID: &seqID];" << endl;
+  out << indent() << "  NSInvocation * invocation = [mMethodMap valueForKey: messageName];" << endl;
+  out << indent() << "  if (invocation == nil) {" << endl;
+  out << indent() << "    [TProtocolUtil skipType: TType_STRUCT onProtocol: inProtocol];" << endl;
+  out << indent() << "    [inProtocol readMessageEnd];" << endl;
+  out << indent() << "    TApplicationException * x = [TApplicationException exceptionWithType: TApplicationException_UNKNOWN_METHOD reason: [NSString stringWithFormat: @\"Invalid method name: '%@'\", messageName]];" << endl;
+  out << indent() << "    [outProtocol writeMessageBeginWithName: messageName" << endl;
+  out << indent() << "                                      type: TMessageType_EXCEPTION" << endl;
+  out << indent() << "                                sequenceID: seqID];" << endl;
+  out << indent() << "    [x write: outProtocol];" << endl;
+  out << indent() << "    [outProtocol writeMessageEnd];" << endl;
+  out << indent() << "    [[outProtocol transport] flush];" << endl;
+  out << indent() << "    return YES;" << endl;
+  out << indent() << "  }" << endl;
+  out << indent() << "  // NSInvocation does not conform to NSCopying protocol" << endl;
+  out << indent() << "  NSInvocation * i = [NSInvocation invocationWithMethodSignature: [invocation methodSignature]];" << endl;
+  out << indent() << "  [i setSelector: [invocation selector]];" << endl;
+  out << indent() << "  [i setArgument: &seqID atIndex: 2];" << endl;
+  out << indent() << "  [i setArgument: &inProtocol atIndex: 3];" << endl;
+  out << indent() << "  [i setArgument: &outProtocol atIndex: 4];" << endl;
+  out << indent() << "  [i setTarget: self];" << endl;
+  out << indent() << "  [i invoke];" << endl;
+  out << indent() << "  return YES;" << endl;
+  out << indent() << "}" << endl;
+  
+  // generate a process_XXXX method for each service function, which reads args, calls the service, and writes results
+  functions = tservice->get_functions();
+  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+    out << endl;
+    string funname = (*f_iter)->get_name();
+    out << indent() << "- (void) process_" << funname << "_withSequenceID: (int32_t) seqID inProtocol: (id<TProtocol>) inProtocol outProtocol: (id<TProtocol>) outProtocol" << endl;
+    scope_up(out);
+    string argstype = cocoa_prefix_ + function_args_helper_struct_type(*f_iter);
+    out << indent() << argstype << " * args = [[" << argstype << " alloc] init];" << endl;
+    out << indent() << "[args read: inProtocol];" << endl;
+    out << indent() << "[inProtocol readMessageEnd];" << endl;
+    
+    string resulttype = cocoa_prefix_ + function_result_helper_struct_type(*f_iter);
+    out << indent() << resulttype << " * result = [[" << resulttype << " alloc] init];" << endl;
+
+    // make the call to the actual service object
+    out << indent();
+    if (!(*f_iter)->get_returntype()->is_void()) {
+      out << "[result setSuccess: ";
+    }
+    out << "[mService " << funname;
+    // supplying arguments
+    t_struct* arg_struct = (*f_iter)->get_arglist();
+    const vector<t_field*>& fields = arg_struct->get_members();
+    vector<t_field*>::const_iterator fld_iter;
+    for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+      string fieldName = (*fld_iter)->get_name();
+      out << ": [args " << fieldName << "]";
+    }
+    out << "]";
+    if (!(*f_iter)->get_returntype()->is_void()) {
+      out << "]";
+    }
+    out << ";" << endl;
+    
+    // write out the result
+    out << indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" << endl;
+    out << indent() << "                                  type: TMessageType_REPLY" << endl;
+    out << indent() << "                            sequenceID: seqID];" << endl;
+    out << indent() << "[result write: outProtocol];" << endl;
+    out << indent() << "[outProtocol writeMessageEnd];" << endl;
+    out << indent() << "[[outProtocol transport] flush];" << endl;
+    out << indent() << "[result release];" << endl;
+    out << indent() << "[args release];" << endl;
+    
+    scope_down(out);
+  }
+  
+  // dealloc
+  out << endl;
+  out << "- (void) dealloc" << endl;
+  scope_up(out);
+  out << indent() << "[mService release];" << endl;
+  out << indent() << "[mMethodMap release];" << endl;
+  out << indent() << "[super dealloc];" << endl;
+  scope_down(out);
+  out << endl;
+
+  indent_down();
+
+  out << "@end" << endl << endl;
+}
+
+
+/**
  * Deserializes a field of any type.
  *
  * @param tfield The field
diff --git a/lib/cocoa/src/TProcessor.h b/lib/cocoa/src/TProcessor.h
index e361d96..980be94 100644
--- a/lib/cocoa/src/TProcessor.h
+++ b/lib/cocoa/src/TProcessor.h
@@ -18,6 +18,7 @@
  */
 
 #import <Foundation/Foundation.h>
+#import "TProtocol.h"
 
 
 @protocol TProcessor <NSObject>
diff --git a/lib/cocoa/src/TProcessorFactory.h b/lib/cocoa/src/TProcessorFactory.h
new file mode 100644
index 0000000..29d12b3
--- /dev/null
+++ b/lib/cocoa/src/TProcessorFactory.h
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <Foundation/Foundation.h>
+#import "TProcessor.h"
+
+@protocol TProcessorFactory <NSObject>
+
+- (id<TProcessor>) processorForTransport: (id<TTransport>) transport;
+
+@end
diff --git a/lib/cocoa/src/TSharedProcessorFactory.h b/lib/cocoa/src/TSharedProcessorFactory.h
new file mode 100644
index 0000000..d3e55c4
--- /dev/null
+++ b/lib/cocoa/src/TSharedProcessorFactory.h
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <Foundation/Foundation.h>
+#import "TProcessorFactory.h"
+
+@interface TSharedProcessorFactory : NSObject <TProcessorFactory> {
+  id<TProcessor> mSharedProcessor;
+}
+
+@end
diff --git a/lib/cocoa/src/TSharedProcessorFactory.m b/lib/cocoa/src/TSharedProcessorFactory.m
new file mode 100644
index 0000000..b38e73a
--- /dev/null
+++ b/lib/cocoa/src/TSharedProcessorFactory.m
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "TSharedProcessorFactory.h"
+
+
+@implementation TSharedProcessorFactory
+
+
+- (id) initWithSharedProcessor: (id<TProcessor>) sharedProcessor
+{
+  self = [super init];
+  if (!self) {
+    return nil;
+  }
+  
+  mSharedProcessor = [sharedProcessor retain];
+  return self;
+}
+
+
+- (void) dealloc
+{
+  [mSharedProcessor release];
+  [super dealloc];
+}
+
+
+- (id<TProcessor>) processorForTransport: (id<TTransport>) transport
+{
+  return [[mSharedProcessor retain] autorelease];
+}
+
+@end
diff --git a/lib/cocoa/src/server/TSocketServer.h b/lib/cocoa/src/server/TSocketServer.h
index e107aaa..0d66404 100644
--- a/lib/cocoa/src/server/TSocketServer.h
+++ b/lib/cocoa/src/server/TSocketServer.h
@@ -19,7 +19,17 @@
 
 #import <Foundation/Foundation.h>
 #import "TProtocolFactory.h"
-#import "TProcessor.h"
+#import "TProcessorFactory.h"
+
+#if !TARGET_OS_IPHONE
+#import <CoreServices/CoreServices.h>
+#else
+#import <CFNetwork/CFNetwork.h>
+#endif
+
+extern NSString * const kTSocketServer_ClientConnectionFinishedForProcessorNotification;
+extern NSString * const kTSocketServer_ProcessorKey;
+extern NSString * const kTSockerServer_TransportKey;
 
 
 @interface TSocketServer : NSObject {
@@ -27,12 +37,12 @@
   NSFileHandle * mSocketFileHandle;
   id <TProtocolFactory> mInputProtocolFactory;
   id <TProtocolFactory> mOutputProtocolFactory;
-  id <TProcessor> mProcessor;
+  id <TProcessorFactory> mProcessorFactory;
 }
 
 - (id) initWithPort: (int) port
     protocolFactory: (id <TProtocolFactory>) protocolFactory
-          processor: (id <TProcessor>) processor;
+   processorFactory: (id <TProcessorFactory>) processorFactory;
 
 @end
 
diff --git a/lib/cocoa/src/server/TSocketServer.m b/lib/cocoa/src/server/TSocketServer.m
index 5feb9b6..56a5bea 100644
--- a/lib/cocoa/src/server/TSocketServer.m
+++ b/lib/cocoa/src/server/TSocketServer.m
@@ -22,54 +22,77 @@
 #import "TNSFileHandleTransport.h"
 #import "TProtocol.h"
 #import "TTransportException.h"
+#import <sys/socket.h>
+#include <netinet/in.h>
+
+
+
+NSString * const kTSocketServer_ClientConnectionFinishedForProcessorNotification = @"TSocketServer_ClientConnectionFinishedForProcessorNotification";
+NSString * const kTSocketServer_ProcessorKey = @"TSocketServer_Processor";
+NSString * const kTSockerServer_TransportKey = @"TSockerServer_Transport";
 
 
 @implementation TSocketServer
 
 - (id) initWithPort: (int) port
     protocolFactory: (id <TProtocolFactory>) protocolFactory
-          processor: (id <TProcessor>) processor;
+   processorFactory: (id <TProcessorFactory>) processorFactory;
 {
   self = [super init];
 
   mInputProtocolFactory = [protocolFactory retain];
   mOutputProtocolFactory = [protocolFactory retain];
-  mProcessor = [processor retain];
+  mProcessorFactory = [processorFactory retain];
 
-  // create a socket
-  mServerSocket = [[NSSocketPort alloc] initWithTCPPort: port];
-  // FIXME - move this separate start method and add method to close
-  // and cleanup any open ports
+  // create a socket.
+  int fd = -1;
+  CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL);
+  if (socket) {
+    fd = CFSocketGetNative(socket);
+    int yes = 1;
+    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
 
-  if (mServerSocket == nil) {
-    NSLog(@"Unable to listen on TCP port %d", port);
+    struct sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_len = sizeof(addr);
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)];
+    if (CFSocketSetAddress(socket, (CFDataRef)address) != kCFSocketSuccess) {
+      NSLog(@"*** Could not bind to address");
+      return nil;
+    }
   } else {
-    NSLog(@"Listening on TCP port %d", port);
-
-    // wrap it in a file handle so we can get messages from it
-    mSocketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor: [mServerSocket socket]
-                                                      closeOnDealloc: YES];
-
-    // register for notifications of accepted incoming connections
-    [[NSNotificationCenter defaultCenter] addObserver: self
-                                             selector: @selector(connectionAccepted:)
-                                                 name: NSFileHandleConnectionAcceptedNotification
-                                               object: mSocketFileHandle];
-
-    // tell socket to listen
-    [mSocketFileHandle acceptConnectionInBackgroundAndNotify];
+    NSLog(@"*** No server socket");
+    return nil;
   }
-
+  
+  // wrap it in a file handle so we can get messages from it
+  mSocketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor: fd
+                                                    closeOnDealloc: YES];
+  
+    // register for notifications of accepted incoming connections
+  [[NSNotificationCenter defaultCenter] addObserver: self
+                                           selector: @selector(connectionAccepted:)
+                                               name: NSFileHandleConnectionAcceptedNotification
+                                             object: mSocketFileHandle];
+  
+  // tell socket to listen
+  [mSocketFileHandle acceptConnectionInBackgroundAndNotify];
+  
+  NSLog(@"Listening on TCP port %d", port);
+  
   return self;
 }
 
 
 - (void) dealloc {
+  [[NSNotificationCenter defaultCenter] removeObject: self];
   [mInputProtocolFactory release];
   [mOutputProtocolFactory release];
-  [mProcessor release];
+  [mProcessorFactory release];
   [mSocketFileHandle release];
-  [mServerSocket release];
   [super dealloc];
 }
 
@@ -90,19 +113,35 @@
 - (void) handleClientConnection: (NSFileHandle *) clientSocket
 {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-
+  
   TNSFileHandleTransport * transport = [[TNSFileHandleTransport alloc] initWithFileHandle: clientSocket];
-
+  id<TProcessor> processor = [mProcessorFactory processorForTransport: transport];
+  
   id <TProtocol> inProtocol = [mInputProtocolFactory newProtocolOnTransport: transport];
   id <TProtocol> outProtocol = [mOutputProtocolFactory newProtocolOnTransport: transport];
 
   @try {
-    while ([mProcessor processOnInputProtocol: inProtocol outputProtocol: outProtocol]);
+    BOOL result = NO;
+    do {
+      NSAutoreleasePool * myPool = [[NSAutoreleasePool alloc] init];
+      result = [processor processOnInputProtocol: inProtocol outputProtocol: outProtocol];
+      [myPool release];
+    } while (result);
   }
   @catch (TTransportException * te) {
-    NSLog(@"%@", te);
+    //NSLog(@"Caught transport exception, abandoning client connection: %@", te);
   }
 
+  NSNotification * n = [NSNotification notificationWithName: kTSocketServer_ClientConnectionFinishedForProcessorNotification
+                                                     object: self
+                                                   userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
+                                                              processor,
+                                                              kTSocketServer_ProcessorKey,
+                                                              transport,
+                                                              kTSockerServer_TransportKey,
+                                                              nil]];
+  [[NSNotificationCenter defaultCenter] performSelectorOnMainThread: @selector(postNotification:) withObject: n waitUntilDone: YES];
+  
   [pool release];
 }
 
diff --git a/lib/cocoa/src/transport/TNSFileHandleTransport.m b/lib/cocoa/src/transport/TNSFileHandleTransport.m
index 1533934..b218218 100644
--- a/lib/cocoa/src/transport/TNSFileHandleTransport.m
+++ b/lib/cocoa/src/transport/TNSFileHandleTransport.m
@@ -72,8 +72,12 @@
                                                      length: length
                                                freeWhenDone: NO];
 
-  [mOutputFileHandle writeData: dataObject];
-
+  @try {
+    [mOutputFileHandle writeData: dataObject];
+  } @catch (NSException * e) {
+    @throw [TTransportException exceptionWithName: @"TTransportException"
+                                           reason: [NSString stringWithFormat: @"%s: Unable to write data: %@", __PRETTY_FUNCTION__, e]];
+  }
 
   [dataObject release];
 }