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