13 Writing a Custom Facilities Module

Learn how to add new features and functionality to Oracle Communications Billing and Revenue Management (BRM) by writing custom Facilities Modules (FMs).

Topics in this document:

For information about creating or modifying policy FMs, see "About System and Policy Opcodes".

About Implementing Custom FMs

You implement a custom FM the same way you add a custom client application in C to BRM:

  • You use the same standard client libraries you use to write a custom application. These libraries are included in the BRM SDK (BRM_SDK_home\lib).

  • You use the same coding conventions in an FM and in custom applications.

  • An FM can call system opcodes and use PCM Library macros just like a client application.

  • You can easily implement new features at the client application level and then migrate them into an FM after debugging.

For information on creating client applications, see "Adding New Client Applications".

FMs are configured into CMs at CM execution time as dynamically loaded libraries. When a new operation is implemented with an FM and the FM becomes part of a CM, it appears to all client applications as a fully integrated BRM operation. The custom FMs can perform checks, write log records, call functions in other FMs, call the default DMs, or call custom DMs.

Creating a New FM

Follow these procedures to create a new FM in a CM:

  1. Define new opcodes. See "Defining New Opcodes".

  2. Define input and output specifications for the new opcodes. See "Defining Input and Output Flist Specifications".

  3. If necessary, define new storable classes and fields. See "Defining New Storable Class and Field Definitions".

  4. Write a function to implement the new opcode. See "Writing a Function to Implement a New Opcode".

  5. Use the fm_post_init function to call nonbase opcodes at CM initialization. See "Using the fm_post_init Function to Call Nonbase Opcodes at CM Initialization".

  6. Write a program to map opcodes to functions. See "Creating an Opcode-to-Function Mapping File".

  7. Create a shared library for the new FM. See "Creating a Shared Library for a New FM".

  8. Configure the new FM as part of the CM. See "About Configuring a New FM into a CM".

Defining New Opcodes

You must define your custom opcodes, with their numbers, in a header file and run the parse_custom_ops_fields script on the file.

For more information on the parse_custom_ops_fields script, see "parse_custom_ops_fields".

BRM opcodes and their numbers are defined in the ops/*.h files in the BRM_home/include directory, where BRM_home is the directory in which the BRM server software is installed.

To pass new opcodes from a client application to a new FM, you use the PCM_OP macro.

To define a new opcode:

  1. Create a header file and define your new opcodes by using this format:

    #define opcode_name_1          opcode_number_1

    For example, you might create a header file named my_opcodes.h with these definitions:

    #define MY_OP_SET_AGE       100001
    #define MY_OP_SET_LANGUAGE  100002

    Note:

    Numbers up to 10000 are reserved for internal use.

  2. Run the parse_custom_ops_fields Perl script by using this syntax:

    parse_custom_ops_fields -L language -I input -O output     -P java_package

    For information on the parse_custom_ops_fields parameters and their valid values, see "parse_custom_ops_fields".

  3. For applications written with PCM C (including all MTA applications), in the pin.conf file for each application, create an entry using this format:

    - - ops_fields_extension_file ops_flds_ext

    Where ops_flds_ext is the file name and location of the memory-mapped extension file that the parse_custom_ops_fields script created.

  4. For applications written using Java PCM, add the location of the compiled Java classes that the script generated to the CLASSPATH.

  5. Make sure you include your header file both in the application that is calling the opcode and in the custom FM.

Defining Input and Output Flist Specifications

Flists are passed uninterpreted between the client application and the new FM by the PCM_OP opcode.

Follow these rules when you define flist specifications for your custom opcode:

  • Your flists must conform to the BRM flist specifications.

  • Both the input and output flist specifications must contain the PIN_FLD_POID field to identify the object being manipulated.

    For an example, see the input or output flist specifications for any of the opcode descriptions.

  • The client, the custom FM, and the custom Data Manager must agree on the flist format and content, especially if you are defining new fields.

Defining New Storable Class and Field Definitions

You may have to create custom fields and storable classes for your FM. As with new opcodes and flists, the custom FM and the client application must agree on the semantics of the new fields.

If you define new storable classes, you must create a new opcode to manipulate the data stored in the new objects. Pass the new opcode to your custom FM with an flist containing the POID of the object. The FM then sends a series of basic opcodes to the DM, depending on the operation.

See "Creating, Editing, and Deleting Fields and Storable Classes" for information on defining new storable classes and fields.

You also can implement custom functionality by using /data objects, which contain generic fields and can be used for BLOB (Binary Large Object) processing.

Writing a Function to Implement a New Opcode

To implement a new opcode, you write a new function that calls the base system opcodes to access the DM. The new function then becomes part of a new FM shared library.

The new function you write must conform to the PCM_OP calling convention.

Use the PCM_OP_* reference pages as checklists and templates to determine what your custom function must implement. Pay particular attention to the input and output flists.

Note:

If you are adding a new function in the fm_utils module that can be called outside the module add a prototype of that function in the fm_utils.h.

Using the fm_post_init Function to Call Nonbase Opcodes at CM Initialization

Using the fm_post_init function, custom FMs can call nonbase opcodes at CM initialization. To implement this, ensure that the fm_post_init function is a part of the FM shared library.

For example:

void
fm_post_init(int *err)
{
* errp = 0;
.
.
.
.
} 

The CM initialization takes place in two phases:

  • In the first phase, the fm_post_init functions are called for all the FM modules specified in the CMs pin.conf file. At this time, it is not possible to call nonbase opcodes from the fm_post_init functions.

  • In the second phase, all the fm_post_init methods implemented by all the FM modules are called. At this time, it is possible to call nonbase opcodes from the fm_post_init functions.

Creating an Opcode-to-Function Mapping File

You create a configuration program, fm_*_config.c, to map an opcode to the function that implements it. This configuration file is read when a dynamic library is created, and the mapping information is stored in it. When a parent CM is initialized, it configures the opcode-function pairs into itself, and each child CM inherits the same configuration as its parent.

The configuration program contains an array of struct cm_fm_config for each opcode and its corresponding function and the config function at the end.

For a definition of this struct, see the cm_fm.h file in the BRM_SDK_home/Include directory.

The following example shows an opcode-to-function configuration file.

Note:

Include your .h file with your new custom opcodes.

 /** fm_cust_config.c 1.8 99/02/11      
 *      

   #ifndef lint      
   static  char    Sccs_id[] = "@(#)fm_cust_config.c    1.8 11 Feb 1999 ";      
   #endif
  
   #include                       /* for FILE * in pcm.h */      
   #include "pcm.h"      
   #include "ops/cust.h"
   #include "pcp.h"      
   #include "cm_fm.h"
  
/*******************************************************************
 PIN_EXPORT void * fm_cust_config_func();
  
struct cm_fm_config fm_cust_config[] = {
              /* opcode as a int32, function name (as a string) */
              { PCM_OP_CUST_BILLINFO,         "op_cust_billinfo" },
              { PCM_OP_CUST_CREATE_SERVICE,   "op_cust_create_service" },
              { PCM_OP_CUST_DELETE_ACCT,      "op_cust_delete_acct" },
              { PCM_OP_CUST_INIT_SERVICE,     "op_cust_init_service" },
              { PCM_OP_CUST_NAMEINFO,         "op_cust_nameinfo" },
              { PCM_OP_CUST_STATUS,           "op_cust_status" },
              { PCM_OP_CUST_VERIFY,           "op_cust_verify" },
              { PCM_OP_CUST_FINDSERV_VERIFY,  "op_cust_findserv" },
              { 0,    (char *)0 }
void *
fm_cust_config_func()
{
return ((void *) (fm_cust_config));
}

For example, when the CM gets a PCM_OP_CUST_BILLINFO() opcode through the PCM_OP() call from a client application, the CM looks up this table and calls the op_cust_billinfo() function.

Note:

The CM gets the name of this configuration file from its own configuration file.

Creating a Shared Library for a New FM

Each custom FM must be created as a shared library. BRM code is multi-thread (MT) safe. If you require your custom FM to be MT safe, you must make your custom FM code MT safe.

Linux: See the Linux documentation for more information on shared libraries (LD(1)) and making your code MT safe (threads(3T)).

About Configuring a New FM into a CM

New FMs are implemented as shared libraries and are dynamically linked to CMs at runtime. You add new FMs to the CM configuration file. When a new CM is started or restarted, it reads its configuration file and loads the listed FMs dynamically.

Child CM processes inherit the configuration information read by their parent CM process. Child CM processes do not read the configuration file when they are forced. You can limit the number of CMs that implement the new opcode by leaving the new opcode out of the parent CM configuration file.

You must also make sure that you use the same database number in the configuration files for your client applications, custom FM, and the DMs.

Adding a New FM Module to the CM Configuration File

The configuration file contains the names of the shared libraries that implement the base and custom opcodes. It also contains the names of the corresponding configuration files that contain the opcode-to-function mappings.

The custom shared library (.so on Linux) contains the functions that implement the new opcodes and the opcode-to-function mapping table struct. No opcode-to-function mapping files must be present when the CMs with the custom FMs are started because this information is already stored in the shared library. However, the name of the shared library and the mapping struct still have to be in the configuration file so that the CM can find and configure them.

For the format and description of the entries for an FM, see "Syntax for Facilities Module Entries" in BRM System Administrator's Guide.

Use the entries for the system FMs in the default CM configuration file in BRM_SDK_home/sys/cm as an example to add your custom FM entries.

Initializing Objects for Multiple Processes

In the CM configuration file, you can specify an initialization function that is called when the CM loads an FM or Policy FM. This function initializes objects that are called in multiple processes or threads.

  1. Create a new file named *_init.c (for example, fm_term_pol_init.c) in the appropriate FM or Policy FM directory (for example, fm_term_pol directory).

  2. Add the new file to the Makefile.user file.

  3. Implement an initialization function in the *_init.c file.

    Example pseudo code:

    pin_flist_t *global_flistp = NULL;
    extern void
    fm_term_pol_init(int32 *errp)
    {
          global_flistp = read from custom objects;
           *errp = PIN_ERR_NONE;
    }

    Note:

    In the example above, global_flistp is allocated in the CM master process or thread. When a child process is created, global_flistp is duplicated and still available on the child process.

  4. Add the initialization function to the CM configuration file, using the following example:

    - cm fm_term_pol ../../lib/fm_term_pol.lib  fm_term_pol_config_func  fm_term_pol_init pin

Handling Transactions in Custom FMs

All policy operations conform to the rules for application-level transactions.

See the Transaction Handling section of the individual opcodes for details.

If a read-write or read-only transaction is open when a policy operation is performed, all data read as part of the operation will be consistent with the state of the database when the transaction is opened. This guarantees that the data used by this operation is consistent with related data used by other operations in the transaction.

If this operation is called when a transaction is not already open, the operation is performed without transactional control.

Caution:

Policy operations must not modify object data.

Custom FM code is responsible for starting, stopping, and committing transactions as required depending on the semantics of the opcode. Each base opcode must be surrounded by transactions, unless a transaction is already open when the opcode is called.

All other system opcodes, except for PCM_OP_PYMT_CHARGE and the password opcodes, start transactions if none are open when they are called.

Custom FM transactions must conform to the transaction specifications of the PCM context management functions. See "Context Management Opcodes" for the rules to follow.

For information on system transaction handling, see the Transaction Handling sections of each opcode description.

In your custom FM, you can check for open transactions. See the fm_generic_opcode.c file in BRM_SDK_home/templates/fm_template for sample code to use.

A custom FM can call other FMs by using the Portal Communications Module (PCM) API. Also, custom FMs use the same PCM API used to call other FMs and Data Managers.

Note:

Calls to the Data Manager are made with base opcodes.

Managing Memory in Custom FMs

To manage memory in your custom FMs, follow these rules:

  • Always use pin_malloc(), pin_free(), pin_realloc(), and pin_strdup() for the get(), set(), take(), and put() operations on flists.

  • Use the standard routines-malloc(3C), free(3C), realloc(3C), and strdup(3C)-for other memory operations.

Opening a New Context in an FM

To open a new context in an FM, use (pin_flist_t)NULL for in_flistp in the PCM_CONTEXT_OPEN call.

See the sample_app.c and sample_search.c code examples for more information.

Compiling and Linking a Custom FM

After you define or modify the policy opcodes, create a shared library for the new Policy FM by using the Makefile included in each Policy FM source directory.

Enter make to create the .so or .a file.

Use the libraries in BRM_SDK_home/lib for linking.

See the sample Makefile for BRM_SDK_home/source/templates/fm_temp/fm_generic_opcode.c. The main routine for a custom opcode should look similar to the op_generic function in the fm_generic_opcode.c file. The calling parameters and their types are required.

The following example shows the list of include files required. Make sure you include your own header file containing your new opcodes (custom_opcodes.h in the following example). This example also shows skeleton code for the new opcode, the cm_fm_config struct, and the new function:

#define PCM_OP_FM_SAMPLE_LOOPBACK               999999
  
struct cm_fm_config fm_bill_config[] = {
         { PCM_OP_FM_SAMPLE_LOOPBACK,           "op_fm_sample_loopback" },
         { 0,    (char *)0 } 
};
  
#include "pcm.h" 
#include "cm_fm.h" 
#include "pin_errs.h" 
#include "pinlog.h" 
#include "custom_opcodes.h"
  
op_fm_sample_loopback(connp, opcode, flags, i_flistp, r_flistpp, ebufp)
         cm_nap_connection_t     *connp;
         int32                   opcode;
         int32                   flags;
         pin_flist_t             *i_flistp;
         pin_flist_t             **r_flistpp;
         pin_errbuf_t            *ebufp; 
{
         PIN_ERR_CLEAR_ERR(ebufp);
  
         /***********************************************************
          * Check for errors.
          ***********************************************************/ 
         if (opcode != PCM_OP_FM_SAMPLE_LOOPBACK) {
                 pin_set_err(ebufp, PIN_ERRLOC_FM,
                         PIN_ERRCLASS_SYSTEM_DETERMINATE,
                         PIN_ERR_BAD_OPCODE, 0, 0, opcode);
                 PIN_ERR_LOG_EBUF (PIN_ERR_LEVEL_ERROR,
                         "op_fm_sample_loopback", ebufp);
                 return;
          }
  
          /***********************************************************
           * Return a copy of our input flist.              
           ***********************************************************/
          *out_flistpp = PIN_FLIST_COPY(in_flistp, ebufp);
  
          return; 
}

Configuring Your New Policy FM

The shared library (.so on Linux) for the Policy FM must be included in each CM where its functionality is needed. Whenever a new CM is started or restarted, the CM reads its configuration file and loads the listed System FMs and Policy FMs dynamically. For more information, see "Writing a Custom Facilities Module".

Configure the new FM as part of the applicable CMs by adding the new Policy FMs to the CM configuration file.

For information on the format of the configuration file entries, see the CM configuration file.

Debugging FMs

You can debug custom and policy FMs using BRM SDK and standard programming tools.

To debug FMs and policy opcodes, you need BRM SDK, access to a functioning BRM server, and the programming tools supported by BRM SDK on your platform. See "About BRM SDK" for installation instructions and other information about BRM SDK.

For an overview of the connections required to test an FM, see "Testing New or Customized Policy FMs".

The primary way of debugging an FM is attaching to a running CM.

Various debugging tools are available on the supported operating systems:

  • On Linux, you can use the gdb debugger.