Go to the previous, next section.
Runtime code is in the Common Lisp package ilu
.
Names from interface specifications are transformed into Lisp names
(case-insensitive) by inserting hyphens at lower-to-upper case
transitions. Hyphens that are already present are maintained as
is.(7) Method names are mapped to INTERFACE:TYPE.METHOD
.
A separate package is defined for each interface with
defpackage
. The name of this package
is taken from the name of the interface. This package uses the packages
common-lisp
and ilu
. The Common Lisp
names of all entities defined in the ISL
are exported from the package, including types, classes, constants,
accessors, type predicates,
generic functions, exceptions, etc. Such symbols are also shadowed, to avoid
conflicts with used packages. For example, given the following interface:
INTERFACE MyInterface END; EXCEPTION TotalWinner : Person; TYPE Person = OBJECT METHODS Enemies (someone : Person) : Cardinal RAISES TotalWinner END END;
the stubber generates the following defpackage
:
(defpackage :my-interface (:use :common-lisp :ilu) (:shadow #:total-winner #:person #:person.enemies) (:export #:total-winner #:person #:person.enemies)
This allows symbols defined in the commonlisp
package
to be used by the automatically generated code in the generated package,
but it also means that the user needs to be careful about using any
generated package. In general, we recommend that you explicitly specify
the full name of symbols from ILU interfaces.
The basic ISL types have the following mapping to Common Lisp types:
BOOLEAN
maps to Common Lisp (or nil t)
BYTE
maps to Common Lisp (unsigned-byte 8)
SHORT CARDINAL
maps to Common Lisp (unsigned-byte 16)
CARDINAL
maps to Common Lisp (unsigned-byte 32)
LONG CARDINAL
maps to Common Lisp (unsigned-byte 64)
SHORT INTEGER
maps to Common Lisp (signed-byte 16)
INTEGER
maps to Common Lisp (signed-byte 32)
LONG INTEGER
maps to Common Lisp (signed-byte 64)
CHARACTER
maps to Common Lisp character
SHORT CHARACTER
maps to Common Lisp character
SHORT REAL
maps to Common Lisp single-float
REAL
maps to Common Lisp double-float
LONG REAL
maps to Common Lisp double-float
Constants are implemented in CL by a value of the
appropriate type, defined with defconstant
.
Arrays and sequences of CHARACTER (regular or SHORT) are implemented as
simple-string
s.
Pickles are represented with the CLOS class ilu:pickle
Instances of a pickle may be created by calling cl:make-instance
on ILU:PICKLE
, with the :VALUE
and :TYPE
keywords,
as in
(cl:make-instance 'ilu:pickle :type 'ilu:cardinal :value 3456)
Pickle has three reader functions defined on it:
ilu:pickle-value
returns the value stored in the pickle as a Lisp value.
ilu:pickle-type
returns the type of the value stored in the pickle.
ilu:pickle-bytes
returns the pickled bytes of the pickle.
Enumerations are implemented with symbols, as in
(deftype answer () `(member 'yes 'no 'maybe))
Arrays are implemented as simple-array
s.
Sequences are implemented as list
s, except for sequences of
characters, which are implemented as simple-string
s.
Record types are implemented with CL defstruct
.
Unions are implemented as a cons'ed value, with the cdr containing the union type discriminant, and the cdr containing the actual value.
Classes are implemented with CLOS defclass
.
Private slots are created for methods which are specified as
functional
, and the runtime caches the value of this method
in such slots after the first call to the method.
Instances are always subtypes of ilu:ilu-object
.
Methods always take as their first argument the object which they are a method
on. Subsequent arguments are those specified in the `.isl' file.
Methods that have OUT
or INOUT
arguments may return multiple
values. In general, the parameters to a method are the IN
and
INOUT
parameters specified in the ISL interface, but not
the OUT
parameters. The return values from a method are the
specified return value for the ISL method, if any, followed
by the INOUT
and OUT
parameters for the method, if any, in
the order in which they appear in the ISL specification
of the method.
OMG IDL attributes map to a CLOS method of the same name, and a setf method with the same name (unless the attribute is readonly).
Exceptions are represented with CL conditions, defined by define-condition
.
All ILU conditions
are subtypes of ilu:rpc-exception
, which is a
serious-condition
. If an associated value is
specified for an exception it may be accessed in one of the following
two ways:
"-ilu-prefix-idlExceptionType-"
, the value type is a generated type from
an OMG IDL exception description, and is a record type. In this case, each of the fields
of the record type are placed in the condition individually, and an accessor
with that field name is declared for that field.
value
through
which the associated value may be read.
Custom surrogates allow the user to specify custom surrogate object types
which may have additional functionality in terms of caching or other
side effects, and have them created instead of the default ILU
surrogate object type when an instance is received.
This functionality is provided in the Common Lisp runtime with the function
ilu:register-custom-surrogate
.
It's also possible to find out who is making the call by examining the
value of
.
ilu:*caller-identity*
The program ILU lisp-stubber
takes a interface specification (an `.isl' file) and generates lisp
code to provide both client-side and server-side support for the interface.
The files are generated in the current working directory.
In particular, the following files are generated:
PDEFSYS
(8) how to compile and load the other files. It defines a Common Lisp module :<interface>
, which describes the code needed to support both surrogate and true use of the interface. This file is often called a sysdcl for the module.
For each ILU class interface.otype
,
ILU will define, in the file
`interface-server-procs.lisp',
a CLOS class called interface:otype.IMPL
.
To implement a true object for interface.otype
,
one should further subclass this CLOS class,
and override all of its methods. In particular, do not let
any of the default methods for the class be called from your methods for it.
ILU supports,
in each address space, multiple instances of something called
a kernel server, each of which in turn
supports some set of object instances.
A kernel server exports its objects by making them available
to other modules. It may do so via one or more ports, which are
abstractly a tuple of (rpc protocol, transport type,
transport address). For example, a typical port might
provide access to a kernel server's objects
via (Sun RPC, TCP/IP, UNIX port 2076)
. Another port on the
same kernel server might provide access to the objects via
(Xerox Courier, XNS SPP, XNS port 1394)
.
When creating an instance of a true object, a kernel server for it,
and an instance id (the name by which the kernel server knows it) for
it must be determined. These may be specified explicitly by use of the
keyword arguments to commonlisp:make-instance
:ilu-kernel-server
and :ilu-instance-handle
, respectively. If they are
not specified explicitly,
the variable ilu:*default-server*
will be bound,
and its value will be used; a default instance
handle, unique relative to the kernel server, will be generated.
A kernel server may be created by instantiating the class
ilu:kernel-server
. The keyword argument :id
may
be specified to select a name for the server. Note that ILU
object IDs, which consist of the kernel server ID, plus the instance
handle of the object on that server, must be unique "across space
and time", as the saying goes. If no kernel server id is specified,
ILU will generate one automatically, using an algorithm
that provides a high probability of uniqueness. If you explicitly
specify a kernel server ID, a good technique is to use a prefix or
suffix which uniquely identifies some domain in which you can assure
the uniqueness of the remaining part of the ID. For example, when
using ILU at some project called NIFTY at some internet
site in the IP domain department.company.com
, one might use
kernel server IDs with names like
something.NIFTY.department.company.com
.
=> (make-instance 'ilu:kernel-server :id "FOO-SERVER-1") #<ILU:KERNEL-SERVER "FOO-SERVER-1"> => (make-instance 'ilu:kernel-server) #<ILU:KERNEL-SERVER "121.2.100.231.1404.2c7577eb.3e5a28f"> =>
To export a module for use by other modules,
simply instantiate one or more instances
of your subtypes of interface:otype.IMPL
(which
will inherit from ilu:ilu-true-object
.
=> (make-instance 'foo:my-bar.impl :ilu-kernel-server s) #<FOO:MY-BAR.IMPL 0x3b32e8 "1"> =>
The simplest Common Lisp "server" code would look something like:
(defun start-server () (make-instance 'foo:my-bar.impl))
which will create an instance of FOO:MY-BAR.IMPL
and export it via
a default server.
To enable users of your module find the exported objects, you may register the string binding handle of the object or objects, along with their type IDs, in any name service or registry that is convenient for you. In release 1.6 of ILU, we are supporting an experimental simple binding method that allows you to "publish" an object, which registers it in a domain-wide registry, and then to withdraw the object, if necessary. Potential clients can find the string binding handle and type ID of the object by calling a lookup function. Note that this interface and service is experimental, and may be supported differently in future releases of the ILU system.
If you wanted to create an instance, and publish it, the code for starting a service might look something like this:
(defun start-server () (let* ((ks (make-instance 'ilu:kernel-server ;; specify the service id :id "service.localdomain.company.com")) (si (make-instance 'foo:my-bar.impl ;; specify the server :ilu-kernel-server ks ;; specify the instance handle :ilu-instance-handle "theServer"))) ;; the OID for "si" is now "theServer@service.localdomain.company.com" (ilu:publish si) si))
Someone who wanted to use this service could then find it with the following:
(defun find-server () (ilu:lookup 'foo:bar "theServer@service.localdomain.company.com"))
To help with finding errors in your methods, the variable *debug-uncaught-conditions*
is provided.
To use a module from Common Lisp, you must first have loaded
the PDEFSYS "system" that describes the module. Typically,
for an ILU interface called Foo, the system
can be loaded by invoking (pdefsys:load-system :foo)
.
Next, you must bind an instance of an object from that interface.
The most common way of doing this is to receive an instance
of an object from a method called on another object. But to get the
first object exported
by that module, one can use either ilu:sbh->instance
or
ilu:lookup
.
ILU has dynamic runtime state. In particular, after it is
initialized, it uses several Common Lisp threads to maintain
part of its state, and may also keep open connections
on operating system communication interfaces.
If you wish to dump an image containing ILU,
you must dump the image before initializing the ILU
module.
Initialization occurs automatically whenever a instance of ilu:ilu-object
or ilu:rpc-server
is created. Thus you should not create
any instances of either true or surrogate ILU objects before
dumping the image. However, you may load all the interface code for any
interfaces that you are using, before dumping the image.
Initialization may also be accomplished by an explicit
call to ilu:initialize-ilu
. You may check to see whether the system has been initialized by
examining the variable ilu::*ilu-initialized*
, which is t
iff
ilu:initialize-ilu
has been invoked.
To install the Lisp binding on the MS Windows platform proceed as follows: Copy or rename the file `ilu-non-threaded-sysdcl.lisp' in directory `ILUSRC\runtime\lisp' to `ilu-sysdcl.lisp'. Compile the Lisp runtime files; i.e., start Allegro and type:
(load "c:\\ilu\\src\\runtime\\lisp\\compile-files.lisp")Copy the resulting `*.fsl' files and the files `ilu-sysdcl.lisp' and `pdefsys.lisp' to the Lisp installation directory (`ILUHOME\lisp'). Copy the ILU kernel and Lisp DLLs into a directory that is on your
PATH
.
ilu:run-main-loop
Because Allegro 3.0.1 is single-threaded, servers on Windows 95/NT must run the ILU mainloop. To run it indefinitely, use
(ilu:run-main-loop)Or allocate a handle, which can later, presumably in a method call, be used to exit the event loop:
(setf *handle* (ilu:create-main-loop-handle)) (ilu:run-main-loop *handle*) ... (ilu:exit-main-loop *handle*)For example, to run the example server in directory `examples/test1', start Allegro 3.0.1 for Windows, and type the following:
(load "c:\\ilu\\examples\\test1\\load-lisp-example.lisp") (test1-server:start-server) (ilu:run-main-loop)
ILU support uses a portable implementation
of DEFSYSTEM
to specify modules to Common Lisp.
See section The ILU Common Lisp Portable DEFSYSTEM Module,
for details of this system.
ILU currently assumes the existence of lightweight process, or thread, support in your Common Lisp implementation. It uses these internally via a generic veneer, described fully in section The ILU Common Lisp Lightweight Process System.
The Lisp support provided with ILU includes support for the Franz Allegro Common Lisp 4.x implementation. To use ILU with other Common Lisp implementations, please see section Porting ILU to Common Lisp Implementations.
Method: ilu:ilu-class-info (DISC (or ilu:ilu-object type-name)
) (WHAT keyword
) => (or string boolean list)
This routine will return the specified piece of information about the ILU class specified with DISC, which may be either a CLOS class name, or an instance of the class, and with WHAT, which identifies which piece of information to return. WHAT may have the following values:
:authentication
-- what kind of authentication, if any, is expected by the methods of this class
:brand
-- the brand of the object type, if any
:collectible-p
-- whether or not the object type participates in the ILU distributed GC
:doc-string
-- the doc string specified for the object type
:id
-- the ILU unique ID for the object type
:ilu-version
-- which version of ILU the stubber that generated the code for this object type came from
:methods
-- a list of the methods of the object type
:optional-p
-- whether values of this class are allowed to be cl:nil
(a CORBA excrescence)
:name
-- the ILU name of the object type
Method: cl:make-instance 'ilu:kernel-server &key {(id string
nil
)} {(unix-port fixnum
0
)} {(object-table list of 2 elements
nil
)} {(protocol string
"sunrpc"
)} {(transport list of string
("sunrpcrm" "tcp_0_0")
)} => ilu:kernel-server
Creates and returns an instance of ilu:kernel-server
. If id is specified, the server has that value for its server ID. If unix-port is specified, the server attempts to `listen' on that UNIX port, if the notion of a UNIX port is applicable. If object-table is specified, it must consist of a list of two functions. The first function must take a string, which is the instance handle of a desired object on this kernel server, and return a value of type ilu:ilu-true-object
. The second funtion must free up any resources used by this object table. Specific protocols and transport stacks can be specified with the protocol
and transport
keywords; these default to whatever defaults were selected when your ILU installation was built.
Method: cl:make-instance 'ilu:ilu-true-object &key {(ilu-kernel-server ilu:kernel-server
nil
)} {(ilu-instance-handle string
nil
)} => ilu:ilu-true-object
Creates and returns an instance of ilu:ilu-true-object
. If ilu-true-server is specified, the instance is created on the specified server. If ilu-instance-handle is specified, that instance handle is used.
Variable: ilu:*caller-identity*
The identity of the caller is bound to the special variable
ilu:*caller-identity*
. It is a string which begins with the name
of an identity scheme, followed by an identity in that scheme. For example,
an identity in the SunRPC UNIX identity scheme would be something like
"sunrpc-unix:2345,67@13.12.11.10"
(i.e., "sunrpc-unix:<uid>,<gid>@<hostname>"
). If no
identity is furnished, a zero-length string is bound.
Function: ilu:publish (OBJ ilu:ilu-object
) => boolean
Accepts an ilu:ilu-object
instance and registers it with some
domain-wide registration service. The object is known by its
object ID (OID), which is composed of the ID of its kernel server, plus
a server-relative instance ID, typically composed as
instance-ID@server-ID
. Clients may find
the object by looking up the OID via the ilu:lookup
function.
The function returns non-cl:nil
if the publication succeeded.
Function: ilu:withdraw (OBJ ilu:ilu-object
) => boolean
If OBJ is registered, and if it was published by the same
address space that is calling withdraw
, its registration is
withdrawn. The function returns non-cl:nil
if the object
is no longer published.
Variable: ilu:*debug-uncaught-conditions*
If cl:t
, causes a server to invoke the debugger when an unhandled error in user code
is encountered, rather than the default action of signalling an exception back to the
caller. The default value is cl:nil
.
Function: ilu:register-custom-surrogate (CLASS-NAME symbol
) (CUSTOM-CLASS clos:standard-class
)
Instructs the runtime to create an instance of CUSTOM-CLASS whenever it would normally create a new instance of the ILU object type named by CLASS-NAME, which should be the Common Lisp name for the object type. CUSTOM-CLASS must be a subtype of the class named by CLASS-NAME.
Initializes the ILU
module.
Function: ilu:sbh->instance (PUTATIVE-TYPE-NAME symbol
) (SBH string
) &optional (MOST-SPECIFIC-TYPE-ID simple-string
mstid of specified PUTATIVE-TYPE
) => ilu:ilu-object
Accepts an ILU string binding handle and Common Lisp type name, and attempts to locally bind an instance of that type with the OID specified in the string binding handle. If no such instance exists locally, a surrogate instance is created and returned. If a true instance exists locally, that instance will be returned.
Function: ilu:lookup (server-id simple-string
) (instance-handle simple-string
) (PUTATIVE-TYPE-NAME symbol
) => (or nil ilu:ilu-object)
This routine will find and return an object with a server ID of server-id and instance handle of instance-handle, if such an object has been registered in the local domain via the ILU simple binding protocol. See the section on "Exporting Objects" for an example.
Method: ilu:ping (DISC ilu:ilu-object
) => (or t nil)
Returns cl:t
if the true object for DISC exists, and the process
serving it can be contacted; cl:nil
otherwise.
Go to the previous, next section.