org.jsmpp.session.SMPPOutboundServerSession Maven / Gradle / Ivy
The newest version!
/*
* 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.TimeUnit;
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.BindResp;
import org.jsmpp.bean.BindType;
import org.jsmpp.bean.Command;
import org.jsmpp.bean.DataSm;
import org.jsmpp.bean.DeliverSm;
import org.jsmpp.bean.InterfaceVersion;
import org.jsmpp.bean.NumberingPlanIndicator;
import org.jsmpp.bean.OptionalParameter;
import org.jsmpp.bean.OptionalParameter.Sc_interface_version;
import org.jsmpp.bean.Outbind;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author pmoerenhout
*/
public class SMPPOutboundServerSession extends AbstractSession implements OutboundServerSession {
private static final Logger logger = LoggerFactory.getLogger(SMPPOutboundServerSession.class);
private final Connection conn;
private final DataInputStream in;
private final OutputStream out;
private final PDUReader pduReader;
private OutboundSMPPServerSessionContext sessionContext = new OutboundSMPPServerSessionContext(this);
private final OutboundServerResponseHandler responseHandler = new OutboundServerResponseHandlerImpl();
private GenericMessageReceiverListener messageReceiverListener;
private OutboundServerMessageReceiverListener outboundServerMessageReceiverListener;
private OutbindRequestReceiver outbindRequestReceiver = new OutbindRequestReceiver();
public SMPPOutboundServerSession(Connection conn,
SessionStateListener sessionStateListener,
GenericMessageReceiverListener messageReceiverListener,
OutboundServerMessageReceiverListener outboundServerMessageReceiverListener,
int pduProcessorDegree) {
this(conn, sessionStateListener, messageReceiverListener,
outboundServerMessageReceiverListener, pduProcessorDegree,
new SynchronizedPDUSender(new DefaultPDUSender()),
new DefaultPDUReader());
}
public SMPPOutboundServerSession(Connection conn,
SessionStateListener sessionStateListener,
GenericMessageReceiverListener messageReceiverListener,
OutboundServerMessageReceiverListener outboundServerMessageReceiverListener,
int pduProcessorDegree, PDUSender pduSender, PDUReader pduReader) {
super(pduSender);
this.conn = conn;
this.messageReceiverListener = messageReceiverListener;
this.outboundServerMessageReceiverListener = outboundServerMessageReceiverListener;
this.pduReader = pduReader;
this.in = new DataInputStream(conn.getInputStream());
this.out = conn.getOutputStream();
enquireLinkSender = new EnquireLinkSender();
//addSessionStateListener(new OutboundSMPPServerSession.BoundStateListener());
addSessionStateListener(sessionStateListener);
setPduProcessorDegree(pduProcessorDegree);
sessionContext.open();
}
public InetAddress getInetAddress() {
return connection().getInetAddress();
}
/**
* Sending bind.
*
* @param bindType is the bind type.
* @param systemId is the system id.
* @param password is the password.
* @param systemType is the system type.
* @param interfaceVersion is the interface version.
* @param addrTon is the address TON.
* @param addrNpi is the address NPI.
* @param addressRange is the address range.
* @param timeout is the max time waiting for bind response.
* @return SMSC system id.
* @throws PDUException if we enter invalid bind parameter(s).
* @throws ResponseTimeoutException if there is no valid response after defined millisecond.
* @throws InvalidResponseException if there is invalid response found.
* @throws NegativeResponseException if we receive negative response.
* @throws IOException if there is an IO error occur.
*/
private String sendBind(BindType bindType, String systemId,
String password, String systemType,
InterfaceVersion interfaceVersion, TypeOfNumber addrTon,
NumberingPlanIndicator addrNpi, String addressRange, long timeout)
throws PDUException, ResponseTimeoutException,
InvalidResponseException, NegativeResponseException, IOException {
BindCommandTask task = new BindCommandTask(pduSender(), bindType,
systemId, password, systemType, interfaceVersion, addrTon,
addrNpi, addressRange);
BindResp resp = (BindResp)executeSendCommand(task, timeout);
OptionalParameter.Sc_interface_version scVersion = resp.getOptionalParameter(Sc_interface_version.class);
if(scVersion != null) {
logger.debug("Other side reports SMPP interface version {}", scVersion);
}
logger.info("Bind response systemId '{}'", resp.getSystemId());
return resp.getSystemId();
}
/**
* Wait for outbind request.
*
* @param timeout is the timeout.
* @return the {@link OutbindRequest}.
* @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 SMPPOutboundServerSession} are no more valid because
* the connection will be close automatically.
*/
public OutbindRequest waitForOutbind(long timeout)
throws IllegalStateException, TimeoutException {
SessionState currentSessionState = getSessionState();
if (currentSessionState.equals(SessionState.OPEN)) {
new SMPPOutboundServerSession.PDUReaderWorker().start();
try {
return outbindRequestReceiver.waitForRequest(timeout);
}
catch (IllegalStateException e) {
throw new IllegalStateException(
"Invocation of waitForOutbind() has been made", e);
}
catch (TimeoutException e) {
close();
throw e;
}
}
else {
throw new IllegalStateException(
"waitForOutbind() should be invoked on OPEN state, actual state is "
+ currentSessionState);
}
}
@Override
protected Connection connection() {
return conn;
}
@Override
protected AbstractSessionContext sessionContext() {
return sessionContext;
}
@Override
protected GenericMessageReceiverListener messageReceiverListener() {
return messageReceiverListener;
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
public OutboundServerMessageReceiverListener getOutboundServerMessageReceiverListener() {
return outboundServerMessageReceiverListener;
}
/**
* Sets a message receiver listener for this smpp session.
*
* @param outboundServerMessageReceiverListener is the new listener
*/
public void setOutboundServerMessageReceiverListener(
OutboundServerMessageReceiverListener outboundServerMessageReceiverListener) {
this.outboundServerMessageReceiverListener = outboundServerMessageReceiverListener;
}
private void fireAcceptDeliverSm(DeliverSm deliverSm) throws ProcessRequestException {
if (outboundServerMessageReceiverListener != null) {
outboundServerMessageReceiverListener.onAcceptDeliverSm(deliverSm, this);
}
else {
logger.warn("Receive deliver_sm but OutboundServerMessageReceiverListener is null. Short message = {}",
new String(deliverSm.getShortMessage()));
throw new ProcessRequestException("No message receiver listener registered", SMPPConstant.STAT_ESME_RX_T_APPN);
}
}
/**
* Bind immediately.
*
* @param bindParam is the bind parameters.
* @param timeout is the timeout.
* @return the SMSC system id.
* @throws IOException if there is an IO error found.
*/
public String bind(BindParameter bindParam, long timeout)
throws IOException {
try {
String smscSystemId = sendBind(bindParam.getBindType(), bindParam.getSystemId(), bindParam.getPassword(), bindParam.getSystemType(),
bindParam.getInterfaceVersion(), bindParam.getAddrTon(), bindParam.getAddrNpi(), bindParam.getAddressRange(), timeout);
sessionContext.bound(bindParam.getBindType());
logger.info("Start EnquireLinkSender");
enquireLinkSender = new EnquireLinkSender();
enquireLinkSender.start();
return smscSystemId;
} catch (PDUException e) {
logger.error("Failed sending bind command", e);
throw new IOException("Failed sending bind since some string parameter area invalid: " + e.getMessage(), e);
} catch (NegativeResponseException e) {
String message = "Receive negative bind response";
logger.error(message, e);
close();
throw new IOException(message + ": " + e.getMessage(), e);
} catch (InvalidResponseException e) {
String message = "Receive invalid response of bind";
logger.error(message, e);
close();
throw new IOException(message + ": " + e.getMessage(), e);
} catch (ResponseTimeoutException e) {
String message = "Waiting bind response take time too long";
logger.error(message, e);
close();
throw new IOException(message + ": " + e.getMessage(), e);
} catch (IOException e) {
logger.error("IO error occurred", e);
close();
throw e;
}
}
private class OutboundServerResponseHandlerImpl implements OutboundServerResponseHandler {
public void processOutbind(Outbind outbind) throws ProcessRequestException {
SMPPOutboundServerSession.this.outbindRequestReceiver.notifyAcceptOutbind(outbind);
sessionContext.outbind();
}
public void processDeliverSm(DeliverSm deliverSm) throws ProcessRequestException {
try {
fireAcceptDeliverSm(deliverSm);
}
catch (ProcessRequestException e) {
throw e;
}
catch (Exception e) {
String msg = "Invalid runtime exception thrown when processing deliver_sm";
logger.error(msg, e);
throw new ProcessRequestException(msg, SMPPConstant.STAT_ESME_RX_T_APPN);
}
}
/* not used in outbound session */
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);
}
}
/* not used in outbound session */
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("Failed sending data_sm_resp", e);
}
}
public PendingResponse removeSentItem(int sequenceNumber) {
return removePendingResponse(sequenceNumber);
}
public void notifyUnbonded() {
sessionContext.unbound();
}
@Override
public void sendDeliverSmResp(int commandStatus, int sequenceNumber, String messageId) throws IOException {
pduSender().sendDeliverSmResp(out, commandStatus, sequenceNumber, messageId);
logger.debug("deliver_sm_resp with seq_number {} has been sent", sequenceNumber);
}
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);
}
}
/**
* Worker to read the PDU.
*
* @author uudashr
*/
private class PDUReaderWorker extends Thread {
// start with serial execution of pdu processing, when the session is bound the pool will be enlarge up to the PduProcessorDegree
private ExecutorService executorService = Executors.newFixedThreadPool(1);
private Runnable onIOExceptionTask = new Runnable() {
@Override
public void run() {
close();
}
};
PDUReaderWorker() {
super("PDUReaderWorker: " + SMPPOutboundServerSession.this);
}
@Override
public void run() {
logger.info("Starting PDUReaderWorker");
while (isReadPdu()) {
readPDU();
}
logger.info("Close PDUReaderWorker");
close();
executorService.shutdown();
try {
executorService.awaitTermination(getTransactionTimer(), TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
logger.warn("interrupted while waiting for executor service pool to finish");
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted");
}
logger.info("PDUReaderWorker stop");
}
private void readPDU() {
try {
Command pduHeader = pduReader.readPDUHeader(in);
byte[] pdu = pduReader.readPDU(in, pduHeader);
/*
* When the processing PDU is need user interaction via event,
* the code on event might take non-short time, so we need to
* process it concurrently.
*/
PDUProcessOutboundServerTask task = new PDUProcessOutboundServerTask(pduHeader, pdu,
sessionContext, responseHandler,
sessionContext, 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");
if (sessionContext().getSessionState().isBound()) {
enquireLinkSender.enquireLink();
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy