This chapter's example is an implementation of the standard Hello World application, using Common Lisp and CORBA. In this version of Hello World, a client application asks a server application for a string. When the client receives the string, it prints it to the standard output, and then exits.
We will take these basic steps to create the application:
We define the interface to the server using OMG's Interface Definition Language (IDL).
The complete Hello World application is here:
(example-edit-file "corba/hello-world/")
For instance, in a default 64-bit LispWorks 7.1 installation on Microsoft Windows, the location is C:\Program Files\LispWorks\lib\7-1-0-0\examples\corba\hello-world\
.
We first need to define the interface of the Hello World server object. The client and server will communicate across this interface. The interface is defined using IDL in a single file that must have the extension .idl
.
hello-world.idl
.hello-world.idl
file.module HelloWorld {
interface world {
string hello();
};
};
This IDL declaration says that there are CORBA objects of a kind called world
, and that there is an operation called hello
on world
objects that takes no arguments and returns a string. Servers implement world
, and clients call hello
on instances of world
.
Now that we have written the IDL, we can run the IDL parser over it to produce stub and skeleton code for the client and server parts of the application.
We need the IDL parser to parse the IDL to generate appropriate stubs and skeletons. We do this by including the IDL file in the defsystem that defines the code we are writing. For Hello World, the relevant defsystem is:
(defsystem hello-world-corba-object ()
:members (
("hello-world" :type :idl-file)))
The defsystem utility has been extended to correctly handle a file of type idl-file
. In this case, the fasl corresponding to the IDL file contains the compiled stubs and skeletons for the given IDL and is generated when we compile the system.
To create a defsystem file for the Hello World application:
defsys.lisp
.defsys.lisp
file:
(in-package "CL-USER")
(require "corba-orb")
(defsystem hello-world-corba-object ()
:members (
("hello-world" :type :idl-file)
))
:rules ((:in-order-to :compile :all
(:requires (:load :previous)))))
When it comes time to run the application, stubs and skeletons will be generated.
Now we will define some utilities for communicating an object reference from the server to the client by converting the object reference into a string using ORB-supplied functions and writing it to a shared file. The client can then read the string from the shared file and convert it back into an object reference. Note that a real application would probably use a higher level service such as a Name Service for passing object references between applications.
(in-package "CL-USER")
(defparameter *hello-world-ior-file*
#+mswindows "c:/temp/hello.ior"
#-mswindows "/tmp/hello.ior")
(defun object-to-file (orb object)
(with-open-file (st *hello-world-ior-file* :direction :output
:if-exists :supersede)
(prin1 (op:object_to_string orb object) st)))
(defun file-to-object (orb)
(with-open-file (st *hello-world-ior-file*)
(op:string_to_object orb (read st))))
object-to-file
opens the shared file and uses the op:object_to_string
function to convert the object reference into a string, which is then written into the filefile-to-object
performs the inverse operation: it reads the string from the file and uses op:string_to_object
to convert the string back into a client-side proxy objectshared.lisp
file.shared.lisp
to the defsystem by adding one line of code to the defsys.lisp
file, which should then look like this:
(in-package "CL-USER")
(require "corba-orb")
(defsystem hello-world-corba-object ()
:members (
("hello-world" :type :idl-file)
"shared"
))
:rules ((:in-order-to :compile :all
(:requires (:load :previous)))))
Now we will implement the client side of the Hello World application. We create a file hello-world-client.lisp
and add it to the defsystem. (You can implement this as you wish, but here is one possible implementation.)
hello-world-client.lisp
.hello-world-client.lisp
file:(in-package "CL-USER")
(defun run-client ()
(let ((orb (op:orb_init nil "LispWorks ORB")))
(let ((world (op:narrow 'HelloWorld:world (file-to-object
orb))))
(format t "~S~%" (op:hello world)))))
op:hello
on the object to get a stringThe elided details are not important at this stage, they involve getting an object reference from somewhere. In the full source at the end of this chapter (Complete source code for the Hello World example) you can see that a shared file is used to pass a stringified object reference.
hello-world-client.lisp
file. hello-world-client
to the defsystem by adding one line of code to the defsys.lisp
file, which should then look like this:
(in-package "CL-USER")
(require "corba-orb")
(defsystem hello-world-corba-object ()
:members (
("hello-world" :type :idl-file)
"shared"
"hello-world-client"
))
:rules ((:in-order-to :compile :all
(:requires (:load :previous)))))
Implementing the server is also easy. We create a file
hello-world-server.lisp
.
In the server the main function is less interesting because it is concerned with the administrative details of writing out a stringified form of the object reference into the shared file and initializing the server. The actual core of the application implementation is:
(defclass world-implementation (HelloWorld:world-servant) ())
(corba:define-method op:hello ((self world-implementation))
(declare (ignore self))
"Hello World!")
This subclasses a special generated class on the server side called a
servant
, and then implements a method on op:hello
that actually returns the desired string.
hello-world-server.lisp.
hello-world-server.lisp
:
(in-package "CL-USER")
(defclass world-implementation (HelloWorld:world-servant) ())
(corba:define-method op:hello ((self world-implementation))
(declare (ignore self))
"Hello World!")
(defun server-startup ()
(let* ((orb (op:orb_init nil "LispWorks ORB"))
(poa (op:resolve_initial_references orb "RootPOA"))
(impl (make-instance 'world-implementation))
(world (op:narrow 'HelloWorld:world
(op:servant_to_reference poa impl))))
(object-to-file orb world)
(let ((manager (op:the_poamanager poa)))
(op:activate manager))))
hello-world-server
to the defsystem by adding one line of code to the defsys.lisp
file, which should then look like this:
(in-package "CL-USER")
(require "corba-orb")
(defsystem hello-world-corba-object ()
:members (
("hello-world" :type :idl-file)
"shared"
"hello-world-server"
"hello-world-client"
))
:rules ((:in-order-to :compile :all
(:requires (:load :previous)))))
To build and test this distributed Hello World application, you must copy the rest of the source code from Complete source code for the Hello World example into the respective files. The code can also be found in the corba/hello-world
subdirectory of the standard examples directory.
After supplementing your files with the complete source code, perform the following steps in the Listener to run the example:
(load (example-file "corba/hello-world/defsys"))
(compile-system "HELLO-WORLD-CORBA-OBJECT"
:t-dir (get-temp-directory)
:load t)
Now, you can run the application to test that it works.
(mp:initialize-multiprocessing)
You need to run the server first so that it is waiting and ready to receive calls from the client.
(cl-user::server-startup)
(cl-user::run-client)
Note that you do not have to be running the client and the server in the same Lisp image (although you can if desired). In the simple example we have just implemented, they must be running on the same machine (to allow the object reference to be shared using a single file), but we have true location transparency in the way the client can be written with no regard for the location of the server process.
Developing Component Software with CORBA - 7 Aug 2017