From 0c8957114abc809b5db6a4b945a85d8e500a0c27 Mon Sep 17 00:00:00 2001 From: Andrew McGeachie Date: Tue, 21 Jul 2009 21:14:19 +0000 Subject: [PATCH] THRIFT-280. Server-side Cocoa implementation. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@796538 13f79535-47bb-0310-9956-ffa450edef68 --- .../cpp/src/generate/t_cocoa_generator.cc | 187 +++++++++++++++++- lib/cocoa/src/TProcessor.h | 1 + lib/cocoa/src/TProcessorFactory.h | 27 +++ lib/cocoa/src/TSharedProcessorFactory.h | 27 +++ lib/cocoa/src/TSharedProcessorFactory.m | 51 +++++ lib/cocoa/src/server/TSocketServer.h | 16 +- lib/cocoa/src/server/TSocketServer.m | 101 +++++++--- .../src/transport/TNSFileHandleTransport.m | 8 +- 8 files changed, 380 insertions(+), 38 deletions(-) create mode 100644 lib/cocoa/src/TProcessorFactory.h create mode 100644 lib/cocoa/src/TSharedProcessorFactory.h create mode 100644 lib/cocoa/src/TSharedProcessorFactory.m diff --git a/compiler/cpp/src/generate/t_cocoa_generator.cc b/compiler/cpp/src/generate/t_cocoa_generator.cc index b815ce0d..9dadd2d9 100644 --- a/compiler/cpp/src/generate/t_cocoa_generator.cc +++ b/compiler/cpp/src/generate/t_cocoa_generator.cc @@ -91,6 +91,7 @@ class t_cocoa_generator : public t_oop_generator { 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 @@ class t_cocoa_generator : public t_oop_generator { 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 @@ string t_cocoa_generator::cocoa_thrift_imports() { "#import \n" + "#import \n" + "#import \n" + + "#import \n" + "\n"; // Include other Thrift includes @@ -983,8 +987,10 @@ void t_cocoa_generator::generate_cocoa_struct_description(ofstream& out, 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 @@ void t_cocoa_generator::generate_cocoa_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::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 @@ void t_cocoa_generator::generate_function_helpers(t_function* tfunction) { // 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. * @@ -1083,6 +1098,28 @@ void t_cocoa_generator::generate_cocoa_service_client_interface(ofstream& out, } +/** + * 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 "; + + 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<"<get_name() << ">) service;" << endl; + + out << "@end" << endl << endl; +} + + /** * Generates a service client implementation. * @@ -1281,6 +1318,152 @@ void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& o } +/** + * 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 functions = tservice->get_functions(); + vector::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<"<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 ) inProtocol" << endl; + out << indent() << " outputProtocol: (id ) outProtocol" <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) inProtocol outProtocol: (id) 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& fields = arg_struct->get_members(); + vector::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. * diff --git a/lib/cocoa/src/TProcessor.h b/lib/cocoa/src/TProcessor.h index e361d969..980be948 100644 --- a/lib/cocoa/src/TProcessor.h +++ b/lib/cocoa/src/TProcessor.h @@ -18,6 +18,7 @@ */ #import +#import "TProtocol.h" @protocol TProcessor diff --git a/lib/cocoa/src/TProcessorFactory.h b/lib/cocoa/src/TProcessorFactory.h new file mode 100644 index 00000000..29d12b3e --- /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 +#import "TProcessor.h" + +@protocol TProcessorFactory + +- (id) processorForTransport: (id) transport; + +@end diff --git a/lib/cocoa/src/TSharedProcessorFactory.h b/lib/cocoa/src/TSharedProcessorFactory.h new file mode 100644 index 00000000..d3e55c4a --- /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 +#import "TProcessorFactory.h" + +@interface TSharedProcessorFactory : NSObject { + id mSharedProcessor; +} + +@end diff --git a/lib/cocoa/src/TSharedProcessorFactory.m b/lib/cocoa/src/TSharedProcessorFactory.m new file mode 100644 index 00000000..b38e73ab --- /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) sharedProcessor +{ + self = [super init]; + if (!self) { + return nil; + } + + mSharedProcessor = [sharedProcessor retain]; + return self; +} + + +- (void) dealloc +{ + [mSharedProcessor release]; + [super dealloc]; +} + + +- (id) processorForTransport: (id) transport +{ + return [[mSharedProcessor retain] autorelease]; +} + +@end diff --git a/lib/cocoa/src/server/TSocketServer.h b/lib/cocoa/src/server/TSocketServer.h index e107aaae..0d664047 100644 --- a/lib/cocoa/src/server/TSocketServer.h +++ b/lib/cocoa/src/server/TSocketServer.h @@ -19,7 +19,17 @@ #import #import "TProtocolFactory.h" -#import "TProcessor.h" +#import "TProcessorFactory.h" + +#if !TARGET_OS_IPHONE +#import +#else +#import +#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 mInputProtocolFactory; id mOutputProtocolFactory; - id mProcessor; + id mProcessorFactory; } - (id) initWithPort: (int) port protocolFactory: (id ) protocolFactory - processor: (id ) processor; + processorFactory: (id ) processorFactory; @end diff --git a/lib/cocoa/src/server/TSocketServer.m b/lib/cocoa/src/server/TSocketServer.m index 5feb9b69..56a5beaf 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 +#include + + + +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 ) protocolFactory - processor: (id ) processor; + processorFactory: (id ) processorFactory; { self = [super init]; mInputProtocolFactory = [protocolFactory retain]; mOutputProtocolFactory = [protocolFactory retain]; - mProcessor = [processor 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 - - if (mServerSocket == nil) { - NSLog(@"Unable to listen on TCP port %d", port); + mProcessorFactory = [processorFactory retain]; + + // 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)); + + 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 processor = [mProcessorFactory processorForTransport: transport]; + id inProtocol = [mInputProtocolFactory newProtocolOnTransport: transport]; id 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 15339341..b2182183 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]; } -- 2.17.1