/* * 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.deliverysbb; import java.util.ArrayList; import java.util.Date; import javax.slee.ActivityContextInterface; import javax.slee.CreateException; import javax.slee.EventContext; import javax.slee.RolledBackContext; import javax.slee.Sbb; import javax.slee.SbbContext; import javax.slee.facilities.TimerEvent; import javax.slee.facilities.TimerFacility; import javax.slee.facilities.TimerID; import javax.slee.facilities.TimerOptions; import javax.slee.facilities.Tracer; import javax.slee.resource.ResourceAdaptorTypeID; import org.mobicents.protocols.ss7.map.api.errors.MAPErrorMessage; import org.mobicents.protocols.ss7.map.api.primitives.ISDNAddressString; import org.mobicents.slee.SbbContextExt; import org.mobicents.smsc.cassandra.DBOperations; import org.mobicents.smsc.cassandra.PersistenceException; import org.mobicents.smsc.domain.MProcManagement; import org.mobicents.smsc.domain.SmscPropertiesManagement; import org.mobicents.smsc.domain.StoreAndForwordMode; import org.mobicents.smsc.library.CdrGenerator; import org.mobicents.smsc.library.ErrorAction; import org.mobicents.smsc.library.ErrorCode; import org.mobicents.smsc.library.MessageDeliveryResultResponseInterface; import org.mobicents.smsc.library.MessageUtil; import org.mobicents.smsc.library.Sms; import org.mobicents.smsc.library.SmsSet; import org.mobicents.smsc.library.SmsSetCache; import org.mobicents.smsc.library.TargetAddress; import org.mobicents.smsc.mproc.MProcRuleRaProvider; import org.mobicents.smsc.mproc.ProcessingType; import org.mobicents.smsc.mproc.impl.MProcResult; import org.mobicents.smsc.slee.resources.persistence.PersistenceRAInterface; import org.mobicents.smsc.slee.resources.scheduler.SchedulerActivity; import org.mobicents.smsc.slee.resources.scheduler.SchedulerRaSbbInterface; import javolution.util.FastList; /** * * @author sergey vetyutnev * */ public abstract class DeliveryCommonSbb implements Sbb { public static SmscPropertiesManagement smscPropertiesManagement = SmscPropertiesManagement.getInstance(); private static final ResourceAdaptorTypeID PERSISTENCE_ID = new ResourceAdaptorTypeID("PersistenceResourceAdaptorType", "org.mobicents", "1.0"); private static final ResourceAdaptorTypeID SCHEDULE_ID = new ResourceAdaptorTypeID("SchedulerResourceAdaptorType", "org.mobicents", "1.0"); private static final String PERSISTENCE_LINK = "PersistenceResourceAdaptor"; private static final String SCHEDULE_LINK = "SchedulerResourceAdaptor"; public static final ResourceAdaptorTypeID MPROC_RATYPE_ID = new ResourceAdaptorTypeID("MProcResourceAdaptorType", "org.mobicents", "1.0"); private static final String MPROC_RA_LINK = "MProcResourceAdaptor"; private static final int MAX_POSSIBLE_REROUTING = 9; private static int messageReferenceNumberAncor = 0; protected Tracer logger; protected SbbContextExt sbbContext; protected PersistenceRAInterface persistence; protected SchedulerRaSbbInterface scheduler; protected TimerFacility timerFacility; private MProcRuleRaProvider itsMProcRa; private final String className; private long currentMsgNum; private String targetId; private SmsSet smsSet; protected boolean dlvIsInited; private boolean dlvIsEnded; private boolean smsSetIsLoaded; private PendingRequestsList pendingRequestsList; private int[] sequenceNumbers; private int[][] sequenceNumbersExtra; private boolean pendingRequestsListIsLoaded; private boolean pendingRequestsListIsDirty; public DeliveryCommonSbb(String className) { this.className = className; } private void checkSmsSetLoaded() { if (!smsSetIsLoaded) { smsSetIsLoaded = true; dlvIsEnded = this.getDlvIsEnded(); dlvIsInited = this.getDlvIsInited(); currentMsgNum = this.getCurrentMsgNum(); targetId = null; smsSet = null; if (dlvIsInited && !dlvIsEnded) { targetId = this.getTargetId(); if (targetId == null) { this.logger.warning("targetId is null for DeliveryCommonSbb in dlvIsInited state: " + ", targetId" + this.getTargetId() + "\n" + MessageUtil.stackTraceToString()); return; } // ***** no lock ****** smsSet = SmsSetCache.getInstance().getProcessingSmsSet(targetId); if (smsSet == null) { this.logger.warning("smsSet is null for DeliveryCommonSbb in dlvIsInited state: " + ", targetId" + this.getTargetId() + "\n" + MessageUtil.stackTraceToString()); return; } } } } private void checkPendingRequestsListLoaded() { if (!pendingRequestsListIsLoaded) { pendingRequestsListIsLoaded = true; pendingRequestsList = this.getPendingRequestsList(); } } // ********* // sbb overriding methods. // Caring of CMP load / store // Loading of sbbContext and logger @Override public void setSbbContext(SbbContext sbbContext) { this.sbbContext = (SbbContextExt) sbbContext; this.logger = this.sbbContext.getTracer(this.className); // getClass().getSimpleName() this.timerFacility = this.sbbContext.getTimerFacility(); try { this.persistence = (PersistenceRAInterface) this.sbbContext.getResourceAdaptorInterface(PERSISTENCE_ID, PERSISTENCE_LINK); this.scheduler = (SchedulerRaSbbInterface) this.sbbContext.getResourceAdaptorInterface(SCHEDULE_ID, SCHEDULE_LINK); itsMProcRa = (MProcRuleRaProvider) this.sbbContext.getResourceAdaptorInterface(MPROC_RATYPE_ID, MPROC_RA_LINK); } catch (Exception ne) { logger.severe("Could not set SBB context:", ne); } } @Override public void sbbLoad() { this.smsSetIsLoaded = false; this.pendingRequestsListIsLoaded = false; this.pendingRequestsListIsDirty = false; } @Override public void sbbStore() { if (pendingRequestsListIsDirty) { this.setPendingRequestsList(pendingRequestsList); pendingRequestsListIsDirty = false; } } @Override public void sbbActivate() { // TODO Auto-generated method stub } @Override public void sbbCreate() throws CreateException { // TODO Auto-generated method stub } @Override public void sbbExceptionThrown(Exception arg0, Object arg1, ActivityContextInterface arg2) { // TODO Auto-generated method stub } @Override public void sbbPassivate() { // TODO Auto-generated method stub } @Override public void sbbPostCreate() throws CreateException { // TODO Auto-generated method stub } @Override public void sbbRemove() { // TODO Auto-generated method stub } @Override public void sbbRolledBack(RolledBackContext arg0) { // TODO Auto-generated method stub } @Override public void unsetSbbContext() { itsMProcRa = null; } // ********* // Methods for starting / ending of processing /** * This method is used for adding of a initial set of messages for delivering when delivering has not started This method * must be invoked firstly before all other methods * * @param smsSet An initial set of messages that is added provided to SBB for delivering */ protected void addInitialMessageSet(SmsSet smsSet) { addInitialMessageSet(smsSet, 0); } /** * This method is used for adding of a initial set of messages for delivering when delivering has started and we need to * provide current delivering state. This method must be invoked firstly before all other methods * * @param smsSet An initial set of messages that is added provided to SBB for delivering * @param currentMsgNum */ protected void addInitialMessageSet(SmsSet smsSet, long currentMsgNum) { // if (dlvIsInited) { // checkSmsSetLoaded(); // checkPendingRequestsListLoaded(); // // // TODO: implement adding of messages into delivering process ...................... // throw new UnsupportedOperationException("addMessageSet() invoke is not implemented for DeliverSbb initialized step"); // } else { this.smsSet = smsSet; this.currentMsgNum = currentMsgNum; this.targetId = smsSet.getTargetId(); this.pendingRequestsList = null; this.dlvIsEnded = false; this.dlvIsInited = true; this.setCurrentMsgNum(this.currentMsgNum); this.setTargetId(targetId); this.setPendingRequestsList(null); this.setDlvIsEnded(dlvIsEnded); this.setDlvIsInited(dlvIsInited); smsSetIsLoaded = true; pendingRequestsListIsLoaded = true; pendingRequestsListIsDirty = false; // } } /** * Marking that a delivering process is ended in SBB and next coming messages for delivering must be rescheduled to another * delivering SBB */ protected void markDeliveringIsEnded(boolean removeSmsSet) { checkSmsSetLoaded(); if (smsSet != null) { // ***** no lock ****** if (removeSmsSet) { // TODO: we use "removeSmsSet" only till we use SmsSetCache and do not keep SmsSet inside SBB. We need to remove // "removeSmsSet" after this refactoring (always "removeSmsSet==true") !!! SmsSetCache.getInstance().removeProcessingSmsSet(smsSet.getTargetId()); } } dlvIsEnded = true; this.setDlvIsEnded(dlvIsEnded); this.cancelDeliveryTimer(); this.endScheduleActivity(); } /** * @return true if delivering of message in this SBB is already ended (and new incoming messages must be rescheduled to * another delivering SBB) */ protected boolean isDeliveringEnded() { checkSmsSetLoaded(); return dlvIsEnded; } private SchedulerActivity getSchedulerActivity() { ActivityContextInterface[] acis = this.sbbContext.getActivities(); for (int count = 0; count < acis.length; count++) { ActivityContextInterface aci = acis[count]; Object activity = aci.getActivity(); if (activity instanceof SchedulerActivity) { return (SchedulerActivity) activity; } } return null; } protected ActivityContextInterface getSchedulerActivityContextInterface() { ActivityContextInterface[] acis = this.sbbContext.getActivities(); for (int count = 0; count < acis.length; count++) { ActivityContextInterface aci = acis[count]; Object activity = aci.getActivity(); if (activity instanceof SchedulerActivity) { return aci; } } return null; } private void endScheduleActivity() { try { SchedulerActivity schedulerActivity = this.getSchedulerActivity(); if (schedulerActivity != null) { schedulerActivity.endActivity(); } } catch (Exception e) { if (this.logger != null) this.logger.severe("Error while decrementing endScheduleActivity()", e); } } // ********* // Methods for managing of messages for delivering, a message sending pool /** * @return returns currentMsgNum for smsSet in processing */ protected long getCurrentMsgNumValue() { checkSmsSetLoaded(); return this.currentMsgNum; } /** * @return Total messages that are not yet delivered in SmsSet (including messages in a message sending pool and not yet * added to a message sending pool) */ protected int getTotalUnsentMessageCount() { checkSmsSetLoaded(); if (smsSet != null) return (int) (smsSet.getSmsCount() - this.currentMsgNum + getSendingPoolMessageCount()); else return 0; } /** * @return Total message count in a message sending pool */ protected int getSendingPoolMessageCount() { checkSmsSetLoaded(); if (this.smsSet != null) return this.smsSet.getSendingPoolMsgCount(); else return 0; } /** * @return Processing SmsSet */ protected SmsSet getSmsSet() { checkSmsSetLoaded(); return smsSet; } /** * Returns a message number numUnsent (that is neither sent nor in a message sending pool) * * @param numUnsent Number of an unsent message * @return A message or null if no unsent messages or numUnsent is out of range */ protected Sms getUnsentMessage(int numUnsent) { checkSmsSetLoaded(); checkPendingRequestsListLoaded(); if (smsSet != null) return smsSet.getSms(this.currentMsgNum + numUnsent); else return null; } /** * Returns a message number numInSendingPool in a message sending pool * * @param numInSendingPool Number of message in a message sending pool * @return A message or null if a sending pool is empty or numInSendingPool is out of a message sending pool range */ protected Sms getMessageInSendingPool(int numInSendingPool) { checkSmsSetLoaded(); checkPendingRequestsListLoaded(); // ***** no lock ****** if (numInSendingPool < 0 || numInSendingPool >= this.getSendingPoolMessageCount()) { // this is a case when a message number is outside sendingPoolMsgCount return null; } if (smsSet != null) { return smsSet.getMessageFromSendingPool(numInSendingPool); } else return null; } /** * Marking a previously arranged message sending pool as delivered with resource releasing */ protected void commitSendingPoolMsgCount() { checkSmsSetLoaded(); checkPendingRequestsListLoaded(); if (smsSet != null && getSendingPoolMessageCount() > 0) { smsSet.clearSendingPool(); pendingRequestsListIsDirty = true; pendingRequestsList = null; sequenceNumbers = null; sequenceNumbersExtra = null; } } /** * Arrange a new message sending pool with the poolMessageCount message count in it. If pending message count is less then * poolMessageCount then all pending message will be arranged to a message sending pool. Previous arranging pool will be * removed by this operation and pending messages in it will be marked as already processed. If you need to arrange only one * message in message sending pool you can use obtainNextMessage() method. Messages wit expired ValidityPeriod are not added * to a sendingPoolMsg but are processed as perm failed. * * @param poolMessageCount Max message count that must be included into a a new message sending pool * @param processingType * @return a count of messages in a new message pool */ protected int obtainNextMessagesSendingPool(int poolMessageCount, ProcessingType processingType) { commitSendingPoolMsgCount(); if (smsSet != null) { int addedMessageCnt = 0; TargetAddress lock = persistence.obtainSynchroObject(new TargetAddress(smsSet)); // ***** lock ****** try { synchronized (lock) { int gotMessageCnt = 0; int sendingPoolMsgCount = this.getTotalUnsentMessageCount(); for (int i1 = 0; i1 < sendingPoolMsgCount; i1++) { if (addedMessageCnt >= poolMessageCount) { break; } gotMessageCnt++; Sms sms = smsSet.getSms(currentMsgNum + i1); if (sms == null) { this.logger.severe("RxSmpp obtainNextMessagesSendingPool() error: sms is not found num=" + i1 + " from " + sendingPoolMsgCount + ", smsSet=" + smsSet); break; } if (sms.getValidityPeriod() != null && sms.getValidityPeriod().getTime() <= System.currentTimeMillis()) { this.endDeliveryAfterValidityPeriod(sms, processingType, null, null); } else { boolean res1 = applyMProcPreDelivery(sms, processingType); if (res1) { addedMessageCnt++; sms.setDeliveryCount(sms.getDeliveryCount() + 1); smsSet.markSmsAsDelivered(currentMsgNum + i1); smsSet.addMessageToSendingPool(sms); } } } sequenceNumbers = new int[addedMessageCnt]; sequenceNumbersExtra = new int[addedMessageCnt][]; if (gotMessageCnt > 0) { currentMsgNum += gotMessageCnt; this.setCurrentMsgNum(currentMsgNum); } this.rescheduleDeliveryTimer(); } } finally { persistence.releaseSynchroObject(lock); } return addedMessageCnt; } else return 0; } /** * Arrange a new message sending pool with only one message in it. If no pending message then no message will be arranged to * a message sending pool. Previous arranging pool will be removed by this operation and pending messages in it will be * marked as already processed. If you need to arrange more then one message in message sending pool you can use * obtainNextMessagesSendingPool() method. Messages wit expired ValidityPeriod are not added to a sendingPoolMsg but are * processed as perm failed. * * @param processingType * @return a message for sending or null if no more message to send */ protected Sms obtainNextMessage(ProcessingType processingType) { commitSendingPoolMsgCount(); if (smsSet != null) { Sms sms = null; // ***** lock ****** TargetAddress lock = persistence.obtainSynchroObject(new TargetAddress(smsSet)); try { synchronized (lock) { int addedMessageCnt = 0; int gotMessageCnt = 0; int sendingPoolMsgCount = this.getTotalUnsentMessageCount(); for (int i1 = 0; i1 < sendingPoolMsgCount; i1++) { if (addedMessageCnt >= 1) { break; } gotMessageCnt++; sms = smsSet.getSms(currentMsgNum + i1); if (sms == null) { this.logger.severe("RxSmpp obtainNextMessage() error: sms is not found num=" + i1 + " from " + sendingPoolMsgCount + ", smsSet=" + smsSet); break; } if (sms.getValidityPeriod() != null && sms.getValidityPeriod().getTime() <= System.currentTimeMillis()) { this.endDeliveryAfterValidityPeriod(sms, processingType, null, null); sms = null; } else { boolean res1 = applyMProcPreDelivery(sms, processingType); if (!res1) { sms = null; } else { addedMessageCnt++; sms.setDeliveryCount(sms.getDeliveryCount() + 1); smsSet.markSmsAsDelivered(currentMsgNum + i1); smsSet.addMessageToSendingPool(sms); } } } if (gotMessageCnt > 0) { currentMsgNum += gotMessageCnt; this.setCurrentMsgNum(currentMsgNum); } sequenceNumbers = null; sequenceNumbersExtra = null; this.rescheduleDeliveryTimer(); } } finally { persistence.releaseSynchroObject(lock); } return sms; } else return null; } private boolean applyMProcPreDelivery(Sms sms, ProcessingType processingType) { MProcResult mProcResult = MProcManagement.getInstance().applyMProcPreDelivery(itsMProcRa, sms, processingType); if (mProcResult.isMessageIsRerouted()) { // firstly we check if rerouting attempts was not too many if (sms.getReroutingCount() >= MAX_POSSIBLE_REROUTING) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message attempt in PreDelivery, but we have already rerouted "); sb.append(MAX_POSSIBLE_REROUTING); sb.append(" times before: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.warning(sb.toString()); return false; } else if (mProcResult.getNewNetworkId() == sms.getSmsSet().getNetworkId()) { // we do not reroute for the same networkId return true; } else { ArrayList<Sms> lstRerouted = new ArrayList<Sms>(); ArrayList<Integer> lstNewNetworkId = new ArrayList<Integer>(); lstRerouted.add(sms); lstNewNetworkId.add(mProcResult.getNewNetworkId()); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message in PreDelivery: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } postProcessRerouted(lstRerouted, lstNewNetworkId); return false; } } else if (mProcResult.isMessageDropped()) { if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Dropping message after in PreDelivery: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } ArrayList<Sms> lstPermFailured = new ArrayList<Sms>(); lstPermFailured.add(sms); ErrorCode smStatus = ErrorCode.MPROC_PRE_DELIVERY_DROP; ErrorAction errorAction = ErrorAction.permanentFailure; String reason = "Message drop in PreDelivery"; sms.getSmsSet().setStatus(smStatus); // sending of a failure response for transactional mode this.sendTransactionalResponseFailure(lstPermFailured, null, errorAction, null); // Processing messages that were temp or permanent failed or rerouted this.postProcessPermFailures(lstPermFailured, null, null); // generating CDRs for permanent failure messages this.generateCDRs(lstPermFailured, CdrGenerator.CDR_MPROC_DROP_PRE_DELIVERY, reason); // sending of failure delivery receipts this.generateFailureReceipts(sms.getSmsSet(), lstPermFailured, null); return false; } FastList<Sms> addedMessages = mProcResult.getMessageList(); if (addedMessages != null) { for (FastList.Node<Sms> n = addedMessages.head(), end = addedMessages.tail(); (n = n.getNext()) != end;) { Sms smst = n.getValue(); TargetAddress ta = new TargetAddress(smst.getSmsSet().getDestAddrTon(), smst.getSmsSet().getDestAddrNpi(), smst.getSmsSet().getDestAddr(), smst.getSmsSet().getNetworkId()); this.sendNewGeneratedMessage(smst, ta); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Posting of a new message after PreDelivery: targetId="); sb.append(smst.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(smst); this.logger.info(sb.toString()); } } } return true; } /** * @return return next MessageReferenceNumber for this delivery sequence */ // protected int getNextMessageReferenceNumber() { // while (messageReferenceNumberAncor > 30000) // messageReferenceNumberAncor -= 30000; // int res = messageReferenceNumberAncor++; // this.setMessageReferenceNumberAncor(messageReferenceNumberAncor); // return res; // } protected static int getNextMessageReferenceNumber() { int res = messageReferenceNumberAncor + 1; if (res > 60000) res = 0; messageReferenceNumberAncor = res; return res; } // ********* // Methods for confirming of message delivering in a message sending pool /** * Register a sequenceNumber for a message in a message sending pool for further confirming of a message receiving * * @param numInSendingPool A message number in a message sending pool * @param sequenceNumber A sequence number of a message for which we will be able of confirming * @param sequenceNumberExtra Extra sequence numbers of a message (2, 3, ... message parts) for which we will be able of * confirming */ protected void registerMessageInSendingPool(int numInSendingPool, int sequenceNumber, int[] sequenceNumberExtra) { if (sequenceNumbers != null && sequenceNumbersExtra != null && numInSendingPool >= 0 && numInSendingPool < sequenceNumbers.length && numInSendingPool < sequenceNumbersExtra.length) { sequenceNumbers[numInSendingPool] = sequenceNumber; sequenceNumbersExtra[numInSendingPool] = sequenceNumberExtra; } } /** * Ending of registering of messages in a message sending pool for further confirming of a message receiving * This method must be invoked after a first message has been registered by registerMessageInSendingPool(). */ protected void endRegisterMessageInSendingPool() { pendingRequestsListIsDirty = true; if (sequenceNumbers != null && sequenceNumbersExtra != null) { pendingRequestsList = new PendingRequestsList(sequenceNumbers, sequenceNumbersExtra); } else { pendingRequestsList = null; } sequenceNumbers = null; sequenceNumbersExtra = null; } /** * Make a confirmation of sending of a message * * @param sequenceNumber * @return Sms message that is in the pendingRequestsList list with sequenceNumber or null if no such message in * pendingRequestsList, it will return null */ protected ConfirmMessageInSendingPool confirmMessageInSendingPool(int sequenceNumber) { checkPendingRequestsListLoaded(); ConfirmMessageInSendingPool res; if (pendingRequestsList != null) { pendingRequestsListIsDirty = true; res = pendingRequestsList.confirm(sequenceNumber); if (res.sequenceNumberFound) res.sms = getMessageInSendingPool(res.msgNum); } else { res = new ConfirmMessageInSendingPool(); res.sequenceNumberFound = true; res.confirmed = true; res.sms = getMessageInSendingPool(0); } return res; } /** * @return A count of unconfirmed messages in a message sending pool */ protected int getUnconfirmedMessageCountInSendingPool() { checkPendingRequestsListLoaded(); if (pendingRequestsList != null) { return pendingRequestsList.getUnconfurnedCnt(); } else return getSendingPoolMessageCount(); } /** * @param numInSendingPool A message number in a message sending pool * @return true: if a message is confirmed or not inside a message sending pool, false: if a message is not confirmed */ protected boolean isMessageConfirmedInSendingPool(int numInSendingPool) { checkPendingRequestsListLoaded(); if (pendingRequestsList != null) { return pendingRequestsList.isSent(numInSendingPool); } else return false; } // ********* // Methods for managing of delivery timeout protected void rescheduleDeliveryTimer() { this.cancelDeliveryTimer(); if (this.timerFacility != null) { long startTime = System.currentTimeMillis() + 1000 * smscPropertiesManagement.getDeliveryTimeout(); TimerOptions options = new TimerOptions(); ActivityContextInterface activity = getSchedulerActivityContextInterface(); TimerID timer = this.timerFacility.setTimer(activity, null, startTime, options); setDeliveryTimerID(timer); } } protected void cancelDeliveryTimer() { TimerID timer = this.getDeliveryTimerID(); setDeliveryTimerID(null); if (this.timerFacility != null && timer != null) this.timerFacility.cancelTimer(timer); } public void onTimerEvent(TimerEvent event, ActivityContextInterface aci, EventContext eventContext) { SmsSet smsSet = getSmsSet(); if (smsSet != null) { // deliver timer is triggered String reason = "Delivery timeout error: sendingPoolMessageCount=" + this.getSendingPoolMessageCount() + ", smsSet=" + smsSet; this.logger.severe(reason); onDeliveryTimeout(smsSet, reason); } } protected abstract void onDeliveryTimeout(SmsSet smsSet, String reason); // ********* // sending of responses to a message sender for the transactional messaging mode /** * Sending of responses to message senders for a transactional messaging mode for success case * @param sms */ protected void sendTransactionalResponseSuccess(Sms sms) { if (sms.getMessageDeliveryResultResponse() != null) { sms.getMessageDeliveryResultResponse().responseDeliverySuccess(); sms.setMessageDeliveryResultResponse(null); } } /** * Sending of responses to message senders for a transactional messaging mode for failure case * @param lstPermFailured * @param lstTempFailured * @param errorAction */ protected void sendTransactionalResponseFailure(ArrayList<Sms> lstPermFailured, ArrayList<Sms> lstTempFailured, ErrorAction errorAction, MAPErrorMessage errMessage) { MessageDeliveryResultResponseInterface.DeliveryFailureReason delReason = MessageDeliveryResultResponseInterface.DeliveryFailureReason.destinationUnavalable; if (errorAction == ErrorAction.temporaryFailure) delReason = MessageDeliveryResultResponseInterface.DeliveryFailureReason.temporaryNetworkError; if (errorAction == ErrorAction.permanentFailure) delReason = MessageDeliveryResultResponseInterface.DeliveryFailureReason.permanentNetworkError; for (Sms sms : lstPermFailured) { if (sms.getMessageDeliveryResultResponse() != null) { sms.getMessageDeliveryResultResponse().responseDeliveryFailure(delReason, errMessage); sms.setMessageDeliveryResultResponse(null); } } if (lstTempFailured != null) { for (Sms sms : lstTempFailured) { if (sms.getMessageDeliveryResultResponse() != null) { sms.getMessageDeliveryResultResponse().responseDeliveryFailure(delReason, errMessage); sms.setMessageDeliveryResultResponse(null); } } } } // ********* // creating of permanent and temporary failure lists for processing after delivery success or failure /** * Put of unsent messages into a permanent failure list (no more delivery attempts) or a temporary failure list (more * delivery attempts are possible). * * @param lstPermFailured * @param lstTempFailured * @param newDueTime */ protected void createFailureLists(ArrayList<Sms> lstPermFailured, ArrayList<Sms> lstTempFailured, ErrorAction errorAction, Date newDueTime) { // unsent messages in SendingPool int sendingPoolMessageCount = this.getSendingPoolMessageCount(); for (int i1 = 0; i1 < sendingPoolMessageCount; i1++) { if (!this.isMessageConfirmedInSendingPool(i1)) { Sms sms = this.getMessageInSendingPool(i1); if (sms != null) { doCreateFailureLists(lstPermFailured, lstTempFailured, sms, errorAction, newDueTime); } } } // marking messages in SendingPool as sent this.commitSendingPoolMsgCount(); // unsent messages outside a message pool int totalUnsentMessageCount = this.getTotalUnsentMessageCount(); for (int i1 = 0; i1 < totalUnsentMessageCount; i1++) { Sms sms = this.getUnsentMessage(i1); if (sms != null) { doCreateFailureLists(lstPermFailured, lstTempFailured, sms, errorAction, newDueTime); } } } private void doCreateFailureLists(ArrayList<Sms> lstPermFailured, ArrayList<Sms> lstTempFailured, Sms sms, ErrorAction errorAction, Date newDueTime) { if (errorAction == ErrorAction.permanentFailure) { lstPermFailured.add(sms); } else { if (sms.getStoringAfterFailure() || sms.getStored()) { // FAS & SAF // checking validity date - if validity date < now, then it is a permanent failure (we confirm a validity period // for time for now + 2 min) // TODO: ValidityPeriod was removed !!! // if (sms.getValidityPeriod() != null // && sms.getValidityPeriod().getTime() <= System.currentTimeMillis() + 1000 * 120) { if (sms.getValidityPeriod() != null && sms.getValidityPeriod().getTime() < newDueTime.getTime() && sms.getValidityPeriod().getTime() < System.currentTimeMillis() + 1000 * smscPropertiesManagement.getVpProlong()) { lstPermFailured.add(sms); } else { lstTempFailured.add(sms); } } else { // datagramm or transactional lstPermFailured.add(sms); } } } /** * Finishing delivering of a message which validity period is over at the start of delivery time. * * @param sms * @param processingType * @param dlvMessageId * @param dlvDestId */ protected void endDeliveryAfterValidityPeriod(Sms sms, ProcessingType processingType, String dlvMessageId, String dlvDestId) { // ending of delivery process in this SBB ErrorCode smStatus = ErrorCode.VALIDITY_PERIOD_EXPIRED; ErrorAction errorAction = ErrorAction.permanentFailure; String reason = "Validity period is expired"; smsSet.setStatus(smStatus); StringBuilder sb = new StringBuilder(); sb.append("onDeliveryError: errorAction=validityExpired"); 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()); // mproc rules applying for delivery phase MProcResult mProcResult = MProcManagement.getInstance().applyMProcDelivery(itsMProcRa, sms, true, processingType); if (mProcResult.isMessageIsRerouted()) { // we do not reroute a message with expired validity period sb = new StringBuilder(); sb.append("Can not reroute of a message with expired ValidityPeriod, sms="); sb.append(sms); this.logger.warning(sb.toString()); } FastList<Sms> addedMessages = mProcResult.getMessageList(); if (addedMessages != null) { for (FastList.Node<Sms> n = addedMessages.head(), end = addedMessages.tail(); (n = n.getNext()) != end;) { Sms smst = n.getValue(); TargetAddress ta = new TargetAddress(smst.getSmsSet().getDestAddrTon(), smst.getSmsSet().getDestAddrNpi(), smst.getSmsSet().getDestAddr(), smst.getSmsSet().getNetworkId()); this.sendNewGeneratedMessage(smst, ta); if (this.logger.isInfoEnabled()) { sb = new StringBuilder(); sb.append("Posting of a new message after PermFailure-ValidityPeriod: targetId="); sb.append(smst.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(smst); this.logger.info(sb.toString()); } } } ArrayList<Sms> lstPermFailured = new ArrayList<Sms>(); lstPermFailured.add(sms); // sending of a failure response for transactional mode this.sendTransactionalResponseFailure(lstPermFailured, null, errorAction, null); // Processing messages that were temp or permanent failed or rerouted this.postProcessPermFailures(lstPermFailured, dlvMessageId, dlvDestId); // generating CDRs for permanent failure messages this.generateCDRs(lstPermFailured, CdrGenerator.CDR_FAILED, reason); // sending of failure delivery receipts this.generateFailureReceipts(smsSet, lstPermFailured, null); } // ********* // ending / rescheduling of messages after successful / failed delivering and for next delivery attempts /** * Processing messages that were succeeded (sms.inSystem=sent in live database, adding of archive record). * * @param sms * @param dlvMessageId * @param dlvDestId */ protected void postProcessSucceeded(Sms sms, String dlvMessageId, String dlvDestId) { try { persistence.c2_updateInSystem(sms, DBOperations.IN_SYSTEM_SENT, smscPropertiesManagement.getStoreAndForwordMode() == StoreAndForwordMode.fast); sms.setDeliveryDate(new Date()); if (MessageUtil.isNeedWriteArchiveMessage(sms, smscPropertiesManagement.getGenerateArchiveTable())) { persistence.c2_createRecordArchive(sms, dlvMessageId, dlvDestId, !smscPropertiesManagement.getReceiptsDisabling(), smscPropertiesManagement.getIncomeReceiptsProcessing()); } } catch (PersistenceException e) { this.logger.severe("PersistenceException when DeliveryCommonSbb.postProcessSucceeded()" + e.getMessage(), e); } } /** * Processing messages that were failed permanently (sms.inSystem=sent in live database, adding of archive record). * * @param lstPermFailured * @param dlvMessageId * @param dlvDestId */ protected void postProcessPermFailures(ArrayList<Sms> lstPermFailured, String dlvMessageId, String dlvDestId) { try { for (Sms sms : lstPermFailured) { persistence.c2_updateInSystem(sms, DBOperations.IN_SYSTEM_SENT, smscPropertiesManagement.getStoreAndForwordMode() == StoreAndForwordMode.fast); sms.setDeliveryDate(new Date()); if (MessageUtil.isNeedWriteArchiveMessage(sms, smscPropertiesManagement.getGenerateArchiveTable())) { persistence.c2_createRecordArchive(sms, dlvMessageId, dlvDestId, !smscPropertiesManagement.getReceiptsDisabling(), smscPropertiesManagement.getIncomeReceiptsProcessing()); } } } catch (PersistenceException e) { this.logger.severe("PersistenceException when DeliveryCommonSbb.postProcessPermFailures()" + e.getMessage(), e); } } /** * Calculating of new due delay for SmsSet * @param smsSet * @param busySubscriber true if a network reported of "busySubscriber" state that means that we need to reschedule of * delivering in a very short time * @return */ protected int calculateNewDueDelay(SmsSet smsSet, boolean busySubscriber) { int prevDueDelay = smsSet.getDueDelay(); int newDueDelay; if (busySubscriber) { newDueDelay = MessageUtil.computeDueDelaySubscriberBusy(smscPropertiesManagement.getSubscriberBusyDueDelay()); } else { newDueDelay = MessageUtil.computeNextDueDelay(prevDueDelay, smscPropertiesManagement.getSecondDueDelay(), smscPropertiesManagement.getDueDelayMultiplicator(), smscPropertiesManagement.getMaxDueDelay()); } return newDueDelay; } /** * Calculating of new due time for SmsSet * @param smsSet * @param newDueDelay * @return */ protected Date calculateNewDueTime(SmsSet smsSet, int newDueDelay) { Date newDueDate = new Date(new Date().getTime() + newDueDelay * 1000); return newDueDate; } /** * Processing messages that were failed temporary and will be rescheduled (sms.inSystem=sent in live database, message * rescheduling). * * @param smsSet * @param lstTempFailured * @param int newDueDelay * @param newDueDate * @param isCheckScheduleDeliveryTimeNeeded true if we need to schedule messages for time to the nearest * ScheduleDeliveryTime (for SS7 network case) */ protected void postProcessTempFailures(SmsSet smsSet, ArrayList<Sms> lstTempFailured, int newDueDelay, Date newDueDate, boolean isCheckScheduleDeliveryTimeNeeded) { try { if (isCheckScheduleDeliveryTimeNeeded) newDueDate = MessageUtil.checkScheduleDeliveryTime(lstTempFailured, newDueDate); smsSet.setDueDate(newDueDate); smsSet.setDueDelay(newDueDelay); long dueSlot = persistence.c2_getDueSlotForTime(newDueDate); for (Sms sms : lstTempFailured) { persistence.c2_scheduleMessage_NewDueSlot(sms, dueSlot, null, smscPropertiesManagement.getStoreAndForwordMode() == StoreAndForwordMode.fast); } } catch (PersistenceException e) { this.logger.severe("PersistenceException when DeliveryCommonSbb.postProcessTempFailures()" + e.getMessage(), e); } } /** * Processing messages that were rescheduled (sms.inSystem=sent in live database). * * @param lstRerouted * @param lstNewNetworkId */ protected void postProcessRerouted(ArrayList<Sms> lstRerouted, ArrayList<Integer> lstNewNetworkId) { // next we are initiating another delivering process try { for (int i1 = 0; i1 < lstRerouted.size(); i1++) { Sms sms = lstRerouted.get(i1); int newNetworkId = lstNewNetworkId.get(i1); sms.setReroutingCount(sms.getReroutingCount() + 1); sms.setTargetIdOnDeliveryStart(smsSet.getTargetId()); MessageUtil.createNewSmsSetForSms(sms); sms.getSmsSet().setNetworkId(newNetworkId); this.scheduler.injectSmsOnFly(sms.getSmsSet(), true); } } catch (Exception e) { this.logger.severe("Exception when DeliveryCommonSbb.postProcessRerouted() - rerouting" + e.getMessage(), e); } } // ********* // applying of mproc rules /** * mproc rules applying for delivery phase for success case * * @param sms * @param processingType */ protected void applyMprocRulesOnSuccess(Sms sms, ProcessingType processingType) { // PostDeliveryProcessor - success case MProcResult mProcResult = MProcManagement.getInstance().applyMProcDelivery(itsMProcRa, sms, false, processingType); FastList<Sms> addedMessages = mProcResult.getMessageList(); if (addedMessages != null) { for (FastList.Node<Sms> n = addedMessages.head(), end = addedMessages.tail(); (n = n.getNext()) != end;) { Sms smst = n.getValue(); TargetAddress ta = new TargetAddress(smst.getSmsSet().getDestAddrTon(), smst.getSmsSet().getDestAddrNpi(), smst .getSmsSet().getDestAddr(), smst.getSmsSet().getNetworkId()); this.sendNewGeneratedMessage(smst, ta); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Posting of a new message after DeliverySuccess: targetId="); sb.append(smst.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(smst); this.logger.info(sb.toString()); } } } } /** * mproc rules applying for delivery phase for failure case * * @param lstPermFailured * @param lstTempFailured * @param lstPermFailuredNew * @param lstTempFailuredNew * @param lstRerouted * @param lstNewNetworkId * @param processingType */ protected void applyMprocRulesOnFailure(ArrayList<Sms> lstPermFailured, ArrayList<Sms> lstTempFailured, ArrayList<Sms> lstPermFailuredNew, ArrayList<Sms> lstTempFailuredNew, ArrayList<Sms> lstRerouted, ArrayList<Integer> lstNewNetworkId, ProcessingType processingType) { // TempFailureProcessor for (Sms sms : lstTempFailured) { MProcResult mProcResult = MProcManagement.getInstance().applyMProcDeliveryTempFailure(itsMProcRa, sms, processingType); if (mProcResult.isMessageIsRerouted()) { // firstly we check if rerouting attempts was not too many if (sms.getReroutingCount() >= MAX_POSSIBLE_REROUTING) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message attempt after TempFailure, but we have already rerouted "); sb.append(MAX_POSSIBLE_REROUTING); sb.append(" times before: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.warning(sb.toString()); lstTempFailuredNew.add(sms); } else if (mProcResult.getNewNetworkId() == sms.getSmsSet().getNetworkId()) { // we do not reroute for the same networkId lstTempFailuredNew.add(sms); } else { lstRerouted.add(sms); lstNewNetworkId.add(mProcResult.getNewNetworkId()); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message after TempFailure: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } } } else if (mProcResult.isMessageDropped()) { lstPermFailuredNew.add(sms); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Dropping message after TempFailure: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } } else { lstTempFailuredNew.add(sms); } FastList<Sms> addedMessages = mProcResult.getMessageList(); if (addedMessages != null) { for (FastList.Node<Sms> n = addedMessages.head(), end = addedMessages.tail(); (n = n.getNext()) != end;) { Sms smst = n.getValue(); TargetAddress ta = new TargetAddress(smst.getSmsSet().getDestAddrTon(), smst.getSmsSet().getDestAddrNpi(), smst.getSmsSet().getDestAddr(), smst.getSmsSet().getNetworkId()); this.sendNewGeneratedMessage(smst, ta); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Posting of a new message after TempFailure: targetId="); sb.append(smst.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(smst); this.logger.info(sb.toString()); } } } } // PostDeliveryProcessor - failure case for (Sms sms : lstPermFailured) { MProcResult mProcResult = MProcManagement.getInstance().applyMProcDelivery(itsMProcRa, sms, true, processingType); if (mProcResult.isMessageIsRerouted()) { if (sms.getReroutingCount() >= MAX_POSSIBLE_REROUTING) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message attempt after PermFailure, but we have already rerouted "); sb.append(MAX_POSSIBLE_REROUTING); sb.append(" times before: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.warning(sb.toString()); lstPermFailuredNew.add(sms); } else if (mProcResult.getNewNetworkId() == sms.getSmsSet().getNetworkId()) { // we do not reroute for the same networkId lstPermFailuredNew.add(sms); } else { lstRerouted.add(sms); lstNewNetworkId.add(mProcResult.getNewNetworkId()); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message after PermFailure: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } } } else { lstPermFailuredNew.add(sms); } FastList<Sms> addedMessages = mProcResult.getMessageList(); if (addedMessages != null) { for (FastList.Node<Sms> n = addedMessages.head(), end = addedMessages.tail(); (n = n.getNext()) != end;) { Sms smst = n.getValue(); TargetAddress ta = new TargetAddress(smst.getSmsSet().getDestAddrTon(), smst.getSmsSet().getDestAddrNpi(), smst.getSmsSet().getDestAddr(), smst.getSmsSet().getNetworkId()); this.sendNewGeneratedMessage(smst, ta); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Posting of a new message after PermFailure: targetId="); sb.append(smst.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(smst); this.logger.info(sb.toString()); } } } } } /** * mproc rules applying for delivery phase after SRI successful response * * @param smsSet * @param lstPermFailured * @param processingType */ protected void applyMprocRulesOnImsiResponse(SmsSet smsSet, ArrayList<Sms> lstPermFailured, ArrayList<Sms> lstRerouted, ArrayList<Integer> lstNewNetworkId, ISDNAddressString networkNode, String imsiData) { Sms sms = this.getMessageInSendingPool(0); if (sms != null) { while (true) { MProcResult mProcResult = MProcManagement.getInstance().applyMProcImsiRequest(itsMProcRa, sms, imsiData, networkNode.getAddress(), networkNode.getNumberingPlan().getIndicator(), networkNode.getAddressNature().getIndicator()); if (mProcResult.isMessageDropped()) { lstPermFailured.add(sms); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Dropping message after SRI response: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } this.commitSendingPoolMsgCount(); sms = this.obtainNextMessage(ProcessingType.SS7_SRI); if (sms == null) break; else continue; } if (mProcResult.isMessageIsRerouted()) { // firstly we check if rerouting attempts was not too many if (sms.getReroutingCount() >= MAX_POSSIBLE_REROUTING) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message attempt after SRI response, but we have already rerouted "); sb.append(MAX_POSSIBLE_REROUTING); sb.append(" times before: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.warning(sb.toString()); } else if (mProcResult.getNewNetworkId() == sms.getSmsSet().getNetworkId()) { // we do not reroute for the same networkId } else { lstRerouted.add(sms); lstNewNetworkId.add(mProcResult.getNewNetworkId()); if (this.logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Rerouting message after SRI response: targetId="); sb.append(sms.getSmsSet().getTargetId()); sb.append(", newNetworkId="); sb.append(mProcResult.getNewNetworkId()); sb.append(", sms="); sb.append(sms); this.logger.info(sb.toString()); } this.commitSendingPoolMsgCount(); sms = this.obtainNextMessage(ProcessingType.SS7_SRI); if (sms == null) break; else continue; } } break; } } } // ********* // Methods for CDR generating /** * Generating of a temporary failure CDR (one record for all unsent messages). * * @param status CDR status (CdrGenerator.CDR_TEMP_FAILED, CDR_TEMP_FAILED_ESME or CDR_TEMP_FAILED_SIP) * @param reason verbal failure reason for logging */ protected void generateTemporaryFailureCDR(String status, String reason) { int sendingPoolMessageCount = this.getSendingPoolMessageCount(); for (int i1 = 0; i1 < sendingPoolMessageCount; i1++) { if (!this.isMessageConfirmedInSendingPool(i1)) { Sms sms = this.getMessageInSendingPool(i1); if (sms != null) { String s1 = reason.replace("\n", "\t"); CdrGenerator.generateCdr(sms, status, s1, smscPropertiesManagement.getGenerateReceiptCdr(), MessageUtil.isNeedWriteArchiveMessage(sms, smscPropertiesManagement.getGenerateCdr()), false, true, smscPropertiesManagement.getCalculateMsgPartsLenCdr(), smscPropertiesManagement.getDelayParametersInCdr()); return; } } } // if no message was sent in a message pool, let's // ***** no lock ****** Sms sms = this.getUnsentMessage(0); if (sms != null) { String s1 = reason.replace("\n", "\t"); CdrGenerator.generateCdr(sms, status, s1, smscPropertiesManagement.getGenerateReceiptCdr(), MessageUtil.isNeedWriteArchiveMessage(sms, smscPropertiesManagement.getGenerateCdr()), false, true, smscPropertiesManagement.getCalculateMsgPartsLenCdr(), smscPropertiesManagement.getDelayParametersInCdr()); return; } } /** * Generating CDRs for a message * @param sms * @param status * @param reason * @param messageIsSplitted * @param lastSegment */ protected void generateCDR(Sms sms, String status, String reason, boolean messageIsSplitted, boolean lastSegment) { CdrGenerator.generateCdr(sms, status, reason, smscPropertiesManagement.getGenerateReceiptCdr(), MessageUtil.isNeedWriteArchiveMessage(sms, smscPropertiesManagement.getGenerateCdr()), messageIsSplitted, lastSegment, smscPropertiesManagement.getCalculateMsgPartsLenCdr(), smscPropertiesManagement.getDelayParametersInCdr()); } /** * Generating CDRs for a message list * @param lstPermFailured * @param status * @param reason */ protected void generateCDRs(ArrayList<Sms> lstPermFailured, String status, String reason) { for (Sms sms : lstPermFailured) { CdrGenerator.generateCdr(sms, status, reason, smscPropertiesManagement.getGenerateReceiptCdr(), MessageUtil.isNeedWriteArchiveMessage(sms, smscPropertiesManagement.getGenerateCdr()), false, true, smscPropertiesManagement.getCalculateMsgPartsLenCdr(), smscPropertiesManagement.getDelayParametersInCdr()); } } // ********* // delivery receipts generating /** * Generating of a success receipt for a delivered message * @param smsSet * @param sms */ protected void generateSuccessReceipt(SmsSet smsSet, Sms sms) { if (!smscPropertiesManagement.getReceiptsDisabling()) { // SMPP delivery receipt int registeredDelivery = sms.getRegisteredDelivery(); if (MessageUtil.isReceiptOnSuccess(registeredDelivery)) { TargetAddress ta = new TargetAddress(sms.getSourceAddrTon(), sms.getSourceAddrNpi(), sms.getSourceAddr(), smsSet.getNetworkId()); Sms receipt = MessageUtil.createReceiptSms(sms, true, ta, smscPropertiesManagement.getOrigNetworkIdForReceipts()); this.sendNewGeneratedMessage(receipt, ta); } // SMS-STATUS-REPORT if (sms.isStatusReportRequest()) { TargetAddress ta = new TargetAddress(sms.getSourceAddrTon(), sms.getSourceAddrNpi(), sms.getSourceAddr(), smsSet.getNetworkId()); Sms smsStatusReport = MessageUtil.createSmsStatusReport(sms, true, ta, smscPropertiesManagement.getOrigNetworkIdForReceipts()); this.sendNewGeneratedMessage(smsStatusReport, ta); } } } /** * Generating of intermediate receipts for temporary failed messages * @param smsSet * @param lstTempFailured */ protected void generateIntermediateReceipts(SmsSet smsSet, ArrayList<Sms> lstTempFailured) { if (!smscPropertiesManagement.getReceiptsDisabling() && smscPropertiesManagement.getEnableIntermediateReceipts()) { for (Sms sms : lstTempFailured) { // SMPP delivery receipt int registeredDelivery = sms.getRegisteredDelivery(); if (MessageUtil.isReceiptIntermediate(registeredDelivery)) { TargetAddress ta = new TargetAddress(sms.getSourceAddrTon(), sms.getSourceAddrNpi(), sms.getSourceAddr(), smsSet.getNetworkId()); Sms receipt = MessageUtil.createReceiptSms(sms, false, ta, smscPropertiesManagement.getOrigNetworkIdForReceipts(), null, true); this.sendNewGeneratedMessage(receipt, ta); this.logger.info("Adding an intermediate failure receipt: source=" + receipt.getSourceAddr() + ", dest=" + receipt.getSmsSet().getDestAddr()); } } } } /** * Generating of failure receipts for permanent failed messages * * @param smsSet * @param lstPermFailured */ protected void generateFailureReceipts(SmsSet smsSet, ArrayList<Sms> lstPermFailured, String extraString) { if (!smscPropertiesManagement.getReceiptsDisabling()) { for (Sms sms : lstPermFailured) { // SMPP delivery receipt int registeredDelivery = sms.getRegisteredDelivery(); if (MessageUtil.isReceiptOnFailure(registeredDelivery)) { TargetAddress ta = new TargetAddress(sms.getSourceAddrTon(), sms.getSourceAddrNpi(), sms.getSourceAddr(), smsSet.getNetworkId()); Sms receipt = MessageUtil.createReceiptSms(sms, false, ta, smscPropertiesManagement.getOrigNetworkIdForReceipts(), extraString); this.sendNewGeneratedMessage(receipt, ta); this.logger.info("Adding an failire receipt: source=" + receipt.getSourceAddr() + ", dest=" + receipt.getSmsSet().getDestAddr()); } // SMS-STATUS-REPORT if (sms.isStatusReportRequest()) { TargetAddress ta = new TargetAddress(sms.getSourceAddrTon(), sms.getSourceAddrNpi(), sms.getSourceAddr(), smsSet.getNetworkId()); Sms smsStatusReport = MessageUtil.createSmsStatusReport(sms, false, ta, smscPropertiesManagement.getOrigNetworkIdForReceipts()); this.sendNewGeneratedMessage(smsStatusReport, ta); this.logger.info("Adding an failire SMS-STATUS-REPORT: source=" + smsStatusReport.getSourceAddr() + ", dest=" + smsStatusReport.getSmsSet().getDestAddr()); } } } } // ********* // sending of generated messages (delivery receipts and messages that were generated by mproc rules) private void sendNewGeneratedMessage(Sms sms, TargetAddress ta) { boolean storeAndForwMode = MessageUtil.isStoreAndForward(sms); TargetAddress lock = SmsSetCache.getInstance().addSmsSet(ta); try { synchronized (lock) { if (!storeAndForwMode) { try { this.scheduler.injectSmsOnFly(sms.getSmsSet(), true); } catch (Exception e) { this.logger.severe("Exception when runnung injectSmsOnFly() for receipt in sendNewGeneratedMessage(): " + e.getMessage(), e); } } else { if (smscPropertiesManagement.getStoreAndForwordMode() == StoreAndForwordMode.fast) { try { sms.setStoringAfterFailure(true); this.scheduler.injectSmsOnFly(sms.getSmsSet(), true); } catch (Exception e) { this.logger.severe( "Exception when runnung injectSmsOnFly() for receipt in sendNewGeneratedMessage(): " + e.getMessage(), e); } } else { sms.setStored(true); this.scheduler.setDestCluster(sms.getSmsSet()); try { persistence.c2_scheduleMessage_ReschedDueSlot(sms, smscPropertiesManagement.getStoreAndForwordMode() == StoreAndForwordMode.fast, true); } catch (PersistenceException e) { this.logger.severe( "PersistenceException when running c2_scheduleMessage_ReschedDueSlot() in sendNewGeneratedMessage()" + e.getMessage(), e); } } } } } finally { SmsSetCache.getInstance().removeSmsSet(lock); } } /** * CMPs */ public abstract void setTargetId(String targetId); public abstract String getTargetId(); public abstract void setCurrentMsgNum(long currentMsgNum); public abstract long getCurrentMsgNum(); public abstract void setDlvIsInited(boolean deliveringIsInited); public abstract boolean getDlvIsInited(); public abstract void setDlvIsEnded(boolean deliveringIsEnded); public abstract boolean getDlvIsEnded(); public abstract void setPendingRequestsList(PendingRequestsList pendingRequestsList); public abstract PendingRequestsList getPendingRequestsList(); public abstract TimerID getDeliveryTimerID(); public abstract void setDeliveryTimerID(TimerID val); }