PageRenderTime 377ms CodeModel.GetById 359ms RepoModel.GetById 0ms app.codeStats 0ms

/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/net/AbstractStreamLink.java

http://mobicents.googlecode.com/
Java | 357 lines | 183 code | 24 blank | 150 comment | 20 complexity | 13336013b66cee11e8d41dc657788ccf MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  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.net;
  23. import java.io.EOFException;
  24. import java.io.IOException;
  25. import java.io.InputStream;
  26. import java.io.OutputStream;
  27. import java.net.SocketTimeoutException;
  28. import org.slf4j.Logger;
  29. import org.slf4j.LoggerFactory;
  30. import org.mobicents.protocols.smpp.message.SMPPPacket;
  31. import org.mobicents.protocols.smpp.util.APIConfig;
  32. import org.mobicents.protocols.smpp.util.APIConfigFactory;
  33. import org.mobicents.protocols.smpp.util.PacketDecoder;
  34. import org.mobicents.protocols.smpp.util.PacketDecoderImpl;
  35. import org.mobicents.protocols.smpp.util.PacketEncoder;
  36. import org.mobicents.protocols.smpp.util.PacketEncoderImpl;
  37. import org.mobicents.protocols.smpp.util.PacketFactory;
  38. import org.mobicents.protocols.smpp.util.PropertyNotFoundException;
  39. import org.mobicents.protocols.smpp.util.SMPPIO;
  40. /**
  41. * Abstract base implementation of the {@link org.mobicents.protocols.smpp.net.SmscLink}
  42. * interface that operates on java.io streams.
  43. * <p>
  44. * This class implements basic read and write functionality that can be
  45. * re-used by other implementations.
  46. * </p>
  47. * <p>
  48. * Concrete implementations of this class <b>must</b> call both
  49. * {@link #setInputStream(InputStream)} and
  50. * {@link #setOutputStream(OutputStream)} during the
  51. * {@link SmscLink#connect()} operation in order to set up instances of
  52. * this class properly.
  53. * </p>
  54. * @version $Id: AbstractStreamLink.java 457 2009-01-15 17:37:42Z orank $
  55. *
  56. * @author amit bhayani
  57. * @author orank
  58. */
  59. public abstract class AbstractStreamLink implements SmscLink {
  60. private static final String END_OF_STREAM_ERR = "EOS reached. No data available";
  61. private static final String LINK_NOT_UP_ERR = "Link not established.";
  62. private static final Logger LOG = LoggerFactory.getLogger(SmscLink.class);
  63. private byte[] buffer;
  64. private InputStream in;
  65. private OutputStream out;
  66. private OutputStream snoopIn;
  67. private OutputStream snoopOut;
  68. private PacketEncoder encoder;
  69. private PacketDecoder decoder;
  70. private PacketEncoder snoopOutEncoder;
  71. private PacketFactory packetFactory = new PacketFactory();
  72. /**
  73. * Set to automatically flush the output stream after every packet. Default
  74. * is <code>false</code>, but can be reconfigured in the API config.
  75. */
  76. private boolean autoFlush;
  77. /**
  78. * Create a new unconnected SmscLink.
  79. */
  80. public AbstractStreamLink() {
  81. try {
  82. APIConfig config = APIConfigFactory.getConfig();
  83. autoFlush = config.getBoolean(APIConfig.LINK_AUTO_FLUSH);
  84. } catch (PropertyNotFoundException x) {
  85. autoFlush = true;
  86. } finally {
  87. LOG.debug("autoFlush set to {}", autoFlush);
  88. }
  89. }
  90. /**
  91. * Close the connection to the SMSC. Calling this method will close the
  92. * network link to the remote SMSC system. Applications should be unbound
  93. * from the SMPP link (using {@link org.mobicents.protocols.smpp.Session#unbind}) before
  94. * closing the underlying network link. The connection may be reestablished
  95. * using {@link #open}.
  96. *
  97. * @throws java.io.IOException
  98. * If an exception occurs while closing the connection.
  99. */
  100. public void disconnect() throws IOException {
  101. out = null;
  102. in = null;
  103. buffer = null;
  104. encoder = null;
  105. decoder = null;
  106. if (isAutoCloseSnoop()) {
  107. closeQuietly(snoopOut);
  108. closeQuietly(snoopIn);
  109. } else {
  110. flushQuietly(snoopOut);
  111. flushQuietly(snoopIn);
  112. }
  113. }
  114. /**
  115. * Send a packet to the SMSC.
  116. *
  117. * @param pak
  118. * the SMPP packet to send.
  119. * @param withOptional
  120. * true to send the optional parameters over the link too, false
  121. * to only send the mandatory parameters.
  122. * @throws java.io.IOException
  123. * if an exception occurs during writing or if the connection is
  124. * not open.
  125. */
  126. public void write(SMPPPacket pak, boolean withOptional) throws IOException {
  127. if (out == null) {
  128. throw new IOException(LINK_NOT_UP_ERR);
  129. }
  130. pak.writeTo(encoder, withOptional);
  131. try {
  132. if (snoopOutEncoder != null) {
  133. pak.writeTo(snoopOutEncoder, withOptional);
  134. }
  135. } catch (IOException x) {
  136. LOG.warn("IOException writing to snoop output stream.", x);
  137. }
  138. if (autoFlush) {
  139. out.flush();
  140. }
  141. }
  142. /**
  143. * Flush the output stream of the SMSC link.
  144. *
  145. * @throws java.io.IOException
  146. * If an exception occurs while flushing the output stream.
  147. */
  148. public void flush() throws IOException {
  149. if (out != null) {
  150. out.flush();
  151. }
  152. }
  153. /**
  154. * Get the auto flush behaviour of this link. The default behaviour is
  155. * defined in the smppapi properties file. If no properties are found at
  156. * runtime, the default behaviour is set to <code>true</code>.
  157. *
  158. * @see #setAutoFlush
  159. * @see org.mobicents.protocols.smpp.util.APIConfig
  160. */
  161. public boolean getAutoFlush() {
  162. return autoFlush;
  163. }
  164. /**
  165. * Set the auto flush behaviour of this link. If set to true, the link will
  166. * flush the output stream after every packet written. In high-load
  167. * environments this may be undesirable.
  168. *
  169. * @see #getAutoFlush
  170. */
  171. public void setAutoFlush(boolean flush) {
  172. this.autoFlush = flush;
  173. }
  174. /**
  175. * Read the next SMPP packet from the SMSC. This method will block until a
  176. * full packet can be read from the SMSC. The caller should pass in a byte
  177. * array to read the packet into. If the passed in byte array is too small,
  178. * a new one will be allocated and returned to the caller.
  179. *
  180. * @param array
  181. * a byte array buffer to read the packet into.
  182. * @return the handle to the passed in buffer or the reallocated one.
  183. * @throws java.io.EOFException
  184. * If the end of stream is reached before a full packet can be
  185. * read.
  186. * @throws java.io.IOException
  187. * If an exception occurs when reading the packet from the input
  188. * stream.
  189. */
  190. public SMPPPacket read() throws IOException {
  191. if (in == null) {
  192. throw new IOException(LINK_NOT_UP_ERR);
  193. }
  194. int count = 0;
  195. try {
  196. count = readBytes(buffer, 0, 4, 16);
  197. int cmdLen = SMPPIO.readInt4(buffer, 0);
  198. if (cmdLen > buffer.length) {
  199. byte[] newbuf = new byte[cmdLen];
  200. System.arraycopy(buffer, 0, newbuf, 0, count);
  201. buffer = newbuf;
  202. decoder = new PacketDecoderImpl(buffer);
  203. }
  204. int remaining = cmdLen - count;
  205. readBytes(buffer, count, remaining, remaining);
  206. int commandId = SMPPIO.readInt4(buffer, 4);
  207. SMPPPacket packet = packetFactory.newInstance(commandId);
  208. decoder.setParsePosition(0);
  209. packet.readFrom(decoder);
  210. return packet;
  211. } catch (SocketTimeoutException x) {
  212. throw new ReadTimeoutException(x);
  213. } finally {
  214. dump(snoopIn, buffer, 0, count);
  215. }
  216. }
  217. /**
  218. * Get the number of bytes currently available on the input stream.
  219. */
  220. public final int available() {
  221. try {
  222. return (in != null) ? in.available() : 0;
  223. } catch (IOException x) {
  224. LOG.debug("IOException in available", x);
  225. return 0;
  226. }
  227. }
  228. /**
  229. * Set the snooper streams. The snooper streams will receive every byte that
  230. * is either received or sent using this class. This functionality is
  231. * intended as a debugging aid for SMPP developers. It will be up to the
  232. * application using the API to provide valid output streams for the data to
  233. * be written to. Either or both of the streams may be set to null, which in
  234. * effect turns off snooping.
  235. *
  236. * @param snoopIn
  237. * stream to receive incoming bytes from the SMSC (may be null).
  238. * @param snoopOut
  239. * stream to receive outgoing bytes to the SMSC (may be null).
  240. */
  241. public void setSnoopStreams(OutputStream snoopIn, OutputStream snoopOut) {
  242. this.snoopIn = snoopIn;
  243. this.snoopOut = snoopOut;
  244. snoopOutEncoder = new PacketEncoderImpl(snoopOut);
  245. }
  246. protected void setInputStream(InputStream inputStream) {
  247. this.in = inputStream;
  248. buffer = new byte[512];
  249. this.decoder = new PacketDecoderImpl(buffer);
  250. }
  251. protected void setOutputStream(OutputStream outputStream) {
  252. this.out = outputStream;
  253. this.encoder = new PacketEncoderImpl(this.out);
  254. }
  255. /**
  256. * Attempt to read the bytes for an SMPP packet from the inbound stream.
  257. * @param buf The buffer to read bytes in to.
  258. * @param offset The offset into buffer to begin writing bytes from.
  259. * @param maxLen The maximum number of bytes to read in.
  260. * @param minimum The minimum number of bytes to read before returning. Once
  261. * this method has read at least this number of bytes, it will return.
  262. * @return The number of bytes read by this method.
  263. * @throws IOException
  264. */
  265. private int readBytes(byte[] buf, int offset, int minimum, int maxLen) throws IOException {
  266. int ptr = in.read(buf, offset, maxLen);
  267. if (ptr < minimum) {
  268. if (ptr == -1) {
  269. throw new EOFException(END_OF_STREAM_ERR);
  270. }
  271. while (ptr < minimum) {
  272. int count = in.read(buf, offset + ptr, maxLen - ptr);
  273. if (count < 0) {
  274. throw new EOFException(END_OF_STREAM_ERR);
  275. }
  276. ptr += count;
  277. }
  278. }
  279. return ptr;
  280. }
  281. /**
  282. * Dump bytes to an output stream.
  283. *
  284. * @param s
  285. * the stream to write to (if null, do nothing).
  286. * @param b
  287. * the byte array to dump bytes from.
  288. * @param offset
  289. * the offset in <code>b</code> to begin from.
  290. * @param len
  291. * the number of bytes to dump.
  292. */
  293. private void dump(OutputStream s, byte[] b, int offset, int len) {
  294. try {
  295. if (s != null) {
  296. s.write(b, offset, len);
  297. }
  298. } catch (IOException x) {
  299. LOG.warn("Couldn't write incoming bytes to input snooper.", x);
  300. }
  301. }
  302. /**
  303. * Check the configuration to see if the snoop streams should be
  304. * automatically closed when this link is closed.
  305. * @return <code>true</code> to close the snoop streams when
  306. * {@link #close} is called, <code>false</code> otherwise.
  307. */
  308. private boolean isAutoCloseSnoop() {
  309. boolean autoClose = true;
  310. try {
  311. APIConfig config = APIConfigFactory.getConfig();
  312. autoClose = config.getBoolean(APIConfig.LINK_AUTOCLOSE_SNOOP);
  313. } catch (PropertyNotFoundException x) {
  314. LOG.debug("{} property not found. Using the default of {}",
  315. APIConfig.LINK_AUTOCLOSE_SNOOP, autoClose);
  316. }
  317. return autoClose;
  318. }
  319. private void closeQuietly(OutputStream stream) {
  320. try {
  321. stream.close();
  322. } catch (IOException x) {
  323. LOG.debug("Exception closing a stream quietly", x);
  324. }
  325. }
  326. private void flushQuietly(OutputStream stream) {
  327. try {
  328. stream.flush();
  329. } catch (IOException x) {
  330. LOG.debug("Exception flushing a stream quietly", x);
  331. }
  332. }
  333. }