The following section describe aspects and generation of EDRs, alarms, CDRs, and statistics:
Aspects allow developers to manage cross-cutting concerns in their code in a straight forward and coherent way. Aspects in Network Gatekeeper (pointcuts, advice, etc.) are written in the AspectJ 1.5.3 annotation style. There is already support for editing annotations in many modern IDEs, and aspects are simply set up as annotated classes.
All aspects are applied at build time by weaving the byte code of previously complied Java packages. Minimal reflection is used at runtime to make aspect based decisions.
Different aspect types are applicable to different Network Gatekeeper modules. In general there are two categories of aspects:
Note: | In this case, traffic flow is defined to include only plug-in implementations. |
Traffic aspects are subdivided into two categories:
Only statistics aspects are always applied because they are used in to enforce licensing. Traffic aspects are applied to North and South boundaries of a plug-in as well as to the internal processing of the plug-in.
Annotations are used to control the aspects that are not always applied for each plug-in. These annotations are defined as part of the functional areas that a given set of aspect implements. They allow the plug-in to communicate with the aspects as well as to customize their behavior.
The Context aspect is woven at compile time, using PluginSouth as a marker.
While requests coming from the north interface have a valid context (with attributes like Service provider account ID, application Account ID, and so on) any events triggered by the network and entering a plug-in’s south interface do not have a valid context.
The Context aspect solves this problem by rebuilding the context as soon as a south interface method is invoked: after this aspect is executed, a valid context will be available for any subsequent usages, such as the EDR aspect. All methods inside a class implementing the interface PluginSouth are woven by the Context aspect.
The Context aspect requires the following in order to correctly weave the south interface methods and be able to rebuild the context:
The following rules apply for methods in classes that implement PluginNorth:
The following rule applies for methods in classes that implement PluginSouth:
resolveAppInstanceGroupdId(ContextMapperInfo info)
in PluginSouth and return the application instance ID that corresponds to the original request to the network.The ways of doing this are plug-in-specific, but normally a network triggered request is tied to an application instance in a store that is managed by the plug-in. The store used for context mapping may be a local cache or a cluster wide store, depending on wether responses are known to always arrive on the same plug-in instance, or if they can arrive to a plug-in on another server in the cluster.
resolveAppInstanceGroupId()
is called by aspects.
In the example below the method deliver(...)
is a request from the underlying network. The destinationAddress is annotated to be available by the aspect that handles network-triggered requests associated with this request represented by constant C.
NotificationHandler handles the store for notifications and supplies all necessary parameters to the store.
protected static final String C = "destinationAddress";
@Edr
public void deliver(String data,
@ContextKey(EdrConstants.FIELD_DESTINATION_ADDRESS)
@MapperInfo(C) String destinationAddress,
@ContextKey(EdrConstants.FIELD_ORIGINATING_ADDRESS) String originatingAddress,
String nwTransactionId)
throws Exception {
notificationHandler.deliver(data, destinationAddress, originatingAddress, nwTransactionId);
}
When a network triggered event occurs, the aspect calls resolveApplicationInstanceGroup(...)
in PluginSouth and the plug-in looks up the application instance using any argument available in ContextMapperInfo that can help the plug-in to resolve this ID from ContextMapperInfo, using info.getArgument(C)
. The application instance ID is returned to the aspect and the execution flow continues in the plug-in, with a RequestContext that contains the application instance ID, session ID and so on.
protected static final String C = "destinationAddress";
public String resolveAppInstanceGroupdId(ContextMapperInfo info) {
String destinationAddress = (String) info.getArgument(C);
NotificationData notificationData = null;
try {
notificationData = StoreHelper.getInstance().getNotificationData(destinationAddress);
} catch (StorageException e) {
return null;
}
if (notificationData == null) {
return null;
}
return notificationData.getAppInstanceGroupId();
}
Below are the steps you have to take to make your plug-in compliant with the Context aspect:
resolveAppInstanceGroupdId()
method for each PluginSouth instance.resolveAppInstanceGroupId()
or the prepareRequestContext()
methods. All the annotated parameters will be available in the ContextMapperInfo parameter. The aspect needs to have them annotated to be able to store them into the ContextMapperInfo object.
EDRs are generated in the two following ways:
EDRs should be generated at the plug-in boundaries (north and south), using the @Edr annotation to ensure that the boundaries are covered. Additional Edrs can be added elsewhere in the plug-in if needed: for example for CDRs.
For extensions, the EDR ID should be in the range 500 000 to 999 999.
EDRs are generated automatically by an aspect in the following locations in the plug-in:
Note: | Note that aspects are not applied outside the plug-in. |
The following values are always available in an EDR when it is generated from an aspect:
Exceptions are automatically woven by the aspect.
The diagram illustrates typical scenarios when a library (or core service) throws an exception in the plug-in.
The plug-in method in Stage 2 simply catches the exception but does not re-throw it or throw another exception. Since it just consume the exception, the aspect will not trigger an EDR.
The plug-in method in Stage 2 lets the exception A propagate (or re-throw exception A).
In this case, the aspect triggers an EDR after the method in stage 2. Since the same exception A (the same exception instance object) is propagated (or re-thrown), only the first method triggers an EDR.
This scenario is almost identical to scenario 2 except that the method in stage 1 is not throwing the exception A but another exception, named B. In this case, because B is not the same instance as A, the aspect will trigger another EDR after the method in stage 1.
In addition to the default values, an EDR also contains all the values put into the RequestContext using the putEdr()
method.
...
RequestContext ctx = RequestContextManager.getCurrent();
// this value will be part of any EDRs generated in the current request
ctx.putEdr("address", "tel:1234");
// this value will NOT be part of any EDRs since ctx.put(...) is used
ctx.put("foo", "bar");
...
Note: | Common key names are defined in the class com.bea.wlcp.wlng.api.edr.EdrConstants. |
When a parameter is a more complex object, it is possible to specify a translator that will take care of extracting the relevant information from this parameter.
The annotation is @ContextTranslate.
For example, the following method declares:
...
@Edr
public @ContextTranslate(ACContextTranslator.class) PlayTextMessageResponse playTextMessage(@ContextTranslate(ACContextTranslator.class) PlayTextMessage parameters) {
...
return response;
}
...
The Translator is a class implementing the ContextTranslator interface.
public class ACContextTranslator implements ContextTranslator {
public void translate(Object param, ContextInfo info) {
if(param instanceof PlayTextMessage) {
PlayTextMessage msg = (PlayTextMessage) param;
info.put("address", msg.getAddress().toString());
} else if(param instanceof PlayTextMessageResponse) {
PlayTextMessageResponse response = (PlayTextMessageResponse) param;
info.put("correlator", response.getResult());
} ...
}
}
The ContextTranslator class specified in the @ContextTranslate annotation is automatically instantiated by the aspect when needed. It is however possible to explicitly register it using the ContextTranslatorManager.
ContextTranslatorManager.register(ACContextTranslator.class.getName(), new ACContextTranslator());
Below is a summary of annotations to use.
Network Gatekeeper triggers EDRs automatically in all plug-ins where aspects have been applied. It is also possible to trigger EDRs explicitly. In this case, you will have to manually create and trigger the EDR by following these steps:
Below is an example of triggering an EDR from inside a plug-in.
public class SamplePlugin {
// Get the EdrDataHelper like a logger
private static final EdrDataHelper helper = EdrDataHelper.getHelper(SamplePlugin.class);
public void doSomething() {
...
// Create a new EdrData using the EdrDataHelper class to allow
// the WLNG to automatically populate some fields
EdrData data = helper.createData();
// Since we are creating the EdrData manually,
// we have to provide the mandatory fields.
// Note that the EdrDataHelper will provide most of them
data.setValue(EdrConstants.FIELD_SOURCE, EdrConstants.VALUE_SOURCE_METHOD);
data.setValue(EdrConstants.FIELD_METHOD_NAME, "doSomething");
// Log the EDR
EdrServiceFactory.getService().logEdr(data);
...
}
}
The following table describes the content of an EDR. It describes which values are mandatory, who is responsible for providing these values, and other information.
Note: | EDRs triggered by aspects will have all the mandatory fields provided by the aspect. |
The destination address(es) with scheme included (For example “tel:1234”). See Using send lists.
|
||
If more than one address need to be stored in the DestAddress field, use the following pattern. Both patterns described below can be used.
EdrData data = ...;
// If there is only one address
data.setValue(EdrConstants.FIELD_DESTINATION_ADDRESS, address);
// If there are multiple addresses
data.setValues(EdrConstants.FIELD_DESTINATION_ADDRESS, addresses);
If you are using the current RequestContext object, simply store a List of addresses. The EdrDataHelper will automatically take care of converting this to a List of Strings in the EdrData.
RequestContext ctx = RequestContextManager.getCurrent();
// If there is only one address
ctx.putEdr(EdrConstants.FIELD_DESTINATION_ADDRESS, address);
// If there are multiple addresses
URI[] addresses = ...;
ctx.putEdr(EdrConstants.FIELD_DESTINATION_ADDRESS, Arrays.asList(addresses));
The following diagram shows how and where information for the EDR is added to the RequestContext and how it finally ends up in the additional info column of the alarm and CDR databases.
There are 3 ways of putting information in the RequestContext that will end up in the EDR (more precisely in the EdrData object):
putEdr()
API of the RequestContextprepareRequestContext()
method. When an EDR is created, the EdrDataHelper (which is the recommended way to create the EDR) will populate the EdrData with all the key/value pairs found in the RequestContext.
When the EdrService writes the alarm or CDR additional information content into the database, it will use all the EdrData key/value pairs EXCEPT a set of well-known keys that are either not relevant or already included in other columns of the database, see Alarm content and CDR content.
Only one type of EDR exists: alarms and CDRs are subsets of this EDR type. In order to categorize the flow of EDRs as either a pure EDRS, an alarm or a CDR, the EDR service uses 3 descriptors:
These XML descriptors can be manipulated using the EDR Configuration Pane as described in Managing and Configuring EDRs, CDRs and Alarms in the System Administrator’s Guide. File representations of these must be included in edrjmslistener.jar if using external EDR listeners.
Each descriptor contains a list of EDR descriptors that define an EDR as a pure-EDR, as an alarm or as a CDR.
The descriptor is composed of two parts:
The following table describes the elements allowed in the <filter> part:
The following table describes the values allowed for each element of the <filter> part:
Method name. The arguments can be omitted with the parenthesis. See Special characters below.
|
|||
Fully qualified class name. See Special characters below.
|
|||
Fully qualified exception class name. See Special characters below.
|
|||
Fully qualified class name where the exception was triggered. See Special characters below.
|
|||
Method name. The arguments can be omitted with the parenthesis See Special characters below.
|
|||
The filter uses special characters to indicate more precisely how to match certain values.
Using * at the end of a method, class or exception name matches all names that match the string specified prior to the * (that is, what the string starts with).
Note: | The usage of any of these characters disables the caching of the filter containing them. To avoid a performance hit, using the other way of matching is strongly encouraged. |
The exact value in these fields depends on who triggered the EDR. If the aspect triggered the EDR, then the name of the method (with return type and parameters) or the fully qualified name of the class/exception is indicated. If the EDR is manually triggered from the code, it is up to the implementer to decide what name to use. Here are some examples of fully qualified method/class names as specified by the aspect:
SendSmsResponse sendSms(SendSms)
void receivedMobileOriginatedSMS(NotificationInfo, boolean, SmsMessageState, String, SmsNotificationRemote)
TpAppMultiPartyCallBack reportNotification(TpMultiPartyCallIdentifier, TpCallLegIdentifier[], TpCallNotificationInfo, int)
com.bea.wlcp.wlng.plugin.sms.smpp.SMPPManagedPluginImpl
The following diagram shows briefly how the filter works:
The following filter will categorize EDRs as pure EDRs with an id of 1000 when the following conditions are met:
<edr id="1000" description="...">
<filter>
<method>
<class>com.bea.wlcp.wlng.plugin.AudioCallPlugin</class>
<direction>south</direction>
<interface>north</interface>
<position>after</position>
</method>
</filter>
</edr>
The following filter will categorize EDRs into alarms when the following conditions are met:
The alarms descriptor has a <alarm-group> element that is used to group alarms by service/source: this group id and each individual alarm id is used to generate the OID of SNMP traps.
<alarm-group id="104" name="parlayX" description="Parlay X alarms">>
<alarm id="1000" severity="minor" description="Parlay X exception">
<filter>
<exception>
<name>com.bea.wlcp.wlng.plugin.PluginException</name>
<name>org.csapi*</name>
</exception>
</filter>
</alarm>
</alarm-group>
The following filter will categorize EDRs into alarms when the following conditions are met:
If the filter determines that the EDR is an alarm, the following attributes are available to the alarm listener (they are defined in the <data> part):
<alarm id="1000" severity="minor" description="Parlay X exception">
<filter>
<exception>
<name>com.bea.wlcp.wlng.plugin.PluginException</name>
<name>org.csapi*</name>
<class>com.bea.wlcp.wlng.plugin*</class>
<direction>north</direction>
</exception>
</filter>
<data>
<attribute key="identifier" value="123"/>
<attribute key="source" value="wlng_nt1"/>
</data>
</alarm>
The following filter (for example purposes only) will categorize EDRs into pure EDRs with the id 1002 when the following conditions are met:
<edr id="1002">
<filter>
<method>
<name>void play*</name>
<class>com.bea.wlcp.wlng.plugin.AudioCallPluginNorth</class>
<position>after</position>
</method>
<method>
<name>String getMessageStatus</name>
<class>com.bea.wlcp.wlng.plugin.AudioCallPluginNorth</class>
<position>before</position>
</method>
<exception>
<name>com.bea.wlcp.wlng.bar*</name>
<interface>north</interface>
</exception>
<exception>
<name>com.bea.wlcp.wlng.plugin.exceptionA</name>
<class>com.bea.wlcp.wlng.plugin.classD</class>
<method>void com.bea.wlcp.wlng.plugin.methodA</method>
<interface>north</interface>
</exception>
<attribute key="attribute_a" value="value_a"/>
<attribute key="attribute_b" value="value_b"/>
</filter>
</edr>
The following example shows a manually triggered EDR with its corresponding filter. The EDR is triggered using these lines.
// Declare the EdrDataHelper for each class
private static final EdrDataHelper helper = EdrDataHelper.getHelper(MyClass.class);
public void myMethodName() {
...
// Create a new EdrData. Use the EdrDataHelper class to allow the WLNG to automatically populate some fields
EdrData data = helper.createData();
// Because we are creating the EdrData manually, we have to provide the mandatory fields
data.setValue(EdrConstants.FIELD_SOURCE, EdrConstants.VALUE_SOURCE_METHOD);
data.setValue(EdrConstants.FIELD_METHOD_NAME, "myMethodName");
data.setValue("myKey", "myValue");
// Log the EDR
EdrManager.getInstance().logEdr(data);
...
}
This EDR can be filtered using the following filter (note the various way of identifying this EDR):
<edr id="1003">
<filter>
<!-- Match both method name and class name -->
<method>
<name>myMethodName</name>
<class>com.bea.wlcp.wlng.myClassName</class>
</method>
<!-- OR match only the method name (looser than matching also the class name) -->
<method>
<name>myMethodName</name>
</method>
<!-- OR match only the classname (looser than matching also the method name) -->
<method>
<class>com.bea.wlcp.wlng.myClassName</class>
</method>
<!-- OR match only the custom attribute -->
<attribute key="myKey" value="myValue"/>
</filter>
</edr>
Below is a list of steps to make your plug-in able to take advantage of the aspect EDR:
edrjmslistener.jar
needs to be updated on all the listeners. Add the contents of the EDR descriptors to edr.xml, CDR descriptor to cdr.xml, and alarm descriptor to alarm.xml. The xml files resides in the directory edr
in edrjmslistener.jar
.
Question: Is it possible to specify both exception and method name in the filter section?
<filter>
<method>
<name>internalSendSms</name>
</method>
<exception>
<name>com.bea.wlcp.wlng.plugin.sms.smpp.TooManyAddressesException</name>
</exception>
</filter>
Yes, make sure that the <method> element is before the <exception> element. Otherwise the XSD will complain.
Q: Is it possible to specify multiple method names?
Q: In some places I have methods re-throwing an exception. Is it possible to have only one of the methods generate the EDR and map that edr to an alarm?
myMethodA()throws MyException{
myMethodB();
}
myMethodB()throws MyException{
myMethodC();
}
myMethodC()throws MyException{
...
//on error
throw new MyException(“Exception text..”);
}
In this case, only the first exception will be caught by aspect. Or more precisely, they will all be caught by aspect but will only trigger an EDR for the first one, but not for the re-thrown ones (if they are the same, of course). So you don’t need to use the @NoEdr annotation for myMethodA and myMethodB.
Q: Will aspect detect the following exception?
try{
throw new ReceiverConnectionFailureException(message);
}catch(ReceiverConnectionFailureException connfail){
//EDR-ALARM-MAPPING
}
This exception will NOT be detected by the aspects. If you need to generate an EDR you will have to either manually create an EDR or call a method throwing an exception.
Q: Will EDRs for exceptions also work for private methods?
Yes, EDRs can work for any method.
Q: Will exceptions be disabled with the @NoEdr annotation?
Yes, with the @NoEdr annotation you will not get any EDRs, not even for exceptions.
Q: How can data from the current context be included in an alarm?
Can an alarm be generated in a request with more than 12 destination addresses? How can information be added to the alarm about how many addresses that were included in the request?
It is possible to specify some info in the alarm descriptor with something like
<data>
<attribute key="source" value="thesource"/>
</data>
. Can something be put in the RequestContext using the putEdr method and then get it into the alarm in some way?
Yes, add custom information by putting this information into the current RequestContext, as show below.
RequestContext ctx = RequestContextManager.getCurrent();
ctx.putEdr("address", "tel:1234");
This value is part of any EDRs generated in the current request.
The information will be available in the database in the additional_info column. Make sure you are putting in only relevant information.
Q: Is it possible to specify classname in the filtering section?
Yes, use the <class> tag inside <method> or <exception> in the filter.
<filter>
<exception>
<class>com.y.y.z.MyClass</class>
<name>com.x.y.z.MyException</name>
</exception>
</filter>
An alarm is a subset of an EDR. To generate an alarm, generate an EDR, either using one generated in aspects or programmatically and define the ID, and the descriptor of the alarm in the alarm descriptor.
The alarm ID, severity, description and other kind of attributes are defined in the alarm descriptor, see The EDR descriptor. For extensions, the alarm ID shall be in the range 500 000 to 999 999.
Note: | The alarm filter that provides the first match in the alarm descriptor is used for triggering the alarm. |
There are two ways to trigger an alarm:
Trigger an EDR as described in EDR Content. Then specify in the alarm descriptor the corresponding alarms.
private static final EdrDataHelper helper = EdrDataHelper.getHelper(MyClass.class);
...
EdrData data = helper.createData();
data.setValue(EdrConstants.FIELD_SOURCE, EdrConstants.VALUE_SOURCE_METHOD);
data.setValue(EdrConstants.FIELD_METHOD_NAME, "com.bea.wlcp.wlng.myMethod");
data.setValue("myAdditionalInformation", ...);
EdrManager.getInstance().logEdr(data);
...
The corresponding entry in the alarm descriptor that matches this EDR is shown below.
<alarm id="2006"
severity="major"
description="Sample alarm">
<filter>
<method>
<name>com.bea.wlcp.wlng.myMethod</name>
<class>com.bea.wlcp.wlng.myClass</class>
</method>
</filter>
</alarm>
Below is a list of the information provided in alarms.
A CDR is a subset of an EDR. To generate a CDR, generate an EDR and define the ID of the EDR in the CDR descriptor.
There are two ways to trigger a CDR:
If none of the existing EDR is appropriate for a CDR, you can programmatically trigger an EDR that will become a CDR. See the section, Trigger an EDR programmatically for information on how to create and trigger an EDR. Specify in the CDR descriptor the description necessary for this EDR to be considered a CDR.
private static final EdrDataHelper helper = EdrDataHelper.getHelper(MyClass.class);
...
EdrData data = helper .createData();
data.setValue(EdrConstants.FIELD_SOURCE, EdrConstants.VALUE_SOURCE_METHOD);
data.setValue(EdrConstants.FIELD_METHOD_NAME, "com.bea.wlcp.wlng.myEndOfRequestMethod");
// Fill the required fields for a CDR
data.setValue(EdrConstants.FIELD_CDR_START_OF_USAGE, ...);
...
EdrManager.getInstance().logEdr(data);
...
The description, in the CDR descriptor, that matches this EDR is shown below.
<cdr>
<filter>
<method>
<name>com.bea.wlcp.wlng.myEndOfRequestMethod</name>
<class>com.bea.wlcp.wlng.myClass</class>
</method>
</filter>
</cdr>
In addition to the EDR fields, the following table lists the specific fields used only for CDRs.
Same pattern applies as for send lists, see Using send lists.
|
|
The CDR content is aligned toward the 3GPP Charging Applications specifications. As a result the database schema has been changed to accommodate these ends and to facilitate future extensions.
The EDR populates the additional_info column of the DB with all the custom key/value pairs found in the EdrData except the ones listed below.
Excluded keys (EdrConstants if not specified):
Two keys not present in the EdrData are added to additional_info.
The format of the additional_info field is formatted as:
key=value\n
similar to the Java properties file.
It is difficult to come up with a CDR generation scheme which will fulfill the requirements of all customers. Network Gatekeeper generates a default set of CDRs which can be customized by re-configuring the CDR descriptor.
The guiding principle for deciding when to generate CDRs is:
In other words, after the last method, in a potential sequence of method calls, returns.
For network-triggered requests this means that you should a CDR at the south interface after the method has returned back to the network. For application-triggered requests generate a CDR at the north interface after the method has returned to the Network Tier SLSB.
Aspects are also used to generate statistics. To add a new statistic type to your Communication Service requires two steps:
addStatisticType
operation in the Management Console. For more information, see “Managing and Configuring Statistics and Transaction Licenses” in the System Administration Guide.PluginNorth
. By default extension Communication Services generate information identified with the transaction type TRANSACTION_TYPE_EXTENSION. To generate more specific types, annotate your code with @Statistics(id=<My_Statistics_Type>)
For extensions, the statistics ID shall be in the range 1000 to 2250.