Skip to content

Domino Billing

Chapter 13-3
Domino Billing

Introduction

Domino servers can track usage activity and support a billing process. You can configure the server to track, record, and store usage details for the following Domino activities. Each type of activity is designated by a Domino billing class.


Activity DescriptionBilling Class
AgentTracks the user, task, and database activity related to agent execution on the billing server.BILL_CLASS_AGENT
DatabaseTracks each database opened and closed on the billing server. Database billing tracks the amount of elapsed time a database is open per session.BILL_CLASS_DATABASE
DocumentTracks access for documents in a database that contain hidden BILLCHARGEREAD or BILLCHARGEWRITE fields and associates a cost charge with the action.BILL_CLASS_DOCUMENT
HttpRequestTracks Web server requests.BILL_CLASS_HTTPREQUEST
MailTracks the mailing of documents. When you enable mail billing, mail messages leaving the billing server mailbox are tracked and recorded in mail billing records. BILL_CLASS_MAIL
ReplicationTracks replication of databases when initiated by the billing server with another Domino server.BILL_CLASS_REPLICATION
SessionTracks network traffic that a user generates during a session. For example, when the user logs onto the server, Domino records any network-related activity needed to complete the transaction.BILL_CLASS_SESSION

There are two components to Domino billing:


      Domino server
      You can enable Domino servers to track specific activities for billing. You select the activities you want the server to track by adding classes to the BillingClass setting in the notes.ini file. As billable activities occur, the server adds this information to the billing message queue. We refer to a Domino server with the billing process enabled as a "billing server."

      For more information, see the Domino Administration Help documentaiton.

Billing server task

      The billing server add-in task collects billing information by periodically polling the message queue and removing billing records. You can store this data in either a Billing database (billing.nsf) or a binary file (billing.nbf) for later retrieval.
      For more information, see the Domino Administration Help documentation.


The remainder of this chapter describes the billing message and record structures and describes how to design and program a custom billing server and billing add-in server task using the HCL C API for Domino and Notes toolkit.


Anatomy of a Billing Message

The billing message queue is the heart of the billing process. All billing messages generated by the billing server are written to this queue. In turn, the billing server task retrieves the messages from the queue for further processing. The HCL C API for Domino and Notes provides functions for reading from and writing to the billing message queue and also defines the data types and symbols that comprise the contents of the message.

A billing message is defined by a general purpose data structure that can be applied to all supported billing types. It consists of header information that defines the attributes of the record, followed by the billing record itself. The following sections describe the default data types and symbols that comprise a billing message and support the billing process, as defined in the billing include file billing.h.

Billing Message Queue

#define BILL_QUEUE_NAME   "MQ$BILLING"  

This is the default billing message queue. The billing server and billing server add-in tasks use this literal when calling the C API to retrieve an open message queue handle, which subsequent message queue reads and writes can then use.

Billing Classes

#define BILL_CLASS_SESSION       0x00000001  / Session /

define BILL_CLASS_REPLICATION   0x00000002  / Replication/

define BILL_CLASS_DOCUMENT      0x00000004  / Document Charge /

define BILL_CLASS_MAIL          0x00000008  / Mail /

define BILL_CLASS_DATABASE      0x00000010  / Database /

#define BILL_CLASS_AGENT         0x00000020  / Agent /
#define BILL_CLASS_HTTPREQUEST   0x00000040  / Http Request /

These constant values represent the different billing classes supported by the HCL Domino Server Billing Process. The billing server uses these values to interpret the currently active classes of supported billing on the Domino server, as returned by the API function BillingGetClass. The billing add-in also uses these values when reading the billing message header structures to determine the type of reporting to perform.

Billing Structure Types

#define BILL_SESSIONREC      1  / Session Record Type /

define BILL_REPLREC      1001  / Replication Record Type /

define BILL_DOCCHARGE    2001  / Document Record Type /

define BILL_MAILREC      3001  / Mail Record Type /

define BILL_DBREC        4001  / Database Record Type/

#define BILL_AGENTREC     5001  / Agent Record Type /
#define BILL_HTTPREQREC   6001  / Http Request Record Type /

These constant values define the types of billing record structures that a billing message can contain. By default, there is one billing structure type per billing class, although you can extend this for customized billing structure types. There is always a corresponding billing record structure for each billing structure type. The billing structure type is part of the billing message header information that the billing server writes and the billing add-in reads.

Session and Database Billing Actions

#define BILL_SESSION_START   1    / Session Start record /

define BILL_DB_OPEN         2    / DB Open /

define BILL_DB_CLOSE        3    / DB Close by User  /

define BILL_SESSION_STAMP   4    / Periodic session record /

define BILL_DB_STAMP        5    / Periodic database stamp /

define BILL_DB_CLOSE_END    6    / Physical close when session terminates. /

define BILL_SESSION_END     255  / Session End /


These constant values define the types of actions that the session and database activity billing track. The billing actions are part of the session and database billing record structures that the billing server writes and the billing add-in reads.

Document Billing Charges

#define BILLCHARGEREAD    1   / Document Read charge /

define BILLCHARGEWRITE   2   / Document Write charge /


These constant values define the type of access charge actions that are tracked when documents are read or written in a database on the billing server. This document charge is part of the document billing record structure that the billing server writes and the billing add-in reads.

Billing Record Structures

/ session billing /
typedef struct
{
  SESSIONID   SessionID;                / Session ID /
  WORD        Action;                   / Start, Stamp, End /
  char        Username[MAXUSERNAME+1];  / Username /
  DWORD       BytesIn;                  / Bytes read thus far in this session /
  DWORD       BytesOut;                 / Bytes written thus far in this session /
  char        NetAdr[MAXNETADR];        / IP Address of client /
} SESSIONREC;


/ database billing /
typedef struct
{
  SESSIONID   SessionID;                 / Session ID /
  WORD        Action;                    / DbOpen, Stamp, DbClose /
  char        Username[MAXUSERNAME+1];   / Username /
  DWORD       DBOpenTime;                / DB Open Time /
  TIMEDATE    ReplicaID;                 / Replica ID of Database opened or closed/
} DBREC;

/ replication billing /    
typedef struct
{
  SESSIONID   SessionID;                   / Session ID /
  DWORD       BytesIn;                     / Bytes read to replicate this database. /
  DWORD       BytesOut;                    / Bytes written to replicate this database. /
  TIMEDATE    ReplicaID;                   / Replica ID /
  char        Source[MAXUSERNAME+1];       / Replication source /
  char        Destination[MAXUSERNAME+1];  / Replication Destination /
  WORD        Priority;                    / Replication priority/
} REPLREC;

/ document charge billing /
typedef struct
{
  WORD       Type;                     / $ChargeWrite or $ChargeRead /
  TIMEDATE   ReplicaID;                / Database replica id /
  char       Username[MAXUSERNAME+1];  / Username /
  OID        OriginatorID;             / Universally unique Note ID of document /
  NUMBER     Charge;                   / Value stored in $Charge Field /
} DOCUMENT;


/ mail billing /
typedef struct
{
  char       FormType[DESIGN_NAME_MAX];  / Type of Form /
  OID        OriginatorID;               / Message Id /
  UNID       OrigMessageID;              / $Ref if message is split /  
  DWORD      MessageSize;                / Size of message /
  char       HopName[MAXUSERNAME+1];     / Next Server Name in route /
  WORD       Priority;                   / Delivery priority /
  WORD       RoutingState;               / Delivery state of message /
  WORD       Report;                     / Delivery report request /
  char       Originator[MAXUSERNAME+1];  / From: /
  WORD       RecipientCount;             / Recipient count /
  WORD       RecipientSize;              / Recipients string size /
  TIMEDATE   PostedDate;                 / Date message was posted to router mailbox /
/ Recipients char string follows structure.  This is a packed character array of recipient

   names delimited by a comma.  The array is not NULL-terminated therefore, the RecipientSize
   field is used to traverse the array
/
} MAILREC;


/ agent billing /
typedef struct
{
  WORD       ULen;            / UserName Length /
  WORD       TLen;            / TaskName Length /
  WORD       DLen;            / DatabaseName Length /
  DWORD      ElapsedRunTime;  / Elapsed run-time for agent /
  DWORD      Flags;           / Agent Flags: BILL_AGENT_XXX /
/ UserName, TaskName, Database Name strings follows structure /
} AGENTREC;


/ http request billing /
typedef struct
{
  DWORD HttpContentLength; / Content Length send/received to/from client /
  DWORD HttpReqTimeMs; / Number of milliseconds to process request /
  DWORD HttpStatusCode; / Status code returned by the server /
  WORD HttpTimeStampOffset; / Offset to http time stamp in a string format /
  WORD HttpAuthUserOffset; / Offset to Authenticated User string /
  WORD HttpPartnerOffset; / Offset to remote machine name string /
  WORD HttpRefererOffset; / Offset to refering URL string /
  WORD HttpServerAddrOffset; / Offset to connection IP address /
  WORD HttpUserAgentOffset; / Offset to user agent string /
  WORD HttpRequestLineOffset; / Offset to Request line string /
  WORD HttpContentTypeOffset; / Offset to content type string /
/ Time Stamp, AuthUser, Partner and other strings follow /
} HTTPREQREC;


The above structure definitions correspond to the billing record structures for the billing activities tracked by the default HCL Domino Server Billing Process. They contain all the information pertinent to the billing class and structure type and enough to apportion a service bill to the consumer. For more information on the elements of each billing record structure, refer to the Reference. The billing server writes the billing record structure information and the billing add-in reads it.

Billing Record

typedef union
{
  SESSIONREC   sess;        / session billing record /
  REPLREC      repl;        / replication billing record /
  DOCUMENT     doc;         / document charge billing record /
  MAILREC      mail;        / mail billing record /
  DBREC        db;          / database billing record /

   AGENTREC     agent;       / agent billing record /
   HTTPREQREC   HttpRequest; / http request billing record /
/ extend for custom billing record structures /
} BILLREC;

As defined above, the billing record is the union of the default billing record structures plus any customized billing record structures that you define. This structure is contained in the billing message immediately after the billing message header information. Since a billing message is based on a particular billing class and structure type, only one billing record structure per billing message contains valid information. The billing server and billing server add-in task determine the billing record structure to use while writing or reading a billing record.

Billing Message

typedef struct
{
  WORD       Len;                        / length of the billing message /
  WORD       StructType;                 / billing record structure type /
  DWORD      Class;                      / billing class /
  char       ServerName[MAXUSERNAME+1];  / billing server name /
  TIMEDATE   TimeStamp;                  / timestamp of transaction /
  BILLREC    rec;                        / the billing record /
} BILLMSG;


The above structure defines the billing message that is written to and read from the billing message queue. The first five elements comprise the billing message header and are followed by the billing record union. The billing header elements provide programmatic information specific to the billing record. The header lets programmers filter billing records for particular billing classes or structure types to be processed in their applications.

The billing server is responsible for constructing the billing message. Typically, this task uses the API function BillingGetClass to check to see if a particular billing class is configured for the Domino server. Then, if the class is configured, the billing server uses the API function BillingWrite to appropriately construct the billing message and write it to the billing message queue. The billing server task uses API message queue functions such as MQGet to read the billing message from the queue and formats the information into the desired report format.


Writing a Billing Message (Billing Server Sample)

In designing a custom billing server, you must first define any new data type and symbols related to the activity being tracked. At a minimum, this involves defining a new billing structure type and billing record structure for each unique billing activity and extending the billing record to contain the new billing record structure.

In general, the responsibility of the billing server is to generate billing messages that relate to the activities on the Domino server for the types of each configured billing class. The Extension Manager support on the Domino server provides a programmatic hook into many of the internal operations on the Domino server and is an ideal framework for extending the billing activities with a custom billing server.

The remainder of this section contains code segments from the billing sample BILLMNGR to help illustrate how to write a custom billing server. Default Domino server database billing tracks the amount of elapsed time a database is open per session. The BILLMNGR sample extends the database billing class by tracking creation and deletion of databases, providing the capability to bill consumers for additional database-related activities.

The BILLMNGR sample is a program library (DLL) that registers a Domino Server Extension Manager (EM) handler routine for tracing the NSFDbCreate and NSFDbDelete API functions. The Domino server calls these functions whenever a database is created or deleted. Each time the API is called, an associated Extension Manager event is generated. The EM handler routine creates new billing messages for these events and writes them to the billing message queue using the API function BillingWrite. For more information on programming to the Extension Manager, refer to the "Extension Manager" chapter of this User Guide. For API billing function information, refer to the Reference.


Custom Billing Message Definitions Used by Sample Program BILLMNGR (bill_ext.h)

/ New Billing Structure Type /
#define BILL_NOTECREATEREC   32001  / Billmngr extension: Note create type /

/ New Billing Actions /
#define BILL_NOTECREATE       1001  / Billmngr extension: Note create action /

/ New Billing Record Structure /.
typedef struct
{
  char        Username[MAXUSERNAME+1];   / Username /
  NOTEID      dbNoteID;                  / Created note id /
  TIMEDATE    ReplicaID;                 / Database replica id /
} NOTECREATEREC;


/ Modified Billing Record /
typedef union
{
  SESSIONREC    sess;
  REPLREC       repl;
  DOCUMENT      doc;
  MAILREC       mail;
  DBREC         db;

   AGENTREC      agent;
   HTTPREQREC    HttpRequest;
   NOTECREATEREC notecreate;   / Extended note create billing record /
} BILLREC;



The above code shows the data type and symbol definition extensions for the note creation billing activity that the BILLMNGR sample billing server uses. For this sample, the API billing header file billing.h has been copied to the sample directory, renamed bill_ext.h, and modified with the above definition extensions. This file replaces the billing.h include file packaged with the HCL C API for Domino and Notes toolkit. It is included by the BILLMNGR sample source file.


Billing Extension Manager Handler Routine for Sample Program BILLMNGR (billmngr.c)

STATUS LNCALLBACK BillHandler ( EMRECORD FAR * theData )
{
 
  STATUS     error = NOERROR;
  BILLMSG    BillMsg;
  VARARG_PTR argPtr;


   NOTEHANDLE hNote;                        / NSFNoteUpdateExtended argument /
  HANDLE     hDb;                          / Associated DB handle /
  char       Username[MAXUSERNAME+1];      / Note create user /  
  NOTEID     NoteId;                       / NOTEID of note /
  DBID       DbId;                         / DBID of note /
  char       Servername[MAXUSERNAME+1];    / Server name /  

   DWORD      BillClass;

/ Check to see if Database billing class is enabled. If not, return without
  sending billing record
/


   if (error = BillingGetClass( &BillClass ))

/ Only bill if the API was successful. If not, return without sending
  billing record.
/
   
     goto Done;


   if ((BOOL)(BillClass & BILL_CLASS_DATABASE))
  {


      if ( theData->Status != NOERROR )
        goto Done;
     

/ otherwise handle Note Create billing by interpreting NSFNoteUpdateExtended calls /
     
     argPtr = theData->Ap;


/ Before the API:  check to see if the Note ID is equal to zero.   If it is
  then this means a new note is being created and we should track the output.
/
 
     if ( theData->NotificationType == EM_BEFORE )
     {
        hNote = VARARG_GET (argPtr, HANDLE);   / save note handle /
        (void) VARARG_GET (argPtr, DWORD);      / skip update flags /
           
  / get NOTEID of input note handle /
        NSFNoteGetInfo(hNote, _NOTE_ID, &NoteId);


   / if NOTEID = 0, then set for billing /
        if (NoteId == 0)
           gCreatedNote = TRUE;
        else
           gCreatedNote = FALSE;
           
        return ( ERR_EM_CONTINUE ); / continue;, billing occurs after call /
     }


/ If after the call and a new note was created, fill in the UserName, Note ID,
  and Replica ID of the created note in the billing record.
/


/ NOTE: Since a global is used to determine if a new note was created, this
        logic assumes that the EM_AFTER handling occurs before a different
        NSFNoteUpdateExtended EM_BEFORE thread is handled by Domino.  For heavily

         loaded systems, it may be necessary to serialize these requests.
/

      if (gCreatedNote == TRUE)
     {
        memset( &(BillMsg.rec.notecreate), (char)0, sizeof( BillMsg.rec.notecreate ) );
        hNote = VARARG_GET (argPtr, HANDLE);   / save note handle /
        (void) VARARG_GET (argPtr, DWORD);      / skip update flags /


   / get NOTEID of note handle /
        NSFNoteGetInfo(hNote, _NOTE_ID, &NoteId);


   / get the DBHANDLE for note handle /
        NSFNoteGetInfo(hNote, _NOTE_DB, &hDb);


   / get DBID associated with the DBHANDLE /
        NSFDbIDGet(hDb, &DbId);


   / get USERNAME associated with the DBHANDLE /
        (void) NSFDbUserNameGet(hDb, Username, MAXUSERNAME);


   / if user is the server, then do not bill /
        (void) SECKFMGetUserName(Servername);
        if (!strcmp(Username, Servername))
           goto Done;    


   / else, set billing message fields with appropriate info/
        strcpy(BillMsg.rec.notecreate.Username, Username);
        BillMsg.rec.notecreate.dbNoteID = NoteId;  
        BillMsg.rec.notecreate.ReplicaID = DbId;  


   / and write the billing record /
        error = BillingWrite( (DWORD)BILL_CLASS_DATABASE,
                              (WORD)BILL_NOTECREATEREC,
                              sizeof( BillMsg ),
                              &BillMsg,
                              BILL_QUEUE_NAME );
     }
  }


/ Whether or not the Billing the record was written, return the original
  NSFNoteUpdateExtended (EM_AFTER) status back to Domino
/    


Done:
  if (theData && theData->NotificationType == EM_BEFORE)
     return( ERR_EM_CONTINUE );
  else

      return( theData->Status );

}


The above routine is the callback function (EM handler routine) that is registered with the Extension Manager to handle the EM_NFSNOTEUPDATE event generated by NFSNoteUpdate API. This routine performs the following billing server-related tasks:

  • Call the API function BillingGetClass to determine if the database billing class is configured on the server; only continue if active.
  • Check the status of the function being tracked; only continue if successful.
  • During EM_BEFORE handling, check that the input Note handle argument to NSFNoteUpdate is associated with a newly-created note.
  • During EM_AFTER handling, fill in the billing record structure for the customized BILL_NOTECREATEREC structure type for the note create (as long as the note is not being created by the server).
  • Call the API function BillingWrite to write the billing message to the message queue.

NOTE: The billing server is responsible for calling the Extension Manager API functions for registering and deregistering the EM handler routines. The BILLMNGR sample calls these functions in other routines that are not displayed above.

Also note that the BILLMNGR sample writes the billing messages to the default billing message queue, BILL_QUEUE_NAME. You do not have to use this message queue; in fact, it may be desirable to separate the custom billing messages.


Reading a Billing Message (Billing Add-In Sample)

As billing messages are being queued, a program needs to be running to extract, interpret, and store the information as appropriate for subsequent reporting. Since you can only call the Domino message queue API functions from a local server task, you must write a server add-in program, referred to here as a "billing add-in."

The general responsibility of the billing add-in is to get an open message queue handle to the billing message queue (or queues, if you are using more than one), retrieve and store any billing messages, and report on those relevant to the important billing classes and record types. To store the messages from the queue, the billing add-in uses the same billing data structures and symbols that the billing server uses to write the billing messages. Once it reads the information, the billing add-in should place this information in an appropriate billing system form, such as a Domino database or a proprietary accounting file format.

The remainder of this section contains code segments from the billing sample NBILLSES to illustrate how to program a billing add-in. NBILLSES is loaded as a Domino server task. It periodically reads billing messages off the message queue, filters out all session and database billing class records, and appends the information to a predesigned database. The sample reads both standard and custom (extended) billing messages generated on the Domino server. To use the custom billing record structures and symbols, NBILLSES includes the header file bill_ext.h.

For details on programming a server add-in, Refer to the Server Add-In Task chapter of this User Guide. For information about the API message queue refer to the Message Queues chapter of this guide.


Billing AddInMain Routine for Sample Program NBILLSES (billses.c)

STATUS LNPUBLIC AddInMain (HMODULE hResourceModule, int argc, char argv[])
{
  STATUS   error;
  MQHANDLE bMsgQueue=NULL;
  DWORD    Wakeup=0, Runtime=0, BillingClass=0;
  HANDLE   hDB;
  char    
Message=0;
  WORD     msgsize=sizeof(BILLMSG);
  BOOL     fBillingDBOpen=FALSE;


/ Default is to wake up every minute and run for 10 seconds. /

   if (! (Wakeup = OSGetEnvironmentLong("BillingAddinWakeup")) )
  Wakeup = 60;


   if (! (Runtime = OSGetEnvironmentLong("BillingAddinRuntime")) )
  Runtime = 10;


   if (Runtime > Wakeup)
  {
     AddInLogMessageText(MSG_ADDIN_TERMINATING, NULL);
     return (NOERROR);
  }


/ Log that the billing task has started. /

   AddInLogMessageText(MSG_ADDIN_STARTED, NULL);

/ Set the task into "Initializing" state /

   AddInSetStatusText(MSG_ADDIN_INITIALIZING, 0L);

/ Initialize the Message Queue /

   if (error = MQCreate(BILL_QUEUE_NAME, MAXWORD, 0))
  {
     if (error != ERR_DUPLICATE_MQ)
     {
        AddInLogErrorText(MSG_ADDIN_ERROR, error, "MQCreate");
        return (NOERROR);
     }
  }

  if (error = MQOpen (BILL_QUEUE_NAME, 0, &bMsgQueue))
  {
     AddInLogErrorText(MSG_ADDIN_ERROR, error,"MQOpen");
     return (NOERROR);
  }


/ Allocate enough memory to hold the session struct returned in message /

   if( (Message = malloc( msgsize)) == NULL)
  {
     AddInLogErrorText(MSG_ADDIN_ERROR_NOMEMORY, 0L,"Malloc()");
     return(NOERROR);
  }


/ Create/Open the billing sample database /

   if (error = BillingDBOpen (&hDB))
  {
     AddInLogErrorText (MSG_ADDIN_ERROR_DBOPEN, error, NULL);
     goto done;
  }
  else
     fBillingDBOpen = TRUE;


/ Run until Message Queue is not in QUIT state /

   while (!MQIsQuitPending(bMsgQueue))
  {
     WORD len;


      AddInSetStatusText(MSG_ADDIN_IDLE, 0L);

  / Yield processor to other tasks and reset schedules.
     Quit if server terminates.
/


      if (AddInIdle())
     {

     / Empty the queue. /
 
        AddInSetStatusText(MSG_ADDIN_PROCESSBILLING, 0L);
        while (TRUE)
        {
           error=MQGet(bMsgQueue, Message, msgsize, 0, 0, &len);
   
/ Queue is empty.  Stop processing. /


            if (error)
           {
              if (error != ERR_MQ_EMPTY)
                 AddInLogErrorText(MSG_ADDIN_ERROR, error,"MQGet Quit");
           }

           if ((len <= 0) || (error))
              break;
   
           if (error = BillingMessageToDB ((BILLMSG) Message, hDB, len))
               AddInLogErrorText (MSG_ADDIN_ERROR_WRITEDB, error, NULL);
        }

        goto done;
     }

  /
Read the Billing message queue every X (default: 60) seconds. /


      if (AddInSecondsHaveElapsed(Wakeup))
     {


         TIMEDATE Now, DueTime;

         AddInSetStatusText(MSG_ADDIN_PROCESSBILLING, 0L);

      /
Get a billing message /
     
        error=MQGet(bMsgQueue, Message, msgsize, 0, 0, &len);


         if (error)
        {
           if (error != ERR_MQ_EMPTY)
              AddInLogErrorText(MSG_ADDIN_ERROR, error,"MQGet");
        }


     /
Got a message, bill it (and the rest) to the database. /

        if ((len > 0) && (error==NOERROR) )
        {              
           OSCurrentTIMEDATE(&DueTime);
           TimeDateIncrement(&DueTime, Runtime
100);


            while (TRUE)
           {
              if (error = BillingMessageToDB ((BILLMSG) Message, hDB, len))
                 AddInLogErrorText (MSG_ADDIN_ERROR_WRITEDB, error, NULL);


               OSCurrentTIMEDATE(&Now);

        /
Time is up.  Stop processsing. /

               if (TimeDateCompare(&Now, &DueTime) >= 0)
              {
                 OSCurrentTIMEDATE(&DueTime);
                 TimeDateIncrement(&DueTime, Runtime
100);
                 break;
              }


               error=MQGet(bMsgQueue, Message, msgsize, 0, 0, &len);
   
              if (error)
              {
                 if (error != ERR_MQ_EMPTY)
                    AddInLogErrorText(MSG_ADDIN_ERROR, error,"MQGet");
              }


         / Queue is empty.  Stop processing. /

               if ((len == 0) || (error))
                 break;
     
           }  / end while true- mqget /


         } / end len > 0 /

     }  / end minutes elapsed/

  } / end while !quit /



done:
  if (Message)
     free (Message);


   AddInLogMessageText(MSG_ADDIN_TERMINATING, NULL);

   MQClose(bMsgQueue, 0);

   if (fBillingDBOpen)
  {
     if (error = NSFDbClose (hDB))
        AddInLogErrorText (MSG_ADDIN_ERROR, error, "closing sessbill.nsf");
  }


   return(NOERROR);
}


The above routine is the main add-in function for the NBILLSES sample. It illustrates a well-behaved server add-in task that performs the following billing add-in tasks:

  • Create and open the billing message queue.
  • Periodically retrieve billing messages from the queue.
  • Store the billing message in an allocated BILLMSG buffer.
  • Call a routine to store the message in a Domino database.
  • Close the message queue when told to exit.

NOTE: This routine references add-in status message literals defined in the header file billses.h, which is included by the NBILLSES sample. The "Wakeup" and "Runtime" variables are read from user-configured settings in the server notes.ini file. They specify how frequently the NBILLSES sample reads from the message queue and the length of processing time. For more information, see the Domino Administration Help documentation.


Routine to Store Billing Messages to a Database for Sample Program NBILLSES (billses.c)
STATUS LNPUBLIC BillingMessageToDB (BILLMSG *Message, HANDLE hDb, DWORD len)
{
  HANDLE hNote;
  NUMBER FloatTemp, DWordTemp;
  STATUS error;
  long   tmp;


   /* Create a new note to hold the billing record */
   
  if (error = NSFNoteCreate(hDb, &hNote))
     goto Done;


   /* First write the billing record header (shared) info to the note */
   
  /* Header:  Add Message class */


   FloatTemp = (NUMBER) Message->Class;
  if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_HEADER_CLASS,
                    sizeof(BILL_ITEM_HEADER_CLASS) - 1,    
                    TYPE_NUMBER,                                                                            
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))                                                
     goto Done;
   
  /* Header:  Add Message StructType */


   FloatTemp = (NUMBER) Message->StructType;
  if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_HEADER_STRUCTTYPE,
                    sizeof(BILL_ITEM_HEADER_STRUCTTYPE) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
     goto Done;


   /* Header:  Add timestamp */

   if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_HEADER_TIMESTAMP,
                    sizeof(BILL_ITEM_HEADER_TIMESTAMP) - 1,
                    TYPE_TIME,
                    &Message->TimeStamp, (DWORD) sizeof(TIMEDATE)))
     goto Done;


   /* Header:  Add Servername */

   if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_HEADER_SERVERNAME,
                    sizeof(BILL_ITEM_HEADER_SERVERNAME) - 1,
                    TYPE_TEXT,
                    Message->ServerName, (DWORD) strlen(Message->ServerName)))
     goto Done;


   /* Then write the billing message structtype specific info to the note */

   switch (Message->StructType)
  {


   /* Standard Session billing */

      case BILL_SESSIONREC:
 
     /* Set the billing record type to type Session */


         if (error = NSFItemSetText(hNote, FIELD_FORM, BILLING_SESSION_FORM,
                    (WORD) strlen(BILLING_SESSION_FORM)))              
           goto Done;


      /* Session:  Add SessionID*/

         memmove( &tmp, &Message->rec.sess.SessionID, sizeof( long));

        FloatTemp = (NUMBER) tmp;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_SESSION_SESSIONID,
                    sizeof(BILL_ITEM_SESSION_SESSIONID) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;


      /* Session:  Add BytesIn */

         FloatTemp = (NUMBER) Message->rec.sess.BytesIn;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_SESSION_BYTESIN,
                    sizeof(BILL_ITEM_SESSION_BYTESIN) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;


      /* Session:  Add BytesOut */

         FloatTemp = (NUMBER) Message->rec.sess.BytesOut;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_SESSION_BYTESOUT,
                    sizeof(BILL_ITEM_SESSION_BYTESOUT) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;

     /* Session:  Username */


         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_SESSION_USERNAME,
                    sizeof(BILL_ITEM_SESSION_USERNAME) - 1,
                    TYPE_TEXT,
                    Message->rec.sess.Username,
                    (DWORD) strlen(Message->rec.sess.Username)))
           goto Done;
 
     /* Session:  Add Action*/


         FloatTemp = (NUMBER) Message->rec.sess.Action;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_SESSION_ACTION,
                    sizeof(BILL_ITEM_SESSION_ACTION) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;


      /* Session:  Network Address of client */

         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_SESSION_NETADR,
                    sizeof(BILL_ITEM_SESSION_NETADR) - 1,
                    TYPE_TEXT,
                    Message->rec.sess.NetAdr,
                    (DWORD) strlen(Message->rec.sess.NetAdr)))
           goto Done;


         break;


   /* Standard Database billing */

      case BILL_DBREC:

      /* Set the billing record type to type DB */

         if (error = NSFItemSetText(hNote, FIELD_FORM, BILLING_DB_FORM,
                    (WORD) strlen(BILLING_DB_FORM)))          
           goto Done;


      /* Database:  Add SessionID*/

         memmove (&tmp, &Message->rec.db.SessionID, sizeof (long));
 
        FloatTemp = (NUMBER) tmp;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_DB_SESSIONID,
                    sizeof(BILL_ITEM_DB_SESSIONID) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;


      /* Database:  Add ReplicaID */

         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_DB_REPLICAID,
                    sizeof(BILL_ITEM_DB_REPLICAID) - 1,
                    TYPE_TIME,
                    &Message->rec.db.ReplicaID, (DWORD) sizeof(TIMEDATE)))
           goto Done;


      /* Database:  Username */

         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_DB_USERNAME,
                    sizeof(BILL_ITEM_DB_USERNAME) - 1,
                    TYPE_TEXT,
                    Message->rec.db.Username,
                    (DWORD) strlen(Message->rec.sess.Username)))
           goto Done;


      /* Database:  Add DBOpenTime */
     /* Convert from Centiseconds to Seconds, and Round up */


         tmp = (Message->rec.db.DBOpenTime + 50) / 100;
        FloatTemp = (NUMBER) tmp;


         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_DB_OPENTIME,
                    sizeof(BILL_ITEM_DB_OPENTIME) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;


      /* Database:  Add Action*/
        FloatTemp = (NUMBER) Message->rec.db.Action;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_DB_ACTION,
                    sizeof(BILL_ITEM_DB_ACTION) - 1,
                    TYPE_NUMBER,
                    &FloatTemp, (DWORD) sizeof(FloatTemp)))
           goto Done;


         break;
     


   /* Note Create (extended) billing */

      case BILL_NOTECREATEREC:

      /* Set the billing record type to type  NOTECREATE */

         if (error = NSFItemSetText(hNote, FIELD_FORM, BILLING_NOTECREATE_FORM,
                    (WORD) strlen(BILLING_NOTECREATE_FORM)))              
            goto Done;


      /* Note Create:  Add NoteUser*/

         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_NOTECREATE_USER,
                    sizeof(BILL_ITEM_NOTECREATE_USER) - 1,
                    TYPE_TEXT,
                    Message->rec.notecreate.Username,
                    (DWORD) strlen(Message->rec.notecreate.Username)))
           goto Done;


      /* Note Create:  Add NOTEID */
        DWordTemp = (NUMBER) Message->rec.notecreate.dbNoteID;
        if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_NOTECREATE_NOTEID,
                    sizeof(BILL_ITEM_NOTECREATE_NOTEID) - 1,
                    TYPE_NUMBER,
                    &DWordTemp, (DWORD) sizeof(DWordTemp)))
           goto Done;



      /* Note Create:  Add ReplicaID */

         if (error = NSFItemAppend(hNote, ITEM_SUMMARY,
                    BILL_ITEM_NOTECREATE_REPLICAID,
                    sizeof(BILL_ITEM_NOTECREATE_REPLICAID) - 1,
                    TYPE_TIME,
                    &Message->rec.notecreate.ReplicaID, (DWORD) sizeof(TIMEDATE)))
           goto Done;


         break;


      default:

/* No processing for "other" billing record messages */

         goto Done;
        break;


   }  /* end switch */


/*  Update the note */

   error = NSFNoteUpdate(hNote, UPDATE_FORCE|UPDATE_NOREVISION);   /* Suppress ERR_CONFLICT */

/* and close the note when done */

Done:
  if (hNote != NULLHANDLE)
     NSFNoteClose(hNote);
  return (error);


}


The AddInMain function of the NBILLSES sample calls the above routine to store a billing message buffer in a database. This routine illustrates how to selectively produce billing information by filtering the message for specific billing record structure types. In this case, only session, database, and the custom (BILLMNGR) billing information is written to the database.

NOTE: This routine references database form item literals defined in the header file billses.h, which is included by the NBILLSES sample. The database template sessbill.ntf, supplied with the NBILLSES sample, contains the necessary forms and views to report on the billing information. ---