On this page
|
This page describes the way to develop a Diameter Ro application using HP OpenCall Diameter, as defined in 3GPP TS 32.260 & 32.299.
Ro is the online charging interface defined by 3GPP between CTF (Charging Triggering Function, i.e. MRF, A.S…) and the OCS (Online Charging Server) in the scope of IMS (IP Multimedia Subsystem) networks.
For more details and information on the HP OpenCall Diameter API usage, please refer to the documentation and Javadoc available with the SDK.
This tutorial aims at giving you the ability to develop a basic Ro application using the HP OpenCall Diameter Java API. In order to take maximum benefits from it, you should meet the following prerequisites:
- You have completed the Diameter Base Protocol Java API tutorial
- Basic knowledge of the diameter 3GPP Ro interface
Presentation of the HP OpenCall Diameter Ro Java interfaces
Like the other Diameter-based applications, the Ro application relies on all the Diameter base protocol interfaces (DiameterFactory, DiameterStack …). The Ro application code structure itself is identical to the base-protocol one.
In order to ease the development of Ro applications, HP OpenCall Diameter also provides a set of Ro-dedicated Java interfaces on top of the Base Protocol API. These interfaces derive directly from the base protocol interfaces:
- DiameterRoListener (derives from DiameterListener) to handle incoming Ro messages and events
- DiameterRoProvider (derives from DiameterProvider) provides Ro sessions/state-machines creation and message-sending methods
- DiameterRoMessageFactory (derives from DiameterMessageFactory) gives access to the Ro protocol dictionary and to Ro messages and AVPs creation methods
The DiamRoClient & DiamRoServer example applications
DiamRoClient.java and DiamRoServer.java are client and server programs that implement a 3GPP Ro Diameter application exchanging CCR / CCA messages (Credit Control Request, Credit Control Answer). The client application (playing the role of a Charging Triggering Function) makes use of Ro standard message (CCR) and AVPs to request credit for a session for a given user to the server (acting as the Online Charging System). Further CCR/CCA exchanges are performed till the user session reaches an end (see call-flow below).
The complete source code for the applications is available here: DiamRoClient.java and DiamRoServer.java. These examples may be used as a starting point to develop a real-world Ro application, even if the complexity of Ro AVPs will not be treated in details.
In this tutorial, we will review in details the steps that are specific to Ro application development, assuming that the reader has the knowledge of the Diameter Base Protocol application development. For the sake of simplicity, the source code shown in this page is not an exact copy of what you will find in DiamRoClient.java and DiamRoServer.java. However, the majority of the operations that are performed by these example applications are explained below.
Note also that the code below is presented for conceptual purposes only and may lack code grammar coherence (mostly: check for null references, and catch of DiameterExceptions).
The following diagram describes the behavior of the example applications:
Since the code structure of the Ro application is the same as the base-protocol applications, we will focus on the Ro specific elements in the next sections.
Application initialization |
The steps that are required to initialize the Diameter stack for an Ro application are mostly the same as for any other HP OpenCall Diameter application. Therefore, we won't go through all these steps again.
There is however a difference concerning the registration of the protocol dictionary for the Ro interface. When writing a custom application on top of the Diameter Base Protocol API, the Diameter stack has to be provided with an XML fragment that describes the commands and AVPs that will be used by the application, as described in the base protocol tutorial. This step is not required when using the Ro API. Indeed, the OpenCall Diameter stack will take care of automatically extending the base protocol dictionary with the Ro syntax when a DiameterRoProvider is instantiated.
A Diameter Ro application is represented by an instance of the class DiameterRoProvider and by implementing the interface DiameterRoListener.
DiameterRoListener represents the Diameter Ro application interface. It provides a set of Ro-dedicated callback functions that must be implemented by the application, easing the Ro message reception and handling.
The DiameterRoProvider allows:
- The selection of the RFC4006 accounting state-machines (Client Stateful, Client Stateless, Server) and Ro-specific properties (for the whole application scope or per session)
- The creation of client and server accounting sessions
- The sending of Ro messages
These interfaces are defined in the package com.hp.opencall._3gpp.ro, which must be imported.
A typical Diameter Ro application implements the DiameterRoListener, as in the following example code (Ro Client code). Ro specific code fragments are represented in bold:
import com.hp.opencall.diameter.*;
import com.hp.opencall.diameter._3gpp.ro.*;
class DiameterRoClientApplicationSolution implements DiameterRoListener
{
DiameterStack myStack;
DiameterRoProvider myRoProvider;
String localRealm = “ExampleRealm”;
String localFQDN = “localhost.localdomain.com”;
// Class constructor
DiameterRoClientApplicationSolution(String[] args)
throws DiameterException {
// creates a DiameterFactory
DiameterFactory myFactory;
// creates a DiameterStack
myStack = myFactory.createDiameterStack(
localRealm, localFQDN, null);
// create Ro provider for stateful client
// (i.e. for session-based charging):
// create and set Ro provider’s properties with
// value AUTO_DELETION (the session will be deleted
// automatically after reception and handling of CCA with
// CC-Request-Type == TERMINATION_REQUEST)
Properties myProviderProperties = new Properties();
myProviderProperties.setProperty(
"AUTO_DELETION",
"true");
myRoProvider = myStack.createDiameterRoProvider(
myProviderProperties,DiameterRoProvider.RFC_CL_FULL );
// and attach listener to the provider
myRoProvider.setDiameterRoListener(this);
// create a listening point for incoming messages
myStack.createDiameterListeningPoint(localURI);
// create a route to peer
myStack.createDiameterRoute("Ro", peerRealm, peerURI, 1);
} // end of constructor
…
Creating and sending Ro messages
A Diameter Ro application uses the same API as any other OpenCall Diameter application to send messages. The Ro interface defines both stateful and stateless client modes. However, contrary to Sh which is really a stateless interface, the Ro stateless mode actually means event-based, and a statemachine applies to this mode. For this reason, even when using the Ro stateless client mode, messages must be exchanged by means of a Session object (which is not the case when using Sh); otherwise Ro event-based client state-machine would be bypassed.
Although it is possible to build Ro messages using only the Base Protocol API (more specifically, the DiameterMessageFactory interface), it is much more desirable to use the specially designed DiameterRoMessageFactory for that purpose. This interface permits the creation of complete Ro messages in a single call. It is then possible to take advantage of the Base Protocol API to add any custom AVP to the Ro message.
Here’s an example of how to create a Credit-Control-Request message (with CC-Request-Type = INITIAL_REQUEST) and send it from the Ro client.
void createSessionAndSendRoCreditControlRequest() {
//get the Ro message factory
DiameterRoMessageFactory myRoMessageFactory =
myStack.getDiameterRoMessageFactory() ;
// create a client accounting session in order to manage
// the message sending/reception and Ro timers/events
DiameterSession mySession =
MyRoProvider.createClientDiameterAcctSession(null);
//Construct the CCR message:
// - Auth-Application-Id AVP = 4 as specified in
// 3GPP TS-32.299 (only supported value)
// - Service-Context-Id AVP = 32260@3gpp.org means
// "IMS Charging" (cf 3GPP TS32.299)
// - CCRequestNumber identifies the request within the session
// - CCRequestType = INITIAL_REQUEST. Subsequent requests will
// have value UPDATE_REQUEST, and the last TERMINATION_REQUEST
DiameterMessage CCR =
myRoMessageFactory.createCreditControlRequest(
peerRealm,
4,
"32260@3gpp.org",
DiameterRoMessageFactory.INITIAL_REQUEST,
0);
// display and send the CCR
System.out.println("Sending CCR message: \n" + CCR);
mySession.sendMessage(CCR);
}
Handling events
Like any application using HP OpenCall Diameter, an Ro application can receive events from the Diameter stack. The processing of any event that is not an incoming Ro message should be done as described for an application developed over the Base Protocol API (by implementing the processEvent() callback method).
The Ro interface (and especially the underlying Credit Control Application, RFC4006) defines session state-machines and timers to define the credit control behavior. All events associated to state-machines and timers are raised to the application by means of Ro specific events. The following examples are extracted from the long list of available Diameter Ro Events:
- DiameterTimeOutEvent: raised upon expiry of timers such as Tx (started when sending CCR, stopped when receiving the matching CCA)
- DiameterServiceEvent: raised by the Diameter stack when service related actions are required: need for the client to resend the message to a back-up Ro server, to terminate the service for a given user session …
Note that not all the available events have to be handled by the application. Ro provides support for many different use cases: refer to Ro and CCA specifications as well as HP OpenCall Diameter Javadoc to identify the events that make sense in your application’s context.
Incoming Ro messages, however, are processed and decoded by the Diameter stack before they are delivered to one of the dedicated methods of the user-registered DiameterRoListener instance, as described in the next section.
Receiving and answering to CCR messages on the Ro server
All incoming messages that comply with the Ro syntax are decoded by the Diameter stack, and delivered to the suitable DiameterRoListener method. The processEvent() method handles only events and messages that are not known as Ro messages.
The example below shows the list of DiameterRoListener interface’s message-handling callback functions as implemented on the server side. The creditControlAnswerReceived() callback implementation is empty since the server should not receive any CCA message. The next section will present the creditControlAnswerReceived() callback implementation taken from DiamRoClient.java
This example code implements the call-flow presented above, and assumes that the client initiated the message exchange using the method createSessionAndSendRoCreditControlRequest().
The implementation of the creditControlRequestReceived() method is simplistic: it automatically answers to each CCR message with a CCA message with the same Session-Id, CC-Request-Type, CC-Request-Number, and a Result-Code = DIAMETER_SUCCESS. A real life Ro application would perform database lookups and updates to validate the subscriber’s credit and debit his account.
//Process an incoming Re-Auth-Answer (RAA) message.
//RAA should never be received by a Ro server
public void reAuthAnswerReceived(
java.lang.String resultCode,
DiameterMessageEvent msgEvent) {}
//Process an incoming Re-Auth-Request (RAR) message.
//Not implemented in this example.
public void reAuthRequestReceived(
String destinationRealm,
String destinationHost,
int authApplicationId,
String reAuthRequestType,
DiameterMessageEvent msgEvent) {}
// Processes an incoming Credit-Control-Answer (CCA) message.
//CCA should never be received by a Ro server
public void creditControlAnswerReceived(
String resultCode,
int authApplicationId,
String CCRequestType,
int CCRequestNumber,
DiameterMessageEvent msgEvent){}
//Processes an incoming Credit-Control-Request (CCR) message.
public void creditControlRequestReceived(
String destinationRealm,
int authApplicationId,
String serviceContextId,
String CCRequestType,
int CCRequestNumber,
DiameterMessageEvent msgEvent)
{
// get the incoming CCR message contents from the event
DiameterMessage CCR = msgEvent.getDiameterMessage ();
// DiameterMessage implements the toString() method, hence
// can be printed out.
System.out.println("CCR received :\n"+CCR);
// Get the reference to the Ro message factory
// in order to build the CCA message
DiameterRoMessageFactory myRoMessageFactory =
myStack.getDiameterRoMessageFactory() ;
// Create the CCA message from the CCR, with
// Result-Code = DIAMETER_SUCCESS
DiameterMessage CCA =
myRoMessageFactory.createCreditControlAnswer("DIAMETER_SUCCESS", CCR);
// If the message is the first received with this Session-Id,
// create the session.
// If not, get the session from the incoming event
DiameterSession myRoSession = msgEvent.getDiameterSession();
if (myRoSession == null)
{
myRoSession =
myRoProvider.createServerDiameterSession(CCR);
System.out.println("creating new server session");
}
//Send CCA
System.out.println("Server sending CCA:\n"+CCA);
myRoSession.sendMessage(CCA);
// Once the server has answered to the termination request,
// delete the session
if (CCRequestType.compareTo("TERMINATION_REQUEST") == 0)
{
System.out.println("Server answered to TERMINATION_REQUEST, exiting.");
myRoSession.delete();
}
}
Note that all callback methods must be implemented whatever the application context (client or server), despite request-related callbacks are applicable to the Online Charging System (server) and answer-related ones are relevant only for the Charging Triggering Function (client).
Receiving and answering to CCA messages on the Ro client
The example below shows the creditControlAnswerReceived() callback implementation extracted from DiamRoClient.java.
This example code is called upon reception of a CCA from the Ro server: when receiving a CCA(INITIAL_REQUEST), it sends a new CCR(UPDATE_REQUEST). For an incoming CCA(UPDATE_REQUEST) it generates a new CCA(TERMINATION_REQUEST). Finally when receiving a CCA(TERMINATION_REQUEST) it deletes the Session. Each time the same Session-Id is used, and the CC-Request-Number is incremented, as per RFC4006.
// Process an incoming Credit-Control-Answer (CCA) message.
public void creditControlAnswerReceived(
String resultCode,
int authApplicationId,
String CCRequestType,
int CCRequestNumber,
DiameterMessageEvent msgEvent)
{
DiameterMessage CCA = msgEvent.getDiameterMessage() ;
System.out.println("CCA msg received: \n"+ CCA);
//Get the Ro message factory
DiameterRoMessageFactory myRoMessageFactory =
myStack.getDiameterRoMessageFactory();
// Get the diameter session. It already exists since it was
// created when sending the CCR(INITIAL_REQUEST)
DiameterSession mySession = msgEvent.getDiameterSession();
// If the message has a CC-Request-Type = INITIAL_REQUEST,
// create a new CCR with CC-Request-Type = UPDATE_REQUEST
if (CCRequestType.compareTo("INITIAL_REQUEST") == 0)
{
//Auth-Application-Id AVP = 4 as specified in 3GPP
// TS-32.299 (only supported value)
//Service-Context-Id AVP = 32260@3gpp.org
// means "IMS Charging" (cf 3GPP TS32.299)
//CCRequestNumber identifies the request within
// the session
DiameterMessage CCR =
myRoMessageFactory.createCreditControlRequest(
peerRealm,
4,
"32260@3gpp.org",
DiameterRoMessageFactory.UPDATE_REQUEST,
0);
System.out.println("Sending CCR message: \n" + CCR);
mySession.sendMessage(CCR);
}
// If the message has an CC-Request-Type = UPDATE_REQUEST,
// create a new CCR with CC-Request-Type = TERMINATION_REQUEST
if (CCRequestType.compareTo("UPDATE_REQUEST") == 0)
{
DiameterMessage CCR =
myRoMessageFactory.createCreditControlRequest(
peerRealm,
4,
"32260@3gpp.org",
DiameterRoMessageFactory.TERMINATION_REQUEST,
0);
System.out.println("Sending CCR message: \n" + CCR);
mySession.sendMessage(CCR);
}
if (CCRequestType.compareTo("TERMINATION_REQUEST") == 0)
{
System.out.println("Credit control terminated, exiting");
mySession.delete();
}
}
For more details, you can have a look at the Java source files available here: DiamBaseClient.java, DiamBaseServer.java
