Message intercepting

From Complete Cyclos documentation wiki
Jump to: navigation, search

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.

  • Create a new Java project in Eclipse menu file->new->Project->Java project.
  • Include the following files from an SMS AIO installation and add them to the new project (select the new project in Eclipse and go to: menu project->properties->Java Build Path->add external jars):
    • SMS AIO, WEB-INF/lib/controller-driver_*.jar
    • SMS AIO, WEB-INF/lib/driver-common_*.jar
  • Create a new class, for example with the name ‘CustomInterceptor’ (any name will do). Make sure the new class implements de MessageInterceptor (available in the package nl.strohalm.cyclos.driver.message.interceptor od de driver-common_*.jar library).
  • In Eclipse menu File->new->class you can add the MessageInterceptor interface
  • Write the code the required logic in the methods handle(DriverMessage) and handle(ControllerMessage).
  • Compile the project by selecting the project Project->Build project
  • Copy the generated class file (e.g. CustomInterceptor.class) to your installation environment SMSAIO/WEB-INF/classes. The generated class will be in the directory ‘bin’. If you have problems locating it you search for the generated .class file in Eclipse by using with the Eclipse navigator (menu Window->Show View->Navigator)
  • In the file baseDriverconfig.properties of your installation change the value of property messageInterceptor.class to your custom class. For more details please refer to the documentation of the file baseDriverConfig.properties


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:

  • public void handle(ControllerMessage controllerMessage)
  • public void handle(DriverMessage driverMessage)

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):

  • Phone number received by gateway: 12345678901
  • Phone number registered in Cyclos: 0012345678901

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

  • Phone number registered in Cyclos: 002098765432
  • Phone number that needs to be passed to gateway: 2098765432


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:

  • Will support different phone lengths (stored in Cyclos)
  • Will add a area prefix to the incoming messages from the gateway (MO)
  • Will remove the area prefix from the outgoing message to the gateway (MT)

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, "");
    }
}