Oracle® Communication and Mobility Server Developer's Guide Release 10.1.3 Part Number E10293-02 |
|
|
View PDF |
This appendix describes programming Oracle Diameter applications in the following sections:
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:
Create a DiameterStack
instance.
Register the Diameter application to the Diameter stack.
Create listening points to bind to local transport addresses.
Configure routes and connect to Diameter peers.
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
.
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 callingDiameterStack.getDiameterListeningPoints()
.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 increateDiameterRoute()
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.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"); } // ... }
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:
Directly, by calling one of the different methods defined in both management interface.
Remotely, by registering the Diameter MBeans to a JMX agent using javax.management package. Only the JDK 1.5 provides this package.
There are two management interfaces defined in the oracle.sdp.diameterimpl
package:
DiameterStackImplMBean
: This interface represents the management API for an instance of the DiameterStack
interface.
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:
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.
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 requiredjavax.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
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.
This section includes the following topics:
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.
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.
The <vendor>
element defines a vendor by a name and associated IANA.
The <vendor>
attributes are:
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].
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.
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:
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.
The application name
attribute is the human-readable name of this application.
The application vendor
attribute is the name of the application vendor as previously defined in the <vendor>
element.
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.
A <command>
element defines the attributes for a command.
The <command>
attributes are:
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.
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:
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
.
The <avp>
element defines an AVP as described in RFC-3588.
The <avp>
attributes are:
The avp name
attribute is the human-readable name of this AVP.
The avp code
attribute defines the integer value used to encode the AVP for transmission on the network.
The avp mandatory
attribute defines whether the mandatory bit of this AVP should or should not be set. Possible values are "must" or "mustnot".
The avp protected
attribute defines whether the protected bit of this AVP should or should not be set. Possible values are "may" or "maynot".
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".
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.
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.
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:
The enum name
attribute is the text corresponding to a particular value for the attribute.
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>
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:
The gavp name
attribute must correspond to some existing AVP's name
attribute.
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".
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.
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(...); } }