org.jsmpp.session.SMPPServerSession Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.jsmpp.session;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;
import org.jsmpp.DefaultPDUReader;
import org.jsmpp.DefaultPDUSender;
import org.jsmpp.InvalidCommandLengthException;
import org.jsmpp.InvalidResponseException;
import org.jsmpp.PDUException;
import org.jsmpp.PDUReader;
import org.jsmpp.PDUSender;
import org.jsmpp.PDUStringException;
import org.jsmpp.SMPPConstant;
import org.jsmpp.SynchronizedPDUSender;
import org.jsmpp.bean.Bind;
import org.jsmpp.bean.BindType;
import org.jsmpp.bean.CancelSm;
import org.jsmpp.bean.Command;
import org.jsmpp.bean.DataCoding;
import org.jsmpp.bean.DataSm;
import org.jsmpp.bean.ESMClass;
import org.jsmpp.bean.InterfaceVersion;
import org.jsmpp.bean.MessageState;
import org.jsmpp.bean.NumberingPlanIndicator;
import org.jsmpp.bean.OptionalParameter;
import org.jsmpp.bean.QuerySm;
import org.jsmpp.bean.RegisteredDelivery;
import org.jsmpp.bean.ReplaceSm;
import org.jsmpp.bean.SubmitMulti;
import org.jsmpp.bean.SubmitMultiResult;
import org.jsmpp.bean.SubmitSm;
import org.jsmpp.bean.TypeOfNumber;
import org.jsmpp.extra.NegativeResponseException;
import org.jsmpp.extra.PendingResponse;
import org.jsmpp.extra.ProcessRequestException;
import org.jsmpp.extra.ResponseTimeoutException;
import org.jsmpp.extra.SessionState;
import org.jsmpp.session.connection.Connection;
import org.jsmpp.util.MessageId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author uudashr
*
*/
public class SMPPServerSession extends AbstractSession implements ServerSession {
private static final String MESSAGE_RECEIVER_LISTENER_IS_NULL = "Received SubmitMultiSm but MessageReceiverListener is null, returning SMPP error";
private static final String NO_MESSAGE_RECEIVER_LISTENER_REGISTERED = "No message receiver listener registered";
private static final Logger logger = LoggerFactory.getLogger(SMPPServerSession.class);
private final Connection conn;
private final DataInputStream in;
private final OutputStream out;
private final PDUReader pduReader;
private SMPPServerSessionContext sessionContext = new SMPPServerSessionContext(this);
private final ServerResponseHandler responseHandler = new ResponseHandlerImpl();
private ServerMessageReceiverListener messageReceiverListener;
private ServerResponseDeliveryListener responseDeliveryListener;
private BindRequestReceiver bindRequestReceiver = new BindRequestReceiver(responseHandler);
public SMPPServerSession(Connection conn,
SessionStateListener sessionStateListener,
ServerMessageReceiverListener messageReceiverListener,
ServerResponseDeliveryListener responseDeliveryListener,
int pduProcessorDegree) {
this(conn, sessionStateListener, messageReceiverListener,
responseDeliveryListener, pduProcessorDegree,
new SynchronizedPDUSender(new DefaultPDUSender()),
new DefaultPDUReader());
}
public SMPPServerSession(Connection conn,
SessionStateListener sessionStateListener,
ServerMessageReceiverListener messageReceiverListener,
ServerResponseDeliveryListener responseDeliveryListener,
int pduProcessorDegree, PDUSender pduSender, PDUReader pduReader) {
super(pduSender);
this.conn = conn;
this.messageReceiverListener = messageReceiverListener;
this.responseDeliveryListener = responseDeliveryListener;
this.pduReader = pduReader;
this.in = new DataInputStream(conn.getInputStream());
this.out = conn.getOutputStream();
enquireLinkSender = new EnquireLinkSender();
addSessionStateListener(new BoundStateListener());
addSessionStateListener(sessionStateListener);
setPduProcessorDegree(pduProcessorDegree);
sessionContext.open();
}
public InetAddress getInetAddress() {
return connection().getInetAddress();
}
/**
* Wait for bind request.
*
* @param timeout is the timeout.
* @return the {@link BindRequest}.
* @throws IllegalStateException if this invocation of this method has been
* made or invoke when state is not OPEN.
* @throws TimeoutException if the timeout has been reach and
* {@link SMPPServerSession} are no more valid because the
* connection will be close automatically.
*/
public BindRequest waitForBind(long timeout) throws IllegalStateException,
TimeoutException {
SessionState currentSessionState = getSessionState();
if (currentSessionState.equals(SessionState.OPEN)) {
new PDUReaderWorker().start();
try {
return bindRequestReceiver.waitForRequest(timeout);
} catch (IllegalStateException e) {
throw new IllegalStateException(
"Invocation of waitForBind() has been made", e);
} catch (TimeoutException e) {
close();
throw e;
}
} else {
throw new IllegalStateException(
"waitForBind() should be invoked on OPEN state, actual state is "
+ currentSessionState);
}
}
public void deliverShortMessage(String serviceType,
TypeOfNumber sourceAddrTon, NumberingPlanIndicator sourceAddrNpi,
String sourceAddr, TypeOfNumber destAddrTon,
NumberingPlanIndicator destAddrNpi, String destinationAddr,
ESMClass esmClass, byte protocoId, byte priorityFlag,
RegisteredDelivery registeredDelivery, DataCoding dataCoding,
byte[] shortMessage, OptionalParameter... optionalParameters)
throws PDUException, ResponseTimeoutException,
InvalidResponseException, NegativeResponseException, IOException {
ensureReceivable("deliverShortMessage");
DeliverSmCommandTask task = new DeliverSmCommandTask(pduSender(),
serviceType, sourceAddrTon, sourceAddrNpi, sourceAddr,
destAddrTon, destAddrNpi, destinationAddr, esmClass, protocoId,
protocoId, registeredDelivery, dataCoding, shortMessage,
optionalParameters);
executeSendCommand(task, getTransactionTimer());
}
/* (non-Javadoc)
* @see org.jsmpp.session.ServerSession#alertNotification(int, org.jsmpp.bean.TypeOfNumber, org.jsmpp.bean.NumberingPlanIndicator, java.lang.String, org.jsmpp.bean.TypeOfNumber, org.jsmpp.bean.NumberingPlanIndicator, java.lang.String, org.jsmpp.bean.OptionalParameter[])
*/
public void alertNotification(int sequenceNumber,
TypeOfNumber sourceAddrTon, NumberingPlanIndicator sourceAddrNpi,
String sourceAddr, TypeOfNumber esmeAddrTon,
NumberingPlanIndicator esmeAddrNpi, String esmeAddr,
OptionalParameter... optionalParameters) throws PDUStringException,
IOException {
ensureReceivable("alertNotification");
pduSender().sendAlertNotification(connection().getOutputStream(),
sequenceNumber, sourceAddrTon.value(), sourceAddrNpi.value(),
sourceAddr, esmeAddrTon.value(), esmeAddrNpi.value(), esmeAddr,
optionalParameters);
}
private MessageId fireAcceptSubmitSm(SubmitSm submitSm) throws ProcessRequestException {
if (messageReceiverListener != null) {
return messageReceiverListener.onAcceptSubmitSm(submitSm, this);
}
logger.warn("Received SubmitSm but MessageReceiverListener is null, returning SMPP error");
throw new ProcessRequestException(NO_MESSAGE_RECEIVER_LISTENER_REGISTERED,
SMPPConstant.STAT_ESME_RX_R_APPN);
}
private SubmitMultiResult fireAcceptSubmitMulti(SubmitMulti submitMulti) throws ProcessRequestException {
if (messageReceiverListener != null) {
return messageReceiverListener.onAcceptSubmitMulti(submitMulti, this);
}
logger.warn(MESSAGE_RECEIVER_LISTENER_IS_NULL);
throw new ProcessRequestException(NO_MESSAGE_RECEIVER_LISTENER_REGISTERED,
SMPPConstant.STAT_ESME_RX_R_APPN);
}
private QuerySmResult fireAcceptQuerySm(QuerySm querySm) throws ProcessRequestException {
if (messageReceiverListener != null) {
return messageReceiverListener.onAcceptQuerySm(querySm, this);
}
logger.warn("Received SubmitQuerySm but MessageReceiverListener is null, returning SMPP error");
throw new ProcessRequestException(NO_MESSAGE_RECEIVER_LISTENER_REGISTERED,
SMPPConstant.STAT_ESME_RX_R_APPN);
}
private void fireAcceptReplaceSm(ReplaceSm replaceSm) throws ProcessRequestException {
if (messageReceiverListener != null) {
messageReceiverListener.onAcceptReplaceSm(replaceSm, this);
} else {
logger.warn(MESSAGE_RECEIVER_LISTENER_IS_NULL);
throw new ProcessRequestException(NO_MESSAGE_RECEIVER_LISTENER_REGISTERED,
SMPPConstant.STAT_ESME_RX_R_APPN);
}
}
private void fireAcceptCancelSm(CancelSm cancelSm) throws ProcessRequestException {
if (messageReceiverListener != null) {
messageReceiverListener.onAcceptCancelSm(cancelSm, this);
} else {
logger.warn(MESSAGE_RECEIVER_LISTENER_IS_NULL);
throw new ProcessRequestException(NO_MESSAGE_RECEIVER_LISTENER_REGISTERED,
SMPPConstant.STAT_ESME_RX_R_APPN);
}
}
private void fireSubmitSmRespSent(MessageId messageId) {
if (responseDeliveryListener != null) {
responseDeliveryListener.onSubmitSmRespSent(messageId, this);
}
}
private void fireSubmitSmRespFailed(MessageId messageId, Exception cause) {
if (responseDeliveryListener != null) {
responseDeliveryListener.onSubmitSmRespError(messageId, cause,
this);
}
}
private void fireSubmitMultiRespSent(SubmitMultiResult submitMultiResult) {
if (responseDeliveryListener != null) {
responseDeliveryListener.onSubmitMultiRespSent(
submitMultiResult, this);
}
}
private void fireSubmitMultiRespSentError(
SubmitMultiResult submitMultiResult, Exception cause) {
if (responseDeliveryListener != null) {
responseDeliveryListener.onSubmitMultiRespError(
submitMultiResult, cause, this);
}
}
@Override
protected Connection connection() {
return conn;
}
@Override
protected AbstractSessionContext sessionContext() {
return sessionContext;
}
@Override
protected GenericMessageReceiverListener messageReceiverListener() {
return messageReceiverListener;
}
public ServerMessageReceiverListener getMessageReceiverListener() {
return messageReceiverListener;
}
public void setMessageReceiverListener(
ServerMessageReceiverListener messageReceiverListener) {
this.messageReceiverListener = messageReceiverListener;
}
public void setResponseDeliveryListener(
ServerResponseDeliveryListener responseDeliveryListener) {
this.responseDeliveryListener = responseDeliveryListener;
}
private class ResponseHandlerImpl implements ServerResponseHandler {
public PendingResponse removeSentItem(int sequenceNumber) {
return removePendingResponse(sequenceNumber);
}
public void notifyUnbonded() {
sessionContext.unbound();
}
public void sendEnquireLinkResp(int sequenceNumber) throws IOException {
logger.debug("Sending enquire_link_resp");
pduSender().sendEnquireLinkResp(out, sequenceNumber);
}
public void sendGenerickNack(int commandStatus, int sequenceNumber)
throws IOException {
pduSender().sendGenericNack(out, commandStatus, sequenceNumber);
}
public void sendNegativeResponse(int originalCommandId,
int commandStatus, int sequenceNumber) throws IOException {
pduSender().sendHeader(out, originalCommandId | SMPPConstant.MASK_CID_RESP, commandStatus, sequenceNumber);
}
public void sendUnbindResp(int sequenceNumber) throws IOException {
pduSender().sendUnbindResp(out, SMPPConstant.STAT_ESME_ROK, sequenceNumber);
}
public void sendBindResp(String systemId, InterfaceVersion interfaceVersion, BindType bindType, int sequenceNumber) throws IOException {
sessionContext.bound(bindType);
try {
pduSender().sendBindResp(out, bindType.responseCommandId(), sequenceNumber, systemId, interfaceVersion);
} catch (PDUStringException e) {
logger.error("Failed sending bind response", e);
// TODO uudashr: we have double checking when accept the bind request
}
}
public void processBind(Bind bind) {
SMPPServerSession.this.bindRequestReceiver.notifyAcceptBind(bind);
}
public MessageId processSubmitSm(SubmitSm submitSm)
throws ProcessRequestException {
try {
MessageId messageId = fireAcceptSubmitSm(submitSm);
if (messageId == null) {
String msg = "Invalid message_id, shouldn't null value. " + ServerMessageReceiverListener.class + "#onAcceptSubmitSm(SubmitSm) return null value";
logger.error(msg);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RX_R_APPN);
}
return messageId;
}
catch(ProcessRequestException e) {
throw e;
}
catch(Exception e) {
String msg = "Invalid runtime exception thrown when processing SubmitSm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RSYSERR);
}
}
public void sendSubmitSmResponse(MessageId messageId, int sequenceNumber)
throws IOException {
try {
pduSender().sendSubmitSmResp(out, sequenceNumber,
messageId.getValue());
fireSubmitSmRespSent(messageId);
} catch (PDUStringException e) {
/*
* There should be no PDUStringException thrown since creation
* of MessageId should be save.
*/
logger.error("SYSTEM ERROR. Failed sending submitSmResp", e);
fireSubmitSmRespFailed(messageId, e);
} catch (IOException e) {
fireSubmitSmRespFailed(messageId, e);
throw e;
} catch (RuntimeException e) {
fireSubmitSmRespFailed(messageId, e);
throw e;
}
}
public SubmitMultiResult processSubmitMulti(SubmitMulti submitMulti)
throws ProcessRequestException {
try {
return fireAcceptSubmitMulti(submitMulti);
} catch(Exception e) {
String msg = "Invalid runtime exception thrown when processing SubmitMultiSm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RSYSERR);
}
}
public void sendSubmitMultiResponse(
SubmitMultiResult submitMultiResult, int sequenceNumber)
throws IOException {
try {
pduSender().sendSubmitMultiResp(out, sequenceNumber,
submitMultiResult.getMessageId(),
submitMultiResult.getUnsuccessDeliveries());
fireSubmitMultiRespSent(submitMultiResult);
} catch (PDUStringException e) {
/*
* There should be no PDUStringException thrown since creation
* of the response parameter has been validated.
*/
logger.error("SYSTEM ERROR. Failed sending submitMultiResp", e);
fireSubmitMultiRespSentError(submitMultiResult, e);
} catch (IOException e) {
fireSubmitMultiRespSentError(submitMultiResult, e);
throw e;
} catch (RuntimeException e) {
fireSubmitMultiRespSentError(submitMultiResult, e);
throw e;
}
}
public QuerySmResult processQuerySm(QuerySm querySm)
throws ProcessRequestException {
try {
return fireAcceptQuerySm(querySm);
} catch(Exception e) {
String msg = "Invalid runtime exception thrown when processing QuerySm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RSYSERR);
}
}
public void sendQuerySmResp(String messageId, String finalDate,
MessageState messageState, byte errorCode, int sequenceNumber) throws IOException {
try {
pduSender().sendQuerySmResp(out, sequenceNumber, messageId,
finalDate, messageState, errorCode);
} catch (PDUStringException e) {
/*
* There should be no PDUStringException thrown since creation
* of parsed messageId has been validated.
*/
logger.error("SYSTEM ERROR. Failed sending cancelSmResp", e);
}
}
public DataSmResult processDataSm(DataSm dataSm)
throws ProcessRequestException {
try {
return fireAcceptDataSm(dataSm);
} catch(Exception e) {
String msg = "Invalid runtime exception thrown when processing DataSm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RSYSERR);
}
}
// TODO uudashr: we can generalize this method
public void sendDataSmResp(DataSmResult dataSmResult, int sequenceNumber)
throws IOException {
try {
pduSender().sendDataSmResp(out, sequenceNumber,
dataSmResult.getMessageId(),
dataSmResult.getOptionalParameters());
} catch (PDUStringException e) {
/*
* There should be no PDUStringException thrown since creation
* of MessageId should be save.
*/
logger.error("SYSTEM ERROR. Failed sending dataSmResp", e);
}
}
public void processCancelSm(CancelSm cancelSm)
throws ProcessRequestException {
try {
fireAcceptCancelSm(cancelSm);
} catch(Exception e) {
String msg = "Invalid runtime exception thrown when processing CancelSm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RSYSERR);
}
}
public void sendCancelSmResp(int sequenceNumber) throws IOException {
pduSender().sendCancelSmResp(out, sequenceNumber);
}
public void processReplaceSm(ReplaceSm replaceSm)
throws ProcessRequestException {
try {
fireAcceptReplaceSm(replaceSm);
} catch(Exception e) {
String msg = "Invalid runtime exception thrown when processing ReplaceSm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RSYSERR);
}
}
public void sendReplaceSmResp(int sequenceNumber) throws IOException {
pduSender().sendReplaceSmResp(out, sequenceNumber);
}
}
private class PDUReaderWorker extends Thread {
private ExecutorService executorService = Executors.newFixedThreadPool(getPduProcessorDegree());
private Runnable onIOExceptionTask = new Runnable() {
@Override
public void run() {
close();
}
};
@Override
public void run() {
logger.info("Starting PDUReaderWorker with processor degree:{} ...", getPduProcessorDegree());
while (isReadPdu()) {
readPDU();
}
close();
executorService.shutdown();
logger.info("PDUReaderWorker stop");
}
private void readPDU() {
try {
Command pduHeader = pduReader.readPDUHeader(in);
byte[] pdu = pduReader.readPDU(in, pduHeader);
PDUProcessServerTask task = new PDUProcessServerTask(pduHeader,
pdu, sessionContext.getStateProcessor(),
sessionContext, responseHandler, onIOExceptionTask);
executorService.execute(task);
} catch (InvalidCommandLengthException e) {
logger.warn("Receive invalid command length", e);
try {
pduSender().sendGenericNack(out, SMPPConstant.STAT_ESME_RINVCMDLEN, 0);
} catch (IOException ee) {
logger.warn("Failed sending generic nack", ee);
}
unbindAndClose();
} catch (SocketTimeoutException e) {
notifyNoActivity();
} catch (IOException e) {
logger.warn("IOException while reading:", e);
close();
} catch (RuntimeException e) {
logger.warn("RuntimeException:", e);
unbindAndClose();
}
}
/**
* Notify for no activity.
*/
private void notifyNoActivity() {
logger.debug("No activity notified, sending enquireLink");
enquireLinkSender.enquireLink();
}
}
private class BoundStateListener implements SessionStateListener {
public void onStateChange(SessionState newState, SessionState oldState,
Session source) {
if (newState.isBound()) {
enquireLinkSender.start();
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy