diff --git a/.gitignore b/.gitignore
index 7f275add..a53e58e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,9 @@ target/
.project
.settings/
+*.iml
+.idea/
+*.ipr
+*.iws
+/out/
+.idea_modules/
diff --git a/.travis.yml b/.travis.yml
index f8c1fdde..c16d2f86 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,3 @@
language: java
jdk:
- - openjdk6
+ - openjdk7
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 970b9a49..fd42ee28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@ Cloudhopper by Twitter
cloudhopper-smpp
----------------
## 6.0.0-netty4-beta-2
- - ch-commons-util version bumped from 6.0.1 to 6.0.2 to fix race condition bug
+ - ch-commons-util version bumped from 6.0.1 to 6.0.2 to fix race condition bug
in WindowFuture:
https://github.com/twitter/cloudhopper-smpp/issues/61
@@ -13,6 +13,16 @@ cloudhopper-smpp
on Trustin's review.
- Netty dependency changed to 4.0.25.Final.
+
+## 5.0.9 - 2015-11-24
+ - Add support for missing pdus and tags
+ - Allow unbound channel to respond to enquire_link PDU
+ - Corrected name of DataCoding SmppConstant to IA5 (not GSM)
+ - Updated value of DATA_CODING_GSM constant and made it deprecated
+ - Add ESM_CLASS Message Mode constants
+ - DefaultSmppServer should use IO executor passed to its constructor
+ - Fix-up comments on the SMPP error code constants
+
## 5.0.8 - 2015-04-17
- Fixed issue where rawErrorCode not set on DeliveryReceipt (khaing211)
- Support for host address in SmppServerConfiguration (pgoergler)
diff --git a/pom.xml b/pom.xml
index 7b821441..8d716a1e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
org.restcomm.smpp
ch-smpp
jar
- 6.0.0-netty4-beta-3-SNAPSHOT
+ 6.0.0-netty4-beta-4-SNAPSHOT
ch-smpp
Efficient, scalable, and flexible Java implementation of the Short Messaging Peer to Peer Protocol (SMPP)
https://github.com/Restcomm/cloudhopper-smpp
@@ -24,11 +24,13 @@
+ 1.6
+ 1.6
com.cloudhopper.smpp
7.0.6
7.0.6
7.0.6
- 4.0.28.Final
+ 4.1.16.Final
1.1.3
4.12
1.7.25
@@ -86,4 +88,4 @@
-
+
\ No newline at end of file
diff --git a/src/etc/SMPP_v5_0.pdf b/src/etc/SMPP_v5_0.pdf
new file mode 100644
index 00000000..07fbf625
Binary files /dev/null and b/src/etc/SMPP_v5_0.pdf differ
diff --git a/src/main/java/com/cloudhopper/smpp/SmppConstants.java b/src/main/java/com/cloudhopper/smpp/SmppConstants.java
index be45f4fc..d29502cd 100644
--- a/src/main/java/com/cloudhopper/smpp/SmppConstants.java
+++ b/src/main/java/com/cloudhopper/smpp/SmppConstants.java
@@ -46,6 +46,7 @@ public class SmppConstants {
public static final byte VERSION_3_3 = 0x33;
public static final byte VERSION_3_4 = 0x34;
+ public static final byte VERSION_5_0 = 0x50;
public static final int DEFAULT_WINDOW_SIZE = 1;
public static final long DEFAULT_WINDOW_WAIT_TIMEOUT = 60000;
@@ -79,8 +80,12 @@ public class SmppConstants {
public static final int CMD_ID_OUTBIND = 0x0000000B;
public static final int CMD_ID_ENQUIRE_LINK = 0x00000015;
public static final int CMD_ID_SUBMIT_MULTI = 0x00000021;
+ public static final int CMD_ID_ALERT_NOTIFICATION = 0x00000102;
public static final int CMD_ID_DATA_SM = 0x00000103;
-
+ public static final int CMD_ID_BROADCAST_SM = 0x00000111;
+ public static final int CMD_ID_QUERY_BROADCAST_SM = 0x00000112;
+ public static final int CMD_ID_CANCEL_BROADCAST_SM = 0x00000113;
+
//
// SMPP Command ID (Responses)
//
@@ -97,10 +102,14 @@ public class SmppConstants {
public static final int CMD_ID_ENQUIRE_LINK_RESP = 0x80000015;
public static final int CMD_ID_SUBMIT_MULTI_RESP = 0x80000021;
public static final int CMD_ID_DATA_SM_RESP = 0x80000103;
+ public static final int CMD_ID_BROADCAST_SM_RESP = 0x80000111;
+ public static final int CMD_ID_QUERY_BROADCAST_SM_RESP = 0x80000112;
+ public static final int CMD_ID_CANCEL_BROADCAST_SM_RESP = 0x80000113;
//
// Optional TLV Tags
//
+ public static final short TAG_SOURCE_TELEMATICS_ID = 0x0010;
public static final short TAG_PAYLOAD_TYPE = 0x0019;
public static final short TAG_PRIVACY_INDICATOR = 0x0201;
public static final short TAG_USER_MESSAGE_REFERENCE = 0x0204;
@@ -135,6 +144,8 @@ public class SmppConstants {
public static final short TAG_MORE_MSGS_TO_FOLLOW = 0x0426;
// Message State
public static final short TAG_MSG_STATE = 0x0427;
+ // Congestion State
+ public static final short TAG_CONGESTION_STATE = 0x0428;
// Callback Number Presentation Indicator
public static final short TAG_CALLBACK_NUM_PRES_IND = 0x0302;
// Callback Number Alphanumeric Tag
@@ -151,6 +162,38 @@ public class SmppConstants {
public static final short TAG_ITS_SESSION_INFO = 0x1383;
// USSD Service Op
public static final short TAG_USSD_SERVICE_OP = 0x0501;
+ // Broadcast Channel Indicator
+ public static final short TAG_BROADCAST_CHANNEL_INDICATOR = 0x0600;
+ // Broadcast Content Type
+ public static final short TAG_BROADCAST_CONTENT_TYPE = 0x0601;
+ // Broadcast Content Type Info
+ public static final short TAG_BROADCAST_CONTENT_TYPE_INFO = 0x0602;
+ // Broadcast Message Class
+ public static final short TAG_BROADCAST_MESSAGE_CLASS = 0x0603;
+ // Broadcast Rep Num
+ public static final short TAG_BROADCAST_REP_NUM = 0x0604;
+ // Broadcast Frequency Interval
+ public static final short TAG_BROADCAST_FREQUENCY_INTERVAL = 0x0605;
+ // Broadcast Area Identifier
+ public static final short TAG_BROADCAST_AREA_IDENTIFIER = 0x0606;
+ // Broadcast Error Status
+ public static final short TAG_BROADCAST_ERROR_STATUS = 0x0607;
+ // Broadcast Area Success
+ public static final short TAG_BROADCAST_AREA_SUCCESS = 0x0608;
+ // Broadcast End Time
+ public static final short TAG_BROADCAST_END_TIME = 0x0609;
+ // Broadcast Service Group
+ public static final short TAG_BROADCAST_SERVICE_GROUP = 0x060A;
+ // Source Network Id
+ public static final short TAG_SOURCE_NETWORK_ID = 0x060D;
+ // Dest Network Id
+ public static final short TAG_DEST_NETWORK_ID = 0x060E;
+ // Source Node Id
+ public static final short TAG_SOURCE_NODE_ID = 0x060F;
+ // Dest Node Id
+ public static final short TAG_DEST_NODE_ID = 0x0610;
+ // Billing Identification
+ public static final short TAG_BILLING_IDENTIFICATION = 0x060B;
// Originating MSC Address
public static final short TAG_ORIG_MSC_ADDR = (short)0x8081;
// Destination MSC Address
@@ -177,12 +220,20 @@ public class SmppConstants {
public static final short TAG_ADD_STATUS_INFO = 0x001D;
// Receipted Message ID
public static final short TAG_RECEIPTED_MSG_ID = 0x001E;
-
+ // MS Message Wait Facilities
+ public static final short TAG_MS_MSG_WAIT_FACILITIES = 0x0030;
/** ESM Class */
+ /** Message Mode (bits 1-0) */
+ public static final byte ESM_CLASS_MM_MASK = 0x03; // BIN 00000011
+ public static final byte ESM_CLASS_MM_DEFAULT = 0x00; // BIN 00000000
+ public static final byte ESM_CLASS_MM_DATAGRAM = 0x01; // BIN 00000001
+ public static final byte ESM_CLASS_MM_TRANSACTION = 0x02; // BIN 00000010
+ public static final byte ESM_CLASS_MM_STORE_FORWARD = 0x03; // BIN 00000011
+
/** Message Type (bits 5-2) */
- public static final byte ESM_CLASS_MT_MASK = (byte)0x1C; // BIN: 11100
+ public static final byte ESM_CLASS_MT_MASK = (byte)0x1C; // BIN: 11100
public static final byte ESM_CLASS_MT_SMSC_DELIVERY_RECEIPT = (byte)0x04; // BIN: 100, Recv Msg contains SMSC delivery receipt
public static final byte ESM_CLASS_MT_ESME_DELIVERY_RECEIPT = (byte)0x08; // BIN: 1000, Send/Recv Msg contains ESME delivery acknowledgement
public static final byte ESM_CLASS_MT_MANUAL_USER_ACK = (byte)0x10; // BIN: 10000, Send/Recv Msg contains manual/user acknowledgment
@@ -200,6 +251,7 @@ public class SmppConstants {
public static final byte REGISTERED_DELIVERY_SMSC_RECEIPT_NOT_REQUESTED = 0x00;
public static final byte REGISTERED_DELIVERY_SMSC_RECEIPT_REQUESTED = 0x01;
public static final byte REGISTERED_DELIVERY_SMSC_RECEIPT_ON_FAILURE = 0x02;
+ public static final byte REGISTERED_DELIVERY_SMSC_RECEIPT_ON_SUCCESS = 0x03;
// SME originated acknowledgement (bits 3 & 2)
public static final byte REGISTERED_DELIVERY_SME_ACK_MASK = 0x0c;
@@ -211,9 +263,9 @@ public class SmppConstants {
// Intermediate notification (bit 4)
// NOTE: SMPP 3.4 specs originally wrote (bit 5) but their matrix actually used bit 4
// the confirmed value is bit 4, not 5.
- public static final byte REGISTERED_DELIVERY_INTERMEDIATE_NOTIFICATION_MASK = 0x10;
- public static final byte REGISTERED_DELIVERY_INTERMEDIATE_NOTIFICATION_NOT_REQUESTED = 0x0;
- public static final byte REGISTERED_DELIVERY_INTERMEDIATE_NOTIFICATION_REQUESTED = 0x10;
+ public static final byte REGISTERED_DELIVERY_INTERMEDIATE_NOTIFICATION_MASK = 0x10;
+ public static final byte REGISTERED_DELIVERY_INTERMEDIATE_NOTIFICATION_NOT_REQUESTED = 0x00;
+ public static final byte REGISTERED_DELIVERY_INTERMEDIATE_NOTIFICATION_REQUESTED = 0x10;
// Replace if Present flag
@@ -275,7 +327,15 @@ public class SmppConstants {
// SMPP Data Coding
//
public static final byte DATA_CODING_DEFAULT = (byte)0x00; // SMSC Default Alphabet
- public static final byte DATA_CODING_GSM = (byte)0x01; // IA5 (CCITT T.50)/ASCII (ANSI X3.4)
+ public static final byte DATA_CODING_IA5 = (byte)0x01; // IA5 (CCITT T.50)/ASCII (ANSI X3.4)
+
+ /**
+ * @deprecated May be removed in a future version
+ * Please use IA5 for DCS 0x01 or DEFAULT for DCS 0x00
+ */
+ @Deprecated
+ public static final byte DATA_CODING_GSM = (byte)0x01;
+
public static final byte DATA_CODING_8BITA = (byte)0x02; // Octet unspecified (8-bit binary) defined for TDMA and/ or CDMA but not defined for GSM
public static final byte DATA_CODING_LATIN1 = (byte)0x03; // Latin 1 (ISO-8859-1)
public static final byte DATA_CODING_8BIT = (byte)0x04; // Octet unspecified (8-bit binary) ALL TECHNOLOGIES
@@ -302,153 +362,160 @@ public class SmppConstants {
/** Command Length is invalid */
public static final int STATUS_INVCMDLEN = 0x00000002;
-
- public static final int STATUS_INVCMDID = 0x00000003;
- // Invalid Command ID
- public static final int STATUS_INVBNDSTS = 0x00000004;
+ // Invalid Command ID
+ public static final int STATUS_INVCMDID = 0x00000003;
// Incorrect BIND Status for given command
-
- public static final int STATUS_ALYBND = 0x00000005;
+ public static final int STATUS_INVBNDSTS = 0x00000004;
// ESME Already in Bound State
-
- public static final int STATUS_INVPRTFLG = 0x00000006;
+ public static final int STATUS_ALYBND = 0x00000005;
// Invalid Priority Flag
-
- public static final int STATUS_INVREGDLVFLG = 0x00000007;
+ public static final int STATUS_INVPRTFLG = 0x00000006;
// Invalid Registered Delivery Flag
+ public static final int STATUS_INVREGDLVFLG = 0x00000007;
+ // System Error
+ public static final int STATUS_SYSERR = 0x00000008;
- public static final int STATUS_SYSERR = 0x00000008; // System Error
-
- // Reserved = 0x00000009 Reserved
+ // 0x00000009 Reserved
- public static final int STATUS_INVSRCADR = 0x0000000A;
// Invalid Source Address
-
- public static final int STATUS_INVDSTADR = 0x0000000B;
+ public static final int STATUS_INVSRCADR = 0x0000000A;
// Invalid Dest Addr
-
- public static final int STATUS_INVMSGID = 0x0000000C;
+ public static final int STATUS_INVDSTADR = 0x0000000B;
// Message ID is invalid
-
- public static final int STATUS_BINDFAIL = 0x0000000D; // Bind Failed
-
- public static final int STATUS_INVPASWD = 0x0000000E; // Invalid Password
-
- public static final int STATUS_INVSYSID = 0x0000000F;
+ public static final int STATUS_INVMSGID = 0x0000000C;
+ // Bind Failed
+ public static final int STATUS_BINDFAIL = 0x0000000D;
+ // Invalid Password
+ public static final int STATUS_INVPASWD = 0x0000000E;
// Invalid System ID
+ public static final int STATUS_INVSYSID = 0x0000000F;
- // Reserved = 0x00000010 Reserved
+ // 0x00000010 Reserved
- public static final int STATUS_CANCELFAIL = 0x00000011;
// Cancel SM Failed
+ public static final int STATUS_CANCELFAIL = 0x00000011;
- // Reserved = 0x00000012 Reserved
- public static final int STATUS_REPLACEFAIL = 0x00000013;
+ // 0x00000012 Reserved
// Replace SM Failed
- public static final int STATUS_MSGQFUL = 0x00000014;
-
+ public static final int STATUS_REPLACEFAIL = 0x00000013;
// Message Queue Full
+ public static final int STATUS_MSGQFUL = 0x00000014;
+ // Invalid Service Type
public static final int STATUS_INVSERTYP = 0x00000015;
- // Invalid Service Type
- // Reserved = 0x00000016-0x00000032
- public static final int STATUS_INVNUMDESTS = 0x00000033;
+ // 0x00000016-0x00000032 Reserved
// Invalid number of destinations
+ public static final int STATUS_INVNUMDESTS = 0x00000033;
+ // Invalid Distribution List name
public static final int STATUS_INVDLNAME = 0x00000034;
- // Invalid Distribution List name
- // Reserved = 0x00000035-0x0000003F
- public static final int STATUS_INVDESTFLAG = 0x00000040;
+ // 0x00000035-0x0000003F Reserved
// Destination flag is invalid (submit_multi)
- // Reserved = 0x00000041 Reserved
- public static final int STATUS_INVSUBREP = 0x00000042;
+ public static final int STATUS_INVDESTFLAG = 0x00000040;
- // Invalid submit with replace request
- // (i.e. submit_sm with replace_if_present_flag set)
- public static final int STATUS_INVESMCLASS = 0x00000043;
+ // 0x00000041 Reserved
+ // Invalid ‘submit with replace’ request
+ // (i.e. submit_sm with replace_if_present_flag set)
+ public static final int STATUS_INVSUBREP = 0x00000042;
// Invalid esm_class field data
- public static final int STATUS_CNTSUBDL = 0x00000044;
-
+ public static final int STATUS_INVESMCLASS = 0x00000043;
// Cannot Submit to Distribution List
+ public static final int STATUS_CNTSUBDL = 0x00000044;
+ // submit_sm or submit_multi failed
public static final int STATUS_SUBMITFAIL = 0x00000045;
- // submit_sm or submit_multi failed
- // Reserved = 0x00000046-0x00000047 Reserved
- public static final int STATUS_INVSRCTON = 0x00000048;
+ // 0x00000046-0x00000047 Reserved
// Invalid Source address TON
- public static final int STATUS_INVSRCNPI = 0x00000049;
-
+ public static final int STATUS_INVSRCTON = 0x00000048;
// Invalid Source address NPI
- public static final int STATUS_INVDSTTON = 0x00000050;
-
+ public static final int STATUS_INVSRCNPI = 0x00000049;
// Invalid Destination address TON
+ public static final int STATUS_INVDSTTON = 0x00000050;
+ // Invalid Destination address NPI
public static final int STATUS_INVDSTNPI = 0x00000051;
- // Invalid Destination address NPI
- // Reserved = 0x00000052 Reserved
- public static final int STATUS_INVSYSTYP = 0x00000053;
+ // 0x00000052 Reserved
// Invalid system_type field
- public static final int STATUS_INVREPFLAG = 0x00000054;
-
+ public static final int STATUS_INVSYSTYP = 0x00000053;
// Invalid replace_if_present flag
+ public static final int STATUS_INVREPFLAG = 0x00000054;
+ // Invalid number of messages
public static final int STATUS_INVNUMMSGS = 0x00000055;
- // Invalid number of messages
- // Reserved = 0x00000056-0x00000057 Reserved
+ // 0x00000056-0x00000057 Reserved
+
+ // Throttling error (ESME has exceeded allowed message limits)
public static final int STATUS_THROTTLED = 0x00000058;
- // Throttling error (ESME has exceeded allowed message
- // limits)
- // Reserved = 0x00000059-0x00000060 Reserved
- public static final int STATUS_INVSCHED = 0x00000061;
+ // 0x00000059-0x00000060 Reserved
// Invalid Scheduled Delivery Time
- public static final int STATUS_INVEXPIRY = 0x00000062;
-
+ public static final int STATUS_INVSCHED = 0x00000061;
// Invalid message validity period (Expiry time)
- public static final int STATUS_INVDFTMSGID = 0x00000063;
-
+ public static final int STATUS_INVEXPIRY = 0x00000062;
// Predefined Message Invalid or Not Found
- public static final int STATUS_X_T_APPN = 0x00000064;
-
+ public static final int STATUS_INVDFTMSGID = 0x00000063;
// ESME Receiver Temporary App Error Code
- public static final int STATUS_X_P_APPN = 0x00000065;
-
+ public static final int STATUS_X_T_APPN = 0x00000064;
// ESME Receiver Permanent App Error Code
- public static final int STATUS_X_R_APPN = 0x00000066;
-
+ public static final int STATUS_X_P_APPN = 0x00000065;
// ESME Receiver Reject Message Error Code
+ public static final int STATUS_X_R_APPN = 0x00000066;
+ // query_sm request failed
public static final int STATUS_QUERYFAIL = 0x00000067;
- // query_sm request failed
- // Reserved = 0x00000068-0x000000BF Reserved
- public static final int STATUS_INVOPTPARSTREAM = 0x000000C0;
+ // 0x00000068-0x000000BF Reserved
// Error in the optional part of the PDU Body.
- public static final int STATUS_OPTPARNOTALLWD = 0x000000C1;
-
+ public static final int STATUS_INVOPTPARSTREAM = 0x000000C0;
// Optional Parameter not allowed
- public static final int STATUS_INVPARLEN = 0x000000C2;
-
+ public static final int STATUS_OPTPARNOTALLWD = 0x000000C1;
// Invalid Parameter Length.
- public static final int STATUS_MISSINGOPTPARAM = 0x000000C3;
-
+ public static final int STATUS_INVPARLEN = 0x000000C2;
// Expected Optional Parameter missing
+ public static final int STATUS_MISSINGOPTPARAM = 0x000000C3;
+ // Invalid Optional Parameter Value
public static final int STATUS_INVOPTPARAMVAL = 0x000000C4;
- // Invalid Optional Parameter Value
- // Reserved = 0x000000C5-0x000000FD Reserved
- public static final int STATUS_DELIVERYFAILURE = 0x000000FE;
+ // 0x000000C5-0x000000FD Reserved
// Delivery Failure (used for data_sm_resp)
- public static final int STATUS_UNKNOWNERR = 0x000000FF; // Unknown Error
+ public static final int STATUS_DELIVERYFAILURE = 0x000000FE;
+ // Unknown Error
+ public static final int STATUS_UNKNOWNERR = 0x000000FF;
+ // ESME Not authorised to use specified service_type
+ public static final int STATUS_SERTYPUNAUTH = 0x00000100;
+ // ESME Prohibited from using specified operation
+ public static final int STATUS_PROHIBITED = 0x00000101;
+ // Specified service_type is unavailable
+ public static final int STATUS_SERTYPUNAVAIL = 0x00000102;
+ // Specified service_type is denied
+ public static final int STATUS_SERTYPDENIED = 0x00000103;
+ // Invalid Data Coding Scheme
+ public static final int STATUS_INVDCS = 0x00000104;
+ // Source Address Sub unit is Invalid
+ public static final int STATUS_INVSRCADDRSUBUNIT = 0x00000105;
+ // Destination Address Sub unit is Invalid
+ public static final int STATUS_INVDSTADDRSUBUNIT = 0x00000106;
+ // Broadcast Frequency Interval is invalid
+ public static final int STATUS_INVBCASTFREQINT = 0x00000107;
+ // Broadcast Alias Name is invalid
+ public static final int STATUS_INVBCASTALIAS_NAME = 0x00000108;
+ // Broadcast Area Format is invalid
+ public static final int STATUS_INVBCASTAREAFMT = 0x00000109;
+ // Numberof Broadcast Areas is invalid
+ public static final int STATUS_INVNUMBCAST_AREAS = 0x0000010A;
+ // Broadcast Content Type is invalid
+ public static final int STATUS_INVBCASTCNTTYPE = 0x0000010B;
+ // Broadcast Message Class is invalid
+ public static final int STATUS_INVBCASTMSGCLASS = 0x0000010C;
public static final Map STATUS_MESSAGE_MAP;
public static final Map TAG_NAME_MAP;
@@ -503,8 +570,22 @@ public class SmppConstants {
STATUS_MESSAGE_MAP.put(STATUS_INVOPTPARAMVAL, "Optional parameter value invalid");
STATUS_MESSAGE_MAP.put(STATUS_DELIVERYFAILURE, "Deliver SM failed");
STATUS_MESSAGE_MAP.put(STATUS_UNKNOWNERR, "Unknown error");
+ STATUS_MESSAGE_MAP.put(STATUS_SERTYPUNAUTH, "Not authorised to use specified service_type");
+ STATUS_MESSAGE_MAP.put(STATUS_PROHIBITED, "Prohibited from using specified operation");
+ STATUS_MESSAGE_MAP.put(STATUS_SERTYPUNAVAIL, "Specified service_type is unavailable");
+ STATUS_MESSAGE_MAP.put(STATUS_SERTYPDENIED, "Specified service_type is denied");
+ STATUS_MESSAGE_MAP.put(STATUS_INVDCS, "Invalid Data Coding Scheme");
+ STATUS_MESSAGE_MAP.put(STATUS_INVSRCADDRSUBUNIT, "Source Address Sub unit is Invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVDSTADDRSUBUNIT, "Destination Address Sub unit is Invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVBCASTFREQINT, "Broadcast Frequency Interval is invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVBCASTALIAS_NAME, "Broadcast Alias Name is invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVBCASTAREAFMT, "Broadcast Area Format is invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVNUMBCAST_AREAS, "Number of Broadcast Areas is invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVBCASTCNTTYPE, "Broadcast Content Type is invalid");
+ STATUS_MESSAGE_MAP.put(STATUS_INVBCASTMSGCLASS, "Broadcast Message Class is invalid");
TAG_NAME_MAP = new HashMap();
+ TAG_NAME_MAP.put(TAG_SOURCE_TELEMATICS_ID, "source_telematics_id");
TAG_NAME_MAP.put(TAG_PAYLOAD_TYPE, "payload_type");
TAG_NAME_MAP.put(TAG_PRIVACY_INDICATOR, "privacy_indicator");
TAG_NAME_MAP.put(TAG_USER_MESSAGE_REFERENCE, "user_message_reference");
@@ -529,6 +610,7 @@ public class SmppConstants {
TAG_NAME_MAP.put(TAG_DELIVERY_FAILURE_REASON, "delivery_failure_reason");
TAG_NAME_MAP.put(TAG_MORE_MSGS_TO_FOLLOW, "more_msgs_to_follow");
TAG_NAME_MAP.put(TAG_MSG_STATE, "message_state");
+ TAG_NAME_MAP.put(TAG_CONGESTION_STATE, "congestion_state");
TAG_NAME_MAP.put(TAG_CALLBACK_NUM_PRES_IND, "callback_num_pres_ind");
TAG_NAME_MAP.put(TAG_CALLBACK_NUM_ATAG, "callback_num_atag");
TAG_NAME_MAP.put(TAG_NUM_MSGS, "num_msgs_in_mailbox");
@@ -537,6 +619,22 @@ public class SmppConstants {
TAG_NAME_MAP.put(TAG_ITS_REPLY_TYPE, "its_reply_type");
TAG_NAME_MAP.put(TAG_ITS_SESSION_INFO, "its_session_info");
TAG_NAME_MAP.put(TAG_USSD_SERVICE_OP, "ussd_service_op");
+ TAG_NAME_MAP.put(TAG_BROADCAST_CHANNEL_INDICATOR, "broadcast_channel_indicator");
+ TAG_NAME_MAP.put(TAG_BROADCAST_CONTENT_TYPE, "broadcast_content_type");
+ TAG_NAME_MAP.put(TAG_BROADCAST_CONTENT_TYPE_INFO, "broadcast_content_type_info");
+ TAG_NAME_MAP.put(TAG_BROADCAST_MESSAGE_CLASS, "broadcast_message_class");
+ TAG_NAME_MAP.put(TAG_BROADCAST_REP_NUM, "broadcast_rep_num");
+ TAG_NAME_MAP.put(TAG_BROADCAST_FREQUENCY_INTERVAL, "broadcast_frequency_interval");
+ TAG_NAME_MAP.put(TAG_BROADCAST_AREA_IDENTIFIER, "broadcast_area_identifier");
+ TAG_NAME_MAP.put(TAG_BROADCAST_ERROR_STATUS, "broadcast_error_status");
+ TAG_NAME_MAP.put(TAG_BROADCAST_AREA_SUCCESS, "broadcast_area_success");
+ TAG_NAME_MAP.put(TAG_BROADCAST_END_TIME, "broadcast_end_time");
+ TAG_NAME_MAP.put(TAG_BROADCAST_SERVICE_GROUP, "broadcast_service_group");
+ TAG_NAME_MAP.put(TAG_SOURCE_NETWORK_ID, "source_network_id");
+ TAG_NAME_MAP.put(TAG_DEST_NETWORK_ID, "dest_network_id");
+ TAG_NAME_MAP.put(TAG_SOURCE_NODE_ID, "source_node_id");
+ TAG_NAME_MAP.put(TAG_DEST_NODE_ID, "dest_node_id");
+ TAG_NAME_MAP.put(TAG_BILLING_IDENTIFICATION, "billing_identification");
TAG_NAME_MAP.put(TAG_ORIG_MSC_ADDR, "orig_msc_addr");
TAG_NAME_MAP.put(TAG_DEST_MSC_ADDR, "dest_msc_addr");
TAG_NAME_MAP.put(TAG_DEST_ADDR_SUBUNIT, "dest_addr_subunit");
@@ -550,6 +648,7 @@ public class SmppConstants {
TAG_NAME_MAP.put(TAG_QOS_TIME_TO_LIVE, "qos_time_to_live");
TAG_NAME_MAP.put(TAG_ADD_STATUS_INFO, "additional_status_info");
TAG_NAME_MAP.put(TAG_RECEIPTED_MSG_ID, "receipted_message_id");
+ TAG_NAME_MAP.put(TAG_MS_MSG_WAIT_FACILITIES, "ms_msg_wait_facilities");
}
}
diff --git a/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppClient.java b/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppClient.java
index 10f4df3a..e53a3b4c 100644
--- a/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppClient.java
+++ b/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppClient.java
@@ -34,32 +34,22 @@
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ConnectTimeoutException;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.oio.OioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
-import io.netty.channel.socket.nio.NioSocketChannel;
-import io.netty.channel.socket.oio.OioSocketChannel;
-import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.net.InetSocketAddress;
-import java.net.InetSocketAddress;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngine;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -231,7 +221,8 @@ protected void doBind(DefaultSmppSession session, SmppSessionConfiguration confi
protected DefaultSmppSession doOpen(SmppSessionConfiguration config, SmppSessionHandler sessionHandler) throws SmppTimeoutException, SmppChannelException, InterruptedException {
// create and connect a channel to the remote host
- this.clientChannel = createConnectedChannel(config.getHost(), config.getPort(), config.getConnectTimeout());
+ this.clientChannel = createConnectedChannel(config.getHost(), config.getPort(), config.getConnectTimeout(),
+ config.getClientBindHost(), config.getClientBindPort());
// tie this new opened channel with a new session
return createSession(clientChannel, config, sessionHandler);
}
@@ -294,23 +285,32 @@ protected DefaultSmppSession createSession(Channel channel, SmppSessionConfigura
}
protected Channel createConnectedChannel(String host, int port, long connectTimeoutMillis) throws SmppTimeoutException, SmppChannelException, InterruptedException {
+ return createConnectedChannel(host, port, connectTimeoutMillis, null, 0);
+ }
+
+ protected Channel createConnectedChannel(
+ String host, int port, long connectTimeoutMillis, String clientLocalHost, int clientLocalPort
+ ) throws SmppTimeoutException, SmppChannelException, InterruptedException {
// a socket address used to "bind" to the remote system
InetSocketAddress socketAddr = new InetSocketAddress(host, port);
// set the timeout
this.clientBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int)connectTimeoutMillis);
+ // a socket address used to "bind" in the local system (e.g. for using specific IP if current host has many)
+ InetSocketAddress localAddr = clientLocalHost == null ? null :
+ new InetSocketAddress(clientLocalHost, clientLocalPort);
// attempt to connect to the remote system
- ChannelFuture connectFuture = this.clientBootstrap.connect(socketAddr);
+ ChannelFuture connectFuture = this.clientBootstrap.connect(socketAddr, localAddr);
/* It turns out that under certain unknown circumstances the connect waits forever: https://github.com/twitter/cloudhopper-smpp/issues/117
* That's why the future is canceled 1 second after the specified timeout.
* This is a workaround and hopefully not needed after the switch to netty 4.
*/
- logger.debug("Waiting for client connection to {}", socketAddr);
+ logger.debug("Waiting for client connection to {} from {}", socketAddr, localAddr);
if (!connectFuture.await(connectTimeoutMillis + 1000)) {
logger.error("connectFuture did not finish in expected time! Try to cancel the connectFuture");
- boolean isCanceled = connectFuture.cancel();
+ boolean isCanceled = connectFuture.cancel(true);
logger.error("connectFuture: isCanceled {} isDone {} isSuccess {}", isCanceled, connectFuture.isDone(), connectFuture.isSuccess());
throw new SmppChannelConnectTimeoutException("Could not connect to the server within timeout");
}
diff --git a/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppServer.java b/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppServer.java
index e9bfff2f..03a3ce48 100644
--- a/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppServer.java
+++ b/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppServer.java
@@ -69,7 +69,7 @@ public class DefaultSmppServer implements SmppServer, DefaultSmppServerMXBean {
private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup;
private ServerBootstrap serverBootstrap;
- private Channel serverChannel;
+ private Channel serverChannel;
// shared instance of a timer background thread to close unbound channels
private final Timer bindTimer;
// shared instance of a session id generator (an atomic long)
@@ -242,8 +242,8 @@ public void start() throws SmppChannelException {
// this.serverBootstrap.bind(...).sync();
// ChannelFuture.sync() is a very useful operation. It waits until the future is
// fulfilled and then rethrow the exception if the operation has failed.
- // Not sure if we can do this, as we actually need to retry on timeout.
-
+ // Not sure if we can do this, as we actually need to retry on timeout.
+
if (timeout)
throw new SmppChannelException("Can't bind to port " + configuration.getPort()
+ " after " + configuration.getBindTimeout() + " milliseconds");
diff --git a/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppSession.java b/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppSession.java
index e205b8db..f3458d7f 100644
--- a/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppSession.java
+++ b/src/main/java/com/cloudhopper/smpp/impl/DefaultSmppSession.java
@@ -520,7 +520,7 @@ public WindowFuture sendRequestPdu(PduRequest pd
// check if the write was a success
if (!channelFuture.isSuccess()) {
// the write failed, make sure to throw an exception
- throw new SmppChannelException(channelFuture.cause() != null ? channelFuture.getCause().getMessage()
+ throw new SmppChannelException(channelFuture.cause() != null ? channelFuture.cause().getMessage()
: "ChannelFuture failed without cause.", channelFuture.cause());
}
diff --git a/src/main/java/com/cloudhopper/smpp/impl/UnboundSmppSession.java b/src/main/java/com/cloudhopper/smpp/impl/UnboundSmppSession.java
index 43e85290..58f88f86 100644
--- a/src/main/java/com/cloudhopper/smpp/impl/UnboundSmppSession.java
+++ b/src/main/java/com/cloudhopper/smpp/impl/UnboundSmppSession.java
@@ -69,59 +69,65 @@ public void firePduReceived(Pdu pdu) {
// always log the PDU received on an unbound session
logger.info("received PDU: {}", pdu);
- // only bind requests are permitted
- if (!(pdu instanceof BaseBind)) {
- logger.warn("Only bind requests are permitted on new connections, closing connection [{}]", channelName);
-
- // FIXME: we could create a response with an error and THEN close the connection...
+ // only bind and enquire_link requests are permitted
+ if (pdu instanceof BaseBind) {
+ // delegate any bind request to the server handler
+ // variables we track for a successful bind request
+ BaseBind bindRequest = (BaseBind)pdu;
+
+ // create a default session configuration based on this bind request
+ SmppSessionConfiguration sessionConfiguration = createSessionConfiguration(bindRequest);
+
+ // assign a new identifier for this session
+ Long sessionId = server.nextSessionId();
+
+ try {
+ // delegate the bind request upstream to server handler
+ this.server.bindRequested(sessionId, sessionConfiguration, bindRequest);
+ } catch (SmppProcessingException e) {
+ logger.warn("Bind request rejected or failed for connection [{}] with error [{}]", channelName, e.getMessage());
+ // create a failed bind response and send back to connection
+ BaseBindResp bindResponse = server.createBindResponse(bindRequest, e.getErrorCode());
+ this.sendResponsePdu(bindResponse);
+ // cancel the timer task & close connection
+ closeChannelAndCancelTimer();
+ return;
+ }
- // cancel the timer task & close connection
- closeChannelAndCancelTimer();
+ // if we got there then 98% "bound" -- we just need to create the
+ // new session and tie everything together -- cancel the bind timer
+ this.bindTimeoutTask.cancel();
+
+ // prepare an "OK" bind response that the session will send back once flagged as 'serverReady'
+ BaseBindResp preparedBindResponse = server.createBindResponse(bindRequest, SmppConstants.STATUS_OK);
+
+ try {
+ // create a new a new session and tie the bind response to it
+ server.createSession(sessionId, channel, sessionConfiguration, preparedBindResponse);
+ } catch (SmppProcessingException e) {
+ logger.warn("Bind request was approved, but createSession failed for connection [{}] with error [{}]", channelName, e.getMessage());
+ // create a failed bind response and send back to connection
+ BaseBindResp bindResponse = server.createBindResponse(bindRequest, e.getErrorCode());
+ this.sendResponsePdu(bindResponse);
+ // cancel the timer task & close connection
+ closeChannelAndCancelTimer();
+ return;
+ }
+ } else if (pdu instanceof EnquireLink) {
+ EnquireLinkResp response = ((EnquireLink) pdu).createResponse();
+ logger.info("Responding to enquire_link with response [{}]", response);
+ this.sendResponsePdu(response);
return;
- }
-
- // delegate any bind request to the server handler
- // variables we track for a successful bind request
- BaseBind bindRequest = (BaseBind)pdu;
+ } else {
+ logger.warn("Only bind or enquire_link requests are permitted on new connections, closing connection [{}]", channelName);
- // create a default session configuration based on this bind request
- SmppSessionConfiguration sessionConfiguration = createSessionConfiguration(bindRequest);
-
- // assign a new identifier for this session
- Long sessionId = server.nextSessionId();
+ // FIXME: we could create a response with an error and THEN close the connection...
- try {
- // delegate the bind request upstream to server handler
- this.server.bindRequested(sessionId, sessionConfiguration, bindRequest);
- } catch (SmppProcessingException e) {
- logger.warn("Bind request rejected or failed for connection [{}] with error [{}]", channelName, e.getMessage());
- // create a failed bind response and send back to connection
- BaseBindResp bindResponse = server.createBindResponse(bindRequest, e.getErrorCode());
- this.sendResponsePdu(bindResponse);
// cancel the timer task & close connection
closeChannelAndCancelTimer();
return;
}
- // if we got there then 98% "bound" -- we just need to create the
- // new session and tie everything together -- cancel the bind timer
- this.bindTimeoutTask.cancel();
-
- // prepare an "OK" bind response that the session will send back once flagged as 'serverReady'
- BaseBindResp preparedBindResponse = server.createBindResponse(bindRequest, SmppConstants.STATUS_OK);
-
- try {
- // create a new a new session and tie the bind response to it
- server.createSession(sessionId, channel, sessionConfiguration, preparedBindResponse);
- } catch (SmppProcessingException e) {
- logger.warn("Bind request was approved, but createSession failed for connection [{}] with error [{}]", channelName, e.getMessage());
- // create a failed bind response and send back to connection
- BaseBindResp bindResponse = server.createBindResponse(bindRequest, e.getErrorCode());
- this.sendResponsePdu(bindResponse);
- // cancel the timer task & close connection
- closeChannelAndCancelTimer();
- return;
- }
}
public void closeChannelAndCancelTimer() {
diff --git a/src/main/java/com/cloudhopper/smpp/pdu/AlertNotification.java b/src/main/java/com/cloudhopper/smpp/pdu/AlertNotification.java
new file mode 100644
index 00000000..094a5ed8
--- /dev/null
+++ b/src/main/java/com/cloudhopper/smpp/pdu/AlertNotification.java
@@ -0,0 +1,86 @@
+package com.cloudhopper.smpp.pdu;
+
+/*
+ * #%L
+ * ch-smpp
+ * %%
+ * Copyright (C) 2009 - 2015 Cloudhopper by Twitter
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import com.cloudhopper.commons.util.StringUtil;
+import com.cloudhopper.smpp.SmppConstants;
+import com.cloudhopper.smpp.type.Address;
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
+import com.cloudhopper.smpp.util.ByteBufUtil;
+import com.cloudhopper.smpp.util.PduUtil;
+import io.netty.buffer.ByteBuf;
+
+public class AlertNotification extends Pdu {
+
+ protected Address sourceAddress;
+ protected Address esmeAddress;
+
+ public AlertNotification(){
+ super( SmppConstants.CMD_ID_ALERT_NOTIFICATION, "alert_notification", true );
+ }
+
+ public Address getSourceAddress() {
+ return this.sourceAddress;
+ }
+
+ public void setSourceAddress(Address value) {
+ this.sourceAddress = value;
+ }
+
+ public Address getEsmeAddress() {
+ return this.esmeAddress;
+ }
+
+ public void setEsmeAddress(Address value) {
+ this.esmeAddress = value;
+ }
+
+ @Override
+ protected int calculateByteSizeOfBody(){
+ int bodyLength = 0;
+ bodyLength += PduUtil.calculateByteSizeOfAddress(this.sourceAddress);
+ bodyLength += PduUtil.calculateByteSizeOfAddress(this.esmeAddress);
+ return bodyLength;
+ }
+
+ @Override
+ public void readBody( ByteBuf buffer ) throws UnrecoverablePduException, RecoverablePduException{
+ this.sourceAddress = ByteBufUtil.readAddress(buffer);
+ this.esmeAddress = ByteBufUtil.readAddress(buffer);
+ }
+
+ @Override
+ public void writeBody( ByteBuf buffer ) throws UnrecoverablePduException, RecoverablePduException{
+ ByteBufUtil.writeAddress(buffer, this.sourceAddress);
+ ByteBufUtil.writeAddress(buffer, this.esmeAddress);
+ }
+
+ @Override
+ protected void appendBodyToString( StringBuilder buffer ){
+ buffer.append("( sourceAddr [");
+ buffer.append(StringUtil.toStringWithNullAsEmpty(this.sourceAddress));
+ buffer.append("] esmeAddr [");
+ buffer.append(StringUtil.toStringWithNullAsEmpty(this.esmeAddress));
+ buffer.append("])");
+ }
+
+}
diff --git a/src/main/java/com/cloudhopper/smpp/pdu/ReplaceSm.java b/src/main/java/com/cloudhopper/smpp/pdu/ReplaceSm.java
new file mode 100644
index 00000000..d882191f
--- /dev/null
+++ b/src/main/java/com/cloudhopper/smpp/pdu/ReplaceSm.java
@@ -0,0 +1,181 @@
+package com.cloudhopper.smpp.pdu;
+
+/*
+ * #%L
+ * ch-smpp
+ * %%
+ * Copyright (C) 2009 - 2015 Cloudhopper by Twitter
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import com.cloudhopper.commons.util.HexUtil;
+import com.cloudhopper.commons.util.StringUtil;
+import com.cloudhopper.smpp.SmppConstants;
+import com.cloudhopper.smpp.type.Address;
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.SmppInvalidArgumentException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
+import com.cloudhopper.smpp.util.ByteBufUtil;
+import com.cloudhopper.smpp.util.PduUtil;
+import io.netty.buffer.ByteBuf;
+
+public class ReplaceSm extends PduRequest {
+
+ private String messageId;
+ private Address sourceAddress;
+ private String scheduleDeliveryTime;
+ private String validityPeriod;
+ private byte registeredDelivery;
+ private byte defaultMsgId;
+ private byte[] shortMessage;
+
+ public ReplaceSm() {
+ super(SmppConstants.CMD_ID_REPLACE_SM, "replace_sm");
+ }
+
+ @Override
+ public ReplaceSmResp createResponse() {
+ ReplaceSmResp resp = new ReplaceSmResp();
+ resp.setSequenceNumber(this.getSequenceNumber());
+ return resp;
+ }
+
+ @Override
+ public Class getResponseClass() {
+ return ReplaceSmResp.class;
+ }
+
+
+ public String getMessageId(){
+ return messageId;
+ }
+
+ public void setMessageId(String messageId){
+ this.messageId = messageId;
+ }
+
+ public int getShortMessageLength() {
+ return (this.shortMessage == null ? 0 : this.shortMessage.length);
+ }
+
+ public byte[] getShortMessage() {
+ return this.shortMessage;
+ }
+
+ public void setShortMessage(byte[] value) throws SmppInvalidArgumentException {
+ if (value != null && value.length > 255) {
+ throw new SmppInvalidArgumentException("A short message in a PDU can only be a max of 255 bytes [actual=" + value.length + "]; use optional parameter message_payload as an alternative");
+ }
+ this.shortMessage = value;
+ }
+
+ public byte getDefaultMsgId() {
+ return this.defaultMsgId;
+ }
+
+ public void setDefaultMsgId(byte value) {
+ this.defaultMsgId = value;
+ }
+
+ public byte getRegisteredDelivery() {
+ return this.registeredDelivery;
+ }
+
+ public void setRegisteredDelivery(byte value) {
+ this.registeredDelivery = value;
+ }
+
+ public String getValidityPeriod() {
+ return this.validityPeriod;
+ }
+
+ public void setValidityPeriod(String value) {
+ this.validityPeriod = value;
+ }
+
+ public String getScheduleDeliveryTime() {
+ return this.scheduleDeliveryTime;
+ }
+
+ public void setScheduleDeliveryTime(String value) {
+ this.scheduleDeliveryTime = value;
+ }
+
+ public Address getSourceAddress() {
+ return this.sourceAddress;
+ }
+
+ public void setSourceAddress(Address value) {
+ this.sourceAddress = value;
+ }
+
+ @Override
+ public void readBody(ByteBuf buffer) throws UnrecoverablePduException, RecoverablePduException {
+ this.messageId = ByteBufUtil.readNullTerminatedString(buffer);
+ this.sourceAddress = ByteBufUtil.readAddress(buffer);
+ this.scheduleDeliveryTime = ByteBufUtil.readNullTerminatedString(buffer);
+ this.validityPeriod = ByteBufUtil.readNullTerminatedString(buffer);
+ this.registeredDelivery = buffer.readByte();
+ this.defaultMsgId = buffer.readByte();
+ // this is always an unsigned version of the short message length
+ short shortMessageLength = buffer.readUnsignedByte();
+ this.shortMessage = new byte[shortMessageLength];
+ buffer.readBytes(this.shortMessage);
+ }
+
+ @Override
+ public int calculateByteSizeOfBody() {
+ int bodyLength = 0;
+ bodyLength += PduUtil.calculateByteSizeOfNullTerminatedString(this.messageId);
+ bodyLength += PduUtil.calculateByteSizeOfAddress(this.sourceAddress);
+ bodyLength += PduUtil.calculateByteSizeOfNullTerminatedString(this.scheduleDeliveryTime);
+ bodyLength += PduUtil.calculateByteSizeOfNullTerminatedString(this.validityPeriod);
+ bodyLength += 3; // regDelivery, defaultMsgId, messageLength bytes
+ bodyLength += getShortMessageLength();
+ return bodyLength;
+ }
+
+ @Override
+ public void writeBody(ByteBuf buffer) throws UnrecoverablePduException, RecoverablePduException {
+ ByteBufUtil.writeNullTerminatedString(buffer, this.messageId);
+ ByteBufUtil.writeAddress(buffer, this.sourceAddress);
+ ByteBufUtil.writeNullTerminatedString(buffer, this.scheduleDeliveryTime);
+ ByteBufUtil.writeNullTerminatedString(buffer, this.validityPeriod);
+ buffer.writeByte(this.registeredDelivery);
+ buffer.writeByte(this.defaultMsgId);
+ buffer.writeByte((byte)getShortMessageLength());
+ if (this.shortMessage != null) {
+ buffer.writeBytes(this.shortMessage);
+ }
+ }
+
+ @Override
+ public void appendBodyToString(StringBuilder buffer) {
+ buffer.append("( messageId [");
+ buffer.append(StringUtil.toStringWithNullAsEmpty(this.messageId));
+ buffer.append("] sourceAddr [");
+ buffer.append(StringUtil.toStringWithNullAsEmpty(this.sourceAddress));
+ buffer.append("] scheduleDeliveryTime [");
+ buffer.append(StringUtil.toStringWithNullAsEmpty(this.scheduleDeliveryTime));
+ buffer.append("] validityPeriod [");
+ buffer.append(StringUtil.toStringWithNullAsEmpty(this.validityPeriod));
+ buffer.append("] regDlvry [0x");
+ buffer.append(HexUtil.toHexString(this.registeredDelivery));
+ buffer.append("] message [");
+ HexUtil.appendHexString(buffer, this.shortMessage);
+ buffer.append("])");
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/cloudhopper/smpp/pdu/ReplaceSmResp.java b/src/main/java/com/cloudhopper/smpp/pdu/ReplaceSmResp.java
new file mode 100644
index 00000000..9646460e
--- /dev/null
+++ b/src/main/java/com/cloudhopper/smpp/pdu/ReplaceSmResp.java
@@ -0,0 +1,53 @@
+package com.cloudhopper.smpp.pdu;
+
+/*
+ * #%L
+ * ch-smpp
+ * %%
+ * Copyright (C) 2009 - 2015 Cloudhopper by Twitter
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import com.cloudhopper.smpp.SmppConstants;
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
+import io.netty.buffer.ByteBuf;
+
+public class ReplaceSmResp extends PduResponse {
+
+ public ReplaceSmResp() {
+ super(SmppConstants.CMD_ID_REPLACE_SM_RESP, "replace_sm_resp");
+ }
+
+ @Override
+ public void readBody(ByteBuf buffer) throws UnrecoverablePduException, RecoverablePduException {
+ // nothing
+ }
+
+ @Override
+ public int calculateByteSizeOfBody() {
+ return 0;
+ }
+
+ @Override
+ public void writeBody(ByteBuf buffer) throws UnrecoverablePduException, RecoverablePduException {
+ // do nothing
+ }
+
+ @Override
+ public void appendBodyToString(StringBuilder buffer) {
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/cloudhopper/smpp/transcoder/DefaultPduTranscoder.java b/src/main/java/com/cloudhopper/smpp/transcoder/DefaultPduTranscoder.java
index 6023b0ad..ae4f0d99 100644
--- a/src/main/java/com/cloudhopper/smpp/transcoder/DefaultPduTranscoder.java
+++ b/src/main/java/com/cloudhopper/smpp/transcoder/DefaultPduTranscoder.java
@@ -156,6 +156,8 @@ protected Pdu doDecode(int commandLength, ByteBuf buffer) throws UnrecoverablePd
pdu = new CancelSm();
} else if (commandId == SmppConstants.CMD_ID_QUERY_SM) {
pdu = new QuerySm();
+ } else if (commandId == SmppConstants.CMD_ID_REPLACE_SM) {
+ pdu = new ReplaceSm();
} else if (commandId == SmppConstants.CMD_ID_BIND_TRANSCEIVER) {
pdu = new BindTransceiver();
} else if (commandId == SmppConstants.CMD_ID_BIND_TRANSMITTER) {
@@ -164,6 +166,8 @@ protected Pdu doDecode(int commandLength, ByteBuf buffer) throws UnrecoverablePd
pdu = new BindReceiver();
} else if (commandId == SmppConstants.CMD_ID_UNBIND) {
pdu = new Unbind();
+ } else if (commandId == SmppConstants.CMD_ID_ALERT_NOTIFICATION) {
+ pdu = new AlertNotification();
} else if (commandId == SmppConstants.CMD_ID_SUBMIT_MULTI) {
pdu = new SubmitMulti();
} else {
@@ -180,6 +184,8 @@ protected Pdu doDecode(int commandLength, ByteBuf buffer) throws UnrecoverablePd
pdu = new CancelSmResp();
} else if (commandId == SmppConstants.CMD_ID_QUERY_SM_RESP) {
pdu = new QuerySmResp();
+ } else if (commandId == SmppConstants.CMD_ID_REPLACE_SM_RESP) {
+ pdu = new ReplaceSmResp();
} else if (commandId == SmppConstants.CMD_ID_ENQUIRE_LINK_RESP) {
pdu = new EnquireLinkResp();
} else if (commandId == SmppConstants.CMD_ID_BIND_TRANSCEIVER_RESP) {
diff --git a/src/main/java/com/cloudhopper/smpp/type/SmppConnectionConfiguration.java b/src/main/java/com/cloudhopper/smpp/type/SmppConnectionConfiguration.java
index d0fa0827..32272c3b 100644
--- a/src/main/java/com/cloudhopper/smpp/type/SmppConnectionConfiguration.java
+++ b/src/main/java/com/cloudhopper/smpp/type/SmppConnectionConfiguration.java
@@ -32,15 +32,24 @@ public class SmppConnectionConfiguration {
private String host;
private int port;
private long connectTimeout;
+ private String clientBindHost;
+ private int clientBindPort;
public SmppConnectionConfiguration() {
this(null, 0, SmppConstants.DEFAULT_CONNECT_TIMEOUT);
}
public SmppConnectionConfiguration(String host, int port, long connectTimeout) {
+ this(host, port, connectTimeout, null, 0);
+ }
+
+ public SmppConnectionConfiguration(String host, int port, long connectTimeout,
+ String clientBindHost, int clientBindPort) {
this.host = host;
this.port = port;
this.connectTimeout = connectTimeout;
+ this.clientBindHost = clientBindHost;
+ this.clientBindPort = clientBindPort;
}
public void setHost(String value) {
@@ -67,4 +76,19 @@ public long getConnectTimeout() {
return this.connectTimeout;
}
+ public String getClientBindHost() {
+ return clientBindHost;
+ }
+
+ public void setClientBindHost(String clientBindHost) {
+ this.clientBindHost = clientBindHost;
+ }
+
+ public int getClientBindPort() {
+ return clientBindPort;
+ }
+
+ public void setClientBindPort(int clientBindPort) {
+ this.clientBindPort = clientBindPort;
+ }
}
diff --git a/src/test/java/com/cloudhopper/smpp/impl/DefaultSmppSessionTest.java b/src/test/java/com/cloudhopper/smpp/impl/DefaultSmppSessionTest.java
index 3d3eb9cf..5257eda7 100644
--- a/src/test/java/com/cloudhopper/smpp/impl/DefaultSmppSessionTest.java
+++ b/src/test/java/com/cloudhopper/smpp/impl/DefaultSmppSessionTest.java
@@ -33,13 +33,13 @@
import com.cloudhopper.smpp.type.*;
import com.cloudhopper.smpp.util.SmppSessionUtil;
import io.netty.channel.Channel;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import io.netty.util.internal.ThreadLocalRandom;
+import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.net.BindException;
+import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
// my imports
@@ -57,7 +57,9 @@ public class DefaultSmppSessionTest {
static SmppSimulatorServer server;
static DefaultSmppClient bootstrap;
-
+
+ SmppSession session;
+
@BeforeClass
public static void startSimulator() {
server = new SmppSimulatorServer();
@@ -73,6 +75,20 @@ public static void stopSimulator() {
bootstrap.destroy();
}
+ @Before
+ public void before() throws Exception {
+ session = null;
+ }
+
+ @After
+ public void after() throws Exception {
+ SmppSessionUtil.close(session);
+ session = null;
+ logger.info("cleaning up server");
+ unregisterServerBindProcessor();
+ clearAllServerSessions();
+ }
+
public SmppSessionConfiguration createDefaultConfiguration() {
SmppSessionConfiguration configuration = new SmppSessionConfiguration();
configuration.setWindowSize(1);
@@ -106,14 +122,11 @@ public void bindToBadPortThrowsSmppChannelConnectException() throws Exception {
// change this to a port we know a server isn't running on
configuration.setPort(PORT+1);
- DefaultSmppSession session = null;
try {
- session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
Assert.fail();
} catch (SmppChannelConnectException e) {
// correct behavior
- } finally {
- SmppSessionUtil.close(session);
}
}
@@ -123,15 +136,12 @@ public void bindToUnknownHostThrowsSmppChannelConnectException() throws Exceptio
// change to a host that doesn't exist
configuration.setHost("jfjdjdjdjdjjdjd");
- DefaultSmppSession session = null;
try {
- session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
Assert.fail();
} catch (SmppChannelConnectException e) {
// correct behavior
logger.info("Expected error message: {}", e.getMessage());
- } finally {
- SmppSessionUtil.close(session);
}
}
@@ -142,15 +152,12 @@ public void bindToFirewalledHostThrowsSmppChannelConnectTimeoutException() throw
configuration.setHost("www.twitter.com");
configuration.setPort(81);
- DefaultSmppSession session = null;
try {
- session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
Assert.fail();
} catch (SmppChannelConnectTimeoutException e) {
// correct behavior
logger.info("Expected error message: {}", e.getMessage());
- } finally {
- SmppSessionUtil.close(session);
}
}
@@ -159,16 +166,13 @@ public void bindConnectsButNoResponseThrowsSmppTimeoutException() throws Excepti
SmppSessionConfiguration configuration = createDefaultConfiguration();
unregisterServerBindProcessor();
- DefaultSmppSession session = null;
try {
- session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
Assert.fail();
} catch (SmppTimeoutException e) {
// correct behavior (underlying cause MUST be a response timeout)
// Assert.assertNotNull(e.getCause());
// Assert.assertEquals(ResponseTimeoutException.class, e.getCause().getClass());
- } finally {
- SmppSessionUtil.close(session);
}
}
@@ -180,16 +184,13 @@ public void bindWithBadCredentialsThrowsSmppBindException() throws Exception {
// set a bad system id
configuration.setSystemId("BADID");
- DefaultSmppSession session = null;
try {
- session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
Assert.fail();
} catch (SmppBindException e) {
// correct behavior
Assert.assertNotNull(e.getBindResponse());
Assert.assertEquals(SmppConstants.STATUS_INVSYSID, e.getBindResponse().getCommandStatus());
- } finally {
- SmppSessionUtil.close(session);
}
}
@@ -198,12 +199,10 @@ public void bindOK() throws Exception {
SmppSessionConfiguration configuration = createDefaultConfiguration();
registerServerBindProcessor();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
// verify the session stuff...
Assert.assertEquals(true, session.isBound());
-
- SmppSessionUtil.close(session);
}
@Test
@@ -213,7 +212,7 @@ public void enquireLinkWithGenericNackResponse() throws Exception {
clearAllServerSessions();
// bind and get the simulator session
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
// register a generic nack will come next
simulator0.setPduProcessor(new SmppSimulatorPduProcessor() {
@@ -224,15 +223,12 @@ public boolean process(SmppSimulatorSessionHandler session, Channel channel, Pdu
}
});
+
try {
- try {
- session.enquireLink(new EnquireLink(), 1000);
- Assert.fail();
- } catch (GenericNackException e) {
- // correct behavior
- }
- } finally {
- SmppSessionUtil.close(session);
+ session.enquireLink(new EnquireLink(), 1000);
+ Assert.fail();
+ } catch (GenericNackException e) {
+ // correct behavior
}
}
@@ -243,7 +239,7 @@ public void enquireLinkWithARequestWithSameSequenceNumber() throws Exception {
clearAllServerSessions();
// bind and get the simulator session
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
// create an enquire link response back -- we should skip it and wait for a response instead
simulator0.setPduProcessor(new SmppSimulatorPduProcessor() {
@@ -257,16 +253,12 @@ public boolean process(SmppSimulatorSessionHandler session, Channel channel, Pdu
});
try {
- try {
- session.enquireLink(new EnquireLink(), 100);
- Assert.fail();
- } catch (SmppTimeoutException e) {
- // correct behavior (underlying cause MUST be a response timeout)
+ session.enquireLink(new EnquireLink(), 100);
+ Assert.fail();
+ } catch (SmppTimeoutException e) {
+ // correct behavior (underlying cause MUST be a response timeout)
// Assert.assertNotNull(e.getCause());
// Assert.assertEquals(ResponseTimeoutException.class, e.getCause().getClass());
- }
- } finally {
- SmppSessionUtil.close(session);
}
}
@@ -277,7 +269,7 @@ public void multipleEnquireLinks() throws Exception {
clearAllServerSessions();
// bind and get the simulator session
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
// create an enquire link response back -- we should skip it and wait for a response instead
simulator0.setPduProcessor(new SmppSimulatorPduProcessor() {
@@ -288,15 +280,11 @@ public boolean process(SmppSimulatorSessionHandler session, Channel channel, Pdu
}
});
- try {
- session.enquireLink(new EnquireLink(), 100);
- session.enquireLink(new EnquireLink(), 100);
- session.enquireLink(new EnquireLink(), 100);
- session.enquireLink(new EnquireLink(), 100);
- session.enquireLink(new EnquireLink(), 100);
- } finally {
- SmppSessionUtil.close(session);
- }
+ session.enquireLink(new EnquireLink(), 100);
+ session.enquireLink(new EnquireLink(), 100);
+ session.enquireLink(new EnquireLink(), 100);
+ session.enquireLink(new EnquireLink(), 100);
+ session.enquireLink(new EnquireLink(), 100);
}
@Test
@@ -309,72 +297,67 @@ public void windowSizeBlocksAsyncRequest() throws Exception {
configuration.setWindowSize(3);
// bind and get the simulator session
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration);
+ session = bootstrap.bind(configuration);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
// make sure the processor is null
simulator0.setPduProcessor(null);
+ // create the requests and response we plan on sending
+ EnquireLink el0 = new EnquireLink();
+ EnquireLink el1 = new EnquireLink();
+ EnquireLink el2 = new EnquireLink();
+ EnquireLink el3 = new EnquireLink();
+ el0.setSequenceNumber(0x7000);
+ el1.setSequenceNumber(0x4541);
+ el2.setSequenceNumber(0x5414);
+ el3.setSequenceNumber(0x2414);
+ EnquireLinkResp el0Resp = el0.createResponse();
+ EnquireLinkResp el1Resp = el1.createResponse();
+ EnquireLinkResp el2Resp = el2.createResponse();
+ EnquireLinkResp el3Resp = el3.createResponse();
+
+ // this request should be permitted (with window size = 2)
+ WindowFuture future0 = session.sendRequestPdu(el0, 3000, true);
+ WindowFuture future1 = session.sendRequestPdu(el1, 3000, true);
+ WindowFuture future2 = session.sendRequestPdu(el2, 3000, true);
+
+ Assert.assertEquals(3, session.getSendWindow().getSize());
+
try {
-// try {
- // create the requests and response we plan on sending
- EnquireLink el0 = new EnquireLink();
- EnquireLink el1 = new EnquireLink();
- EnquireLink el2 = new EnquireLink();
- EnquireLink el3 = new EnquireLink();
- el0.setSequenceNumber(0x7000);
- el1.setSequenceNumber(0x4541);
- el2.setSequenceNumber(0x5414);
- el3.setSequenceNumber(0x2414);
- EnquireLinkResp el0Resp = el0.createResponse();
- EnquireLinkResp el1Resp = el1.createResponse();
- EnquireLinkResp el2Resp = el2.createResponse();
- EnquireLinkResp el3Resp = el3.createResponse();
-
- // this request should be permitted (with window size = 2)
- WindowFuture future0 = session.sendRequestPdu(el0, 3000, true);
- WindowFuture future1 = session.sendRequestPdu(el1, 3000, true);
- WindowFuture future2 = session.sendRequestPdu(el2, 3000, true);
-
- Assert.assertEquals(3, session.getSendWindow().getSize());
-
- try {
- // window size of 3 is now filled up, this one should timeout
- session.sendRequestPdu(el3, 100, true);
- Assert.fail();
- } catch (SmppTimeoutException e) {
- Assert.assertNotNull(e.getCause());
- Assert.assertEquals(OfferTimeoutException.class, e.getCause().getClass());
- }
-
- Assert.assertEquals(3, session.getSendWindow().getSize());
-
- // now the smsc will send a response back to the second request
- simulator0.sendPdu(el1Resp);
-
- // wait for the response to make its way back in
- future1.await();
-
- // there should be 1 slot free now in the window
- Assert.assertEquals(2, session.getSendWindow().getSize());
-
- // this request should now succeed
- WindowFuture future3 = session.sendRequestPdu(el3, 3000, true);
-
- // send back responses for everything that's missing
- simulator0.sendPdu(el2Resp);
- simulator0.sendPdu(el0Resp);
- simulator0.sendPdu(el3Resp);
-
- // make sure they all finished
- future0.await();
- future1.await();
- future2.await();
- future3.await();
-
- Assert.assertEquals(0, session.getSendWindow().getSize());
- } finally {
- SmppSessionUtil.close(session);
+ // window size of 3 is now filled up, this one should timeout
+ session.sendRequestPdu(el3, 100, true);
+ Assert.fail();
+ } catch (SmppTimeoutException e) {
+ Assert.assertNotNull(e.getCause());
+ Assert.assertEquals(OfferTimeoutException.class, e.getCause().getClass());
}
+
+ Assert.assertEquals(3, session.getSendWindow().getSize());
+
+ // now the smsc will send a response back to the second request
+ simulator0.sendPdu(el1Resp);
+
+ // wait for the response to make its way back in
+ future1.await();
+
+ // there should be 1 slot free now in the window
+ Assert.assertEquals(2, session.getSendWindow().getSize());
+
+ // this request should now succeed
+ WindowFuture future3 = session.sendRequestPdu(el3, 3000, true);
+
+ // send back responses for everything that's missing
+ simulator0.sendPdu(el2Resp);
+ simulator0.sendPdu(el0Resp);
+ simulator0.sendPdu(el3Resp);
+
+ // make sure they all finished
+ future0.await();
+ future1.await();
+ future2.await();
+ future3.await();
+
+ Assert.assertEquals(0, session.getSendWindow().getSize());
}
@@ -386,39 +369,35 @@ public void cumulationOfMultipleByteBuffersToParsePdu() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
-
- // send 1 byte
- logger.debug("Sending 1 byte");
- simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("00")).sync();
-
- // nothing received yet
- Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
-
- // send 14 more bytes
- logger.debug("Sending 14 more bytes");
- simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("00001000000015000000000a342e")).sync();
-
- // send 1 more byte
- logger.debug("Sending 1 more bytes");
- simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("e7")).sync();
-
- // we should have received a PDU request, poll for it
- PduRequest pdu0 = sessionHandler.getReceivedPduRequests().poll(2000, TimeUnit.MILLISECONDS);
- Assert.assertNotNull(pdu0);
- Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(171192039, pdu0.getSequenceNumber());
- } finally {
- SmppSessionUtil.close(session);
- }
+ Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
+
+ // send 1 byte
+ logger.debug("Sending 1 byte");
+ simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("00")).sync();
+
+ // nothing received yet
+ Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
+
+ // send 14 more bytes
+ logger.debug("Sending 14 more bytes");
+ simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("00001000000015000000000a342e")).sync();
+
+ // send 1 more byte
+ logger.debug("Sending 1 more bytes");
+ simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("e7")).sync();
+
+ // we should have received a PDU request, poll for it
+ PduRequest pdu0 = sessionHandler.getReceivedPduRequests().poll(2000, TimeUnit.MILLISECONDS);
+ Assert.assertNotNull(pdu0);
+ Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(171192039, pdu0.getSequenceNumber());
}
@Test
@@ -429,7 +408,7 @@ public void routePduResponseToWaitingThread() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(new SmppSimulatorPduProcessor() {
@@ -440,24 +419,20 @@ public boolean process(SmppSimulatorSessionHandler session, Channel channel, Pdu
}
});
- try {
- // this should entirely succeed
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1000);
+ // this should entirely succeed
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1000);
- EnquireLinkResp el0Resp = session.enquireLink(el0, 200);
+ EnquireLinkResp el0Resp = session.enquireLink(el0, 200);
- // check everything is correct afterwards
- Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, el0Resp.getCommandId());
- Assert.assertEquals(0, el0Resp.getCommandStatus());
- Assert.assertEquals(16, el0Resp.getCommandLength());
- Assert.assertEquals(0x1000, el0Resp.getSequenceNumber());
- Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
- Assert.assertEquals(0, sessionHandler.getReceivedExpectedPduResponses().size());
- Assert.assertEquals(0, sessionHandler.getReceivedUnexpectedPduResponses().size());
- } finally {
- SmppSessionUtil.close(session);
- }
+ // check everything is correct afterwards
+ Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, el0Resp.getCommandId());
+ Assert.assertEquals(0, el0Resp.getCommandStatus());
+ Assert.assertEquals(16, el0Resp.getCommandLength());
+ Assert.assertEquals(0x1000, el0Resp.getSequenceNumber());
+ Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
+ Assert.assertEquals(0, sessionHandler.getReceivedExpectedPduResponses().size());
+ Assert.assertEquals(0, sessionHandler.getReceivedUnexpectedPduResponses().size());
}
@@ -469,33 +444,29 @@ public void receiveExpectedPduResponseViaAnAsynchronousSend() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1000);
- EnquireLinkResp el0Resp = el0.createResponse();
-
- // send asynchronously (no response will be received yet)
- session.sendRequestPdu(el0, 2000, false);
-
- // send the response back -- this should be routed as n ExpectedPduResponse....
- simulator0.sendPdu(el0Resp);
-
- // we should have received a PDU response
- PduAsyncResponse asyncpdu0 = sessionHandler.getReceivedExpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
- PduResponse pdu0 = asyncpdu0.getResponse();
- Assert.assertNotNull("Unable to receive expected PDU response -- perhaps it was routed incorrectly?", pdu0);
- Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
- } finally {
- SmppSessionUtil.close(session);
- }
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1000);
+ EnquireLinkResp el0Resp = el0.createResponse();
+
+ // send asynchronously (no response will be received yet)
+ session.sendRequestPdu(el0, 2000, false);
+
+ // send the response back -- this should be routed as n ExpectedPduResponse....
+ simulator0.sendPdu(el0Resp);
+
+ // we should have received a PDU response
+ PduAsyncResponse asyncpdu0 = sessionHandler.getReceivedExpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
+ PduResponse pdu0 = asyncpdu0.getResponse();
+ Assert.assertNotNull("Unable to receive expected PDU response -- perhaps it was routed incorrectly?", pdu0);
+ Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
}
@@ -507,24 +478,20 @@ public void impossiblePDULengthCausesUnrecoverablePduException() throws Exceptio
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- // send some bytes that should trigger a MAJOR issue during parsing
- simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("F000001000000015000000000a342ee7")).sync();
+ // send some bytes that should trigger a MAJOR issue during parsing
+ simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("F000001000000015000000000a342ee7")).sync();
- // we should have received an Unrecoverable exception, poll for it
- Throwable t = sessionHandler.getThrowables().poll(2000, TimeUnit.MILLISECONDS);
- logger.debug("polled for exception: {}", t.getMessage());
+ // we should have received an Unrecoverable exception, poll for it
+ Throwable t = sessionHandler.getThrowables().poll(2000, TimeUnit.MILLISECONDS);
+ logger.debug("polled for exception: {}", t.getMessage());
- Assert.assertNotNull(t);
- Assert.assertTrue(t.getMessage(), (t instanceof UnrecoverablePduException));
- } finally {
- SmppSessionUtil.close(session);
- }
+ Assert.assertNotNull(t);
+ Assert.assertTrue(t.getMessage(), (t instanceof UnrecoverablePduException));
}
/** THIS TEST NO LONGER APPLIES, WE SUPPORT ALL 32-BITS NOW
@@ -565,35 +532,30 @@ public void noTerminatingZeroCausesRecoverablePduException() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- // send some bytes that should trigger a MAJOR issue during parsing
- simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("0000001180000004000000000a342ee139")).sync();
+ // send some bytes that should trigger a MAJOR issue during parsing
+ simulator0.getChannel().writeAndFlush(BufferHelper.createBuffer("0000001180000004000000000a342ee139")).sync();
- // we should have received an Unrecoverable exception, poll for it
- Throwable t = sessionHandler.getThrowables().poll(2000, TimeUnit.MILLISECONDS);
- logger.debug("polled for exception: {}", t.getMessage());
+ // we should have received an Unrecoverable exception, poll for it
+ Throwable t = sessionHandler.getThrowables().poll(2000, TimeUnit.MILLISECONDS);
+ logger.debug("polled for exception: {}", t.getMessage());
- Assert.assertNotNull(t);
- Assert.assertTrue(t.getMessage(), (t instanceof TerminatingNullByteNotFoundException));
-
- TerminatingNullByteNotFoundException ex = (TerminatingNullByteNotFoundException)t;
- // unwrap it
- SubmitSmResp pdu0 = (SubmitSmResp)ex.getPartialPdu();
- Assert.assertEquals(17, pdu0.getCommandLength());
- Assert.assertEquals(SmppConstants.CMD_ID_SUBMIT_SM_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(171192033, pdu0.getSequenceNumber());
- Assert.assertEquals(true, pdu0.isResponse());
- Assert.assertEquals(null, pdu0.getMessageId());
+ Assert.assertNotNull(t);
+ Assert.assertTrue(t.getMessage(), (t instanceof TerminatingNullByteNotFoundException));
- } finally {
- SmppSessionUtil.close(session);
- }
+ TerminatingNullByteNotFoundException ex = (TerminatingNullByteNotFoundException)t;
+ // unwrap it
+ SubmitSmResp pdu0 = (SubmitSmResp)ex.getPartialPdu();
+ Assert.assertEquals(17, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_SUBMIT_SM_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(171192033, pdu0.getSequenceNumber());
+ Assert.assertEquals(true, pdu0.isResponse());
+ Assert.assertEquals(null, pdu0.getMessageId());
}
@@ -605,7 +567,7 @@ public void closeDoesNotTriggerUnexpectedlyClosedEvent() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
@@ -626,7 +588,7 @@ public void unbindWithNoResponseDoesNotTriggerUnexpectedlyClosedEvent() throws E
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
@@ -646,7 +608,7 @@ public void unbindTriggeringRemoteToCloseDoesNotTriggerUnexpectedlyClosedEvent()
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(new SmppSimulatorPduProcessor() {
@@ -672,7 +634,7 @@ public void multipleClosesWorkOK() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
@@ -705,7 +667,7 @@ public void remoteCloseDoesTriggerUnexpectedlyClosedEvent() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
@@ -736,35 +698,31 @@ public void sendRequestAndGetResponseOKWithResponseTypeNotMatchingOriginalReques
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- // NOTE: send enquireLink but receive an unbind resp (with same sequence #)
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1000);
-
- UnbindResp ubResp = new UnbindResp();
- ubResp.setSequenceNumber(el0.getSequenceNumber());
-
- // schedule this response on the simulator
- simulator0.addPduToWriteOnNextPduReceived(ubResp);
-
- // send asynchronously (no response will be received yet)
- PduResponse pdu0 = session.sendRequestAndGetResponse(el0, 2000);
- logger.debug("{}", pdu0);
-
- // NOTE: this internal method is OK to return an unexpected response
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(SmppConstants.CMD_ID_UNBIND_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
- Assert.assertEquals(true, pdu0.isResponse());
- } finally {
- SmppSessionUtil.close(session);
- }
+ // NOTE: send enquireLink but receive an unbind resp (with same sequence #)
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1000);
+
+ UnbindResp ubResp = new UnbindResp();
+ ubResp.setSequenceNumber(el0.getSequenceNumber());
+
+ // schedule this response on the simulator
+ simulator0.addPduToWriteOnNextPduReceived(ubResp);
+
+ // send asynchronously (no response will be received yet)
+ PduResponse pdu0 = ((DefaultSmppSession)session).sendRequestAndGetResponse(el0, 2000);
+ logger.debug("{}", pdu0);
+
+ // NOTE: this internal method is OK to return an unexpected response
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_UNBIND_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
+ Assert.assertEquals(true, pdu0.isResponse());
}
@Test
@@ -775,41 +733,37 @@ public void enquireLinkFailsWithResponseTypeNotMatchingOriginalRequest() throws
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- // NOTE: send enquireLink but receive an unbind resp (with same sequence #)
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1000);
-
- UnbindResp ubResp = new UnbindResp();
- ubResp.setSequenceNumber(el0.getSequenceNumber());
-
- // schedule this response on the simulator
- simulator0.addPduToWriteOnNextPduReceived(ubResp);
-
- PduResponse pdu0 = null;
- try {
- EnquireLinkResp el0Resp = session.enquireLink(el0, 200);
- Assert.fail();
- } catch (UnexpectedPduResponseException e) {
- // correct behavior
- pdu0 = e.getResponsePdu();
- }
+ // NOTE: send enquireLink but receive an unbind resp (with same sequence #)
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1000);
- logger.debug("unexpected pdu: {}", pdu0);
+ UnbindResp ubResp = new UnbindResp();
+ ubResp.setSequenceNumber(el0.getSequenceNumber());
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(SmppConstants.CMD_ID_UNBIND_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
- Assert.assertEquals(true, pdu0.isResponse());
- } finally {
- SmppSessionUtil.close(session);
+ // schedule this response on the simulator
+ simulator0.addPduToWriteOnNextPduReceived(ubResp);
+
+ PduResponse pdu0 = null;
+ try {
+ EnquireLinkResp el0Resp = session.enquireLink(el0, 200);
+ Assert.fail();
+ } catch (UnexpectedPduResponseException e) {
+ // correct behavior
+ pdu0 = e.getResponsePdu();
}
+
+ logger.debug("unexpected pdu: {}", pdu0);
+
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_UNBIND_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
+ Assert.assertEquals(true, pdu0.isResponse());
}
@Test
@@ -820,53 +774,49 @@ public void asynchronousPduRequestWithResponseTypeNotMatchingOriginalRequest() t
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- // NOTE: send enquireLink but receive an unbind resp (with same sequence #)
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1000);
+ // NOTE: send enquireLink but receive an unbind resp (with same sequence #)
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1000);
- UnbindResp ubResp = new UnbindResp();
- ubResp.setSequenceNumber(el0.getSequenceNumber());
+ UnbindResp ubResp = new UnbindResp();
+ ubResp.setSequenceNumber(el0.getSequenceNumber());
- // schedule this response on the simulator
- simulator0.addPduToWriteOnNextPduReceived(ubResp);
+ // schedule this response on the simulator
+ simulator0.addPduToWriteOnNextPduReceived(ubResp);
- // send this PDU asynchronously
- session.sendRequestPdu(el0, 200, false);
+ // send this PDU asynchronously
+ session.sendRequestPdu(el0, 200, false);
- // wait for an expected pdu response
- PduAsyncResponse asyncpdu0 = sessionHandler.getReceivedExpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
- logger.debug("{}", asyncpdu0);
+ // wait for an expected pdu response
+ PduAsyncResponse asyncpdu0 = sessionHandler.getReceivedExpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
+ logger.debug("{}", asyncpdu0);
- PduResponse pdu0 = asyncpdu0.getResponse();
+ PduResponse pdu0 = asyncpdu0.getResponse();
- /**
- PduResponse pdu0 = null;
- try {
- EnquireLinkResp el0Resp = session.enquireLink(el0, 200);
- Assert.fail();
- } catch (UnexpectedPduResponseException e) {
- // correct behavior
- pdu0 = e.getResponsePdu();
- }
+ /*
+ PduResponse pdu0 = null;
+ try {
+ EnquireLinkResp el0Resp = session.enquireLink(el0, 200);
+ Assert.fail();
+ } catch (UnexpectedPduResponseException e) {
+ // correct behavior
+ pdu0 = e.getResponsePdu();
+ }
- logger.debug("unexpected pdu: {}", pdu0);
+ logger.debug("unexpected pdu: {}", pdu0);
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(SmppConstants.CMD_ID_UNBIND_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
- Assert.assertEquals(true, pdu0.isResponse());
- */
- } finally {
- SmppSessionUtil.close(session);
- }
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_UNBIND_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
+ Assert.assertEquals(true, pdu0.isResponse());
+ */
}
@@ -878,42 +828,38 @@ public void receiveUnexpectedPduResponseAfterSenderThreadTimeoutWaiting() throws
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1001);
- EnquireLinkResp el0Resp = el0.createResponse();
-
- // send a request and wait for a response that never shows up
- WindowFuture future = session.sendRequestPdu(el0, 50, true);
- Assert.assertFalse(future.await());
- // a call to cancel() is usually done in sendRequestPduAndGetResponse
- // but for this test we'll manually need to call it here
- future.cancel();
-
- Assert.assertEquals(WindowFuture.CALLER_WAITING_TIMEOUT, future.getCallerStateHint());
-
- // send a response now after the caller would have timed out
- simulator0.sendPdu(el0Resp);
-
- // we should have received an unexpected PDU response
- Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
- Assert.assertEquals(0, sessionHandler.getReceivedExpectedPduResponses().size());
- PduResponse pdu0 = sessionHandler.getReceivedUnexpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
- Assert.assertNotNull("Unable to receive unexpected PDU response -- perhaps it was routed incorrectly?", pdu0);
- Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(0x1001, pdu0.getSequenceNumber());
-
- Assert.assertEquals(0, sessionHandler.getReceivedUnexpectedPduResponses().size());
- } finally {
- SmppSessionUtil.close(session);
- }
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1001);
+ EnquireLinkResp el0Resp = el0.createResponse();
+
+ // send a request and wait for a response that never shows up
+ WindowFuture future = session.sendRequestPdu(el0, 50, true);
+ Assert.assertFalse(future.await());
+ // a call to cancel() is usually done in sendRequestPduAndGetResponse
+ // but for this test we'll manually need to call it here
+ future.cancel();
+
+ Assert.assertEquals(WindowFuture.CALLER_WAITING_TIMEOUT, future.getCallerStateHint());
+
+ // send a response now after the caller would have timed out
+ simulator0.sendPdu(el0Resp);
+
+ // we should have received an unexpected PDU response
+ Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
+ Assert.assertEquals(0, sessionHandler.getReceivedExpectedPduResponses().size());
+ PduResponse pdu0 = sessionHandler.getReceivedUnexpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
+ Assert.assertNotNull("Unable to receive unexpected PDU response -- perhaps it was routed incorrectly?", pdu0);
+ Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(0x1001, pdu0.getSequenceNumber());
+
+ Assert.assertEquals(0, sessionHandler.getReceivedUnexpectedPduResponses().size());
}
@Test
@@ -924,33 +870,29 @@ public void receiveUnexpectedPduResponse() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
- try {
- EnquireLink el0 = new EnquireLink();
- el0.setSequenceNumber(0x1000);
- EnquireLinkResp el0Resp = el0.createResponse();
-
- // send a response to a request that was NEVER sent
- simulator0.sendPdu(el0Resp);
-
- // we should have received a PDU response
- PduResponse pdu0 = sessionHandler.getReceivedUnexpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
- Assert.assertNotNull("Unable to receive unexpected PDU response -- perhaps it was routed incorrectly?", pdu0);
- Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, pdu0.getCommandId());
- Assert.assertEquals(0, pdu0.getCommandStatus());
- Assert.assertEquals(16, pdu0.getCommandLength());
- Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
-
- Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
- Assert.assertEquals(0, sessionHandler.getReceivedExpectedPduResponses().size());
- Assert.assertEquals(0, sessionHandler.getReceivedUnexpectedPduResponses().size());
- } finally {
- SmppSessionUtil.close(session);
- }
+ EnquireLink el0 = new EnquireLink();
+ el0.setSequenceNumber(0x1000);
+ EnquireLinkResp el0Resp = el0.createResponse();
+
+ // send a response to a request that was NEVER sent
+ simulator0.sendPdu(el0Resp);
+
+ // we should have received a PDU response
+ PduResponse pdu0 = sessionHandler.getReceivedUnexpectedPduResponses().poll(1000, TimeUnit.MILLISECONDS);
+ Assert.assertNotNull("Unable to receive unexpected PDU response -- perhaps it was routed incorrectly?", pdu0);
+ Assert.assertEquals(SmppConstants.CMD_ID_ENQUIRE_LINK_RESP, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(0x1000, pdu0.getSequenceNumber());
+
+ Assert.assertEquals(0, sessionHandler.getReceivedPduRequests().size());
+ Assert.assertEquals(0, sessionHandler.getReceivedExpectedPduResponses().size());
+ Assert.assertEquals(0, sessionHandler.getReceivedUnexpectedPduResponses().size());
}
@@ -962,26 +904,22 @@ public void synchronousSendButNeverGetResponse() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
try {
- try {
- session.enquireLink(new EnquireLink(), 100);
- // request should timeout
- Assert.fail();
- } catch (SmppTimeoutException e) {
- // correct behavior
- }
-
- // with a "synchronous" type of send, after a timeout, the request
- // should have been cancelled
- Assert.assertEquals(0, session.getSendWindow().getSize());
- } finally {
- SmppSessionUtil.close(session);
+ session.enquireLink(new EnquireLink(), 100);
+ // request should timeout
+ Assert.fail();
+ } catch (SmppTimeoutException e) {
+ // correct behavior
}
+
+ // with a "synchronous" type of send, after a timeout, the request
+ // should have been cancelled
+ Assert.assertEquals(0, session.getSendWindow().getSize());
}
@Test
@@ -992,7 +930,7 @@ public void shutdown() throws Exception {
// bind and get the simulator session
PollableSmppSessionHandler sessionHandler = new PollableSmppSessionHandler();
- DefaultSmppSession session = (DefaultSmppSession)bootstrap.bind(configuration, sessionHandler);
+ session = bootstrap.bind(configuration, sessionHandler);
SmppSimulatorSessionHandler simulator0 = server.pollNextSession(1000);
simulator0.setPduProcessor(null);
@@ -1008,4 +946,58 @@ public void shutdown() throws Exception {
Assert.assertEquals(0, session.getSendWindow().getSize());
}
+ @Test
+ public void bindToSpecificHost() throws Exception {
+ final String expectedBindHost = "127.0.0.1";
+
+ SmppSessionConfiguration configuration = createDefaultConfiguration();
+ configuration.setConnectTimeout(120000);
+ configuration.setClientBindHost(expectedBindHost);
+ registerServerBindProcessor();
+
+ session = bootstrap.bind(configuration);
+
+ // verify the session stuff...
+ Assert.assertEquals(true, session.isBound());
+ // verify that the local address we used to connect is the expected
+ DefaultSmppSession dss = (DefaultSmppSession) session;
+ InetSocketAddress localAddress = (InetSocketAddress) dss.getChannel().localAddress();
+
+ Assert.assertEquals(expectedBindHost, localAddress.getAddress().getHostAddress());
+ }
+
+ @Test
+ public void bindToSpecificLocalAddress() throws Exception {
+ final String expectedBindHost = "127.0.0.1";
+ // try to use a random local port and pray that it hasn't been used earlier.
+ final int expectedBindPort = ThreadLocalRandom.current().nextInt(PORT+1, 65535);
+
+ SmppSessionConfiguration configuration = createDefaultConfiguration();
+ configuration.setConnectTimeout(120000);
+ configuration.setClientBindHost(expectedBindHost);
+ configuration.setClientBindPort(expectedBindPort);
+ registerServerBindProcessor();
+
+ try {
+ session = bootstrap.bind(configuration);
+ } catch (SmppChannelConnectException e) {
+ // if we have bound to the expectedLocalPort in a previous run (or test), then the TCP connection may
+ // very well be still open, in a TIME_WAIT status. In that case there's not much to do, other than
+ // ignoring this test
+ Assume.assumeFalse(
+ "Error trying to bind to local port. Address already in use. Probably it is still in TIME_WAIT?",
+ e.getCause() != null &&
+ e.getCause().getCause() != null &&
+ e.getCause().getCause() instanceof BindException);
+ throw e;
+ }
+
+ // verify the session stuff...
+ Assert.assertEquals(true, session.isBound());
+ // verify that the local address we used to connect is the expected
+ DefaultSmppSession dss = (DefaultSmppSession) session;
+ InetSocketAddress localAddress = (InetSocketAddress) dss.getChannel().localAddress();
+ Assert.assertEquals(expectedBindHost, localAddress.getAddress().getHostAddress());
+ Assert.assertEquals(expectedBindPort, localAddress.getPort());
+ }
}
diff --git a/src/test/java/com/cloudhopper/smpp/ssl/SslSessionTest.java b/src/test/java/com/cloudhopper/smpp/ssl/SslSessionTest.java
index f757cb7d..91d51d0c 100644
--- a/src/test/java/com/cloudhopper/smpp/ssl/SslSessionTest.java
+++ b/src/test/java/com/cloudhopper/smpp/ssl/SslSessionTest.java
@@ -113,8 +113,8 @@ private SmppSessionConfiguration createClientConfigurationNoSSL() {
configuration.setType(SmppBindType.TRANSCEIVER);
configuration.setHost("localhost");
configuration.setPort(PORT);
- configuration.setConnectTimeout(200);
- configuration.setBindTimeout(200);
+ configuration.setConnectTimeout(2000);
+ configuration.setBindTimeout(2000);
configuration.setSystemId(SYSTEMID);
configuration.setPassword(PASSWORD);
configuration.getLoggingOptions().setLogBytes(true);
@@ -238,7 +238,7 @@ public void bindOverSSL() throws Exception {
try {
// this should actually work
SmppSession session0 = client0.bind(sessionConfig0);
- Thread.sleep(200);
+ Thread.sleep(2000);
Assert.assertEquals(1, serverHandler.sessions.size());
Assert.assertEquals(1, server0.getChannels().size());
@@ -250,7 +250,7 @@ public void bindOverSSL() throws Exception {
Assert.assertEquals(SmppSession.Type.CLIENT, serverSession0.getRemoteType());
serverSession0.close();
- Thread.sleep(200);
+ Thread.sleep(2000);
Assert.assertEquals(0, serverHandler.sessions.size());
Assert.assertEquals(0, server0.getChannels().size());
Assert.assertEquals(false, serverSession0.isBound());
@@ -270,7 +270,7 @@ public void enquireLinkOverSSL() throws Exception {
try {
SmppSession session0 = client0.bind(sessionConfig0);
- Thread.sleep(100);
+ Thread.sleep(1000);
// send encrypted enquire link; receive encrypted response.
EnquireLinkResp enquireLinkResp = session0.enquireLink(new EnquireLink(), 1000);
diff --git a/src/test/java/com/cloudhopper/smpp/transcoder/PduDecoderTest.java b/src/test/java/com/cloudhopper/smpp/transcoder/PduDecoderTest.java
index 1a748461..e9f166b0 100644
--- a/src/test/java/com/cloudhopper/smpp/transcoder/PduDecoderTest.java
+++ b/src/test/java/com/cloudhopper/smpp/transcoder/PduDecoderTest.java
@@ -1392,4 +1392,61 @@ public void decodeQuerySmResp() throws Exception {
Assert.assertEquals(0, buffer.readableBytes());
}
+
+ @Test
+ public void decodeReplaceSm() throws Exception {
+ ByteBuf buffer = BufferHelper.createBuffer("00000050000000070000000000004FE86D73672D313233343500010135353532373130303030003135303230333034303530363730382B00303130323033303430353036303030520001020474657874");
+
+ ReplaceSm pdu0 = (ReplaceSm)transcoder.decode(buffer);
+
+ Assert.assertEquals(80, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_REPLACE_SM, pdu0.getCommandId());
+ Assert.assertEquals(0, pdu0.getCommandStatus());
+ Assert.assertEquals(20456, pdu0.getSequenceNumber());
+ Assert.assertEquals(true, pdu0.isRequest());
+ Assert.assertEquals(0x01, pdu0.getSourceAddress().getTon());
+ Assert.assertEquals(0x01, pdu0.getSourceAddress().getNpi());
+ Assert.assertEquals("5552710000", pdu0.getSourceAddress().getAddress());
+ Assert.assertEquals("150203040506708+", pdu0.getScheduleDeliveryTime());
+ Assert.assertEquals("010203040506000R", pdu0.getValidityPeriod());
+ Assert.assertEquals((byte)0x01, pdu0.getRegisteredDelivery());
+ Assert.assertEquals((byte)0x02, pdu0.getDefaultMsgId());
+ Assert.assertEquals("text", new String(pdu0.getShortMessage(), "ISO-8859-1"));
+
+ Assert.assertEquals(null, pdu0.getOptionalParameters());
+
+ Assert.assertEquals(0, buffer.readableBytes());
+ }
+
+ @Test
+ public void decodeReplaceSmResp() throws Exception {
+ ByteBuf buffer = BufferHelper.createBuffer("00000010800000070000000200004FE8");
+
+ ReplaceSmResp pdu0 = (ReplaceSmResp)transcoder.decode(buffer);
+
+ Assert.assertEquals(16, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_REPLACE_SM_RESP, pdu0.getCommandId());
+ Assert.assertEquals(true, pdu0.isResponse());
+ Assert.assertEquals(2, pdu0.getCommandStatus());
+ Assert.assertEquals(20456, pdu0.getSequenceNumber());
+
+ Assert.assertEquals(0, buffer.readableBytes());
+ }
+
+ @Test
+ public void decodeAlertNotification() throws Exception {
+ ByteBuf buffer = BufferHelper.createBuffer("00000025000001020000000200004FE8010135353532373130303030000101343034303400");
+
+ AlertNotification pdu0 = (AlertNotification)transcoder.decode(buffer);
+
+ Assert.assertEquals(37, pdu0.getCommandLength());
+ Assert.assertEquals(SmppConstants.CMD_ID_ALERT_NOTIFICATION, pdu0.getCommandId());
+ Assert.assertEquals(false, pdu0.isResponse());
+ Assert.assertEquals(2, pdu0.getCommandStatus());
+ Assert.assertEquals(20456, pdu0.getSequenceNumber());
+ Assert.assertEquals("5552710000", pdu0.getSourceAddress().getAddress());
+ Assert.assertEquals("40404", pdu0.getEsmeAddress().getAddress());
+
+ Assert.assertEquals(0, buffer.readableBytes());
+ }
}
diff --git a/src/test/java/com/cloudhopper/smpp/transcoder/PduEncoderTest.java b/src/test/java/com/cloudhopper/smpp/transcoder/PduEncoderTest.java
index d75b264a..2604e922 100644
--- a/src/test/java/com/cloudhopper/smpp/transcoder/PduEncoderTest.java
+++ b/src/test/java/com/cloudhopper/smpp/transcoder/PduEncoderTest.java
@@ -542,4 +542,56 @@ public void encodeQueryRespSm() throws Exception {
// logger.debug("{}", HexUtil.toHexString(BufferHelper.createByteArray(buffer)));
Assert.assertArrayEquals(HexUtil.toByteArray("00000019800000030000000000004FE8313233343500000600"), BufferHelper.createByteArray(buffer));
}
+
+
+ @Test
+ public void encodeReplaceSM() throws Exception {
+ ReplaceSm pdu0 = new ReplaceSm();
+
+ pdu0.setSequenceNumber(20456);
+ pdu0.setMessageId("msg-12345");
+ pdu0.setSourceAddress(new Address((byte)0x01, (byte)0x01, "5552710000"));
+ pdu0.setScheduleDeliveryTime("150203040506708+");
+ pdu0.setValidityPeriod("010203040506000R");
+ pdu0.setRegisteredDelivery((byte)0x01);
+ pdu0.setDefaultMsgId((byte)0x02);
+ pdu0.setShortMessage("text".getBytes("ISO-8859-1"));
+
+ ByteBuf buffer = transcoder.encode(pdu0);
+
+ String expectedHex = "00000050000000070000000000004FE86D73672D313233343500010135353532373130303030003135303230333034303530363730382B00303130323033303430353036303030520001020474657874";
+ String actualHex = HexUtil.toHexString(BufferHelper.createByteArray(buffer)).toUpperCase();
+
+ Assert.assertEquals(expectedHex, actualHex);
+ }
+
+ @Test
+ public void encodeReplaceSmResp() throws Exception {
+ ReplaceSmResp pdu0 = new ReplaceSmResp();
+
+ pdu0.setSequenceNumber(20456);
+ pdu0.setCommandStatus( 2 );
+
+ ByteBuf buffer = transcoder.encode(pdu0);
+
+ String expectedHex = "00000010800000070000000200004FE8";
+ String actualHex = HexUtil.toHexString(BufferHelper.createByteArray(buffer)).toUpperCase();
+ Assert.assertEquals(expectedHex, actualHex);
+ }
+
+ @Test
+ public void encodeAlertNotification() throws Exception {
+ AlertNotification pdu0 = new AlertNotification();
+
+ pdu0.setSequenceNumber(20456);
+ pdu0.setCommandStatus( 2 );
+ pdu0.setSourceAddress( new Address((byte)0x01, (byte)0x01, "5552710000") );
+ pdu0.setEsmeAddress( new Address((byte)0x01, (byte)0x01, "40404") );
+
+ ByteBuf buffer = transcoder.encode(pdu0);
+
+ String expectedHex = "00000025000001020000000200004FE8010135353532373130303030000101343034303400";
+ String actualHex = HexUtil.toHexString(BufferHelper.createByteArray(buffer)).toUpperCase();
+ Assert.assertEquals(expectedHex, actualHex);
+ }
}