*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
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::
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.
: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