HELL named JSMPP

  • Tutorial
Apparently, in the life of every programmer, there comes a time when it becomes necessary for him to learn how to send SMS messages. Yesterday, such a moment came to me. I must say right away that this need is in no way connected with advertising mailings and other spam. SMS-ki needed to be sent for purely peaceful purposes, as part of the response to events detected during equipment monitoring.

The importance of having such a newsletter is difficult to overestimate. Indeed, by sending a notification of the accident to the EMail contact person, we cannot count on an immediate response. It is not known when the recipient will read his mail. SMS is delivered much faster.

Our company has long and successfully used its own implementation of SMPP-service and the idea of ​​using a ready-made SMPP client in Java seemed logical to me. Having courageously driven the words “java smpp client” into the google search bar , I immediately found the library I needed . About what happened next, says my post today.

Since I use maven to build projects , first of all, I downloaded the latest version of the library from the site , uploaded it to the local repository with the following command:

mvn install:install-file -Dfile=jsmpp-2.1.0.jar -DgroupId=org.jsmpp -DartifactId=smpp -Dversion=2.1.0 -Dpackaging=jar

then created a pom file:

pom.xml
4.0.0com.acme.ae.tests.smppSMPPTest1.0.0jarsmppTest-${project.version}http://maven.apache.orgUTF-8${project.basedir}/src/main/resources${project.name}-${project.version}${resource_dir}org.jsmppsmpp2.1.0


The preparation of the test application looked quite standard:

Test.java
package com.acme.ae.tests.smpp;
public class Test {
	private void start() throws IOException {
	}
	private void stop() throws IOException {
	}
	private void test()  {
	}
	public static void main(String[] args) {
		Test t = new Test();
		try {
			try {
				t.start();
				t.test();
			} finally {
				t.stop();
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		}
	}
}


What followed was a study of an example kindly provided by the developers. A translation specification was used as a second source of inspiration .

To connect to the server, in full accordance with the above example, the following code was used:

Server connection
	...
	private SMPPSession session = null;
	private void start() throws IOException {
		session = new SMPPSession();
		session.connectAndBind(SMPP_IP, SMPP_PORT, 
				new BindParameter(
						BindType.BIND_TX, 
						SMPP_LOGIN, 
						SMPP_PASS, 
						"cp", 
						TypeOfNumber.UNKNOWN, 
						NumberingPlanIndicator.UNKNOWN, 
						null));
	}
	private void stop() throws IOException {
		if (session != null) {
			session.unbindAndClose();
		}
	}
	...


The purpose of the part of the parameters here is quite clear from the context. The remaining parameters were taken from the sample code unchanged. Since we are only going to send messages, we use BindType.BIND_TX.

The message transfer code (or rather the encoding task), taken from the example, refused to compile:

...
new GeneralDataCoding(Alphabet.ALPHA_DEFAULT, MessageClass.CLASS1, false)
...

After comparing the sources of the downloaded library (the latest available version 2.1.0) with the sources on GitHub , it turned out that the developers, for some reason I did not understand, changed the designer signature:

-    public GeneralDataCoding(boolean compressed, boolean containMessageClass,
-            MessageClass messageClass, Alphabet alphabet) {
+    public GeneralDataCoding(Alphabet alphabet, MessageClass messageClass,
+            boolean compressed) throws IllegalArgumentException {
            ...
    }

Since I used the old version, I had to make adjustments to the code (sender and recipient addresses in the code were changed):

Short Message Transfer
	...
	private static TimeFormatter timeFormatter = new AbsoluteTimeFormatter();	
	private void test() throws PDUException, ResponseTimeoutException, InvalidResponseException, NegativeResponseException, IOException {
		String messageId = session.submitShortMessage(
				"CMT", 
				TypeOfNumber.ALPHANUMERIC, 
				NumberingPlanIndicator.UNKNOWN, 
				"ACME", 
				TypeOfNumber.INTERNATIONAL, 
				NumberingPlanIndicator.ISDN, 
				"7XXXXXXXXXX", 
				new ESMClass(), 
				(byte)0, 
				(byte)1,  
				timeFormatter.format(new Date()), 
				null, 
				new RegisteredDelivery(SMSCDeliveryReceipt.DEFAULT), 
				(byte)0,
				new GeneralDataCoding(
						false,
						false,
						MessageClass.CLASS1, 
						Alphabet.ALPHA_DEFAULT),
				(byte)0, 
				"jSMPP simplify SMPP on Java platform".getBytes());
		System.out.println("Message submitted, message_id is " + messageId);
	}
	...


Running code for execution was unsuccessful. When trying to create an SMPPSession, an exception was thrown:

Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
	at org.jsmpp.session.AbstractSession.(AbstractSession.java:51)
	at com.amfitel.m2000.ae.tests.smpp.Test.start(Test.java:56)
	at com.amfitel.m2000.ae.tests.smpp.Test.main(Test.java:179)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
	at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
	... 3 more

Indeed, in GettingStarted there was a mean mention of the use of SLF4J . I had to add a dependency to pom.xml:

		...
		org.slf4jslf4j-api1.6.1
		...

The version number (1.6.1, not 1.4.3, as mentioned in GettingStarted) was taken from the pom file on GitHub. Now an unpleasant error began to be written to the log, but at least the connection with the server was established:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

Remark
The problem was solved by adding another dependency, also taken from the sources on GitHub :

		...
		org.slf4jslf4j-log4j121.6.1
		...

After adding it, a normal log began to form, in accordance with the settings specified in log4j.properties. Thanks to Googolplex for the tip.

Sending a message was also successful, but “squares” came to the phone, instead of letters. WireShark came to the rescue :

image

Our SMPP server developer argued that, in order to send messages normally in Latin, Data Coding should send 0. It must be said that the process of generating various numerical codes dictated by the specification when using the JSMPP API is not obvious. After a short meditation on the source code, the problem was solved:

				...
				new GeneralDataCoding(
						false,
						false,
-						MessageClass.CLASS1, 
+						MessageClass.CLASS0, 
						Alphabet.ALPHA_DEFAULT)
				...

Now it remained to learn how to send long messages and messages using the Cyrillic alphabet. To send long messages, I didn’t get smart with MESSAGE_PAYLOAD, using the approach proposed in the example from the developers.

To transmit the Cyrillic alphabet, it was necessary to change the encoding and translate the message into UCS-2. From a practical point of view, the latter resulted in the presentation of the text as a sequence of bytes encoded in UTF-16 (the difference between UCS-2 and UTF-16 can be read here ). As a result, the message sending code looked like this:

Transmission of a long Cyrillic message
GeneralDataCoding coding = new GeneralDataCoding(
	false,
	false,
	MessageClass.CLASS0, 
	Alphabet.ALPHA_UCS2);
final int totalSegments = 3;
Random random = new Random();
OptionalParameter sarMsgRefNum = OptionalParameters.newSarMsgRefNum((short)random.nextInt());
OptionalParameter sarTotalSegments = OptionalParameters.newSarTotalSegments(totalSegments);
for (int i = 0; i < totalSegments; i++) {
	final int seqNum = i + 1;
	String message = "Сообщение " + seqNum + " of " + totalSegments + " ";
	OptionalParameter sarSegmentSeqnum = OptionalParameters.newSarSegmentSeqnum(seqNum);
	String messageId = session.submitShortMessage(
		"CMT", 
		TypeOfNumber.ALPHANUMERIC, 
		NumberingPlanIndicator.UNKNOWN, 
		"ACME", 
		TypeOfNumber.INTERNATIONAL, 
		NumberingPlanIndicator.ISDN, 
		"7XXXXXXXXXX", 
		new ESMClass(), 
		SMPP_PROTOCOL_ID,   // (byte)0
		SMPP_PRIORITY_FLAG, // (byte)1
		null, 
		null, 
		new RegisteredDelivery(SMSCDeliveryReceipt.DEFAULT), 
		SMPP_REP_IF_P_FLAG, // (byte)0
		coding, 
		(byte)0, 
		message.getBytes(Charset.forName("UTF-16")),
		sarMsgRefNum, 
		sarSegmentSeqnum, 
		sarTotalSegments);
	System.out.println("Message submitted, message_id is " + messageId);
}


Concluding this article, I want to emphasize that it was not written to criticize JSMPP. The SMPP specification is complex in itself, and the developers of the library did everything possible to make the process of its use as easy as possible. Minor flaws in such a project are inevitable.

I wrote this post not with the aim of scolding someone, but only out of a desire to make it easier for other people to walk on the same rake that he himself went through. Please do not take my grumbling too seriously.

Also popular now: