org.smpp.Receiver Maven / Gradle / Ivy
/*
* Copyright (c) 1996-2001
* Logica Mobile Networks Limited
* All rights reserved.
*
* This software is distributed under Logica Open Source License Version 1.0
* ("Licence Agreement"). You shall use it and distribute only in accordance
* with the terms of the License Agreement.
*
*/
package org.smpp;
import java.io.IOException;
import org.smpp.pdu.GenericNack;
import org.smpp.pdu.InvalidPDUException;
import org.smpp.pdu.PDU;
import org.smpp.pdu.PDUException;
import org.smpp.pdu.UnknownCommandIdException;
import org.smpp.util.Queue;
import org.smpp.util.Unprocessed;
/**
* Receiver
is class used for receiving PDUs from SMSC.
* It can be used two ways: it has methods for synchronous (blocking)
* receiving of PDUs and as it is derived from ReceiverBase
* whic on turn is derived from ProcessingThread
class,
* it can also receive PDUs on background and puts them into a queue.
*
* @author Logica Mobile Networks SMPP Open Source Team
* @version $Revision: 1.2 $
* @see ReceiverBase
* @see Connection
* @see Session
* @see Queue
*/
public class Receiver extends ReceiverBase {
/**
* Name of the thread created when starting
* the ProcessingThread
.
* @see org.smpp.util.ProcessingThread#start()
* @see org.smpp.util.ProcessingThread#generateIndexedThreadName()
*/
private static final String RECEIVER_THREAD_NAME = "Receiver";
/**
* The correspondent transmitter for transmitting PDUs.
* It's used for sending of generic negative acknowledges, if necessary.
* It is passed to the receiver as a parameter during construction.
*
* @see #receiveAsync()
*/
private Transmitter transmitter = null;
/**
* The network connection which is used for receiving data.
* It is passed to the receiver as a parameter during construction.
*/
private Connection connection = null;
/**
* The queue which holds the received PDUs.
* As the PDUs are received in asynchronnous manner, they are stored
* to a queue from which they can be get using method
* receive
. PDUs are stored to the queue if and only if the
* Receiver
is started as a separate thread using method
* start
.
*
* @see #receive(long)
* @see #receive(PDU)
* @see #start()
* @see ReceiverBase#start()
*/
private Queue pduQueue = new Queue();
/**
* This timeout specifies for how long will go the receiving into wait
* if the PDU (expected or any) isn't in the pduQueue
yet.
* After that the queue is probed again (etc.) until receiving timeout
* expires or the PDU is received.
*
* @see #tryReceivePDU(Connection,PDU)
*/
private long queueWaitTimeout = Data.QUEUE_TIMEOUT;
/**
* Indication if the Receiver
is receiving on background as an
* extra thread.
*
* @see #start()
* @see #tryReceivePDU(Connection,PDU)
*/
private boolean receiver = false;
/**
* This object holds data received from connection which aren't complete
* PDU yet. If this situation occurs, it's likely that the data
* will be received the next time when another attempt
* to receive data from the connection occurs. Its used in
* ReceiverBase
's receivePDUFromConnection
* method.
*
* @see ReceiverBase#receivePDUFromConnection(Connection,Unprocessed)
*/
private Unprocessed unprocessed = new Unprocessed();
/**
* If the receiving is asynchronous, pduListener
must
* contain the callback object used for processing of PDUs received
* from the SMSC. Receiver
after receiving a PDU passes
* the received PDU to apropriate member function of the processor.
* @see #asynchronous
* @see #setServerPDUEventListener(ServerPDUEventListener)
*/
private ServerPDUEventListener pduListener = null;
/**
* Indicates that the sending of PDUs to the SMSC is asynchronous, i.e.
* the session doesn't wait for a response to the sent request as well as
* the receive
functions will return null as all received
* PDUs are passed to the pduListener
object in
* the receiver
* @see #pduListener
* @see #setServerPDUEventListener(ServerPDUEventListener)
*/
private boolean asynchronous = false;
/**
* If true then GenericNack messages will be sent automatically if message can't be parsed
* If false then the pdu will be submitted to listener for processing and submission to peer
*/
private boolean automaticNack = true;
/**
* This constructor sets the connection to receive the messages from.
*
* @param connection the connection to use for receiving
* @see Connection
*/
public Receiver(Connection connection) {
this.connection = connection;
}
/**
* This constructor sets the connection to receive the messages from
* and a transmitter for sending generic negative acknowledges
* if necessary.
*
* @param transmitter the transmitter to use for sending
* GenericNack
* @param connection the connection to use for receiving and transmitting
* @see Transmitter
* @see Connection
* @see GenericNack
*/
public Receiver(Transmitter transmitter, Connection connection) {
this.transmitter = transmitter;
this.connection = connection;
}
/**
* Returns if the receiver receives PDUs on background as an extra thread.
*
* @see #receiver
*/
public boolean isReceiver() {
return receiver;
}
/**
* Sets the event listener for asynchronous Receiver
.
* In case there are unprocessed PDUs in the queue,
* they are removed from the queue and passed to the newly set listener.
*/
public synchronized void setServerPDUEventListener(ServerPDUEventListener pduListener) {
this.pduListener = pduListener;
this.asynchronous = pduListener != null;
if (asynchronous) {
// let's remove all pdu's from the queue as since now all
// processing should be asynchronous -- it's not wise to
// expect that the programmer will try AFTER setting the listener
// to call receive() which when in sync mode removes the pdus from
// the queue
PDU pdu;
int queueSize;
synchronized (pduQueue) {
queueSize = pduQueue.size();
for (int i = 0; i < queueSize; i++) {
pdu = (PDU) pduQueue.dequeue();
process(pdu);
}
}
}
}
/**
* Resets unprocessed data and starts receiving on the background.
*
* @see ReceiverBase#start()
*/
public void start() {
debug.write(DRXTX, "Receiver starting");
receiver = true;
unprocessed.reset();
super.start();
debug.write(DRXTX, "Receiver started");
}
/**
* Stops receiving on the background.
*
* @see ReceiverBase#stop()
*/
public void stop() {
debug.write(DRXTX, "Receiver stoping");
if (isReceiver()) {
super.stop();
receiver = false;
}
debug.write(DRXTX, "Receiver stoped");
}
/**
* This method receives a PDU or returns PDU received on background,
* if there is any. It tries to receive a PDU for the specified timeout.
* If the receiver is asynchronous, then no attempt to receive a PDU
* and null
is returned.
* The function calls are nested as follows:
*
* - No background receiver thread
* Receiver.receive(long)
* ReceiverBase.tryReceivePDUWithTimeout(Connection,PDU,long)
* Receiver.tryReceivePDU(Connection,PDU)
* ReceiverBase.receivePDUFromConnection
* Connection.receive()
* - Has background receiver thread
* Receiver.receive(long)
* ReceiverBase.tryReceivePDUWithTimeout(Connection,PDU,long)
* Receiver.tryReceivePDU(Connection,PDU)
* Queue.dequeue(PDU)
* and the ReceiverBase.run() function which actually receives the
* PDUs and stores them to a queue looks as follows:
* ReceiverBase.run()
* Receiver.receiveAsync()
* ReceiverBase.receivePDUFromConnection
* Connection.receive()
*
* @param timeout for how long is tried to receive a PDU
* @return the received PDU or null if none received for the spec. timeout
*
* @exception IOException exception during communication
* @exception PDUException incorrect format of PDU
* @exception TimeoutException rest of PDU not received for too long time
* @exception UnknownCommandIdException PDU with unknown id was received
* @see ReceiverBase#tryReceivePDUWithTimeout(Connection,PDU,long)
*/
public synchronized PDU receive(long timeout)
throws UnknownCommandIdException, TimeoutException, NotSynchronousException, PDUException, IOException {
PDU pdu = null;
if (!asynchronous) {
pdu = tryReceivePDUWithTimeout(connection, null, timeout);
}
return pdu;
}
/**
* Called from session to receive a response for previously sent request.
*
* @param expectedPDU the template for expected PDU; the PDU returned
* must have the same sequence number
* @return the received PDU or null if none
* @see ReceiverBase#tryReceivePDUWithTimeout(Connection,PDU,long)
*/
public synchronized PDU receive(PDU expectedPDU)
throws UnknownCommandIdException, TimeoutException, NotSynchronousException, PDUException, IOException {
PDU pdu = null;
if (!asynchronous) {
pdu = tryReceivePDUWithTimeout(connection, expectedPDU);
}
return pdu;
}
/**
* This method tries to receive one PDU from the connection.
* It is called in cycle from tryReceivePDUWithTimeout
until
* timeout expires. tryReceivePDUWithTimeout
is called
* either from receiveAsync
as asynchronous receive on
* background or from receive
as synchronous receive.
* It either gets pdu from the queue or tries to receive it from connection
* using receivePDUFromConnection
depending on
* the value of the receiver
flag. The method
* checks if the actualy received PUD is equal to expectedPDU
.
*
* @exception IOException exception during communication
* @exception PDUException incorrect format of PDU
* @exception TimeoutException rest of PDU not received for too long time
* @exception UnknownCommandIdException PDU with unknown id was received
* @see ReceiverBase#tryReceivePDUWithTimeout(Connection,PDU,long)
* @see #receiveAsync()
* @see ReceiverBase#run()
*/
protected PDU tryReceivePDU(Connection connection, PDU expectedPDU)
throws UnknownCommandIdException, TimeoutException, PDUException, IOException {
PDU pdu = null;
if (receiver) {
debug.write(DRXTXD2, "Is receiver/transciever => trying to get from queue.");
synchronized (pduQueue) {
if (expectedPDU == null) { // i.e. any pdu is acceptable
if (!pduQueue.isEmpty()) {
pdu = (PDU) pduQueue.dequeue();
}
} else {
pdu = (PDU) pduQueue.dequeue(expectedPDU);
}
if (pdu == null) {
try {
pduQueue.wait(getQueueWaitTimeout());
} catch (InterruptedException e) {
// we don't care
debug.write(DRXTX, "tryReceivePDU got interrupt waiting for queue");
}
}
}
} else {
debug.write(DRXTX, "Is transmitter only => trying to receive from connection.");
pdu = receivePDUFromConnection(connection, unprocessed);
if (pdu != null) {
if ((expectedPDU == null) || !pdu.equals(expectedPDU)) {
debug.write(DRXTX, "This is not the pdu we expect, processing" + pdu.debugString());
enqueue(pdu);
pdu = null;
}
}
}
return pdu;
}
/**
* This method receives a PDU from connection and stores it into
* pduQueue
. It's called from the ReceiverBase
's
* pprocess
method which is called in loop from
* ProcessingThread
's run
method.
*
* If an exception occurs during receiving, depending on type
* of the exception this method either just reports the exception to
* debug & event objects or stops processing to indicate
* that it isn't able to process the exception. The function
* setTermException
is then called with the caught exception.
*
* @see ReceiverBase#run()
*/
protected void receiveAsync() {
PDU pdu = null;
try {
debug.write(DRXTXD2, "Receiver.receiveAsync() going to receive pdu.");
pdu = receivePDUFromConnection(connection, unprocessed);
// we must catch every exception as this is thread running
// on the background and we don't want the thread to be terminated
} catch (InvalidPDUException e) {
// thrown when enough data were received but further parsing
// required more than indicated by CommandLength, i.e. pdu is
// corrupted or further parsing didn't find terminating zero
// of a c-string i.e. pdu is corrupted
// must send generic nack anyway
event.write(e, "Receiver.receiveAsync(): received PDU is invalid.");
PDU expdu = e.getPDU();
int seqNr = expdu == null ? 0 : expdu.getSequenceNumber();
if(automaticNack) {
sendGenericNack(Data.ESME_RINVMSGLEN, seqNr);
} else {
pdu = new GenericNack(Data.ESME_RINVMSGLEN, seqNr);
}
} catch (UnknownCommandIdException e) {
// if received unknown pdu, we must send generic nack
event.write(e, "Receiver.receiveAsync(): Unknown command id.");
if(automaticNack) {
sendGenericNack(Data.ESME_RINVCMDID, e.getSequenceNumber());
} else {
pdu = new GenericNack(Data.ESME_RINVCMDID, e.getSequenceNumber());
}
} catch (TimeoutException e) {
// too long had unprocessed data
debug.write(DRXTX, "Receiver.receiveAsync() too long had an uncomplete message.");
} catch (PDUException e) {
// something wrong with the PDU
event.write(e, "Receiver.receiveAsync()");
PDU expdu = e.getPDU();
int seqNr = expdu == null ? 0 : expdu.getSequenceNumber();
if(automaticNack) {
sendGenericNack(e.getErrorCode(), seqNr);
} else {
pdu = new GenericNack(e.getErrorCode(), seqNr);
}
} catch (Exception e) {
// don't know what happen, let's end the show
event.write(e, "Receiver.receiveAsync()");
stopProcessing(e);
}
if (pdu != null) {
debug.write(DRXTX, "Receiver.receiveAsync(): PDU received, processing " + pdu.debugString());
if (asynchronous) {
process(pdu);
} else {
enqueue(pdu);
}
}
}
/**
* Passes the pdu
to the pduListener
.
*
* @param pdu the PDU to pass to the processor as an
* ServerPDUEvent
* @see Queue
* @see ServerPDUEventListener
*/
private void process(PDU pdu) {
debug.write(DRXTX, "receiver passing pdu to ServerPDUEventListener");
if (pduListener != null) {
ServerPDUEvent pduReceived = new ServerPDUEvent(this, connection, pdu);
pduListener.handleEvent(pduReceived);
debug.write(DRXTX, "ServerPDUEventListener received pdu");
} else {
debug.write(
DRXTX,
"async receiver doesn't have ServerPDUEventListener, " + "discarding " + pdu.debugString());
}
}
/**
* Puts the pdu
into the pduQueue
.
*
* @param pdu the PDU to put into the queue
* @see Queue
*/
private void enqueue(PDU pdu) {
debug.write(DRXTX, "receiver enqueuing pdu.");
synchronized (pduQueue) {
pduQueue.enqueue(pdu);
pduQueue.notifyAll();
}
// HNK -- If you just received unbind_resp, don't block on
// socket in TCPIPConnection.receive(), call stopProcessing()
if (Data.UNBIND_RESP == pdu.getCommandId()) {
stopProcessing(null);
}
}
/**
* Sends GenericNack
PDU via transmitter if there is one.
* The GenericNack
is sent in case that the message is
* corrupted or has unknown command id. If the sending of
* GenericNack
fails, this method calls
* stopProcessing
and thus stops the receiving
* thread.
*
* @param commandStatus the error code
* @param sequenceNumber the sequence number of received wrong PDU
* @see GenericNack
* @see Transmitter
*/
private void sendGenericNack(int commandStatus, int sequenceNumber) {
if (transmitter != null) {
try {
GenericNack gnack = new GenericNack(commandStatus, sequenceNumber);
transmitter.send(gnack);
} catch (IOException gnacke) {
event.write(gnacke, "Receiver.run(): IOException sending generic_nack.");
} catch (Exception gnacke) {
event.write(gnacke, "Receiver.run(): an exception sending generic_nack.");
stopProcessing(gnacke);
}
}
}
/**
* Sets queue waiting timeout.
*
* @param timeout the new queue timeout
* @see #queueWaitTimeout
*/
public void setQueueWaitTimeout(long timeout) {
queueWaitTimeout = timeout;
}
/**
* Returns current queue waiting timeout.
*
* @return the current queue timeout
* @see #queueWaitTimeout
*/
public long getQueueWaitTimeout() {
return queueWaitTimeout;
}
// ProcessingThread's getThreadName override
public String getThreadName() {
return RECEIVER_THREAD_NAME;
}
public void setAutomaticNack(boolean automaticNack) {
this.automaticNack = automaticNack;
}
public boolean isAutomaticNack() {
return automaticNack;
}
}
/*
* $Log: not supported by cvs2svn $
* Revision 1.1 2003/07/23 00:28:39 sverkera
* Imported
*
*
* Old changelog:
* 13-07-01 [email protected] start(), stop(), setQueueWaitTimeout() &
* getQueueWaitTimeout() made not synchronized;
* receive(long) & receive(PDU) made synchronized
* so the receiver no longer locks up
* 13-07-01 [email protected] bug fixed in tryReceivePDU which caused that the PDUs
* were never removed from the queue - now dequeue(expected)
* is now used instead of find(expected)
* 13-07-01 [email protected] loads of debug lines corrected; some added
* 08-08-01 [email protected] added support for Session's asynchronous processing capability
* 26-09-01 [email protected] debug code categorized to groups
* 01-10-01 [email protected] added function getThreadName for ProcessingThread
* thread name initialisation.
* 02-10-01 [email protected] instead of importing full packages only the used
* classes are iported
*/