Skip Headers

Oracle® Communication and Mobility Server Developer's Guide Release 10.1.3 Part Number E10293-02 |
Home | Contents | Index | Contact Us

Previous | Next
| View PDF

# B Programming Oracle Diameter Applications

This appendix describes programming Oracle Diameter applications in the following sections:

- "IP and Routes Configuration"

- "Counters Management"

- "Dictionary"

- "Tracing and Logging Mechanism"

## IP and Routes Configuration

Before a Java Diameter application is able to process messages exchanged with a distant peer, the IP configuration and Diameter protocol-specific configuration have to be done by the application as follows:

1. Create a `DiameterStack` instance.

2. Register the Diameter application to the Diameter stack.

3. Create listening points to bind to local transport addresses.

4. Configure routes and connect to Diameter peers.

### Creating a Diameter Stack

An instance of the Diameter stack can be created as follows:

    import oracle.sdp.diameter.*;
    DiameterFactory myFactory;
    DiameterStack myStack;
    myFactory = DiameterFactory.getInstance();
    myStack =  myFactory.createDiameterStack("realm.domain.com",
            "server.realm.domain.com",
            null);

This code creates a Diameter stack for use by the local Diameter node in which Fully Qualified Domain Name (FQDN) is `server.realm.domain.com` and Origin Realm is `realm.domain.net`.

### Binding to Local Transport Addresses

When a Diameter application needs to listen for incoming connections on one or several transport addresses, it has to create one or several instances of the `DiameterListeningPoint` interface:

    String localURI = "aaa://server.realm.domain.com:41001";
    myStack.createDiameterListeningPoint(localURI);

As soon as the listening point has been created, the Diameter stack is ready to accept incoming connection from remote peers. If the Diameter stack receives a connection request from a peer that as not been declared in the routing table, then the `isUnknownPeerAuthorized()` of the `DiameterListener` interface is called. The connection is accepted only if this method returns true.

Note:

There is no need for the user application to keep the references on the listening points since they can be retrieved later by calling `DiameterStack.getDiameterListeningPoints()`.

### Configuring Routes and Binding to Diameter Peers

A Diameter client application can declare remote peers by using the `createDiameterRoute()`method.

The code fragment illustrated in Example B-1 configures two Diameter realms, `realm1.domain.com` and `realm2.domain.com`. The first realm is served by two peers: `peer1.realm1.domain.com` and `peer2.realm1.domain.com`, whereas the second realm is served by only one peer, `peer.realm2.domain.com`. The metric values (1 and 2) are such that `peer1` and `peer2` are set up in master/backup mode. Example B-1 illustrates this source code for setting up this peer configuration.

Example B-1 Configuring Peers

    myStack.createDiameterRoute("ExampleApp", "realm1.domain.com",
                                "aaa://peer1.realm1.domain.com", 1);
    myStack.createDiameterRoute("ExampleApp", "realm1.domain.com",
                                "aaa://peer2.realm1.domain.com", 2);
    myStack.createDiameterRoute("ExampleApp", "realm2.domain.com",
                                "aaa://peer.realm2.domain.com:41002", 1);

Note:

If a peer name (FQDN) is used in `createDiameterRoute()` and if that peer is not yet known to the local stack, a transport connection is initiated with the peer using the specified peer URI (taking into account any URI optional information such as port number and transport protocol). On the contrary, if the peer specified by the FQDN part of the URI is already known, the URI is ignored, and the existing peer entry is added to the routing table for the specified realm and application ID.

### Realm State Availability

The `DiameterRealmStateChangeEvent` class is used to notify the application of the reachability or unreachability of a remote realm as a result of peers coming up or down. This is important because the Diameter stack will not accept an outgoing message for which the remote realm is not available. Therefore, the application should wait until the realm is available before sending requests.

A `RealmStateChange` event is passed to `DiameterListener.processEvent()` whenever the availability of a pair (Remote-Realm, Application-ID) changes. The availability of a remote realm for a given application ID depends on the availability of active connections to at least one remote peer that is able to serve the specific realm and application ID. Since the route is not available, the application is not able to exchange messages with the remote realm peers.

Example B-2 illustrates a typical implementation of the `DiameterListener.processEvent()` method.

Example B-2 Implementing the DiameterListener.processEvent() Method

    public void processEvent(DiameterEvent event)
    {
      if (event instanceof DiameterRealmStateChangeEvent) {
          // A remote realm has become available or unavailable
          DiameterRealmStateChangeEvent event =
     (DiameterRealmStateChangeEvent)event;
          if (event.isRealmAvailable()) {
            System.out.println("Realm " + event.getRealm() + " is available");
          } else {
            System.out.println("Realm " + event.getRealm() + " is unavailable");
          }
          // ...
      }

## Counters Management

Upon Diameter stack initialization, a set of defined counters is initialized and associated to each `DiameterStack` and `DiameterProvider` instance created by the application. These counters are defined in the `DiameterStackImplMBean` and `DiameterProviderImplMBean` management interfaces.

There are two ways to access to these counters:

1. Directly, by calling one of the different methods defined in both management interface.

2. Remotely, by registering the Diameter MBeans to a JMX agent using javax.management package. Only the JDK 1.5 provides this package.

### MBeans Management Interface

There are two management interfaces defined in the `oracle.sdp.diameterimpl` package:

1. `DiameterStackImplMBean`: This interface represents the management API for an instance of the `DiameterStack` interface.

2. `DiameterProviderImplMBean`: This interface represents the management API for an instance of the `DiameterProvider` interface

Example B-3 illustrates how to directly get the value of one of these defined counters:

Example B-3 Getting the Value of a Counter

    //--> Example Stack: getting the NbTransactions counter
    MyStack->getNbTransactions();
    //--> Example Provider: getting the NbSessionsCreated counter
    MyProvider->getNbSessionsCreated();

### Managing a Diameter Application with MBeans

A Diameter Application can be managed remotely by registering the Diameter MBeans to a JMX agent and can be monitored by using the Java JConsole program.

#### Registering the Diameter MBeans

A Java application using the Diameter API can publish management information by registering instances of the `DiameterStackImplMBean` and `DiameterProviderImplMBean` interfaced to a JMX agent. This can be done as follows:

    import javax.management.MBeanServerFactory;
    import javax.management.MBeanServer;
    import javax.management.ObjectName;

    import oracle.sdp.diameter.*;

    // Create DiameterStack and DiameterProvider instances.
    // DiameterStack myStack = ...
    // DiameterProvider myProvider = ...
    List srvList = MBeanServerFactory.findMBeanServer(null);

    if (srvList.isEmpty() == false) {
     MBeanServer server = (MBeanServer)srvList.iterator().next();
     try {
       ObjectName name;

       name = new ObjectName( "oracle.sdp.diameterimpl:name=DiameterProvider");
       server.registerMBean(myProvider, name);

       name = new ObjectName("oracle.sdp.diameterimpl:name=DiameterStack");
       server.registerMBean(myStack, name);
     }
     catch (Exception e) {
       // Handle register exception
       // ...
     }
    }

Note:

This code requires the JDK 1.5 or later. Previous versions do not have the required `javax.management` package.

If you intend to allow remote access to the MBeans, you must define the Java property `com.sun.management.jmxremote` when running your application, as follows:

    java -Dcom.sun.management.jmxremote -classpath mdiameter.jar MyApplication

#### Using jconsole to Monitor Diameter Applications

When you have a Diameter application running -- and provided you have registered the MBeans as described above -- you can use the JDK's jconsole application to browse the management characteristics of the stack and application.

Start jconsole as follows:

    jconsole

And then select the application's JVM from the local list. The Diameter MBeans should be visible under the MBeans tab.

## Dictionary

This section includes the following topics:

- "Dictionary Composition"

- "Dictionary Extension"

### Dictionary Composition

When a user's application requires using commands or AVPs that are not defined in the default loaded application dictionary, the user can extend the dictionary to define new commands and/or AVPs syntaxes to be used by the Diameter stack.

#### dictionary Element

The root or top-level element of a Diameter dictionary extension is the `<dictionary>` element:

    <dictionary>
    .... (other elements)
    </dictionary>

The `<dictionary>` element contains zero or more `<vendor>` elements and zero or more `<application>` elements.

#### vendor Element

The `<vendor>` element defines a vendor by a name and associated IANA.

The `<vendor>` attributes are:

1. The vendor `id` attribute must be unique across all `<vendor>` element definitions of the dictionary. The value 0 is dedicated to the base protocol which corresponds to the syntaxes defined in [RFC-3588] and [RFC-4006].

2. The vendor `name` attribute is some text describing the vendor.

In Example B-4, the `<vendor>` element defines the vendor named "3GPP" whose enterprise code is 10415:

Example B-4 Defining a Vendor

    <dictionary>
          <vendor id="10415" name="3GPP">
          ....(other elements)
          </vendor>
    </dictionary>

The `<vendor>` element contains zero or more `<returnCode>` elements and zero or more `<avp>` elements.

#### application Element

One of the ways in which the Diameter protocol can be extended is through the addition of new applications.

The `<application>` element defines the new commands needed to support a new vendor Diameter application.

The `<application>` attributes are:

1. The application `id` attribute is the IANA-assigned Application Identifier for this application. The value 0 is dedicated to the base protocol which corresponds to the commands defined in RFC-3588 and RFC-4006.

2. The application `name` attribute is the human-readable name of this application.

3. The application `vendor` attribute is the name of the application vendor as previously defined in the `<vendor>` element.

4. The application `service-type` attribute defined the type of service delivered by the application. Possible values are "Acct" for accounting and "Auth" for authorization.

In Example B-5, the `<application>` element contains information for the 3GPP accounting "Rf" application identified by the value "3":

Example B-5 Defining an <application> Element

    <dictionary>
          <application id="3" name="Rf" vendor="3GPP" service-type="Acct">
          ....(other elements)
          </application>
    </dictionary>

The `<application>` element contains zero or more `<command>` elements.

#### command Element

A `<command>` element defines the attributes for a command.

The `<command>` attributes are:

1. The command `name` attribute defines the name of the command. Because only one command is defined for both "Request" and "Answer" portions, the "Accounting" command defines both "Accounting-Request" and "Accounting-Answer" messages.

2. The command `code` attribute defines the command code used to transmit this command.

In Example B-6, the Rf application contains the command "Accounting" whose code is 271:

Example B-6 Defining the <command> Element

    <dictionary>
          <application id="3" name="Rf" vendor="3GPP" service-type="Acct">
             <command name="Accounting" code="271" />
             ....
          </application>
    </dictionary>

#### returnCode Element

The `<returnCode>` element defines a possible value of the Result-Code AVP. In Example B-7, the 3GPP vendor defines the `returnCode 5030` named `DIAMETER_USER_UNKNOWN`.

Example B-7 Defining the <returnCode> Element

    <dictionary>
          <vendor id="10415" name="3GPP">
             <returnCode name="DIAMETER_USER_UNKNOWN" code="5030" />
             ....
          </vendor>
    </dictionary>

#### avp Element

The `<avp>` element defines an AVP as described in RFC-3588.

The `<avp>` attributes are:

1. The avp `name` attribute is the human-readable name of this AVP.

2. The avp `code` attribute defines the integer value used to encode the AVP for transmission on the network.

3. The avp `mandatory` attribute defines whether the mandatory bit of this AVP should or should not be set. Possible values are "must" or "mustnot".

4. The avp `protected` attribute defines whether the protected bit of this AVP should or should not be set. Possible values are "may" or "maynot".

5. The avp `may-encrypt` attribute defines whether the AVP has to be encrypted in case of CMS security usage. Possible values are "yes" or "no".

6. The avp `vendor-specific` attribute specifies if this is a vendor specific AVP or not. Possible values are "yes" or "no".

In Example B-8, the 3GPP vendor extends the dictionary with the AVP "Application-provided-called-party-address".

Example B-8 Defining the <avp> Element

    <dictionary>
          <vendor id="10415" name="3GPP">
             <avp name="Application-provided-called-party-address"
                code="837"
                mandatory="mustnot"
                protected="may"
                may-encrypt="no"
                vendor-specific="yes">
                ....
             </avp>
          </vendor>
    </dictionary>

The `<avp>` element regroups either a `<type>` element or a `<grouped>` element.

#### type Element

The `<type>` element defines the data type of the AVP in which it appears. This element must appear in all non-grouped AVP definitions.

The `type-name` attribute of the `<type>` element contains the data type name as defined in RFC-3588: Possible values are:

- "OCTETSTRING"

- "INTEGER32"

- "INTEGER64"

- "UNSIGNED32"

- "UNSIGNED64"

- "FLOAT32"

- "FLOAT64"

- "ADDRESS"

- "IPADDRESS"

- "TIME"

- "UTF8STRING"

- "DIAMETERIDENTITY"

- "DIAMETERURI"

- "IPFILTERRULE"

- "QOSFILTERRULE"

- "ENUMERATED"

- "GROUPED"

Note:

These values are case-sensitive.

In Example B-9, the AVP "Application-provided-called-party-address" is an UTF8String.

Example B-9 Defining the <type> Element

    <dictionary>
       <vendor id="10415" name="3GPP">
          <avp name="Application-provided-called-party-address"
             code="837"
             mandatory="mustnot"
             protected="may"
             may-encrypt="no"
             vendor-specific="yes">
                <type type-name="UTF8String"/>
          </avp>
       </vendor>
    </dictionary>

#### enum Element

The `<enum>` element defines a name which is mapped to an Unsigned32 value used in encoding and decoding AVPs of type Unsigned32. Enumerated elements should only be used with Unsigned32 typed AVPs.

The `<enum>` element's attributes are:

1. The enum `name` attribute is the text corresponding to a particular value for the attribute.

2. The enum `code` attribute is the Unsigned32 value corresponding to this enumerated value

In Example B-10, the Accounting-Record-Type AVP has four values: EVENT_RECORD, START_RECORD, INTERIM_RECORD and STOP_RECORD.

Example B-10 Defining the <enum> Element

    <dictionary>
          <vendor id="10415" name="3GPP">
             <avp name="Accounting-Record-Type"
                code="480"
                mandatory="must"
                protected="may"
                may-encrypt="yes">
                   <type type-name="Unsigned32"/>
                   <enum name="EVENT_RECORD" code="1"/>
                   <enum name="START_RECORD" code="2"/>
                   <enum name="INTERIM_RECORD" code="3"/>
                   <enum name="STOP_RECORD" code="4"/>
             </avp>
          </vendor>
    </dictionary>

#### grouped Element

The `<grouped>` element defines an AVP which encapsulates a sequence of AVPs together as a single payload. It consists in grouping one or more `<gavp>` elements. This way, a single "grouped" element can contain references to multiple AVPs. Each `<gavp>` element holds an AVP `name` and a `vendor-id` attribute.

The <gavp> attributes are:

1. The gavp `name` attribute must correspond to some existing AVP's `name` attribute.

2. The gavp `vendor-id` attribute refers to an existing vendor's `id` attribute.

   In Example B-11, the 3GPP vendor defines an AVP named "CC-Money" which is a set of previously defined AVPs named "Unit-Value" and "Currency-Code".

Example B-11 Defining the <grouped> and <gavp> Elements

    <dictionary>
          <vendor id="10415" name="3GPP">
             <avp name="CC-Money"
                code="413"
                mandatory="must"
                protected="may"
                may-encrypt="yes">
                   <grouped>
                      <gavp name="Unit-Value" />
                      <gavp name="Currency-Code" />
                      ....
                   </grouped>
             </avp>
          </vendor>
    </dictionary

### Dictionary Extension

Once the Diameter dictionary extension has been defined, use the `extendGrammar(`) method to apply the extension to the default dictionary as follows:.

    //--> Define dictionary extension string
    String myDictionary =
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"             +
       "<!DOCTYPE dictionary SYSTEM \"dictionary.dtd\">\n"        +
       "<dictionary>\n"                                           +
       " <vendor id=\"10415\" name=\"3GPP\">\n"                   +
       " </vendor>\n"                                             +
       " <application id=\"3\" name=\"Rf\" vendor=\"3GPP\" \n"    +
       " service-type=\"Acct\">\n"                                +
       " <command name=\"Accounting\" code=\"271\" />\n"          +
       " </application>\n"                                        +
       "</dictionary>\n";

    //--> Apply new extension to current dictionary
    try {
      myStack . extendGrammar(myDictionary);
    } catch (DiameterException e) {
      // Handle dictionary syntax errors ...
    }

For increased flexibility in a real application, you may want to read the XML syntax description from a file rather than having it embedded in the Java source code. This way, it becomes possible to change the mapping between names and codes without recompiling the application.

The 3GPP `Rf` Interface dictionary is returned by `getRfDictionary()` and the 3GPP `Ro` Interface dictionary is returned by `getRoDictionary()` and can be extended by the Diameter stack.

## Tracing and Logging Mechanism

The `DiameterTraceLoggerListener` class is an interface to the Diameter tracing and logging mechanism. This interface represents the communication channel implemented by an application to receive debug traces and logs from the Diameter stack implementation. Logs are messages targeted to the user of `DiameterStack`. Traces are for internal use and are meaningful only to people with a good knowledge of the `DiameterStack` implementation.

All the messages that may be sent through the `DiameterTraceLoggerListener.log()` logging interface are defined in `LogMessages.def`. There is no definition file for trace messages.

By default, logs are sent to stdout and traces are not sent. This behavior may be modified by users either by registering a user-defined subclass of `DiameterTraceLoggerListener` or by defining specific environment variables. An example of a `DiameterTraceLoggerListener` implementation is as follows:

    Class MyTraceLoggerListener implements DiameterTraceLoggerListener
    {
          // true or false.
          public boolean isTracingEnabled()
          {
             return true;
    }

    public void log (String file, int line, int severity, String message)
    {
             String severity;
             switch (severity) {
                case LOG_INFO_SEVERITY: severity="INFO"; break;
                case LOG_WARNING_SEVERITY: severity="WARNING"; break;
                case LOG_ERROR_SEVERITY: severity="ERROR"; break;
                case LOG_DISASTER_SEVERITY: severity="DISASTER"; break;
                default: severity="?"; break;
             }
             // ...
          }

          public void trace (String file, int line, int mask, String message)
          {
             System.err.println(...);
    }
    }