/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/Session.java

http://mobicents.googlecode.com/ · Java · 411 lines · 338 code · 44 blank · 29 comment · 57 complexity · 378b50224ddc38ea07da6183a5038dfe MD5 · raw file

  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.protocols.smpp;
  23. import java.io.IOException;
  24. import java.net.UnknownHostException;
  25. import java.util.Collection;
  26. import java.util.concurrent.atomic.AtomicInteger;
  27. import java.util.concurrent.atomic.AtomicReference;
  28. import org.slf4j.Logger;
  29. import org.slf4j.LoggerFactory;
  30. import org.mobicents.protocols.smpp.event.EventDispatcher;
  31. import org.mobicents.protocols.smpp.event.SessionObserver;
  32. import org.mobicents.protocols.smpp.event.SimpleEventDispatcher;
  33. import org.mobicents.protocols.smpp.message.Bind;
  34. import org.mobicents.protocols.smpp.message.BindReceiver;
  35. import org.mobicents.protocols.smpp.message.BindResp;
  36. import org.mobicents.protocols.smpp.message.BindTransceiver;
  37. import org.mobicents.protocols.smpp.message.BindTransmitter;
  38. import org.mobicents.protocols.smpp.message.CommandId;
  39. import org.mobicents.protocols.smpp.message.SMPPPacket;
  40. import org.mobicents.protocols.smpp.message.Unbind;
  41. import org.mobicents.protocols.smpp.message.UnbindResp;
  42. import org.mobicents.protocols.smpp.message.tlv.Tag;
  43. import org.mobicents.protocols.smpp.net.SmscLink;
  44. import org.mobicents.protocols.smpp.net.TcpLink;
  45. import org.mobicents.protocols.smpp.util.APIConfig;
  46. import org.mobicents.protocols.smpp.util.APIConfigFactory;
  47. import org.mobicents.protocols.smpp.util.DefaultSequenceScheme;
  48. import org.mobicents.protocols.smpp.util.PropertyNotFoundException;
  49. import org.mobicents.protocols.smpp.util.SequenceNumberScheme;
  50. import org.mobicents.protocols.smpp.version.SMPPVersion;
  51. import org.mobicents.protocols.smpp.version.VersionException;
  52. import org.mobicents.protocols.smpp.version.VersionFactory;
  53. // TODO documentation!
  54. /**
  55. * @author amit bhayani
  56. * @author orank
  57. */
  58. public class Session {
  59. private static final AtomicInteger SESSION_ID = new AtomicInteger(1);
  60. private final Logger log;
  61. private String sessionId;
  62. private SMPPVersion version = VersionFactory.getDefaultVersion();
  63. private SessionType type;
  64. private AtomicReference<SessionState> state =
  65. new AtomicReference<SessionState>(SessionState.UNBOUND);
  66. private SmscLink smscLink;
  67. private SequenceNumberScheme numberScheme = new DefaultSequenceScheme();
  68. private EventDispatcher eventDispatcher;
  69. private Receiver receiver;
  70. private boolean useOptionalParams = version.isSupportTLV();
  71. private boolean validating = true;
  72. public Session(SmscLink link) {
  73. sessionId = "Session-" + SESSION_ID.getAndIncrement();
  74. log = LoggerFactory.getLogger(Session.class + "." + sessionId);
  75. this.smscLink = link;
  76. initFromConfig();
  77. }
  78. public Session(String host, int port) throws UnknownHostException {
  79. this(new TcpLink(host, port));
  80. }
  81. public String getSessionId() {
  82. return sessionId;
  83. }
  84. public void addObserver(SessionObserver observer) {
  85. eventDispatcher.addObserver(observer);
  86. }
  87. public void removeObserver(SessionObserver observer) {
  88. eventDispatcher.removeObserver(observer);
  89. }
  90. public SmscLink getSmscLink() {
  91. return smscLink;
  92. }
  93. public EventDispatcher getEventDispatcher() {
  94. return eventDispatcher;
  95. }
  96. public void setEventDispatcher(EventDispatcher eventDispatcher) {
  97. EventDispatcher oldDispatcher = this.eventDispatcher;
  98. initNewDispatcher(oldDispatcher, eventDispatcher);
  99. this.eventDispatcher = eventDispatcher;
  100. if(oldDispatcher!=null){
  101. oldDispatcher.destroy();
  102. }
  103. }
  104. public SMPPVersion getVersion() {
  105. return version;
  106. }
  107. public void setVersion(SMPPVersion version) {
  108. this.version = version;
  109. this.useOptionalParams = version.isSupportTLV();
  110. }
  111. public SequenceNumberScheme getSequenceNumberScheme() {
  112. return numberScheme;
  113. }
  114. public void setSequenceNumberScheme(SequenceNumberScheme numberScheme) {
  115. this.numberScheme = numberScheme;
  116. }
  117. public boolean isValidating() {
  118. return validating;
  119. }
  120. public void setValidating(boolean validating) {
  121. this.validating = validating;
  122. }
  123. public void bind(SessionType type,
  124. String systemID,
  125. String password,
  126. String systemType) throws IOException {
  127. bind(type, systemID, password, systemType, 0, 0, null);
  128. }
  129. public void bind(SessionType type,
  130. String systemID,
  131. String password,
  132. String systemType,
  133. int typeOfNumber,
  134. int numberPlanIndicator,
  135. String addressRange) throws IOException {
  136. Bind bindRequest;
  137. if (type == SessionType.TRANSMITTER) {
  138. bindRequest = new BindTransmitter();
  139. } else if (type == SessionType.RECEIVER) {
  140. bindRequest = new BindReceiver();
  141. } else {
  142. bindRequest = new BindTransceiver();
  143. }
  144. bindRequest.setVersion(version);
  145. bindRequest.setSystemId(systemID);
  146. bindRequest.setPassword(password);
  147. bindRequest.setSystemType(systemType);
  148. bindRequest.setAddressTon(typeOfNumber);
  149. bindRequest.setAddressNpi(numberPlanIndicator);
  150. bindRequest.setAddressRange(addressRange);
  151. bind(bindRequest);
  152. }
  153. public void bind(Bind bindRequest) throws IOException {
  154. if (receiver == null) {
  155. initReceiver();
  156. }
  157. if (getState() != SessionState.UNBOUND) {
  158. throw new IllegalStateException("Already binding or bound.");
  159. }
  160. if (bindRequest.getCommandId() == CommandId.BIND_TRANSMITTER) {
  161. type = SessionType.TRANSMITTER;
  162. } else if (bindRequest.getCommandId() == CommandId.BIND_RECEIVER) {
  163. type = SessionType.RECEIVER;
  164. } else {
  165. type = SessionType.TRANSCEIVER;
  166. }
  167. if (!smscLink.isConnected()) {
  168. smscLink.connect();
  169. }
  170. setLinkTimeout(APIConfig.BIND_TIMEOUT);
  171. log.debug("Sending bind packet to the SMSC..");
  172. sendPacketInternal(bindRequest);
  173. receiver.start();
  174. }
  175. public void unbind() throws IOException {
  176. sendPacketInternal(new Unbind());
  177. }
  178. public void sendPacket(SMPPPacket packet) throws IOException {
  179. int commandId = packet.getCommandId();
  180. switch (commandId) {
  181. case CommandId.BIND_TRANSMITTER:
  182. case CommandId.BIND_TRANSCEIVER:
  183. case CommandId.BIND_RECEIVER:
  184. bind((Bind) packet);
  185. return;
  186. }
  187. if (type == SessionType.RECEIVER) {
  188. // We allow the receiver to send any response type but a very
  189. // limited set of requests.
  190. if (packet.isRequest()
  191. && !(commandId == CommandId.UNBIND
  192. || commandId == CommandId.ENQUIRE_LINK)) {
  193. throw new UnsupportedOperationException(
  194. "Receiver connection cannot send command " + commandId);
  195. }
  196. }
  197. sendPacketInternal(packet);
  198. }
  199. public void closeLink() throws IOException {
  200. if (getState() == SessionState.UNBOUND || getState() == SessionState.UNBINDING) {
  201. smscLink.disconnect();
  202. } else {
  203. throw new IllegalStateException("Cannot close link while connection is bound.");
  204. }
  205. }
  206. public SessionState getState() {
  207. return state.get();
  208. }
  209. public Receiver getReceiver(Receiver receiver) {
  210. return receiver;
  211. }
  212. public void setReceiver(Receiver receiver) {
  213. if (this.receiver != null && this.receiver.isStarted()) {
  214. throw new IllegalStateException(
  215. "Cannot change the receiver while it's running");
  216. }
  217. this.receiver = receiver;
  218. }
  219. public void processReceivedPacket(SMPPPacket packet) {
  220. switch (packet.getCommandId()) {
  221. case CommandId.BIND_TRANSMITTER_RESP:
  222. case CommandId.BIND_RECEIVER_RESP:
  223. case CommandId.BIND_TRANSCEIVER_RESP:
  224. processReceivedBindResponse((BindResp) packet);
  225. break;
  226. case CommandId.UNBIND:
  227. processReceivedUnbind((Unbind) packet);
  228. break;
  229. case CommandId.UNBIND_RESP:
  230. processReceivedUnbindResponse((UnbindResp) packet);
  231. break;
  232. default:
  233. // Do nothing.
  234. }
  235. }
  236. private void setState(SessionState fromState, SessionState toState) {
  237. if (!state.compareAndSet(fromState, toState)) {
  238. log.error("Race condition in setting state - expected {} but is {}. New value is "+ toState,
  239. fromState, getState());
  240. }
  241. }
  242. private void initFromConfig() {
  243. APIConfig config = APIConfigFactory.getConfig();
  244. EventDispatcher dispatcher;
  245. try {
  246. dispatcher = config.getClassInstance(
  247. APIConfig.EVENT_DISPATCHER_CLASS, EventDispatcher.class);
  248. } catch (PropertyNotFoundException x) {
  249. log.debug("Config does not specify an event dispatcher. Using {}",
  250. SimpleEventDispatcher.class);
  251. dispatcher = new SimpleEventDispatcher();
  252. }
  253. setEventDispatcher(dispatcher);
  254. }
  255. private void initReceiver() {
  256. receiver = new ReceiverThread(this);
  257. receiver.setName(sessionId + "-Receiver");
  258. }
  259. private void initNewDispatcher(EventDispatcher oldDispatcher, EventDispatcher newDispatcher) {
  260. newDispatcher.init();
  261. if (oldDispatcher != null) {
  262. Collection<SessionObserver> observers =
  263. oldDispatcher.getObservers();
  264. for (SessionObserver observer : observers) {
  265. newDispatcher.addObserver(observer);
  266. }
  267. }
  268. }
  269. private void sendPacketInternal(SMPPPacket packet) throws IOException {
  270. if (packet.getSequenceNum() < 0L && numberScheme != null) {
  271. packet.setSequenceNum(numberScheme.nextNumber());
  272. }
  273. if (validating) {
  274. packet.validate(version);
  275. }
  276. smscLink.write(packet, useOptionalParams);
  277. processSentPacket(packet);
  278. }
  279. private void processSentPacket(SMPPPacket packet) {
  280. switch (packet.getCommandId()) {
  281. case CommandId.BIND_TRANSMITTER:
  282. case CommandId.BIND_RECEIVER:
  283. case CommandId.BIND_TRANSCEIVER:
  284. processSentBind((Bind) packet);
  285. break;
  286. case CommandId.UNBIND:
  287. processSentUnbind((Unbind) packet);
  288. break;
  289. case CommandId.UNBIND_RESP:
  290. processSentUnbindResponse((UnbindResp) packet);
  291. break;
  292. default:
  293. }
  294. }
  295. private void processSentBind(Bind bindRequest) {
  296. setState(SessionState.UNBOUND, SessionState.BINDING);
  297. }
  298. private void processSentUnbind(Unbind unbindRequest) {
  299. setState(SessionState.BOUND, SessionState.UNBINDING);
  300. }
  301. private void processSentUnbindResponse(UnbindResp unbindResponse) {
  302. int status = unbindResponse.getCommandStatus();
  303. if (status == 0) {
  304. setState(SessionState.UNBINDING, SessionState.UNBOUND);
  305. }
  306. }
  307. private void processReceivedBindResponse(BindResp bindResponse) {
  308. int status = bindResponse.getCommandStatus();
  309. if (status == 0) {
  310. setState(SessionState.BINDING, SessionState.BOUND);
  311. negotiateVersion(bindResponse);
  312. setLinkTimeout(APIConfig.LINK_TIMEOUT);
  313. } else {
  314. log.warn("Received a bind response with status {}", status);
  315. setState(SessionState.BINDING, SessionState.UNBOUND);
  316. }
  317. }
  318. private void negotiateVersion(BindResp bindResponse) {
  319. if (!bindResponse.isSet(Tag.SC_INTERFACE_VERSION)) {
  320. log.info("SMSC did not supply SC_INTERFACE_VERSION."
  321. + " Disabling optional parameter support.");
  322. useOptionalParams = false;
  323. return;
  324. }
  325. int versionId = 0;
  326. try {
  327. versionId =
  328. bindResponse.getTLVTable().getInt(Tag.SC_INTERFACE_VERSION);
  329. SMPPVersion smscVersion =
  330. VersionFactory.getVersion(versionId);
  331. log.info("SMSC states its version as {}", smscVersion);
  332. if (smscVersion.isOlderThan(version)) {
  333. version = smscVersion;
  334. useOptionalParams = version.isSupportTLV();
  335. }
  336. } catch (VersionException x) {
  337. log.debug("SMSC implements a version I don't know: {}", versionId);
  338. }
  339. }
  340. private void processReceivedUnbind(Unbind unbindRequest) {
  341. setState(SessionState.BOUND, SessionState.UNBINDING);
  342. }
  343. private void processReceivedUnbindResponse(UnbindResp unbindResponse) {
  344. int status = unbindResponse.getCommandStatus();
  345. if (status == 0) {
  346. setState(SessionState.UNBINDING, SessionState.UNBOUND);
  347. } else {
  348. log.warn("Received an unbind response with status {}", status);
  349. }
  350. }
  351. private void setLinkTimeout(String propName) {
  352. try {
  353. if (smscLink.isTimeoutSupported()) {
  354. APIConfig config = APIConfigFactory.getConfig();
  355. int linkTimeout = config.getInt(propName);
  356. smscLink.setTimeout(linkTimeout);
  357. if (log.isDebugEnabled()) {
  358. log.debug("Set the link timeout to {}", linkTimeout);
  359. }
  360. } else {
  361. log.info("SMSC link implementation does not support timeouts.");
  362. }
  363. } catch (PropertyNotFoundException x) {
  364. log.debug("Not setting link timeout as it is not configured.");
  365. }
  366. }
  367. }