From: Bruce Tobin

Subject: More on distributed objects

Date: 1996-3-25 12:44

  After reviewing the responses to my questions on distributed objects, I've 
decided to roll my own distributed objects via the sockets interface as a 
temporary measure until ILU support for CL-Windows is available.  A preliminary 
version:

;classes:

(defclass client-stub ()
    ((remote-machine :initarg :machine :accessor machine)
     (server-stub-port-number :initarg :port :accessor port))
   (:documentation "A client-side surrogate for remote objects."))

(defclass server ()
    ((port-number :initarg port :accessor port))

(defclass server-stub (server)
    ((true-instance :initarg true-instance :accessor :true-instance))
   (:documentation 
"Accepts messages from the client stub and hands them over to the true 
instance."))

(defclass remote-instance-manager(server) ()
   (:documentation 
"Accepts requests from make-remote-instance, creating a true instance and a
server stub and sending back the port number of the latter."))
     
;methods:

(defmethod send ((client client-stub) method-name &optional arg-list)
"Sends the method-name and arg-list to the server stub.  Also used to send the 
class name
 and initialization list to the remote instance manager."
    t) ; Not yet implemented

(defmethod receive ((client client-stub))
"Reads an object from the server stub.  Also used to get the port address of 
the server stub
 from the remote instance manager."
    t) ; Not yet implemented

(defmethod service-requests ((mgr remote-instance-manager))
"Listen at port for messages from newly initialized client-stubs.  When a 
message is received,
create an instance of the class whose name is the first symbol in the message.  
Then create
a server stub object attached to the true instance and return its port address 
to the client."
    t) ; Not yet implemented

(defmethod service-requests ((ss server-stub))
"Listen at port for messages from client-stubs.  When a message is received, 
pass it to the true
instance and return the result to the client."
    t) ; Not yet implemented

;macros:

(defmacro make-remote-instance (class &optional
                           (remote-machine *app-server*)
                           (remote-server-mgr-port *app-server-port*) init-list)
"Returns a client stub object for the class, and makes sure that all of the 
class's
methods have a corresponding method specialized on the client stub.  Also 
contacts the
remote instance manager on the server, telling it to create a true instance of 
the class and a
server stub object for the instance. If any of these processes fail, the 
function
returns nil."
   (let ((stub (make-instance
                  'client-stub
                  :machine remote-machine
                  :port remote-server-mgr-port)))
      `(progn
          (send ,stub ,class ,init-list)
          (setf (port ,stub) (receive ,stub))
          (specialize-class-generic-functions-on-client-stub  ,class ,stub)
          ,stub)))


; internal functions
               
(defun get-class-generic-functions (class)
   (remove-duplicates
      (mapcar
         #'method-generic-function
         (apply #'append
            (mapcar
               #'acl::class-direct-methods
               (remove
                  (find-class 't)
                  (class-precedence-list class)))))))

      
; internal macros

(defmacro specialize-on-client-stub (gen-function client)
   `(defmethod ,gen-function ((var ,(list 'eql client) ) 
                                              <(rest at ,> (lambda-list 
gen-function)))
       (send ,client
           ',gen-function <(rest at ,> (lambda-list gen-function)))
       (receive ,client)))

(defmacro specialize-class-generic-functions-on-client-stub (class client)
   (append
      (list 'progn)
      (mapcar
         '(lambda (x)
             (list
                'specialize-on-client-stub
                 x
                 client))
         (mapcar
            #'generic-function-name
            (get-class-generic-functions
                (find-class class))))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Questions:

1.  For the send and receive methods, I need encoding and decoding routines
 such as those found in Sonya Keene's "Object-Oriented Programming in 
Common Lisp."  Are such methods already available in Allegro?  If not, does
anyone know where I can obtain the Keene code via FTP?  (I know, it's not that
much work to type in.  I'm just lazy).

2.  Does anyone see any potential problems with my approach?   After spending
a weekend writing this stuff I found an article in Dr. Dobb's about distributed 
Smalltalk:  what I accomplish by specializing the methods of the distributed
class on the client-stub object, the Smalltalk programmers accomplished by
changing the error-handling function associated with the "message not 
understood" error.  This sounds simpler, and I wonder what advantages/disad-
vantages people might see with either approach.

Thanks.