idl2ada translates IDL code into Ada95 code. It produces the client part and the implementation part of the object interface. It also generates a package containing the typecodes of all the types defined in the interface. idl2ada displays a message for each error or potential error it encounters. No code is generated if a syntax error or a severe lexical error has been found. Minor lexical errors do not forbid code generation.
The generated code corresponds to the IDL to Ada95 binding defined by the OMG in the document Ada_01-10-42.
Ada95 keywords, if used in the IDL code, are prefixed
by 'IDL_' in the generated code, so using for example 'protected'
and 'IDL_protected' in the same IDL code will lead to an Ada95
compilation error
double underscores are translated into '_U_' and ending
underscores are translating into '_U' which may also cause
overloading definitions in the generated Ada95 code
'#include' directives are not considered as real
inclusions but as dependencies, so no Ada95 code is generated for
the included IDL code ('with' clauses on the corresponding packages
are set instead when needed)
recursive sequences are not supported
due to Ada95 freezing rules, inside an interface, a
sequence containing directly or indirectly references from this
interface can only be declared after all the attributes and
operations.
The binding specifies that an instance of
Corba.Sequences.(Un)bounded
must be made for each sequence type definition. But, as there could
be several definition of sequences containing the same element type
in the same declaration region, the instances will collide. To avoid
this, and also to reduce the number of such instances (they are
quite big), idl2ada searches if such an instance is accessible from
this declaration region. If one is found, it is reused for making
the derivation, if none is found, the instance is made at the uppest
possible level so that it can be more widely reused. This occurs
also for bounded strings.
In pragma Subsystem, the word Subsystem is case
sensitive as the name of other IDL pragmas
For naming the unnamed type of a struct or union
component, the name of the enclosing type is prepended to avoid name
clashes with other unnamed types in another struct or union
A single forward instantiation is made even if there
are several forward declarations of the same interface
Aliases of interface types are generated as subtypes
If pragma Subsystem is used, idl2ada does not generate
an empty package with the subsystem name.
Comments are not transferred in the generated code
IDL files are preprocessed by an indicated 'C'
preprocessor
The typecodes of all IDL types are generated in the
child package ".Helper'
When a CORBA exception is raised, the exception message
is used to carry information to retrieve exception data. Such an
exception message will contain no more than four characters and at
least one.
For structures, idl2ada tries to make the Ada
representation identical to the CORBA protocol encoding (CDR) in
order to optimize the (un)marshalling processing. If this
optimization is impossible due to components order only, idl2ada
produces a warning.
The object implementation package files are never
overwritten by idl2ada. Instead, either versioned files (default) or
template files (command line option) are generated.
Client-side, server-side, skeletons, specifications and
bodies files can be generated separately.
idl2ada uses the 'C' preprocessor to resolve file
inclusion. The command line environment variable CPP must be set and
must contain the name of the 'C' preprocessor to be used. idl2ada
will fail if this preprocessor cannot handle the given IDL
file.
idl2ada does not generate any code if the orbriver daemon
named "License_Server" is not running or if no license
token is available.
idl2ada processes only one IDL file at a time
in the generated code, idl2ada has to build some
identifiers, their prefix is 'Tgx_', so identifiers with such a
prefix should be avoided in the IDL code
the OMG IDL language specification and the Ada95
binding do not indicate any rule to apply when a module is reopened.
If a module is reopened, idl2ada generates the package specification
and body as if all the definitions (previous and new) were put in
the module since the beginning providing that the previous module
definition(s) are indicated in a '#include' directive; nevertheless,
no code is generated for this module if the added definitions are
only modules or interfaces. This means that reopening a module is
not a way to realize mutual module dependency unless this dependency
is only needed inside a child module or interface. This behavior may
change in the future if the Ada95 binding indicates what should be
done.
in order to avoid name conflicts with standard Ada
packages or Top Graph'X packages, please don't use the following
names as top level containers (modules or interfaces):
Standard
System
Ada
Corba: root package of Ada CORBA packages
Tgx: root package of Top Graph'X utilities
X, Xt, Xm: root packages of Top Graph'X X and Motif
full Ada implementation XInAda
idl2ada generates a file per compilation unit. The default source naming convention is to use the capitalized full name of the unit suffixed by:
".ads" for the specification file
".adb" for the body file
This behavior can be changed by the following switches:
-gnat : generate files with the ACT-GNAT source naming
convention
-apex : generate files with the Rational-APEX source
naming convention
Client side files are overwritten if they already exist. Service-side files are never overwritten if they exist. For service-side specification and body files, if the file exists, a unique numeric suffix is appended by default.
For example, if the file "My_Interface.Impl.ads"
exists, the specification of the implementation of My_Interface is
stored in the file:
"My_Interface.Impl_1.ads"
if this
file does not already exist, else suffix "_2" is tried,
etc...
This behaviour may be changed by using the switch
-tmpl. The syntax is
-tmpl=<suffix>
For example, using
-tmpl=tpl, the specification of the implementation of My_Interface is
stored in the file:
"My_Interface.Impl.ads.tpl"
-h : display help
-v : display version and build
number
-d : generate debug info for
Top Graph'X.
-O : generate optimized code at
default level 1.
-On : generate optimized code
on level n.
-O0 (letter O + zero digit): no
code optimisation.
-async : generate code for
asynchronous methods (AMI) !!! INCOMPATIBLE
with -noasync option !!!
-noasync : DO NOT
generate code for asynchronous methods (AMI) !!!
INCOMPATIBLE with -async option !!!
-poll : generate code for
polling methods (AMI) !!! INCOMPATIBLE with
-nopoll option !!!
-noasync : DO NOT
generate code for polling methods (AMI) !!!
INCOMPATIBLE with -poll option !!!
-all : causes idl2ada to
generate code corresponding to all included files as if they were
submitted independently.
-I <include_dir> :
specify include searching directory <include_dir>
-tmpl : Impl template extension
to avoid replacement (default _n) where n is an integer
By default, idl2ada generates client and service files unless one or several file generation switches are indicated, in which case it generates only files indicated by these switches. The file generation switches are the following:
-client : generate client files
-impl : generate service files (impl and skeleton)
-skel : generate service skeleton files
-noskel : don't generate service skeleton files
-spec : generate specification files
-body : generate bodies files
The option "-all" causes idl2ada to generate code corresponding to all included files as if they were submitted independently.
The option '-I' must be used to indicate the directories where to look for IDL include files. It is passed unchanged to $CPP.
$ORBRIVER_DIR/bin/idl2ada -impl -noskel store.idl # Unix systems
%ORBRIVER_DIR%\bin\idl2ada -impl -noskel store.idl !! rem Windows-Nt systems
idl2ada recognises the pragma OrbRiver_Directive
(case sensitive).
Its syntax is:
pragma OrbRiver_Directive
<option> <value>
<option> and <value> are
string literals
Most of the options are helpful when "corbaizing"
Ada legacy applications.
<option> can take the following
values
"Object_Id" where the given value represents
the name of the service when using
Corba.Orb.Resolve_Initial_References
"Range" where the given value should indicate
a previously defined numeric type and its bounds; the generated Ada
type will then have the indicated bounds.
Example:
typedef
long t_short;
#pragma OrbRiver_Directive "Range"
"t_short -32768 32767".
"Insert_With" where the given value should be
a Ada library unit name to be withed by the client-side
specification.
Example:
#pragma OrbRiver_Directive
"Insert_With" "Xm"
"Insert_Line" where the given value should be
a Ada code declaration line. The line is inserted in the client-side
specification.
Example:
#pragma OrbRiver_Directive
"Insert_Line" " subtype t_short_natural is t_short
range 0 .. t_short'last;"
"Insert" where the given value should be a
file containing Ada code declaration lines. The file is inserted in
the client-side specification.
Example:
#pragma
OrbRiver_Directive "Insert" "unit_primitives.asi"
"Insert_Body" where the given value should be
a file containing Ada code lines. The file is inserted in the
client-side body.
Example:
#pragma OrbRiver_Directive
"Insert_Body" "unit_primitives.abi"
First, the client side packages of the interfaces of the objects this client needs to access to must have been generated using idl2ada. The generated code must not be edited in any case.
Before doing any method invocation, the client may need to start the OrbRiver orb. This is done by calling Corba.Orb.Orb_Init. This procedure requires two parameters:
Argv : an argument list to supersede command line
parameters
Orb_Identifier : the name of the orbriver daemon to
connect to
if this name is the null string, then the command line
options () are used to determine the orb (see Client
and Service switches), else the ORBRIVER environment variable is
tried. If the name is still a null string, the client uses its own
ORB library only, else if the named OrbRiver daemon is not found, a
message is displayed and the exception Ada.Io_Exceptions.Name_Error
is raised.
Example:
with Corba.Orb ;
procedure Client is
Args : Corba.Orb.Arg_List ;
Orb_Name : Corba.Orb.Orbid ;
begin
-- Initialization of the ORB connection
-- Use null argument list and name to allow command line options
-- and ORBRIVER environment variable
Corba.Orb.Orb_Init (Args, Orb_Name) ;
-- Put client code here
-- Corba processing termination
Corba.Orb.Shutdown (Wait_For_Completion => True) ;
end Client ;
If needed, the client can establish a connection with
orbriver daemons (for example, an administrator who wants to
interconnect all the Naming Services will read the etc/Orbs file,
connect to all the orbriver daemons, get their Naming Service root
reference, and bind it in all the others).
Corba.Orb.Orb_Init
can be called several times for the same daemon (in independent parts
of the client). In this case, as required by the OMG CORBA
specification, the daemon reference count is incremented. There must
be the same number of calls to Corba.Orb.Stop
to effectively terminate the connection with this daemon.
Corba.Orb.Shutdown also
closes all the connections wherever it is called.
In order to invoke methods on an object, the client needs to get a reference to this object. This reference can be obtained by different means:
by a stringified object reference (IOR) which is
transformed into a reference by calling Corba.Orb.String_To_Object
by a corbaloc, corbaname or file URL object reference
which is transformed into a reference by calling
Corba.Orb.String_To_Object
by searching the root object of the service in the
initial references of the orb by calling
Corba.Orb.Resolve_Initial_References
by searching in the NamingService under an already
defined name
by calling
Corba.Implementation_Repository.Get_Implementation
and Corba.ImplementationDef.Get_Root_Object
to start a new unshared service
as the result of an operation on another object
If the object corresponding to the reference exists
but is not active, the target orb does the job to make it active at
the first method invocation.
To allow its resolution by a
corbaloc URL, a reference must be added to the initial references
using Corba.Orb.Register_Initial_Reference.
Any server created with OrbRiver can resolve the references it
registers by this mean.
OrbRiver client and service library operations are
tasking-safe. They are potentially blocking at the Ada task
level.
Asynchronous Transfer of Control (ATC) is not supported
during method invocation. This would introduce a big performance
penalty due to the mandatory extra actions which would be needed, and
in most of the cases useless (when ATC is not used or pending).
If
ATC has to be used, the client implementor should use an extra task
to perform the method invocations while ATC is pending.
CORBA exceptions carry information when they are
raised. To get these data, an exception handler must have a choice
parameter specification (see Ada RM95 11.2) and must be specific to
the raised exception. The 'Get_Members' method associated with the
exception can then retrieve the data. Several exception handlers can
retrieve the data if the exception is re-raised with the simple
instruction 'raise ;' (e.g. if another exception is re-raised, the
access to the data is lost).
The CORBA exception Impl_Limit is
raised during method invocation if a non CORBA exception is raised
during the execution of the method in the server side.
Example
(taken from a Naming Service context operation) :
exception
when NotFound_Exception : CosNaming.NamingContext.NotFound =>
Ada.Text_Io.Put_Line ("Exception : Not Found");
declare
Members : CosNaming.NamingContext.NotFound_Members :=
CosNaming.NamingContext.Get_Members (NotFound_Exception);
begin
Ada.Text_Io.Put_Line ("Reason : " &
CosNaming.NamingContext.NotFoundReason'Image (Members.Why));
end;
Corba.Orb.Stop
must be called to close the CORBA session with a given orb. If
Corba.Orb.Orb_Init was called
several times for the same orb, the session will be effectively
closed after Corba.Orb.Stop is
called the same number of times, in order to protect the CORBA work
with this orb from the other tasks of the process. The standard
procedure Corba.Orb.Shutdown
forces the termination of the session.
If Corba.Orb.Stop
or Corba.Orb.Shutdown are not
called, the orbriver daemon itself will do the cleaning work when the
connection is closed (for example at least when the client is
killed).
First, the client and service side packages of the interfaces of the objects this service will handle must have been generated using idl2ada. The service side generated code needs be edited:
the interface implementation specification
idl2ada
generates the record extension of the object type as private with a
component for each attribute of the interface IDL specification.
This may need to be changed to fullfil the service implementation
requirements.
The Finalize OrbRiver specific method may also be
added, as well as other methods needed by the implementation.
the interface implementation body
To complete the
initialization procedure and to implement the methods added in the
specification.
the interface implementation attributes and operations
bodies
to fill in their code.
Initialization of the orb must be done as for a
client (see Initialization).
Initialization
of the root POA (Portable Object Adapter) is done automatically on
the Orb_Init call.
Additional POAs may be created.
POAs
must be activated in order to do their job, the Activate method of
their POAManager should be used for this purpose.
Then, the main
loop of the POAs can be started by calling Corba.Orb.Run.
This will make the service able to reply to the method invocations on
the objects it handles. This call only returns when the service is
stopped (Corba.Orb.Shutdown
or destruction of the root POA).
If the service should be an
initial service (ie its root object should be retrieved by
Corba.Orb.Resolve_Initial_References),
Corba.Orb.Register_Initial_Reference
must be used to tell the orb and give it the root object reference.
This allows also the resolution of this reference by a corbaloc URL.
Because they have to be accessed from their POA,
object servants are created by an allocator : "Root := new
Object;". Methods can be invoked on them after their reference
has been build by the POA and published either by calling an
operation on an external object or returned as the result of an
operation invocation in the service.
To get the reference of an
object (newly created or not)
do:
Corba.PortableServer.Poa.Servant_To_Reference
(Self
=> Oa,
P_Servant
=> Root,
Result
=> Root_Ref)
;
OrbRiver client and service library operations are
tasking-safe. They are potentially blocking at the Ada task
level.
When a method is invoked, it can be executed by its own
task or executed by the CORBA main loop task (the task executing
Corba.Orb.Run). The POA threading
policy is fully implemented.
The package
Corba.PortableServer.Poa.Agents
contains the code used to implement the task per method facility. If
needed, the default priority or the storage size of the task type
executing the methods can be changed. The file Agents in
$ORBRIVER_DIR/etc can be used for this purpose. An example of this
file is given in $ORBRIVER_DIR/etc/Agents.ex. If the Agents file
exists in the current directory of a given running service, its
values are taken into account, thus allowing specific values for a
given application. The service can also specify theses values by
calling the procedures in PortableServer.Agents_Conf
The
example Agents file contains:
INITIAL_PRIORITY=16384
STACK_SIZE=65536
These values are the default values. There must be no
spaces on the lines. The priority must be in the range 0 .. 32767 and
is internally mapped to the system priority values, thus allowing
system independent configuration.
INITIAL_PRIORITY indicates
the default priority of the tasks executing the requests, this value
can be overwritten dynamically by the priority assigned on a the POA
responsible of the execution of the request.
STACK_SIZE
indicates the size in bytes of the stack used by each agent task.
There is also the possibility to use the Orb methods Work_Pending and Perform_Work to fully control the thread executing the object requests. To be able to use these methods, the procedure Corba.Orb.Set_Main_Thread_Control must be called prior Orb_Init, with “true” as parameter. In that case, Corba.Orb.Run returns immediately without doing anything. If Corba.Orb.Set_Main_Thread_Control is not called, Work_Pending always returns false and Perform_Work does nothing.
The method invocation parameters may lead to a
situation where a CORBA exception must be returned to the invoking
client.
The subprogram Corba.Raise_Exception
raises the required exception after setting up the data so that the
exception handler in the skeleton can return them to the client. As
this call raises an exception, it does not return.
Example:
declare Param_NotFound : NotFound_Members; begin -- this name does not exist, raise NotFound exception Param_NotFound.Why := Missing_Node; Param_NotFound.Rest_Of_Name := N; Corba.Raise_Exception (Param_NotFound); end ;
If there are exception handlers which will handle
the exception before the skeleton one, they should re-raise the
exception with the single instruction 'raise ;' to avoid loosing the
data associated with the exception.
Exceptions raised during
method execution (whether they are CORBA or not) are handled by the
skeleton and are transformed into a CORBA exception reply. If the
exception is not a CORBA one, Corba.Imp_Limit is returned instead
with a minor of 0 and a completion status of Corba.Completed_MayBe.
A service can stop itself by destroying the root POA. In this case, all the POAs will stop processing requests. Etherealization should do all the cleaning actions before the effective stop is done:
save (with its reference) and deactivate any persistent
object it handles
destroy all other objects it handles
The same processing also occurs when the
orbriver daemon itself stops (see Stopping
OrbRiver with stop_orb).
The CORBA main loop will then
exit and the service can then call Corba.Orb.Shutdown
to close all the orb connections.
Copyright
Micro Focus 2002-2014. All rights reserved.