/*
* TeleStax, Open Source Cloud Communications Copyright 2012.
* and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.smsc.slee.services.smpp.server.rx;
import static org.mobicents.smsc.slee.services.util.SbbStatsUtils.warnIfLong;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.slee.ActivityContextInterface;
import javax.slee.ActivityEndEvent;
import javax.slee.EventContext;
import javax.slee.Sbb;
import javax.slee.SbbContext;
import javax.slee.ServiceID;
import javax.slee.serviceactivity.ServiceActivity;
import javax.slee.serviceactivity.ServiceStartedEvent;
import org.mobicents.protocols.ss7.map.api.MAPProvider;
import org.mobicents.protocols.ss7.map.api.MAPSmsTpduParameterFactory;
import org.mobicents.protocols.ss7.map.api.smstpdu.CharacterSet;
import org.mobicents.protocols.ss7.map.api.smstpdu.DataCodingScheme;
import org.mobicents.protocols.ss7.map.api.smstpdu.UserDataHeader;
import org.mobicents.protocols.ss7.map.datacoding.GSMCharset;
import org.mobicents.protocols.ss7.map.datacoding.GSMCharsetEncoder;
import org.mobicents.protocols.ss7.map.datacoding.GSMCharsetEncodingData;
import org.mobicents.protocols.ss7.map.datacoding.Gsm7EncodingStyle;
import org.mobicents.protocols.ss7.map.smstpdu.DataCodingSchemeImpl;
import org.mobicents.slee.ChildRelationExt;
import org.mobicents.smsc.domain.SmscStatAggregator;
import org.mobicents.smsc.library.CdrGenerator;
import org.mobicents.smsc.library.ErrorAction;
import org.mobicents.smsc.library.ErrorCode;
import org.mobicents.smsc.library.MessageUtil;
import org.mobicents.smsc.library.SbbStates;
import org.mobicents.smsc.library.Sms;
import org.mobicents.smsc.library.SmsSet;
import org.mobicents.smsc.library.SmscProcessingException;
import org.mobicents.smsc.library.TargetAddress;
import org.mobicents.smsc.mproc.ProcessingType;
import org.mobicents.smsc.slee.resources.scheduler.PduRequestTimeout2;
import org.mobicents.smsc.slee.services.deliverysbb.ConfirmMessageInSendingPool;
import org.mobicents.smsc.slee.services.deliverysbb.DeliveryCommonSbb;
import org.mobicents.smsc.slee.services.smpp.server.events.SmsSetEvent;
import org.mobicents.smsc.slee.services.util.SbbStatsUtils;
import org.restcomm.slee.resource.smpp.PduRequestTimeout;
import org.restcomm.slee.resource.smpp.SmppSessions;
import org.restcomm.slee.resource.smpp.SmppTransaction;
import org.restcomm.slee.resource.smpp.SmppTransactionACIFactory;
import org.restcomm.smpp.Esme;
import org.restcomm.smpp.EsmeManagement;
import org.restcomm.smpp.SmppEncoding;
import org.restcomm.smpp.SmppInterfaceVersionType;
import com.cloudhopper.smpp.SmppConstants;
import com.cloudhopper.smpp.SmppSession.Type;
import com.cloudhopper.smpp.pdu.BaseSmResp;
import com.cloudhopper.smpp.pdu.DeliverSm;
import com.cloudhopper.smpp.pdu.DeliverSmResp;
import com.cloudhopper.smpp.pdu.SubmitSm;
import com.cloudhopper.smpp.pdu.SubmitSmResp;
import com.cloudhopper.smpp.tlv.Tlv;
import com.cloudhopper.smpp.type.Address;
import com.cloudhopper.smpp.type.RecoverablePduException;
/**
*
* @author amit bhayani
* @author sergey vetyutnev
*
*/
public abstract class RxSmppServerSbb extends DeliveryCommonSbb implements Sbb {
private static final String className = RxSmppServerSbb.class.getSimpleName();
private static final long ONE = 1L;
// TODO: default value==100 / 2
protected static int MAX_MESSAGES_PER_STEP = 100;
protected SmppTransactionACIFactory smppServerTransactionACIFactory = null;
protected SmppSessions smppServerSessions = null;
protected MAPSmsTpduParameterFactory mapSmsTpduParameterFactory;
private SmscStatAggregator smscStatAggregator = SmscStatAggregator.getInstance();
private static Charset utf8Charset = Charset.forName("UTF-8");
private static Charset ucs2Charset = Charset.forName("UTF-16BE");
private static Charset isoCharset = Charset.forName("ISO-8859-1");
private static Charset gsm7Charset = new GSMCharset("GSM", new String[] {});
public RxSmppServerSbb() {
super(className);
}
// *********
// SBB staff
@Override
public void setSbbContext(SbbContext sbbContext) {
super.setSbbContext(sbbContext);
try {
Context ctx = (Context) new InitialContext().lookup("java:comp/env");
this.smppServerTransactionACIFactory = (SmppTransactionACIFactory) ctx
.lookup("slee/resources/smppp/server/1.0/acifactory");
this.smppServerSessions = (SmppSessions) ctx.lookup("slee/resources/smpp/server/1.0/provider");
MAPProvider mapProvider = (MAPProvider) ctx.lookup("slee/resources/map/2.0/provider");
this.mapSmsTpduParameterFactory = mapProvider.getMAPSmsTpduParameterFactory();
} catch (Exception ne) {
logger.severe("Could not set SBB context:", ne);
}
}
@Override
public void sbbLoad() {
super.sbbLoad();
}
@Override
public void sbbStore() {
super.sbbStore();
}
/**
* Gets the default SBB usage parameter set.
*
* @return the default SBB usage parameter set
*/
public abstract RxSmppServerSbbUsage getDefaultSbbUsageParameterSet();
public void onServiceStartedEvent(ServiceStartedEvent event, ActivityContextInterface aci, EventContext eventContext) {
ServiceID serviceID = event.getService();
this.logger.info("Rx: onServiceStartedEvent: event=" + event + ", serviceID=" + serviceID);
SbbStates.setSmscRxSmppServerServiceState(true);
}
public void onActivityEndEvent(ActivityEndEvent event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterActivityEnd(ONE);
boolean isServiceActivity = (aci.getActivity() instanceof ServiceActivity);
if (isServiceActivity) {
this.logger.info("Rx: onActivityEndEvent: event=" + event + ", isServiceActivity=" + isServiceActivity);
SbbStates.setSmscRxSmppServerServiceState(false);
}
}
// *********
// initial event
public void onDeliverSm(SmsSetEvent event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterDeliverSm(ONE);
final long start = System.currentTimeMillis();
onDeliverSmLocal(sbbu, event);
sbbu.sampleDeliverSm(System.currentTimeMillis() - start);
}
// *********
// SMPP events
public void onSubmitSmRespParent(SubmitSmResp event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterSubmitSmRespParent(ONE);
final long start = System.currentTimeMillis();
onSubmitSmRespParentLocal(sbbu, event);
sbbu.sampleSubmitSmRespParent(System.currentTimeMillis() - start);
}
public void onDeliverSmRespParent(DeliverSmResp event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterDeliverSmRespParent(ONE);
final long start = System.currentTimeMillis();
onDeliverSmRespParentLocal(sbbu, event);
sbbu.sampleDeliverSmRespParent(System.currentTimeMillis() - start);
}
public void onPduRequestTimeoutParent(PduRequestTimeout2 event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterErrorPduRequestTimeoutParent(ONE);
final long start = System.currentTimeMillis();
onPduRequestTimeoutParentLocal(sbbu, event);
sbbu.samplePduRequestTimeoutParent(System.currentTimeMillis() - start);
}
public void onRecoverablePduExceptionParent(RecoverablePduException event, ActivityContextInterface aci,
EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterRecoverablePduExceptionParent(ONE);
final long start = System.currentTimeMillis();
onRecoverablePduExceptionParentLocal(sbbu, event);
sbbu.sampleRecoverablePduExceptionParent(System.currentTimeMillis() - start);
}
public void onDeliverSmResp(DeliverSmResp event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterDeliverSmResp(ONE);
final long start = System.currentTimeMillis();
onDeliverSmRespLocal(sbbu, event, aci);
sbbu.sampleDeliverSmResp(System.currentTimeMillis() - start);
}
public void onSubmitSmResp(SubmitSmResp event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterSubmitSmResp(ONE);
final long start = System.currentTimeMillis();
onSubmitSmRespLocal(sbbu, event, aci);
sbbu.sampleSubmitSmResp(System.currentTimeMillis() - start);
}
public void onPduRequestTimeout(PduRequestTimeout event, ActivityContextInterface aci, EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementCounterErrorPduRequestTimeout(ONE);
final long start = System.currentTimeMillis();
onPduRequestTimeoutLocal(sbbu, event, aci);
sbbu.samplePduRequestTimeout(System.currentTimeMillis() - start);
}
public void onRecoverablePduException(RecoverablePduException event, ActivityContextInterface aci,
EventContext eventContext) {
final RxSmppServerSbbUsage sbbu = getDefaultSbbUsageParameterSet();
sbbu.incrementErrorRecoverablePduException(ONE);
final long start = System.currentTimeMillis();
onRecoverablePduExceptionLocal(sbbu, event, aci);
sbbu.sampleRecoverablePduException(System.currentTimeMillis() - start);
}
public abstract void fireDeliverSmRespChild(DeliverSmResp event, ActivityContextInterface activity,
javax.slee.Address address);
public abstract void fireSubmitSmRespChild(SubmitSmResp event, ActivityContextInterface activity, javax.slee.Address address);
public abstract void firePduRequestTimeoutChild(PduRequestTimeout2 event, ActivityContextInterface aci, javax.slee.Address address);
public abstract void fireRecoverablePduExceptionChild(RecoverablePduException event, ActivityContextInterface aci, javax.slee.Address address);
public abstract ChildRelationExt getRxSmppServerChildSbb();
private void onDeliverSmLocal(final RxSmppServerSbbUsage anSbbUsage, final SmsSetEvent event) {
try {
if (this.logger.isFineEnabled()) {
this.logger.fine("\nReceived Deliver SMS. event= " + event + "this=" + this);
}
SmsSet smsSet = event.getSmsSet();
this.addInitialMessageSet(smsSet);
try {
this.sendDeliverSm(smsSet);
} catch (SmscProcessingException e) {
String s = "SmscProcessingException when sending initial sendDeliverSm()=" + e.getMessage()
+ ", smsSet=" + smsSet;
logger.severe(s, e);
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, s);
}
} catch (Throwable e1) {
anSbbUsage.incrementCounterErrorDeliverSm(ONE);
logger.severe(
"Exception in RxSmppServerSbb.onDeliverSm() when fetching records and issuing events: "
+ e1.getMessage(), e1);
markDeliveringIsEnded(true);
}
}
// *********
// SMPP events
private void onSubmitSmRespParentLocal(final RxSmppServerSbbUsage anSbbUsage, final SubmitSmResp event) {
try {
if (logger.isFineEnabled()) {
logger.fine(String.format("onSubmitSmResp : SubmitSmResp=%s", event));
}
this.handleResponse(event);
} catch (Throwable e1) {
anSbbUsage.incrementCounterErrorSubmitSmRespParent(ONE);
SmsSet smsSet = this.getSmsSet();
logger.severe(
"Exception in RxSmppServerSbb.onDeliverSmResp() when fetching records and issuing events: "
+ e1.getMessage() + "\nsmsSet=" + smsSet, e1);
if (smsSet != null) {
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR,
"Internal error - Exception in processing: " + event.getSequenceNumber() + ", SmsSet=" + smsSet);
} else {
markDeliveringIsEnded(true);
}
}
}
private void onDeliverSmRespParentLocal(final RxSmppServerSbbUsage anSbbUsage, final DeliverSmResp event) {
try {
if (logger.isFineEnabled()) {
logger.fine(String.format("\nonDeliverSmResp : DeliverSmResp=%s", event));
}
this.handleResponse(event);
} catch (Throwable e1) {
anSbbUsage.incrementCounterErrorDeliverSmRespParent(ONE);
SmsSet smsSet = this.getSmsSet();
logger.severe(
"Exception in RxSmppServerSbb.onDeliverSmResp() when fetching records and issuing events: "
+ e1.getMessage() + "\nsmsSet=" + smsSet, e1);
if (smsSet != null) {
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR,
"Internal error - Exception in processing: " + event.getSequenceNumber() + ", SmsSet=" + smsSet);
} else {
markDeliveringIsEnded(true);
}
}
}
private void onPduRequestTimeoutParentLocal(final RxSmppServerSbbUsage anSbbUsage, final PduRequestTimeout2 event) {
try {
if (isDeliveringEnded()) {
logger.info("RxSmppServerSbb.onPduRequestTimeout(): received PduRequestTimeout but delivery process is already ended, dropping of an event");
return;
}
SmsSet smsSet = getSmsSet();
if (smsSet == null) {
logger.severe("RxSmppServerSbb.onPduRequestTimeout(): CMP smsSet is missed");
markDeliveringIsEnded(true);
return;
}
logger.severe(String.format("\nonPduRequestTimeout : targetId=" + smsSet.getTargetId()
+ ", PduRequestTimeout=" + event));
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, "PduRequestTimeout: ");
} catch (Throwable e1) {
anSbbUsage.incrementCounterErrorPduRequestTimeoutParent(ONE);
logger.severe(
"Exception in RxSmppServerSbb.onPduRequestTimeout() when fetching records and issuing events: "
+ e1.getMessage(), e1);
markDeliveringIsEnded(true);
}
}
private void onRecoverablePduExceptionParentLocal(final RxSmppServerSbbUsage anSbbUsage, final RecoverablePduException event) {
try {
if (isDeliveringEnded()) {
logger.info("RxSmppServerSbb.onRecoverablePduException(): received RecoverablePduException but delivery process is already ended, dropping of an event");
return;
}
SmsSet smsSet = getSmsSet();
if (smsSet == null) {
logger.severe("RxSmppServerSbb.onRecoverablePduException(): In onDeliverSmResp CMP smsSet is missed");
markDeliveringIsEnded(true);
return;
}
logger.severe(String.format("\nonRecoverablePduException : targetId=" + smsSet.getTargetId()
+ ", RecoverablePduException=" + event));
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, "RecoverablePduException: ");
} catch (Throwable e1) {
anSbbUsage.incrementCounterErrorRecoverablePduExceptionParent(ONE);
logger.severe(
"Exception in RxSmppServerSbb.onRecoverablePduException() when fetching records and issuing events: "
+ e1.getMessage(), e1);
markDeliveringIsEnded(true);
}
}
private void onDeliverSmRespLocal(final RxSmppServerSbbUsage anSbbUsage, final DeliverSmResp event,
final ActivityContextInterface aci) {
if (logger.isFineEnabled())
logger.fine("onDeliverSmResp - refire to RxSmppServerChildSbb : activity=" + aci.getActivity());
RxSmppServerChildLocalObject rxSmppServerSbbLocalObject = this.getRxSmppServerChildSbbObject();
if (rxSmppServerSbbLocalObject != null) {
ActivityContextInterface act = getSchedulerActivityContextInterface();
if (act != null) {
try {
act.attach(rxSmppServerSbbLocalObject);
fireDeliverSmRespChild(event, act, null);
} catch (IllegalStateException e) {
if (logger.isInfoEnabled())
logger.info("onDeliverSmResp - IllegalStateException (activity is ending - dropping a SLEE event because it is not needed) : new activity="
+ act.getActivity() + ", event=" + event);
}
}
}
}
private void onSubmitSmRespLocal(final RxSmppServerSbbUsage anSbbUsage, final SubmitSmResp event,
final ActivityContextInterface aci) {
if (logger.isFineEnabled())
logger.fine("onSubmitSmResp - refire to RxSmppServerChildSbb : activity=" + aci.getActivity());
RxSmppServerChildLocalObject rxSmppServerSbbLocalObject = this.getRxSmppServerChildSbbObject();
if (rxSmppServerSbbLocalObject != null) {
ActivityContextInterface act = getSchedulerActivityContextInterface();
if (act != null) {
try {
act.attach(rxSmppServerSbbLocalObject);
fireSubmitSmRespChild(event, act, null);
} catch (IllegalStateException e) {
if (logger.isInfoEnabled())
logger.info("onSubmitSmRespLocal - IllegalStateException (activity is ending - dropping a SLEE event because it is not needed) : new activity="
+ act.getActivity() + ", event=" + event);
}
}
}
}
private void onPduRequestTimeoutLocal(final RxSmppServerSbbUsage anSbbUsage, final PduRequestTimeout event,
final ActivityContextInterface aci) {
if (logger.isFineEnabled())
logger.fine("onPduRequestTimeout - refire to RxSmppServerChildSbb : activity=" + aci.getActivity());
RxSmppServerChildLocalObject rxSmppServerSbbLocalObject = this.getRxSmppServerChildSbbObject();
if (rxSmppServerSbbLocalObject != null) {
ActivityContextInterface act = getSchedulerActivityContextInterface();
if (act != null) {
try {
act.attach(rxSmppServerSbbLocalObject);
PduRequestTimeout2 event2 = new PduRequestTimeout2(event.getPduRequest(), event.getSystemId());
firePduRequestTimeoutChild(event2, act, null);
} catch (IllegalStateException e) {
if (logger.isInfoEnabled())
logger.info("onPduRequestTimeout - IllegalStateException (activity is ending - dropping a SLEE event because it is not needed) : new activity="
+ act.getActivity() + ", event=" + event);
}
}
}
}
private void onRecoverablePduExceptionLocal(final RxSmppServerSbbUsage anSbbUsage,
final RecoverablePduException event, final ActivityContextInterface aci) {
if (logger.isFineEnabled())
logger.fine("onRecoverablePduException - refire to RxSmppServerChildSbb : activity=" + aci.getActivity());
RxSmppServerChildLocalObject rxSmppServerSbbLocalObject = this.getRxSmppServerChildSbbObject();
if (rxSmppServerSbbLocalObject != null) {
ActivityContextInterface act = getSchedulerActivityContextInterface();
if (act != null) {
try {
act.attach(rxSmppServerSbbLocalObject);
fireRecoverablePduExceptionChild(event, act, null);
} catch (IllegalStateException e) {
if (logger.isInfoEnabled())
logger.info("onRecoverablePduException - IllegalStateException (activity is ending - dropping a SLEE event because it is not needed) : new activity="
+ act.getActivity() + ", event=" + event);
}
}
}
}
private RxSmppServerChildLocalObject getRxSmppServerChildSbbObject() {
ChildRelationExt relation = getRxSmppServerChildSbb();
RxSmppServerChildLocalObject ret = (RxSmppServerChildLocalObject) relation.get(ChildRelationExt.DEFAULT_CHILD_NAME);
if (ret == null) {
try {
ret = (RxSmppServerChildLocalObject) relation.create(ChildRelationExt.DEFAULT_CHILD_NAME);
} catch (Exception e) {
if (this.logger.isSevereEnabled()) {
this.logger.severe("Exception while trying to creat RxSmppServerSbb child", e);
}
}
}
return ret;
}
// *********
// Main service methods
/**
* Sending of a set of messages after initial message or when all sent messages was sent
*
* @param smsSet
* @throws SmscProcessingException
*/
private void sendDeliverSm(SmsSet smsSet) throws SmscProcessingException {
// TODO: let make here a special check if ESME in a good state
// if not - skip sending and set temporary error
try {
int deliveryMsgCnt = this.obtainNextMessagesSendingPool(MAX_MESSAGES_PER_STEP, ProcessingType.SMPP);
if (deliveryMsgCnt == 0) {
this.markDeliveringIsEnded(true);
return;
}
EsmeManagement esmeManagement = EsmeManagement.getInstance();
Esme esme = esmeManagement.getEsmeByClusterName(smsSet.getDestClusterName());
if (esme == null) {
String s = "\nRxSmppServerSbb.sendDeliverSm(): Received DELIVER_SM SmsEvent but no Esme found for destClusterName: "
+ smsSet.getDestClusterName() + ", smsSet=" + smsSet;
logger.warning(s);
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, s);
return;
}
smsSet.setDestSystemId(esme.getSystemId());
smsSet.setDestEsmeName(esme.getName());
for (int poolIndex = 0; poolIndex < deliveryMsgCnt; poolIndex++) {
smscStatAggregator.updateMsgOutTryAll();
smscStatAggregator.updateMsgOutTrySmpp();
Sms sms = this.getMessageInSendingPool(poolIndex);
if (sms == null) {
// this should not be
throw new SmscProcessingException(
"sendDeliverSm: getCurrentMessage() returns null sms for msgNum in SendingPool " + poolIndex, 0, 0,
SmscProcessingException.HTTP_ERROR_CODE_NOT_SET, null,
SmscProcessingException.INTERNAL_ERROR_SEND_DELIVER_SM_000007);
}
// message splitting staff
boolean esmeAllowSplitting = esme.getSplitLongMessages();
int esmClass = sms.getEsmClass();
boolean udhPresent = (esmClass & SmppConstants.ESM_CLASS_UDHI_MASK) != 0;
Tlv sarMsgRefNum = sms.getTlvSet().getOptionalParameter(SmppConstants.TAG_SAR_MSG_REF_NUM);
Tlv sarTotalSegments = sms.getTlvSet().getOptionalParameter(SmppConstants.TAG_SAR_TOTAL_SEGMENTS);
Tlv sarSegmentSeqnum = sms.getTlvSet().getOptionalParameter(SmppConstants.TAG_SAR_SEGMENT_SEQNUM);
boolean sarTlvPresent = sarMsgRefNum != null && sarTotalSegments != null && sarSegmentSeqnum != null;
ArrayList<String> lstStrings = new ArrayList<String>();
ArrayList<byte[]> lstUdhs = new ArrayList<byte[]>();
lstStrings.add(sms.getShortMessageText());
lstUdhs.add(sms.getShortMessageBin());
if (esmeAllowSplitting && !udhPresent && !sarTlvPresent) {
DataCodingScheme dataCodingScheme = this.mapSmsTpduParameterFactory.createDataCodingScheme(sms
.getDataCoding());
String[] segmentsStrings = MessageUtil.sliceMessage(sms.getShortMessageText(), dataCodingScheme,
sms.getNationalLanguageLockingShift(), sms.getNationalLanguageSingleShift());
if (segmentsStrings != null && segmentsStrings.length > 1) {
// we need to split a message for segments
lstStrings.clear();
lstUdhs.clear();
int messageReferenceNumber = getNextMessageReferenceNumber();
esmClass |= SmppConstants.ESM_CLASS_UDHI_MASK;
int messageSegmentCount = segmentsStrings.length;
for (int ii1 = 0; ii1 < messageSegmentCount; ii1++) {
lstStrings.add(segmentsStrings[ii1]);
byte[] bf1 = new byte[7];
bf1[0] = 6; // total UDH length
bf1[1] = UserDataHeader._InformationElementIdentifier_ConcatenatedShortMessages16bit; // UDH id
bf1[2] = 4; // UDH length
bf1[3] = (byte) (messageReferenceNumber & 0x00FF);
bf1[4] = (byte) ((messageReferenceNumber & 0xFF00) >> 8);
bf1[5] = (byte) messageSegmentCount; // segmCnt
bf1[6] = (byte) (ii1 + 1); // segmNum
lstUdhs.add(bf1);
}
}
}
int sequenceNumber = 0;
int[] sequenceNumberExt = null;
int segmCnt = lstStrings.size();
if (segmCnt > 1) {
sequenceNumberExt = new int[segmCnt - 1];
}
for (int segmentIndex = 0; segmentIndex < segmCnt; segmentIndex++) {
if (esme.getSmppSessionType() == Type.CLIENT) {
SubmitSm submitSm = new SubmitSm();
submitSm.setSourceAddress(new Address((byte) sms.getSourceAddrTon(), (byte) sms.getSourceAddrNpi(), sms
.getSourceAddr()));
submitSm.setDestAddress(new Address((byte) sms.getSmsSet().getDestAddrTon(), (byte) sms.getSmsSet()
.getDestAddrNpi(), sms.getSmsSet().getDestAddr()));
submitSm.setEsmClass((byte) esmClass);
submitSm.setProtocolId((byte) sms.getProtocolId());
submitSm.setPriority((byte) sms.getPriority());
if (sms.getScheduleDeliveryTime() != null) {
submitSm.setScheduleDeliveryTime(MessageUtil.printSmppAbsoluteDate(sms.getScheduleDeliveryTime(),
-(new Date()).getTimezoneOffset()));
}
if (sms.getValidityPeriod() != null) {
submitSm.setValidityPeriod(MessageUtil.printSmppAbsoluteDate(sms.getValidityPeriod(),
-(new Date()).getTimezoneOffset()));
}
submitSm.setRegisteredDelivery((byte) sms.getRegisteredDelivery());
submitSm.setReplaceIfPresent((byte) sms.getReplaceIfPresent());
submitSm.setDataCoding((byte) sms.getDataCoding());
String msgStr = lstStrings.get(segmentIndex);
byte[] msgUdh = lstUdhs.get(segmentIndex);
if (msgStr != null || msgUdh != null) {
byte[] msg = recodeShortMessage(sms.getDataCoding(), msgStr, msgUdh);
if (msg.length <= 255) {
submitSm.setShortMessage(msg);
} else {
Tlv tlv = new Tlv(SmppConstants.TAG_MESSAGE_PAYLOAD, msg, null);
submitSm.addOptionalParameter(tlv);
}
}
for (Tlv tlv : sms.getTlvSet().getOptionalParameters()) {
submitSm.addOptionalParameter(tlv);
}
SmppTransaction smppServerTransaction = this.smppServerSessions.sendRequestPdu(esme, submitSm,
esme.getWindowWaitTimeout());
if (logger.isInfoEnabled()) {
logger.info(String.format("\nSent submitSm to ESME: %s, msgNumInSendingPool: %d, sms=%s",
esme.getName(), poolIndex, sms.toString()));
}
if (segmentIndex == 0) {
sequenceNumber = submitSm.getSequenceNumber();
} else {
sequenceNumberExt[segmentIndex - 1] = submitSm.getSequenceNumber();
}
ActivityContextInterface smppTxaci = this.smppServerTransactionACIFactory
.getActivityContextInterface(smppServerTransaction);
smppTxaci.attach(this.sbbContext.getSbbLocalObject());
} else {
DeliverSm deliverSm = new DeliverSm();
deliverSm.setSourceAddress(new Address((byte) sms.getSourceAddrTon(), (byte) sms.getSourceAddrNpi(),
sms.getSourceAddr()));
deliverSm.setDestAddress(new Address((byte) sms.getSmsSet().getDestAddrTon(), (byte) sms.getSmsSet()
.getDestAddrNpi(), sms.getSmsSet().getDestAddr()));
deliverSm.setEsmClass((byte) esmClass);
deliverSm.setProtocolId((byte) sms.getProtocolId());
deliverSm.setPriority((byte) sms.getPriority());
if (sms.getScheduleDeliveryTime() != null) {
deliverSm.setScheduleDeliveryTime(MessageUtil.printSmppAbsoluteDate(sms.getScheduleDeliveryTime(),
-(new Date()).getTimezoneOffset()));
}
if (sms.getValidityPeriod() != null && esme.getSmppVersion() == SmppInterfaceVersionType.SMPP50) {
deliverSm.setValidityPeriod(MessageUtil.printSmppAbsoluteDate(sms.getValidityPeriod(),
-(new Date()).getTimezoneOffset()));
}
deliverSm.setRegisteredDelivery((byte) sms.getRegisteredDelivery());
deliverSm.setReplaceIfPresent((byte) sms.getReplaceIfPresent());
deliverSm.setDataCoding((byte) sms.getDataCoding());
String msgStr = lstStrings.get(segmentIndex);
byte[] msgUdh = lstUdhs.get(segmentIndex);
if (msgStr != null || msgUdh != null) {
byte[] msg = recodeShortMessage(sms.getDataCoding(), msgStr, msgUdh);
if (msg.length <= 255) {
deliverSm.setShortMessage(msg);
} else {
Tlv tlv = new Tlv(SmppConstants.TAG_MESSAGE_PAYLOAD, msg, null);
deliverSm.addOptionalParameter(tlv);
}
}
for (Tlv tlv : sms.getTlvSet().getOptionalParameters()) {
deliverSm.addOptionalParameter(tlv);
}
// TODO : waiting for 2 secs for window to accept our
// request,
// is it good? Should time be more here?
SmppTransaction smppServerTransaction = this.smppServerSessions.sendRequestPdu(esme, deliverSm,
esme.getWindowWaitTimeout());
if (logger.isInfoEnabled()) {
logger.info(String.format("\nSent deliverSm to ESME: %s, msgNumInSendingPool: %d, sms=%s",
esme.getName(), poolIndex, sms.toString()));
}
if (segmentIndex == 0) {
sequenceNumber = deliverSm.getSequenceNumber();
} else {
sequenceNumberExt[segmentIndex - 1] = deliverSm.getSequenceNumber();
}
ActivityContextInterface smppTxaci = this.smppServerTransactionACIFactory
.getActivityContextInterface(smppServerTransaction);
smppTxaci.attach(this.sbbContext.getSbbLocalObject());
}
}
this.registerMessageInSendingPool(poolIndex, sequenceNumber, sequenceNumberExt);
}
this.endRegisterMessageInSendingPool();
} catch (Throwable e) {
throw new SmscProcessingException(
"RxSmppServerSbb.sendDeliverSm(): Exception while trying to send DELIVERY Report for received SmsEvent="
+ e.getMessage() + "\nsmsSet: " + smsSet,
0, 0, SmscProcessingException.HTTP_ERROR_CODE_NOT_SET, null, e,
SmscProcessingException.INTERNAL_ERROR_SEND_DELIVER_SM_000008);
}
}
protected byte[] recodeShortMessage(int dataCoding, String msg, byte[] udhPart) {
DataCodingScheme dataCodingScheme = new DataCodingSchemeImpl(dataCoding);
byte[] textPart;
if (msg != null) {
if (dataCodingScheme.getCharacterSet() == CharacterSet.GSM8) {
textPart = msg.getBytes(isoCharset);
} else {
SmppEncoding enc;
if (dataCodingScheme.getCharacterSet() == CharacterSet.GSM7) {
enc = smscPropertiesManagement.getSmppEncodingForGsm7();
} else {
enc = smscPropertiesManagement.getSmppEncodingForUCS2();
}
if (enc == SmppEncoding.Utf8) {
textPart = msg.getBytes(utf8Charset);
} else if (enc == SmppEncoding.Unicode) {
textPart = msg.getBytes(ucs2Charset);
} else {
GSMCharsetEncoder encoder = (GSMCharsetEncoder) gsm7Charset.newEncoder();
encoder.setGSMCharsetEncodingData(new GSMCharsetEncodingData(Gsm7EncodingStyle.bit8_smpp_style, null));
ByteBuffer bb = null;
try {
bb = encoder.encode(CharBuffer.wrap(msg));
} catch (CharacterCodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
textPart = new byte[bb.limit()];
bb.get(textPart);
}
}
} else {
textPart = new byte[0];
}
if (udhPart == null) {
return textPart;
} else {
byte[] res = new byte[textPart.length + udhPart.length];
System.arraycopy(udhPart, 0, res, 0, udhPart.length);
System.arraycopy(textPart, 0, res, udhPart.length, textPart.length);
return res;
}
}
/* (non-Javadoc)
* @see org.mobicents.smsc.slee.services.deliverysbb.DeliveryCommonSbb#onDeliveryTimeout(org.mobicents.smsc.library.SmsSet, java.lang.String)
*/
@Override
protected void onDeliveryTimeout(SmsSet smsSet, String reason) {
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, reason);
}
/**
* Processing of a positive delivery response to smpp destination.
*
* @param event
* @throws Exception
*/
private void handleResponse(BaseSmResp event) throws Exception {
long ts = System.currentTimeMillis();
if (isDeliveringEnded()) {
if (logger.isFineEnabled()) {
this.logger.fine("SMPP Response received when DeliveringEnded state: status=" + event.getCommandStatus());
}
}
if (isDeliveringEnded()) {
logger.info("RxSmppServerSbb.handleResponse(): received submit/deliver_sm_response but delivery process is already ended, dropping of a response");
return;
}
SmsSet smsSet = getSmsSet();
if (smsSet == null) {
logger.severe("RxSmppServerSbb.handleResponse(): CMP smsSet is missed");
markDeliveringIsEnded(true);
return;
}
int status = event.getCommandStatus();
if (status == 0) {
smscStatAggregator.updateMsgOutSentAll();
smscStatAggregator.updateMsgOutSentSmpp();
ConfirmMessageInSendingPool confirmMessageInSendingPool = confirmMessageInSendingPool(event.getSequenceNumber());
if (!confirmMessageInSendingPool.sequenceNumberFound) {
this.logger.severe("RxSmppServerSbb.handleResponse(): no sms in MessageInSendingPool: UnconfirmedCnt="
+ this.getUnconfirmedMessageCountInSendingPool() + ", sequenceNumber=" + event.getSequenceNumber());
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR,
"Received undefined SequenceNumber: " + event.getSequenceNumber() + ", SmsSet=" + smsSet);
return;
}
Sms sms = confirmMessageInSendingPool.sms;
if (!confirmMessageInSendingPool.confirmed) {
this.generateCDR(sms, CdrGenerator.CDR_PARTIAL_ESME, CdrGenerator.CDR_SUCCESS_NO_REASON, true, false);
return;
}
// firstly we store remote messageId if sms has a request to delivery receipt
String clusterName = smsSet.getDestClusterName();
String dlvMessageId = event.getMessageId();
sms.setDlvMessageId(dlvMessageId);
// if (MessageUtil.isDeliveryReceiptRequest(sms)) {
// SmsSetCache.getInstance().putDeliveredRemoteMsgIdValue(dlvMessageId, clusterName, sms.getMessageId(), 30);
// }
// current message is sent
// firstly sending of a positive response for transactional mode
sendTransactionalResponseSuccess(sms);
// mproc rules applying for delivery phase
this.applyMprocRulesOnSuccess(sms, ProcessingType.SMPP);
// Processing succeeded
sms.getSmsSet().setStatus(ErrorCode.SUCCESS);
this.postProcessSucceeded(sms, dlvMessageId, clusterName);
// success CDR generating
boolean isPartial = MessageUtil.isSmsNotLastSegment(sms);
this.generateCDR(sms, isPartial ? CdrGenerator.CDR_PARTIAL_ESME : CdrGenerator.CDR_SUCCESS_ESME,
CdrGenerator.CDR_SUCCESS_NO_REASON, confirmMessageInSendingPool.splittedMessage, true);
// adding a success receipt if it is needed
this.generateSuccessReceipt(smsSet, sms);
if (this.getUnconfirmedMessageCountInSendingPool() == 0) {
// all sent messages are confirmed - we are sending new message set
TargetAddress lock = persistence.obtainSynchroObject(new TargetAddress(smsSet));
try {
synchronized (lock) {
// marking the message in cache as delivered
this.commitSendingPoolMsgCount();
ts = warnIfLong(logger, ts, "handleResponse/status=0/in-lock/commitSendingPoolMsgCount");
// now we are trying to sent other messages
if (this.getTotalUnsentMessageCount() > 0) {
try {
this.sendDeliverSm(smsSet);
ts = warnIfLong(logger, ts, "handleResponse/status=0/in-lock/sendDeliverSm");
return;
} catch (SmscProcessingException e) {
SbbStatsUtils.handleProcessingException(e, getDefaultSbbUsageParameterSet());
String s = "SmscProcessingException when sending next sendDeliverSm()=" + e.getMessage()
+ ", Message=" + sms;
logger.severe(s, e);
this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, s);
}
}
// no more messages to send - remove smsSet
smsSet.setStatus(ErrorCode.SUCCESS);
this.markDeliveringIsEnded(true);
}
} finally {
persistence.releaseSynchroObject(lock);
}
}
} else {
ErrorAction errorAction = ErrorAction.permanentFailure;
smsSet.setSmppCommandStatus(status);
if (status == SmppConstants.STATUS_THROTTLED || status == SmppConstants.STATUS_X_T_APPN
|| status == SmppConstants.STATUS_SYSERR || status == SmppConstants.STATUS_INVBNDSTS)
errorAction = ErrorAction.temporaryFailure;
logger.warning("RxSmppServerSbb.handleResponse(): error code response received: status=" + status + ", errorAction="
+ errorAction + ", smsSet=" + smsSet);
this.onDeliveryError(smsSet, errorAction, ErrorCode.SC_SYSTEM_ERROR, event.getName()
+ " has a bad status: " + status);
}
}
/**
* Processing a case when an error in message sending process. This stops of message sending, reschedule or drop messages
* and clear resources.
*
* @param smsSet
* @param errorAction
* @param smStatus
* @param reason
*/
private void onDeliveryError(SmsSet smsSet, ErrorAction errorAction, ErrorCode smStatus, String reason) {
getDefaultSbbUsageParameterSet().incrementCounterErrorDelivery(ONE);
try {
smscStatAggregator.updateMsgOutFailedAll();
// generating of a temporary failure CDR (one record for all unsent messages)
if (smscPropertiesManagement.getGenerateTempFailureCdr())
this.generateTemporaryFailureCDR(CdrGenerator.CDR_TEMP_FAILED_ESME, reason);
ArrayList<Sms> lstPermFailured = new ArrayList<Sms>();
ArrayList<Sms> lstTempFailured = new ArrayList<Sms>();
ArrayList<Sms> lstPermFailured2 = new ArrayList<Sms>();
ArrayList<Sms> lstTempFailured2 = new ArrayList<Sms>();
ArrayList<Sms> lstRerouted = new ArrayList<Sms>();
ArrayList<Integer> lstNewNetworkId = new ArrayList<Integer>();
TargetAddress lock = persistence.obtainSynchroObject(new TargetAddress(smsSet));
synchronized (lock) {
try {
// ending of delivery process in this SBB
smsSet.setStatus(smStatus);
this.markDeliveringIsEnded(true);
// calculating of newDueDelay and NewDueTime
int newDueDelay = calculateNewDueDelay(smsSet, false);
Date newDueTime = calculateNewDueTime(smsSet, newDueDelay);
// creating of failure lists
this.createFailureLists(lstPermFailured, lstTempFailured, errorAction, newDueTime);
// mproc rules applying for delivery phase
this.applyMprocRulesOnFailure(lstPermFailured, lstTempFailured, lstPermFailured2, lstTempFailured2,
lstRerouted, lstNewNetworkId, ProcessingType.SMPP);
// sending of a failure response for transactional mode
this.sendTransactionalResponseFailure(lstPermFailured2, lstTempFailured2, errorAction, null);
// Processing messages that were temp or permanent failed or rerouted
this.postProcessPermFailures(lstPermFailured2, null, null);
this.postProcessTempFailures(smsSet, lstTempFailured2, newDueDelay, newDueTime, false);
this.postProcessRerouted(lstRerouted, lstNewNetworkId);
// generating CDRs for permanent failure messages
this.generateCDRs(lstPermFailured2, CdrGenerator.CDR_FAILED_ESME, reason);
// sending of intermediate delivery receipts
this.generateIntermediateReceipts(smsSet, lstTempFailured2);
// sending of failure delivery receipts
this.generateFailureReceipts(smsSet, lstPermFailured2, null);
} finally {
persistence.releaseSynchroObject(lock);
}
}
} catch (Throwable e) {
getDefaultSbbUsageParameterSet().incrementCounterErrorDeliveryException(ONE);
logger.severe("Exception in RxSmppServerSbb.onDeliveryError(): " + e.getMessage(), e);
markDeliveringIsEnded(true);
}
}
}