package cn.richinfo.cmail.vasp.common.util;
import ie.omk.smpp.Address;
import ie.omk.smpp.Connection;
import ie.omk.smpp.message.BindResp;
import ie.omk.smpp.message.EnquireLink;
import ie.omk.smpp.message.EnquireLinkResp;
import ie.omk.smpp.message.SMPPPacket;
import ie.omk.smpp.message.SMPPResponse;
import ie.omk.smpp.message.SubmitSM;
import ie.omk.smpp.message.SubmitSMResp;
import ie.omk.smpp.util.EncodingFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import cn.richinfo.cmail.common.config.PropertiesUtil;
/**
* @todo 长连接发短信
* @date 2017年8月15日
* @author linying
*/
public class SMPPUtil {
private Logger logger = Logger.getLogger(SMPPUtil.class);
private boolean connected = false; // 是否已连接
private boolean received = false; // 是否接收心跳包
private Connection conn = null;// ESME到SMSC的连接
private SMPPUtil() {
}
public static final SMPPUtil getInstance() {
return SMPPUtilHolder.SMPP_UTIL;
}
/**
* @todo 打开链接
* @throws Exception
*/
private void openLink() throws Exception {
logger.info("open smsc service...");
String host = PropertiesUtil.getProperty("zte.notice.smsc.host");
int port = PropertiesUtil.getPropertyInt("zte.notice.smsc.port", 0);
String systemId = PropertiesUtil.getProperty("zte.notice.smsc.systemId");
String password = PropertiesUtil.getProperty("zte.notice.smsc.password");
String systemType = PropertiesUtil.getProperty("zte.notice.smsc.systemType");
logger.info(String.format("openLink parameter | host=%s | port=%s | systemId=%s | password=%s | systemType=%s", host, port, systemId, password, systemType));
if (StringUtils.isEmpty(host) || StringUtils.isEmpty(systemId) || StringUtils.isEmpty(password) || StringUtils.isEmpty(systemType) || port == 0) {
logger.error("parameter is empty");
return;
}
conn = new Connection(host, port);
conn.autoAckLink(true);
conn.autoAckMessages(true);
BindResp resp = conn.bind(Connection.TRANSMITTER, systemId, password, systemType);
if (null != resp && resp.getCommandStatus() == 0) {
connected = true;
// new Thread(new KeepHeartbeat()).start(); // 发送心跳
new Thread(new ReceivePackets()).start(); // 接收心跳
}
}
/**
*
* @todo 发送心跳
* @date 2017年8月15日
* @author linying
*/
class KeepHeartbeat implements Runnable {
private long delay = PropertiesUtil.getPropertyLong("zte.notice.smsc.keep.delay");
public void run() {
while (connected) {
try {
Thread.sleep(delay);// 心跳延时
EnquireLinkResp resp = conn.enquireLink();
if (null != resp && resp.getCommandStatus() == 0) {
logger.info("[system request packet ok]");
} else {
logger.info("[system request packet err]");
}
} catch (Exception e) {
logger.error(e);
closeLink();
}
}
}
}
/**
*
* @todo 接收心跳
* @date 2017年8月15日
* @author linying
*/
class ReceivePackets implements Runnable {
public void run() {
while (connected) {
try {
if (received) {
SMPPPacket packet = conn.readNextPacket();
if (null != packet && packet.getCommandStatus() == 0) {
logger.info("[smsc request packet ok]");
EnquireLink link = new EnquireLink();
link.setSequenceNum(packet.getSequenceNum());
conn.ackEnquireLink(link);
} else {
logger.info("[smsc request packet err]");
}
}
logger.info("[smsc request packet stop]");
Thread.sleep(10000);
} catch (Exception e) {
logger.error(e);
closeLink();
}
}
}
}
/**
* @todo 关闭链接
* @return void
*/
private void closeLink() {
logger.info("close smsc service...");
connected = false;
received = false;
try {
if (conn != null && conn.isBound()) {
conn.unbind();
}
conn = null;
} catch (Exception e) {
logger.error(e);
}
}
/**
* @todo 发送短信
* @param address
* @param text
* @return String
* @throws Exception
*/
public String sendSms(String address, String text) throws Exception {
int ton = PropertiesUtil.getPropertyInt("zte.notice.sms.ton", 0);
int npi = PropertiesUtil.getPropertyInt("zte.notice.sms.npi", 0);
String source = PropertiesUtil.getProperty("zte.notice.sms.sourceAddress");
int encode = PropertiesUtil.getPropertyInt("zte.notice.sms.encoding", 8);
int count = 1;
return sendSms(ton, npi, source, address, text, encode, count);
}
private String sendSms(int ton, int npi, String source, String address, String text, int encode, int count) throws Exception {
// 发送短信中,停止接收心跳
received = false;
// 没有绑定
if (!connected) {
openLink();
}
logger.info("connected is true");
SubmitSM sm = (SubmitSM) conn.newInstance(SMPPPacket.SUBMIT_SM);
if (StringUtils.isNotEmpty(source)) {
sm.setSource(new Address(ton, npi, source));
}
sm.setDestination(new Address(0, 0, address));
sm.setMessageText(text);
sm.setMessageEncoding(EncodingFactory.getInstance().getEncoding(encode));
SMPPResponse response = (SubmitSMResp) conn.sendRequest(sm);
received = true;
// 发送失败,递归重试3次
if (response == null || response.getCommandStatus() != 0) {
Thread.sleep(300);
logger.error("send failed is retrying " + count);
count++;
if (count > 3) {
return "send failed";
}
sendSms(ton, npi, source, address, text, encode, count);
}
logger.info("send success");
return "send success";
}
/**
* @todo SMPPUtil单例
* @date 2017年8月15日
* @author linying
*/
private static class SMPPUtilHolder {
private static final SMPPUtil SMPP_UTIL = new SMPPUtil();
}
}
#heart beat delay(milliseconds)
zte.notice.smsc.keep.delay=20000
#heart beat message
zte.notice.smsc.keep.message=message[send heart beat data package]
#stop the number of heart beats
zte.notice.smsc.keep.count=5
#the request conten to send to the SMSC
zte.notice.msg=【%s】你有1封新邮件,来自%s,主题是%s
#the hostname of the SMSC.
zte.notice.smsc.host=127.0.0.1
#the port to connect to. If 0, use the default SMPP port * number.
zte.notice.smsc.port=80
#the system ID to identify as to the SMSC.
zte.notice.smsc.systemId=0
#password to use to authenticate to the SMSC.
zte.notice.smsc.password=0
#the system type to bind as.
zte.notice.smsc.systemType=0
#SMS coding(1.US-ASCII, 3.ISO-8859-1, 8.ISO-10646-UCS-2)
zte.notice.sms.encoding=8
#the type of number
zte.notice.sms.ton=0
#the numbering plan indicator
zte.notice.sms.npi=0
#source address
zte.notice.sms.sourceAddress=88888888
#new mail notification URL
zte.notice.url=http://127.0.0.1/vaservices/TestURL?act=1&msisdn=%s