From 3f682e6384d74b0cee01a7ccad2a3da9252f1a44 Mon Sep 17 00:00:00 2001 From: Scott McKay Date: Mon, 27 Aug 2012 20:58:19 +0000 Subject: [PATCH] Lose the 'response' argument from the RPC stubs, it's not needed in Lisp git-svn-id: http://svn.internal.itasoftware.com/svn/ita/trunk/qres/lisp/libs/cl-protobufs@559035 f8382938-511b-0410-9cdd-bb47b084005c --- cl-protobufs.rst | 30 ++++++++++++++++++++++-------- define-proto.lisp | 34 ++++++++++++++++++---------------- model-classes.lisp | 2 +- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/cl-protobufs.rst b/cl-protobufs.rst index 2f623b5..ffdbd55 100644 --- a/cl-protobufs.rst +++ b/cl-protobufs.rst @@ -501,7 +501,7 @@ Fields take the form ``(slot &key type name default reader writer)``. *slot* can be either a symbol giving the slot name or a list of the form ``(slot index)``. By default, the field indexes start at 1 and are incremented by 1 for each new field value. *type* is the type of -the slot. For schema forward and backward compatibility, you should +the slot. For schema forward and backward compatibility, you should always use the ``(slot index)`` form. *name* can be used to override the defaultly generated Protobufs field @@ -650,17 +650,17 @@ Protobufs service stubs When you use the ``proto:define-service`` macro to define a service with some methods, the macro defines "stubs" (CLOS generic functions) -for each of the methods in the service. Each method gets a client stub -and a server stub whose signatures are, respectively:: +for each of the methods in the service. Each method named ``foo`` gets +a client stub and a server stub whose signatures are, respectively:: - (rpc-channel input output &key callback) => output - (rpc-channel input output) => output + foo (rpc-channel request &key callback) => response + do-foo (rpc-channel request) => response The type of *rpc-channel* is unspecified, but is meant to be a -"channel" over which some sort of RPC call will be done. The types of -*input* and *output* are classes that were defined via +"channel" over which the RPC call will be done. The types of *request* +and *response* are message classes that were defined via Protobufs. *callback* is a function of two arguments, the RPC channel -and the output; it is intended for use by asynchronous RPC calls. +and the response; it is intended for use by asynchronous RPC calls. For example, this fragment defines four stubs:: @@ -673,6 +673,20 @@ are ``do-get-color`` and ``do-add-color``. An RPC library will implement a method for the client stub. You must fill in the server stub yourself; it will implement the desired functionality. +The client stub also gets a single method defined for it that looks like +something like this:: + + (defmethod foo (rpc-channel (request input-type) &key callback) + (let ((call (and *rpc-package* *rpc-call-function*))) + (funcall call rpc-channel method request :callback vcallback))) + +where *rpc-channel*, *request* and *callback* are as above. +The special variables ``*rpc-package*`` and ``*rpc-call-function*`` +are filled in when the RPC package is loaded. *method* is the +``proto:protobuf-method`` that describes the method; this is +included so that the RPC implementation can determine what type +of response object to create, what timeout to use, etc. + It is beyond the scope of this Protobufs library to provide the RPC service; that is the domain of another library. diff --git a/define-proto.lisp b/define-proto.lisp index 5ff3370..897800a 100644 --- a/define-proto.lisp +++ b/define-proto.lisp @@ -744,43 +744,45 @@ :documentation documentation :source-location source-location))) (setf (proto-methods service) (nconc (proto-methods service) (list method))) - ;; The following are the hooks to CL-Stubby - (let* ((vinput (intern (format nil "~A-~A" (symbol-name input-type) 'in) package)) - (voutput (intern (format nil "~A-~A" (symbol-name output-type) 'out) package)) + ;; The following are the hooks to an RPC implementation + (let* ((vrequest (intern (symbol-name 'request) package)) (vchannel (intern (symbol-name 'channel) package)) (vcallback (intern (symbol-name 'callback) package))) ;; The client side stub, e.g., 'read-air-reservation'. - ;; The expectation is that CL-Stubby will provide macrology to make it + ;; The expectation is that the RPC implementation will provide code to make it ;; easy to implement a method for this on each kind of channel (HTTP, TCP socket, ;; IPC, etc). Unlike C++/Java/Python, we don't need a client-side subclass, ;; because we can just use multi-methods. - ;; The CL-Stubby macros take care of serializing the input, transmitting the + ;; The 'do-XXX' method calls the RPC code with the channel, the method + ;; (i.e., a 'protobuf-method' object), the request and the callback function. + ;; The RPC code should take care of serializing the input, transmitting the ;; request over the wire, waiting for input (or not if it's asynchronous), - ;; filling in the output, and calling the callback (if it's asynchronous). - ;; It's not very Lispy to side-effect an output object, but it makes - ;; asynchronous calls simpler. - (collect-form `(defgeneric ,client-fn (,vchannel ,vinput ,voutput &key ,vcallback) + ;; filling in the output, and either returning the response (if synchronous) + ;; or calling the callback with the response as an argument (if asynchronous). + ;; It will also deserialize the response so that the client code sees the + ;; response as an application object. + (collect-form `(defgeneric ,client-fn (,vchannel ,vrequest &key ,vcallback) ,@(and documentation `((:documentation ,documentation))) #-sbcl (declare (values ,output-type)) - (:method (,vchannel (,vinput ,input-type) (,voutput ,output-type) &key ,vcallback) + (:method (,vchannel (,vrequest ,input-type) &key ,vcallback) (declare (ignorable ,vchannel ,vcallback)) (let ((call (and *rpc-package* *rpc-call-function*))) (assert call () "There is no RPC package loaded!") - (funcall call ,vchannel ',method ,vinput ,voutput + (funcall call ,vchannel ',method ,vrequest :callback ,vcallback))))) ;; The server side stub, e.g., 'do-read-air-reservation'. ;; The expectation is that the server-side program will implement ;; a method with the business logic for this on each kind of channel ;; (HTTP, TCP socket, IPC, etc), possibly on a server-side subclass - ;; of the input class + ;; of the input class. ;; The business logic is expected to perform the correct operations on ;; the input object, which arrived via Protobufs, and produce an output - ;; of the given type, which will be serialized as a result. + ;; of the given type, which will be serialized and sent back over the wire. ;; The channel objects hold client identity information, deadline info, - ;; etc, and can be side-effected to indicate success or failure - ;; CL-Stubby provides the channel classes and does (de)serialization, etc - (collect-form `(defgeneric ,server-fn (,vchannel ,vinput ,voutput) + ;; etc, and can be side-effected to indicate success or failure. + ;; The RPC code provides the channel classes and does (de)serialization, etc + (collect-form `(defgeneric ,server-fn (,vchannel ,vrequest) ,@(and documentation `((:documentation ,documentation))) #-sbcl (declare (values ,output-type)))))))) `(progn diff --git a/model-classes.lisp b/model-classes.lisp index cfffd30..89337fc 100644 --- a/model-classes.lisp +++ b/model-classes.lisp @@ -721,7 +721,7 @@ :initarg :output-name :initform nil) (index :type (unsigned-byte 32) ;an identifying index for this method - :accessor proto-index ; (used by Stubby) + :accessor proto-index ; (used by the RPC implementation) :initarg :index)) (:documentation "The model class that represents one method with a Protobufs service.")) -- 2.45.2