Message intercepting

Contents

Introduction

If specific behavior of message parsing and formatting is required that cannot be done by configuration it is possible to intercept incoming and outgoing messages and modify the numbers and content of the messages, and apply rules based on numbers and message content. This can be done by extending the MessageInterceptor class and defining your custom logic.

The default behavior of the formating (between driver and controller) is defined in the class DefaultMessageInterceptorImpl. This class takes the parameters from the baseDriverConfig.properties file. (for more details on this please see the section standardizing Phone number properties The MessageInterceptor interface allows modification of any of the message parameters (origin number, destination number) as well as the text self. It also can be used to ignore messages that come from a gateway, or being send to a gateway, when the data is incorrect or security is compromised.

The 'handle' methods of the MessageInterceptor class are called before the driver delivers a message to the Controller, and just a message from the controller is taken by the Driver


Schema


Note: The 'handle' method is implemented in the base Driver class, which means that all derived Drivers (gateway, modem, simulator) will have access to the method.

Installation steps

We suggest to use a development environment (IDE) in order to write the class. This makes coding and problem solving much easier. We use Eclipse indigo for java.


Writing your own class

In order to your own class you create a new empty class and implement the MessageInterceptor interface, which requires to implement the following two methods:

If you need access to the attributes phoneWithAreaCodeLength, areaCodePrefix, and countryCode you can extend the class 'BaseMessageHandlerImpl'

Method: ControllerMessage

The method handle(ControllerMessage controllerMessage) is called when a message coming from the Controller is received by the Driver and the parameter of the message contains a message object with the attributes: from, to and text. The values of the attributes (variables) can be modified. The modifications will be dispatched to specific Drivers (Gateway, modem, Simulator).

Method: DriverMessage

The method handle(DriverMessage driverMessage) is called before the driver dispatchers a message towards the controller (coming from any specific Driver such as Gateway, Modem or Simulator). This means that the modifications (customizations) are done before the Controller processes the message.

Ignore messages

In some cases it is required to ignore (not process) certain incoming messages. For example messages coming from an origin numbers that are part of a black list. In this case you can launch a Exception of the type MessageProcessExcepcion (available for both methods: handle(ControllerMessage) and handle(DriverMessage)).

Note: In case you want to implement different logic/rules for phone number formating you might need to disable the option phoneNumbreConvert in the Controller config file (config.xml)

Example

The example is taking from a real life situation. A project that is using Cyclos has phone numbers of users stored with different lengths (10 to 12, depending on the region). The gateway/operators handle those phone numbers not a standard way. So you want to enforce a general format. As described above Cyclos can be configured to convert phone numbers. But this won't work for more complex situations. This example shows the solution.

Note: Be aware that this is an example of modifying the in and outgoing phone number format of the SMS. This is just one of the uses of message intercepting. Besides modifying number formating the message intercepting mechanism allows applying logic and modifying the SMS body.

Examples of processed messages: Message (incoming) from gateway (MO - Mobile Originated):

Message (notification) sent from Cyclos (MT - Mobile Terminated)


The digits “00” can be considered like the area code. The default implementation of the MessageInterceptor does not allow defining an area code when the phone number length can be variable. Because of this we will implement a custom MessageInterceptor that provides the following logic:

Example implementation

baseDriverConfig.properties
The area code (prefix) will need to be defined in the baseDriverconfig.properties file
phone.area.code.prefix=00

Access to values of property files (autowire)
The custom class will need to have access to the area prefix (phone.area.code.prefix). The value of this key is available in a Java bean called baseProperties. You can retrieve the key with the method 'setBaseProperties(Properties)'. (see code example below)


package nl.strohalm.cyclos.driver.message.interceptor;

import java.util.Properties;

import nl.strohalm.cyclos.controller.ControllerMessage;
import nl.strohalm.cyclos.driver.DriverMessage;
import nl.strohalm.cyclos.driver.message.MessageProcessException;

/**
 * This sample custom MessageInterceptor allow:
 * - Work with variable length in phone numbers.
 * - When a message arrive from specific driver (like gateway driver) this class add the PHONE_AREA_CODE_PREFIX_KEY value before send it to the controller.
 * - When a message arrive from the controller this class remove the PHONE_AREA_CODE_PREFIX_KEY value before to send it to the specific driver.
 */
public class SampleCustomMessageInterceptorImpl implements MessageInterceptor {
    private static final String PHONE_AREA_CODE_PREFIX_KEY = "phone.area.code.prefix";

    private String              phoneAreaCodePrefix        = "";

    /**
     * Remove phoneAreaCodePrexix on left of controllerMessage.from
     * @see nl.strohalm.cyclos.driver.message.interceptor.MessageInterceptor#handle(nl.strohalm.cyclos.controller.ControllerMessage)
     */
    @Override
    public void handle(final ControllerMessage controllerMessage) throws MessageProcessException {
        final int phonePrefixLength = phoneAreaCodePrefix.length();
        final String toPhone = controllerMessage.getMessage().getTo();
        if (phonePrefixLength > 0 && toPhone.length() >= phonePrefixLength && toPhone.startsWith(phoneAreaCodePrefix)) {
            controllerMessage.getMessage().setTo(toPhone.substring(phonePrefixLength));
        } else {
            String msg = "The controller message (trace: %1$s) TO phone number (%2$s) prefix, is not match with the prefix %3$s. The message will be ignored";
            msg = String.format(msg, controllerMessage.getMessage().getTraceData(), toPhone, phoneAreaCodePrefix);
            throw new MessageProcessException(msg, controllerMessage.getMessage());
        }
    }

    /**
     * Add phoneAreaCodePrexix on left of controllerMessage.from
     * @see nl.strohalm.cyclos.driver.message.interceptor.MessageInterceptor#handle(nl.strohalm.cyclos.driver.DriverMessage)
     */
    @Override
    public void handle(final DriverMessage driverMessage) throws MessageProcessException {
        driverMessage.getMessage().setFrom(phoneAreaCodePrefix + driverMessage.getMessage().getFrom());
    }

    /**
     * To get the phone area code prefix value we will use the baseProperties bean (it represent baseDriverConfig.properties file)
     * @param baseProperties
     */
    public void setBaseProperties(final Properties baseProperties) {
        phoneAreaCodePrefix = baseProperties.getProperty(PHONE_AREA_CODE_PREFIX_KEY, "");
    }
}