/* * 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.mt; import java.util.ArrayList; import java.util.Date; import javax.naming.Context; import javax.naming.InitialContext; import javax.slee.ActivityContextInterface; import javax.slee.Sbb; import javax.slee.SbbContext; import org.mobicents.protocols.ss7.map.api.MAPApplicationContext; import org.mobicents.protocols.ss7.map.api.MAPApplicationContextName; import org.mobicents.protocols.ss7.map.api.MAPApplicationContextVersion; import org.mobicents.protocols.ss7.map.api.MAPParameterFactory; import org.mobicents.protocols.ss7.map.api.MAPProvider; import org.mobicents.protocols.ss7.map.api.MAPSmsTpduParameterFactory; import org.mobicents.protocols.ss7.map.api.dialog.MAPUserAbortChoice; import org.mobicents.protocols.ss7.map.api.dialog.ProcedureCancellationReason; import org.mobicents.protocols.ss7.map.api.dialog.ResourceUnavailableReason; import org.mobicents.protocols.ss7.map.api.errors.MAPErrorMessage; import org.mobicents.protocols.ss7.map.api.primitives.AddressNature; import org.mobicents.protocols.ss7.map.api.primitives.AddressString; import org.mobicents.protocols.ss7.map.api.primitives.ISDNAddressString; import org.mobicents.protocols.ss7.map.api.primitives.NumberingPlan; import org.mobicents.protocols.ss7.map.api.service.sms.SMDeliveryOutcome; import org.mobicents.protocols.ss7.sccp.impl.parameter.ParameterFactoryImpl; import org.mobicents.protocols.ss7.sccp.parameter.ParameterFactory; import org.mobicents.protocols.ss7.sccp.parameter.SccpAddress; import org.mobicents.protocols.ss7.tcap.asn.comp.Problem; import org.mobicents.slee.ChildRelationExt; import org.mobicents.slee.resource.map.MAPContextInterfaceFactory; import org.mobicents.slee.resource.map.events.DialogAccept; import org.mobicents.slee.resource.map.events.DialogClose; import org.mobicents.slee.resource.map.events.DialogDelimiter; import org.mobicents.slee.resource.map.events.DialogNotice; import org.mobicents.slee.resource.map.events.DialogProviderAbort; import org.mobicents.slee.resource.map.events.DialogReject; import org.mobicents.slee.resource.map.events.DialogRelease; import org.mobicents.slee.resource.map.events.DialogRequest; import org.mobicents.slee.resource.map.events.DialogTimeout; import org.mobicents.slee.resource.map.events.DialogUserAbort; import org.mobicents.slee.resource.map.events.ErrorComponent; import org.mobicents.slee.resource.map.events.InvokeTimeout; import org.mobicents.slee.resource.map.events.RejectComponent; 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.Sms; import org.mobicents.smsc.library.SmsSet; import org.mobicents.smsc.library.TargetAddress; import org.mobicents.smsc.mproc.ProcessingType; import org.mobicents.smsc.slee.services.deliverysbb.DeliveryCommonSbb; import org.mobicents.smsc.slee.services.smpp.server.events.SendRsdsEvent; /** * * @author amit bhayani * @author sergey vetyutnev * */ public abstract class MtCommonSbb extends DeliveryCommonSbb implements Sbb, ReportSMDeliveryStatusInterface2 { protected static final String MAP_USER_ABORT_CHOICE_USER_SPECIFIC_REASON = "userSpecificReason"; protected static final String MAP_USER_ABORT_CHOICE_USER_RESOURCE_LIMITATION = "userResourceLimitation"; protected static final String MAP_USER_ABORT_CHOICE_UNKNOWN = "DialogUserAbort_Unknown"; protected MAPContextInterfaceFactory mapAcif; protected MAPProvider mapProvider; protected MAPParameterFactory mapParameterFactory; protected MAPSmsTpduParameterFactory mapSmsTpduParameterFactory; protected ParameterFactory sccpParameterFact; private AddressString serviceCenterAddress; private SccpAddress serviceCenterSCCPAddress = null; protected SmscStatAggregator smscStatAggregator = SmscStatAggregator.getInstance(); public MtCommonSbb(String className) { super(className); } // ********* // SBB staff @Override public void setSbbContext(SbbContext sbbContext) { super.setSbbContext(sbbContext); try { Context ctx = (Context) new InitialContext().lookup("java:comp/env"); this.mapAcif = (MAPContextInterfaceFactory) ctx.lookup("slee/resources/map/2.0/acifactory"); this.mapProvider = (MAPProvider) ctx.lookup("slee/resources/map/2.0/provider"); this.mapParameterFactory = this.mapProvider.getMAPParameterFactory(); this.mapSmsTpduParameterFactory = this.mapProvider.getMAPSmsTpduParameterFactory(); this.sccpParameterFact = new ParameterFactoryImpl(); } catch (Exception ne) { logger.severe("Could not set SBB context:", ne); } } @Override public void sbbLoad() { super.sbbLoad(); } @Override public void sbbStore() { super.sbbStore(); } // ********* // CMPs public abstract void setSriMapVersion(int sriMapVersion); public abstract int getSriMapVersion(); // ********* // Get Rsds child SBB public abstract ChildRelationExt getRsdsSbb(); public abstract void fireSendRsdsEvent(SendRsdsEvent event, ActivityContextInterface aci, javax.slee.Address address); private RsdsSbbLocalObject getRsdsSbbObject() { ChildRelationExt relation = getRsdsSbb(); RsdsSbbLocalObject ret = (RsdsSbbLocalObject) relation.get(ChildRelationExt.DEFAULT_CHILD_NAME); if (ret == null) { try { ret = (RsdsSbbLocalObject) relation.create(ChildRelationExt.DEFAULT_CHILD_NAME); } catch (Exception e) { if (this.logger.isSevereEnabled()) { this.logger.severe("Exception while trying to creat RsdsSbb child", e); } } } return ret; } protected void setupReportSMDeliveryStatusRequest(String destinationAddress, int ton, int npi, SMDeliveryOutcome sMDeliveryOutcome, String targetId, int networkId) { RsdsSbbLocalObject rsdsSbbLocalObject = this.getRsdsSbbObject(); if (rsdsSbbLocalObject != null) { ActivityContextInterface schedulerActivityContextInterface = this.getSchedulerActivityContextInterface(); schedulerActivityContextInterface.attach(rsdsSbbLocalObject); SendRsdsEvent event = new SendRsdsEvent(); event.setMsisdn(this.getCalledPartyISDNAddressString(destinationAddress, ton, npi)); event.setServiceCentreAddress(getServiceCenterAddressString(networkId)); event.setSMDeliveryOutcome(sMDeliveryOutcome); event.setDestAddress(this.convertAddressFieldToSCCPAddress(destinationAddress, ton, npi)); event.setMapApplicationContext(this.getSRIMAPApplicationContext(MAPApplicationContextVersion.getInstance(this.getSriMapVersion()))); event.setTargetId(targetId); event.setNetworkId(networkId); this.fireSendRsdsEvent(event, schedulerActivityContextInterface, null); } } // ********* // MAP Component events public void onErrorComponent(ErrorComponent event, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onErrorComponent(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (this.logger.isInfoEnabled()) { this.logger.info("\nRx : onErrorComponent " + event + " targetId=" + smsSet.getTargetId() + ", Dialog=" + event.getMAPDialog()); } } public void onRejectComponent(RejectComponent event, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onErrorComponent(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } this.logger.severe("\nRx : onRejectComponent targetId=" + smsSet.getTargetId() + ", " + event); } protected String getRejectComponentReason(RejectComponent event) { Problem problem = event.getProblem(); String reason = null; switch (problem.getType()) { case General: reason = problem.getGeneralProblemType().toString(); break; case Invoke: reason = problem.getInvokeProblemType().toString(); break; case ReturnResult: reason = problem.getReturnResultProblemType().toString(); break; case ReturnError: reason = problem.getReturnErrorProblemType().toString(); break; default: reason = "RejectComponent_unknown_" + problem.getType(); break; } try { event.getMAPDialog().close(false); } catch (Exception e) { } return reason; } public void onInvokeTimeout(InvokeTimeout evt, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onInvokeTimeout(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (logger.isWarningEnabled()) { this.logger.warning("\nRx : onInvokeTimeout targetId=" + smsSet.getTargetId() + ", " + evt); } } // ********* // MAP Dialog events public void onDialogReject(DialogReject evt, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onDialogReject(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (logger.isWarningEnabled()) { this.logger.warning("\nRx : onDialogReject targetId=" + smsSet.getTargetId() + ", " + evt); } } public void onDialogProviderAbort(DialogProviderAbort evt, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onDialogProviderAbort(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (logger.isWarningEnabled()) { this.logger.warning("\nRx : onDialogProviderAbort targetId=" + smsSet.getTargetId() + ", " + evt); } } public void onDialogUserAbort(DialogUserAbort evt, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onDialogUserAbort(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (logger.isWarningEnabled()) { this.logger.warning("\nRx : onDialogUserAbort targetId=" + smsSet.getTargetId() + ", " + evt); } } public void onDialogTimeout(DialogTimeout evt, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onDialogTimeout(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (logger.isWarningEnabled()) { this.logger.warning("\nRx : onDialogTimeout targetId=" + smsSet.getTargetId() + ", " + evt); } } public void onDialogDelimiter(DialogDelimiter evt, ActivityContextInterface aci) { if (logger.isFineEnabled()) { this.logger.fine("\nRx : onDialogDelimiter " + evt); } } public void onDialogAccept(DialogAccept evt, ActivityContextInterface aci) { if (logger.isFineEnabled()) { this.logger.fine("\nRx : onDialogAccept=" + evt); } } public void onDialogClose(DialogClose evt, ActivityContextInterface aci) { if (logger.isFineEnabled()) { this.logger.fine("\nRx : onDialogClose=" + evt); } } public void onDialogNotice(DialogNotice evt, ActivityContextInterface aci) { SmsSet smsSet = getSmsSet(); if (smsSet == null) { logger.severe("MtCommonSbb.onDialogNotice(): CMP smsSet is missed"); markDeliveringIsEnded(true); return; } if (logger.isWarningEnabled()) { this.logger.warning("\nRx : onDialogNotice targetId=" + smsSet.getTargetId() + ", " + evt); } } public void onDialogRequest(DialogRequest evt, ActivityContextInterface aci) { if (logger.isFineEnabled()) { this.logger.fine("\nRx : onDialogRequest=" + evt); } } public void onDialogRelease(DialogRelease evt, ActivityContextInterface aci) { if (logger.isInfoEnabled()) { this.logger.info("\nRx : DialogRelease=" + evt); } } // ********* // Main service methods @Override protected void onDeliveryTimeout(SmsSet smsSet, String reason) { this.onDeliveryError(smsSet, ErrorAction.temporaryFailure, ErrorCode.SC_SYSTEM_ERROR, reason, true, null, false, ProcessingType.SS7_MT); } /** * 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 * @param removeSmsSet * @param errMessage * @param isImsiVlrReject * @param processingType */ protected void onDeliveryError(SmsSet smsSet, ErrorAction errorAction, ErrorCode smStatus, String reason, boolean removeSmsSet, MAPErrorMessage errMessage, boolean isImsiVlrReject, ProcessingType processingType) { try { smscStatAggregator.updateMsgOutFailedAll(); // generating of a temporary failure CDR if (smscPropertiesManagement.getGenerateTempFailureCdr()) this.generateTemporaryFailureCDR(CdrGenerator.CDR_TEMP_FAILED, reason); StringBuilder sb = new StringBuilder(); sb.append("onDeliveryError: errorAction="); sb.append(errorAction); sb.append(", smStatus="); sb.append(smStatus); sb.append(", targetId="); sb.append(smsSet.getTargetId()); sb.append(", smsSet="); sb.append(smsSet); sb.append(", reason="); sb.append(reason); if (this.logger.isInfoEnabled()) this.logger.info(sb.toString()); 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 { Date curDate = new Date(); smsSet.setStatus(smStatus); // calculating of newDueDelay and NewDueTime int newDueDelay = calculateNewDueDelay(smsSet, (errorAction == ErrorAction.subscriberBusy)); 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); // sending of a failure response for transactional mode this.sendTransactionalResponseFailure(lstPermFailured2, lstTempFailured2, errorAction, errMessage); // Processing messages that were temp or permanent failed or rerouted this.postProcessPermFailures(lstPermFailured2, null, null); this.postProcessTempFailures(smsSet, lstTempFailured2, newDueDelay, newDueTime, true); this.postProcessRerouted(lstRerouted, lstNewNetworkId); // generating CDRs for permanent failure messages this.generateCDRs(lstPermFailured2, (isImsiVlrReject ? CdrGenerator.CDR_FAILED_IMSI : CdrGenerator.CDR_FAILED), reason); // sending of intermediate delivery receipts this.generateIntermediateReceipts(smsSet, lstTempFailured2); // sending of failure delivery receipts this.generateFailureReceipts(smsSet, lstPermFailured2, null); // sending of ReportSMDeliveryStatusRequest if needed SMDeliveryOutcome smDeliveryOutcome = null; switch (errorAction) { case memoryCapacityExceededFlag: smDeliveryOutcome = SMDeliveryOutcome.memoryCapacityExceeded; break; case mobileNotReachableFlag: smDeliveryOutcome = SMDeliveryOutcome.absentSubscriber; break; case notReachableForGprs: smDeliveryOutcome = SMDeliveryOutcome.absentSubscriber; break; } if (smDeliveryOutcome != null && lstTempFailured2.size() > 0) { this.setupReportSMDeliveryStatusRequest(smsSet.getDestAddr(), smsSet.getDestAddrTon(), smsSet.getDestAddrNpi(), smDeliveryOutcome, smsSet.getTargetId(), smsSet.getNetworkId()); } this.markDeliveringIsEnded(removeSmsSet); } finally { persistence.releaseSynchroObject(lock); } } } catch (Throwable e) { logger.severe("Exception in MtCommonSbb.onDeliveryError(): " + e.getMessage(), e); markDeliveringIsEnded(true); } } /** * Processing a case when a SRI trigger drops a message. This stops generate a CDR, a receipt. * * @param smsSet * @param lstPermFailured * @param lstRerouted * @param networkNode * @param imsiData */ protected void onImsiDrop(SmsSet smsSet, ArrayList<Sms> lstPermFailured, ArrayList<Sms> lstRerouted, ArrayList<Integer> lstNewNetworkId, ISDNAddressString networkNode, String imsiData) { if (lstPermFailured.size() == 0 && lstRerouted.size() == 0) { // no actions is needed return; } smsSet.setStatus(ErrorCode.MPROC_SRI_REQUEST_DROP); // sending of a failure response for transactional mode this.sendTransactionalResponseFailure(lstPermFailured, null, ErrorAction.mobileNotReachableFlag, null); // generating CDRs for permanent failure messages this.generateCDRs(lstPermFailured, CdrGenerator.CDR_FAILED_IMSI, "Sri-ImsiRequest: incoming messages are dropped by mProc rules"); // Processing messages that were temp or permanent failed or rerouted this.postProcessPermFailures(lstPermFailured, null, null); this.postProcessRerouted(lstRerouted, lstNewNetworkId); // adding an error receipt if it is needed StringBuilder extraString = new StringBuilder(); extraString.append(" imsi:"); extraString.append(imsiData); extraString.append(" nnn_digits:"); extraString.append(networkNode.getAddress()); extraString.append(" nnn_an:"); extraString.append(networkNode.getAddressNature().getIndicator()); extraString.append(" nnn_np:"); extraString.append(networkNode.getNumberingPlan().getIndicator()); this.generateFailureReceipts(smsSet, lstPermFailured, extraString.toString()); } // ********* // private service methods protected String getUserAbortReason(DialogUserAbort evt) { MAPUserAbortChoice userReason = evt.getUserReason(); String reason = null; if (userReason.isUserSpecificReason()) { reason = MAP_USER_ABORT_CHOICE_USER_SPECIFIC_REASON; } else if (userReason.isUserResourceLimitation()) { reason = MAP_USER_ABORT_CHOICE_USER_RESOURCE_LIMITATION; } else if (userReason.isResourceUnavailableReason()) { ResourceUnavailableReason resourceUnavailableReason = userReason.getResourceUnavailableReason(); reason = resourceUnavailableReason.toString(); } else if (userReason.isProcedureCancellationReason()) { ProcedureCancellationReason procedureCancellationReason = userReason.getProcedureCancellationReason(); reason = procedureCancellationReason.toString(); } else { reason = MAP_USER_ABORT_CHOICE_UNKNOWN; } return reason; } /** * TODO : This is repetitive in each Sbb. Find way to make it static * probably? * * This is our own number. We are Service Center. * * @return */ protected AddressString getServiceCenterAddressString(int networkId) { if (networkId == 0) { if (this.serviceCenterAddress == null) { this.serviceCenterAddress = this.mapParameterFactory.createAddressString(AddressNature.international_number, org.mobicents.protocols.ss7.map.api.primitives.NumberingPlan.ISDN, smscPropertiesManagement.getServiceCenterGt()); } return this.serviceCenterAddress; } else { return this.mapParameterFactory.createAddressString(AddressNature.international_number, org.mobicents.protocols.ss7.map.api.primitives.NumberingPlan.ISDN, smscPropertiesManagement.getServiceCenterGt(networkId)); } } /** * TODO: This should be configurable and static as well * * This is our (Service Center) SCCP Address for GT * * @return */ protected SccpAddress getServiceCenterSccpAddress(int networkId) { if (networkId == 0) { if (this.serviceCenterSCCPAddress == null) { this.serviceCenterSCCPAddress = MessageUtil.getSccpAddress(sccpParameterFact, smscPropertiesManagement.getServiceCenterGt(), AddressNature.international_number.getIndicator(), NumberingPlan.ISDN.getIndicator(), smscPropertiesManagement.getServiceCenterSsn(), smscPropertiesManagement.getGlobalTitleIndicator(), smscPropertiesManagement.getTranslationType()); } return this.serviceCenterSCCPAddress; } else { return MessageUtil.getSccpAddress(sccpParameterFact, smscPropertiesManagement.getServiceCenterGt(networkId), AddressNature.international_number.getIndicator(), NumberingPlan.ISDN.getIndicator(), smscPropertiesManagement.getServiceCenterSsn(), smscPropertiesManagement.getGlobalTitleIndicator(), smscPropertiesManagement.getTranslationType()); } } protected ISDNAddressString getCalledPartyISDNAddressString(String destinationAddress, int ton, int npi) { return this.mapParameterFactory.createISDNAddressString(AddressNature.getInstance(ton), org.mobicents.protocols.ss7.map.api.primitives.NumberingPlan.getInstance(npi), destinationAddress); } protected SccpAddress convertAddressFieldToSCCPAddress(String address, int ton, int npi) { return MessageUtil.getSccpAddress(sccpParameterFact, address, ton, npi, smscPropertiesManagement.getHlrSsn(), smscPropertiesManagement.getGlobalTitleIndicator(), smscPropertiesManagement.getTranslationType()); } protected MAPApplicationContext getSRIMAPApplicationContext(MAPApplicationContextVersion applicationContextVersion) { MAPApplicationContext mapApplicationContext = MAPApplicationContext.getInstance( MAPApplicationContextName.shortMsgGatewayContext, applicationContextVersion); this.setSriMapVersion(applicationContextVersion.getVersion()); return mapApplicationContext; } }