PageRenderTime 31ms CodeModel.GetById 17ms app.highlight 10ms RepoModel.GetById 1ms 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
  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}