Developping with OpenFusion RTORB Ada Edition









The IDL to Ada95 compiler: idl2ada



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.






Limitations of the OMG Ada95 binding








Differences from the binding





Specific features






idl2ada requirements



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 limitations








idl2ada switches


Source naming convention

idl2ada generates a file per compilation unit. The default source naming convention is to use the capitalized full name of the unit suffixed by:



This behavior can be changed by the following switches:



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"



Main switches



Generated code




Files generated

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:



The option "-all" causes idl2ada to generate code corresponding to all included files as if they were submitted independently.


IDL include file path

The option '-I' must be used to indicate the directories where to look for IDL include files. It is passed unchanged to $CPP.


Example:

$ORBRIVER_DIR/bin/idl2ada -impl -noskel store.idl # Unix systems
%ORBRIVER_DIR%\bin\idl2ada -impl -noskel store.idl !! rem Windows-Nt systems





idl2ada specific pragmas



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






Building a Client



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.





Initialization



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:

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.







Getting object references



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:

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.







Tasking



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 exception handling



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;







Termination



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).







Building a Service



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:







Initialization



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.







Building objects and references



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
) ;







Tasking



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.






Returning a CORBA exception



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.







Termination



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:



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.







Email Micro Focus support



Copyright Micro Focus 2002-2014. All rights reserved.