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.