Short Message Peer-to-Peer (SMPP) is a protocol used by the telecommunications industry for exchanging messages between Short Message Service Centers (SMSC) and/or External Short Messaging Entities (ESME).

Introduction

In this post I will build a simple app that will send SMS using the SMPP protocol, bootstraped with Spring Boot and Camel. To use CloudHopper to send SMS refer to the CloudHopper Post.

SMPP is a level-7 TCP/IP protocol, which allows fast delivery of SMS messages. The most conmmonly used versions of SMPP are v3.3, the most widely supported standard, and v3.4, which adds transceiver support (single connections that can send and receive messages).

Prerequisites

.
|__src/
|  |__main/
|  |  |__java/
|  |  |  |__com/
|  |  |  |  |__juliuskrah/
|  |  |  |  |  |__smpp/
|  |  |  |  |  |  |__Application.java
|  |  |  |  |  |  |__MessageReceiver.java
|  |  |  |  |  |  |__MessageRoute.java
|  |  |__resources/
|  |  |  |__application.yaml
|__pom.xml

Project Setup

Create a simple Spring Boot Application by heading over to start.spring.io. Add the following to your pom.xml (assuming you picked maven) after creating your Spring-Boot application:

<!-- Sections omitted -->
<properties>
  <java.version>11</java.version>
  <camel-spring.version>3.1.0</camel-spring.version>
</properties>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.camel.springboot</groupId>
      <artifactId>camel-spring-boot-dependencies</artifactId>
      <version>${camel-spring.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-smpp-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-bean-starter</artifactId>
  </dependency>
</dependencies>

Configuring Came SMPP

SMPP using camel is very covenient, as it provides some auto-configuration for Spring. We need the following configuration to get started:

file: src/main/resources/application.yaml

camel:
  component:
    enabled: true
    smpp:
      configuration:
        host: :smpp.host:
        password: :password:
        system-id: :user:
        source-addr-ton: 0x05
        source-addr-npi: 0x01
        dest-addr-npi: 0x01
        dest-addr-ton: 0x01
        registered-delivery: 1
    bean:
      scope: singleton
  springboot:
    name: smpp-app
    main-run-controller: false

NOTE: Be sure to replace the values for host, system-id and password. You can get these values from your SMPP service provider.

That’s all we need. Now let’s add code to send an SMS

Send an SMS

We will add a simple method that sends an SMS on application bootstrap:

private Exchange sendTextMessage(ProducerTemplate template, String sourceAddress,
      String destinationAddress, String message) {
    var exchange = ExchangeBuilder.anExchange(context)
        .withHeader("CamelSmppDestAddr", List.of(destinationAddress))
        .withHeader("CamelSmppSourceAddr", sourceAddress)
        .withPattern(ExchangePattern.InOnly)
        .withBody(message).build();
    
    // exceptions are not thrown from this method
    // exceptions are stored in Exchange#setException()
    return template.send("smpp://{{camel.component.smpp.configuration.host}}", exchange);
    
}

With this method created, we can call it on application startup with a CommandlineRunner

@Bean
CommandLineRunner init(ProducerTemplate template) {
    return args -> {
        var exchange = sendTextMessage(template, "5432", "<replace with phone number>", "Hello World!");
        if (exchange.getException() == null)
            log.info("Message Id - {}", exchange.getMessage().getHeader("CamelSmppId"));
        else
            log.error("Could not send message", exchange.getException());
    };
}

Putting it all together:

file: src/main/java/com/juliuskrah/smpp/Application.java

@SpringBootApplication
public class Application {
    private static final Logger log = LoggerFactory.getLogger(Application.class);
    @Autowired private CamelContext context;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    private Exchange sendTextMessage(ProducerTemplate template, String sourceAddress,
        String destinationAddress, String message) {
        var exchange = ExchangeBuilder.anExchange(context)
            .withHeader("CamelSmppDestAddr", List.of(destinationAddress))
            .withHeader("CamelSmppSourceAddr", sourceAddress)
            .withPattern(ExchangePattern.InOnly)
            .withBody(message).build();
        
        // exceptions are not thrown from this method
        // exceptions are stored in Exchange#setException()
        return template.send("smpp://{{camel.component.smpp.configuration.host}}", exchange);
        
    }

    @Bean
    CommandLineRunner init(ProducerTemplate template) {
        return args -> {
            var exchange = sendTextMessage(template, "5432", "<replace with phone number>", "Hello World!");
            if (exchange.getException() == null)
                log.info("Message Id - {}", exchange.getMessage().getHeader("CamelSmppId"));
            else
                log.error("Could not send message", exchange.getException());
        };
    }

}

Receive delivery receipts

After sending an SMS you may want to receive delivery receipts. To do this, we will create a bean to process the delivery receipts.

file: src/main/java/com/juliuskrah/smpp/MessageReceiver.java

@Component
public class MessageReceiver {
    private final static Logger log = LoggerFactory.getLogger(MessageReceiver.class);

    public void receive(Exchange exchange) {
        if (exchange.getException() == null) {
            var message = exchange.getIn();
            log.info("Received id {}", message.getHeader("CamelSmppId"));
            log.info("Text :- {}", message.getBody());
            log.info("Total delivered {}", message.getHeader("CamelSmppDelivered"));
            log.info("Message status {}", message.getHeader("CamelSmppStatus"));
            log.info("Submitted date {}", message.getHeader("CamelSmppSubmitDate", Date.class));
            log.info("Done date {}", message.getHeader("CamelSmppDoneDate", Date.class));
        } else
            log.error("Error receiving message", exchange.getException());
    }
}

Next we will create a router to route messages from the SMSC to our bean:

file: src/main/java/com/juliuskrah/smpp/MessageRoute.java

@Component
public class MessageRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("smpp://{{camel.component.smpp.configuration.host}}")
                .to("bean:messageReceiver?method=receive");
        
    }   
}

Conclusion

In this post we looked briefly at the SMPP protocol and its usage within the JVM. We also covered how to use Spring Boot and Camel to make developing an SMPP client easier.

As usual you can find the full example to this guide in the github repository. Until the next post, keep doing cool things :+1:.