# Platform Development Studio - Developer’s Guide

# Service Interceptors

The following sections give a high-level overview of service interceptors and describe both the out-of-the-box interceptors that ship with Oracle Communications Services Gatekeeper and the process of developing your own custom interceptors:

- Overview
- Interceptor Decisions and Request Flow

- Decisions
- Flow Control
- Changing the invocation order

- Standard Interceptors

- Retry functionality for plug-ins

- Custom Interceptors

- Developing Custom Interceptors
- Deploying Custom Interceptors

## Overview

Interceptors are used to:

- Provide a mechanism to intercept and manipulate a request flowing through any arbitrary Communication Service in Oracle Communications Services Gatekeeper
- Supply an easy way to modify the request flow
- Simplify the routing mechanism for plug-ins
- Centralize policy and SLA enforcement

Some typical use cases for interceptors are to:

- Deny a request if the user does not subscribe to a particular service in the application layer.
- Deny a request if a PIN is not valid
- Verify that a request’s parameters are valid
- Perform argument manipulation like aliasing

A set of standard interceptors are provided out-of-the-box. Some are required, while others provide extra functionality. In addition, custom interceptors can be developed.

## Interceptor Decisions and Request Flow

An interceptor makes a decision whether to permit, deny, or stay neutral to a particular request: see Decisions. The Plug-in Manager is responsible for calling the first interceptor in the chain of interceptors as defined in the interceptor configuration file: see Flow Control. When changing the chain of interceptors, the interceptor module normally needs to be redeployed: see Changing the invocation order.

### Decisions

For application-initiated requests, the Plug-in Manager is called automatically by the service EJB for the application-facing interface. For network-triggered requests, the Plug-in Manager is called by an aspect that is woven prior to calling the service callback EJB for the application-facing interface.

Figure 11-1 illustrates where interceptors are triggered, both for application-initiated requests and for network-triggered.

Figure 11-1 Interceptors and the request flow

The interceptor chain is invoked at the point-cut that is a Java representation of the application-facing interface. Note that some application-initiated requests are not necessarily propagated to the network, and some network-triggered requests are not necessarily forwarded to the service callback client.

Each interceptor is responsible for deciding whether to continue to proceed down the chain of interceptors or to break it. The interceptor has two ways to break the chain, either to return or to abort.

Figure 11-2 Proceeding or breaking the interceptor chain

When the decision is to:

- Proceed, the request is passed on to the next interceptor in the chain and ultimately to the network protocol plug-in or to the application. When the request is returned from either one of these, the return path traverses the interceptors that were used in the calling path, making in possible to manipulate the request in the return path and ultimately return to the originator of the request, the application or the network node.
- Return, the request is rolled back through the previous interceptors using a regular return statement, making it possible for the previous interceptors to manipulate the request in the rollback path and ultimately return to the originator of the request, the application or the network node.
- Abort the request, is rolled back through each interceptors’ exception catch-block rather than returning in a regular mode.

- For application-initiated requests the exception is reported back to the application. It is possible to reuse the exception catalogue to map the exception thrown by the interceptor to an exception defined by the application-facing interface. `com.bea.wlcp.wlng.api.plugin.DenyPluginException` should be used by the interceptors for this scenario.
- For network-triggered requests, it is the responsibility of the plug-in to act on the thrown exception.

The interceptors have access to context data for the request. The actual data that is available depends on the context of the request. In general, the data that is available is the data that is defined by the application-facing interface, and includes the following items:

- The RequestContext for the request, including:

- Service provider account ID.
- Application account ID.
- Application User ID.
- Transaction ID.
- Session ID.
- A Java Map containing arbitrary request-specific data.

- The type of plug-in targeted by the request for (application-initiated requests).
- The type of object targeted by the request (network-triggered requests).
- The method targeted by the request.
- The arguments that will be used in the method targeted by the request.
- The set of RequestInfo available to the request, including:

- method name.
- arguments to the method.
- plug-in type.

- A list of plug-ins that matches the specified RequestInfo.
- The interception point: is the request is network-triggered or is it application-initiated.

The following data can be set by the interceptor:

- In the RequestContext:

- Session ID.
- Transaction ID.
- Java Map.

- A list of plug-ins that matches the specified RequestInfo.
- Arguments to the method.

### Flow Control

The invocation order of interceptors is defined in an XML-based configuration file that contains the interceptors: see Standard Interceptors.

Each interceptor is identified by the class name of the entry point of the interceptor, that is, the class that implements the Service Provider Interface (SPI) `Interceptor`.

The configuration file, which is expressed in XML, contains the tags described in Table 11-1.

Table 11-1 Description of interceptor configuration file
Tag | Description
--- | ---
<interceptor-config> | Main tag. Contains zero or more <position> tags.
<position> | Contains one or more <interceptor> tags. Has an attribute name which is either: MT_NORTH, which indicates that all <interceptor> tags encapsulated by this tag are valid for application-initiated (mobile terminated) requests. MO_NORTH, which indicates that all <interceptor> tags encapsulated by this tag are valid for network-triggered (mobile originated) requests. An interceptor may be present in both.
<interceptor> | Has the following attributes: class, which identifies the class for the interceptor implementation, see above. index, which indicates the invocation order relative to other interceptors within the same <position> tag. The order is ascending. Must be unique.

Listing 11-1 Example of an interceptor configuration file

    <?xml version="1.0" encoding="UTF-8"?>

    <interceptor-config xmlns="http://www.bea.com/ns/wlng/30"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.bea.com/ns/wlng/30 config.xsd">

      <position name="MT_NORTH">

        <interceptor class="com.bea.wlcp.wlng.interceptor.EnforceApplicationState" index="100"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.EnforceSpAppBudget" index="200"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.ValidateRequestUsingRequestFactory" index="300"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.CreatePluginList" index="400"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.RemoveInactivePlugin" index="500"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.FilterPluginListUsingCustomMatch" index="700"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.RoundRobinPluginList" index="800"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.EnforceNodeBudget" index="900"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.InvokeServiceCorrelation" index="1000"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.FindAndValidateSLAContract" index="1100"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.CreatePolicyData" index="1200"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.CheckMethodParametersFromSLA" index="1300"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.EnforceBlacklistedMethodFromSLA" index="1400"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.InjectValuesInRequestContextFromSLA" index="1500"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.EvaluateILOGPolicy" index="1600"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.InvokePlugin" index="1700"/>

      </position>

      <position name="MO_NORTH">

        <interceptor class="com.bea.wlcp.wlng.interceptor.EnforceApplicationState" index="100"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.InvokeServiceCorrelation" index="200"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.CreatePolicyData" index="300"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.EvaluateILOGPolicy" index="400"/>

        <interceptor class="com.bea.wlcp.wlng.interceptor.InvokeApplication" index="500"/>

      </position>

    </interceptor-config>

Each interceptor is responsible for calling the next interceptor in the chain, as opposed to being invoked by a delegator. This means that:

- For an application-initiated request, the interceptors can change and add request-specific data. This data is then propagated to the next interceptor and ultimately to the network protocol plug-in. When the request returns from the plug-in, the data can be changed as the request is returning through the invocation chain.
- For network-triggered request, the interceptors can change and add request-specific data. This data is then propagated to the next interceptor and ultimately to the application. When the request returns from the application, the data can be changed as the request is returning through the invocation chain.

This is useful for aliasing of data, where the interceptor anonymizes request data such as telephone numbers so that an application is not aware of the true subscriber telephone number.

For application-initiated requests, the last interceptor in the chain is responsible for calling the plug-in. The out-of-the-box interceptor InvokePlugin does this.

For network-triggered requests, the last interceptor in the chain is responsible for calling the callback service EJB, which calls the application. The out-of-the-box interceptor InvokeApplication does this.

In either scenario, the first interceptor is called by the Plug-in Manager. The Plug-in Manager is, for application-triggered requests, invoked by the service EJB. For network triggered requests, the Plug-in Manager is invoked by an aspect applied to the north interface of the plug-in.

### Changing the invocation order

As described in Flow Control, the invocation order of the interceptors is defined in the interceptor configuration file, see Table 11-3.

To rearrange the invocation chain, explode the ear file, edit the config.xml file and change the attribute `index` in the tag <interceptor>. Repackage the ear file and deploy it.

To exclude an interceptor chain, explode the ear file and delete or comment out the `<interceptor>` tag for it. Repackage the ear file and deploy it.

Always use the `interceptors.ear` deployed on the Administration server as the master and use standard WebLogic procedures to redeploy the application `interceptor.ear` to all servers in the network tier cluster from the Administration server.

## Standard Interceptors

Below is a description of the interceptors that are available out of the box as a part of Oracle Communications Services Gatekeeper. The name of the interceptor in the configuration file is the fully qualified class name. That is, it is prefixed with `com.bea.wlcp.wlng.interceptor`.

Table 11-2 Out-of-the-box interceptors
Interceptor | Description
--- | ---
EnforceApplicationState
| Enforces the application state. Verifies that the application with which the request is related has established a session with Oracle Communications Services Gatekeeper.
EnforceSpAppBudget
| Enforces the budget defined in the service provider group SLA and application group SLA. Is related to the SLA tag <rate> in <methodRestrictions>: see Defining Service Provider Level and Application Level Service Agreements.
ValidateRequestUsingRequestFactory
| Validates the request using the RequestFactory corresponding to the type of plug-in the request is intended for. See description of the class RequestFactory.
CreatePluginList |
| Creates a list of plug-ins that are capable of handling the given request. Populates the RequestInfo object.
RemoveInactivePlugin
| Removes any plug-in that is not active from the current plug-in list. CreatePluginList must have been invoked prior to this.
FilterPluginListUsingCustomMatch
| Invokes the custom match method of each plug-in in the current plug-in list. The custom match method either removes the plug-in from the current plug-in list or marks it as required. CreatePluginList must have been invoked prior to this.
RemoveOptional
| Removes any plug-in that is marked as optional if there is a at least one marked as required in the current plug-in list. CreatePluginList must have been invoked prior to this.
RoundRobinPluginList
| Performs a round-robin of the list of available plug-ins. This is not a strict round-robin, but a function of the number of plug-ins that match the request and the number of destination or target addresses in the request. If these parameters are consistent, a true round-robin is performed. CreatePluginList must have been invoked prior to this.
EnforceNodeBugdet
| Enforces all settings in the service provider node SLA and global node SLA, including validity of the dates and the budgets. See Writing Node SLAs. EnforceSpAppBudget must have been invoked prior to this.
InvokeServiceCorrelation
| Invokes the service correlation feature, see Service Correlation.
FindAndValidateSLAContract
| Enforces the existence of application level and service provider level SLAs for the given request. It also verifies that the dates given in the SLA are current. See Defining Service Provider Level and Application Level Service Agreements.
CreatePolicyData
| Creates the policy request data object needed by other interceptors.
RemoveInvalidRoute
| Enforces the plug-in routing logic. CreatePluginList and CreatePolicyData must have been invoked prior to this.
CheckMethodParametersFromSLA
| Checks and enforces that the request parameters are allowed as specified in the service provider group and application group SLAs. Is related to the SLA tags <parameterName> and <parameterValue> in <methodParameters>, see Defining Service Provider Level and Application Level Service Agreements. FindAndValidateSLAContract and CreatePolicyRequest must have been invoked prior to this.
EnforceBlacklistedMethodFromSLA
| Enforces the method blacklist as specified in the service provider group and application group SLAs. Is related to the SLA tag <blacklistedMethod> in <methodAccess>. See Defining Service Provider Level and Application Level Service Agreements. FindAndValidateSLAContract must have been invoked prior to this.
InjectValuesInRequestContextFromSLA
| Adds any optional request context attribute as specified in the service provider group and application group SLAs. Is related to the SLA tags <attributeName>, <attributeValue>, and <contextAttribute> in <requestContext>. See Defining Service Provider Level and Application Level Service Agreements. FindAndValidateSLAContract must have been invoked prior to this.
EvaluateILOGPolicy
| Evaluates any custom ILOG policy rules. CreatePolicyData must have been invoked prior to this.
InjectXParamtersFromRequestContext
| Takes tunnelled parameters from the RequestContext and puts them in the the SOAP header of either a request to an application or in response to a request from an application.
InvokePlugin
| Invokes the plug-in(s). This should be the last interceptor for an application-initiated (mobile terminated) request. CreatePluginList must have been invoked prior to this.
InvokeApplication
| Invokes the Application via the service callback EJB. This should be the last interceptor for an network-triggered (mobile originated) request.
RetryPlugin
| Performs retries of request. See Retry functionality for plug-ins. CreatePluginList must have been invoked prior to this.
EnforceNodeBudget
| Enforces budgets related to the global and service provider node SLAs. CreatePluginList must have been invoked prior to this.
EnforceSubscriberBudget
| Enforces budgets related to the Subscriber SLAs. CreatePluginList must have been invoked prior to this.
ResultFilter
| Applies result filters as specified in the service provider group and application group SLAs. Relates to the SLA tag <resultRestriction>. See Defining Service Provider Level and Application Level Service Agreements. InjectValuesInRequestContextFromSLA must have been invoked prior to this.

Note: | Some interceptors must be invoked before others can be invoked. A quick overview of the necessary sequences is seen in Figure 11-3

Figure 11-3 Required Interceptor Sequences

All out-of-the-box interceptors are classes packaged in `$OCSG_HOME/applications/interceptors.ear`.

Table 11-3 provides a description of the contents of this ear:

Table 11-3 Contents of interceptor.ear
Path | Content
--- | ---
`/`
| `dummy.war` Empty war file. Present in order to deploy the interceptors. Do not remove or change.
`/APP-INF/classes/`
| `config.xml` Interceptor configuration file. See Flow Control.
| `config.xsd` Schema for config.xml
`/APP-INF/classes/com/bea/wlcp/wlng/interceptor/`
| Classes for the out-of-the-box interceptors, see Table 11-2. Do not change the content of this directory.
`/APP-INF/classes/com/bea/wlcp/wlng/interceptor/deploy/`
| Infrastructure for the interceptor functionality. Do not change the content of this directory.
`META-INF/`
| `MANIFEST.MF` Manifest file for the interceptor infrastructure.
| `application.xml` Deployment descriptor. Do not edit or remove.
| `weblogic-application.xml` WebLogic extensions to `application.xml`. Do not edit or remove.
`WEB-INF/`
| No content.

### Retry functionality for plug-ins

The RetryPlugin interceptor handles retry functionality for plug-ins. The retry is attempted among the plug-ins that were chosen based on the data provided in the request. Retries are only performed among the plug-ins in the same Oracle Communications Services Gatekeeper instance.

The RetryPlugin is triggered when a plug-in throws a RetryPluginExeption. This exception is captured by the RetryPlugin interceptor, which removes the plug-in that threw the exception from the list of chosen plug-ins and calls the next interceptor in the chain.

The different decision scenarios are described below.

If the RequestInfo objects in the RequestContext are associated with: | The RetryPlugin interceptor:
--- | ---
PluginHolder objects that are marked as optional | removes the failed RequestInfo from the RequestContext and the next interceptor in the chain is invoked.
PluginHolder objects that are marked as required | treats the request itself as failed. No retry is performed, and an exception is thrown.
some PluginHolder objects that are marked as optional, and some that are marked as required | removes the RequestInfo objects that are associated with the PluginHolder objects that are marked as optional from the RequestContext and the next interceptor in the chain is invoked.

The following out-of-the-box plug-in throws the RetryPluginException:

- Subscriber Profile/LDAPv3.

Custom plug-ins can use the infrastructure for retries as provided by the RetryPlugin interceptor. This exception should be thrown if the communication with the underlying network node fails, or if an unexpected error is reported back from the plug-in.

## Custom Interceptors

### Developing Custom Interceptors

An interceptor implements the interface `com.bea.wlcp.wlng.api.interceptor.Interceptor`.

This interface defines the method:

    Object invoke(com.bea.wlcp.wlng.api.interceptor.Context context) throws Exception;

The interceptor is responsible for invoking the next interceptor in the invocation chain using the method:

    Object com.bea.wlcp.wlng.api.interceptor.Context.invokeNext(Interceptor current) throws Exception;

Since the interceptors call each other, the normal case would be just to return the object that was returned by the called interceptor. But in some cases, the returned object may be changed in order, for example, to do aliasing.

The decisions within the interceptor are expressed in these ways:

- To proceed, continue down the invocation chain by calling the next interceptor.
- To break the chain due to a violation: for example a parameter in the request is out-of bounds, or that usage policies are violated. This aborts the request throwing a `PluginDenyException`.
- To break the chain because the request has been fulfilled (for example because there is no need to call the plug-in or the application in order to fulfill the needs of the request), simply return the request.

See Interceptor Decisions and Request Flow.

Note: | The interceptor must be thread safe.

Listing 11-2 illustrates a very basic interceptor.

Listing 11-2 Example interceptor

    import com.bea.wlcp.wlng.api.interceptor.Interceptor;

    public class SampleInterceptor implements Interceptor {

      private final int ABORT = 0;

      private final int RETURN = 1;

      public Object invoke(Context ctx) throws Exception {

        int decision = // Logic that evaluates the request and makes a decision.

        if (decision == ABORT) {

          throw new Exception();

        } else if (decision == RETURN) {

          Object returnValue = // Define a returnValue here if desired.

          return returnValue;

        } else {

          Object returnValue = ctx.invokeNext(this);

          // Define a new returnValue here if desired, for example for aliasing.

            return returnValue;

        }

      }

    }

All necessary classes are available in the package:

`com.bea.wlcp.wlng.api.interceptor` located in `$PDS_HOME/lib/api/wlng.jar`

As an alternative to embedding the interceptor in `interceptors.ear` and defining the invocation order in`/APP-INF/classes/config.xml` it is possible to put the new interceptor in a separate ear file. Using this alternative, the interceptor must register the interceptor using the `InterceptorManager`, which is retrieved using the `InterceptorManagerFactory`.

When registering the interceptor manually, data corresponding to the data set in `/APP-INF/classes/config.xml` in `interceptors.ear` is supplied as parameters to the method:

    void register(Interceptor interceptor, InterceptionPoint ip, int index);

in the `InterceptorManager` interface.

The attribute name in the tag <position> corresponds to the argument ip, the attribute index in the tag <interceptor> corresponds to the argument index.

Listing 11-3 shows an example of how to register an interceptor manually.

Listing 11-3 Manually registering an interceptor

    InterceptorManager im = InterceptorManagerFactory.getInstance(); // Get manager

    im.register(myInterceptor,

                InterceptionPoint.MT_NORTH.MT_NORTH,

                myIndex); // Register

    im.update(); // Changes do not take effect until update() is called

### Deploying Custom Interceptors

To deploy the interceptor in the common interceptor ear file, explode the `interceptors.ear` file and put the class files for the interceptor in /APP-INF/classes. Add a new <interceptor> tag with the attribute `class` referring to the entry point of the interceptor and a numeric value in the attribute `index` that corresponds to the location in the interceptor invocation chain.

For example:

If the interceptor main class is `com.acompany.interceptor.DoStuff`, the class `DoStuff` should be inserted into `interceptors.ear` in `/APP-INF/classes/com/acompany/interceptor`, and the corresponding entry in `/APP-INF/classes/config.xml` shall be

    <interceptor class="com.acompany.interceptor.DoStuff" index="1150"/>

See Flow Control for more information about `/APP-INF/classes/config.xml`. See Standard Interceptors to get information about where in the invocation chain to insert the new interceptor.

If deploying the interceptor in a separate ear, always deploy it using the Administration server and use standard WebLogic procedures to deploy the application to all servers in the cluster from the Administration server.