/*
* 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.library;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javolution.util.FastMap;
/**
*
* @author sergey vetyutnev
*
*/
public class SmsSetCache {
public static int SMSSET_MSG_PRO_SEGMENT_LIMIT = 50;
public static int SMSSET_FREE_SEGMENT_CNT = 100;
private int processingSmsSetTimeout;
private int correlationIdLiveTime;
private int sriResponseLiveTime;
private int deliveredMsgLiveTime;
private boolean isStarted = false;
private FastMap<TargetAddress, TargetAddressContainer> lstSmsSetUnderAtomicOper = new FastMap<TargetAddress, TargetAddressContainer>();
private AtomicInteger activityCount = new AtomicInteger(0);
private FastMap<String, SmsSet> lstSmsSetInProcessing = new FastMap<String, SmsSet>();
private FastMap<String, SmsSet> lstSmsSetWithBigMessageCount = new FastMap<String, SmsSet>();
private UpdateMessagesInProcessListener smscStatAggregator;
private FastMap<String, CorrelationIdValue> correlationIdCache1 = new FastMap<String, CorrelationIdValue>();
private FastMap<String, CorrelationIdValue> correlationIdCache2 = new FastMap<String, CorrelationIdValue>();
private Object correlationIdCacheSync = new Object();
private FastMap<String, SriResponseValue> sriRespCache1 = new FastMap<String, SriResponseValue>();
private FastMap<String, SriResponseValue> sriRespCache2 = new FastMap<String, SriResponseValue>();
private Object sriRespCacheSync = new Object();
private FastMap<Long, Sms> deliveredMsgCache1 = new FastMap<Long, Sms>();
private FastMap<Long, Sms> deliveredMsgCache2 = new FastMap<Long, Sms>();
private Object deliveredMsgCacheSync = new Object();
private FastMap<String, Long> deliveredRemoteMsgIdCache1 = new FastMap<String, Long>();
private FastMap<String, Long> deliveredRemoteMsgIdCache2 = new FastMap<String, Long>();
private Object deliveredRemoteMsgIdCacheSync = new Object();
private ScheduledExecutorService executor;
private static SmsSetCache singeltone;
static {
singeltone = new SmsSetCache();
}
private SmsSetCache() {
}
public static SmsSetCache getInstance() {
return singeltone;
}
public static void start(int correlationIdLiveTime, int sriResponseLiveTime, int deliveredMsgLiveTime) {
SmsSetCache ssc = SmsSetCache.getInstance();
ssc.correlationIdLiveTime = correlationIdLiveTime;
ssc.sriResponseLiveTime = sriResponseLiveTime;
ssc.deliveredMsgLiveTime = deliveredMsgLiveTime;
ssc.executor = Executors.newScheduledThreadPool(1);
ssc.isStarted = true;
CacheManTask t = ssc.new CacheManTask();
ssc.executor.schedule(t, correlationIdLiveTime, TimeUnit.SECONDS);
CacheManTask_SRI_Resp t2 = ssc.new CacheManTask_SRI_Resp();
ssc.executor.schedule(t2, sriResponseLiveTime, TimeUnit.SECONDS);
CacheManTask_Delivered_Msg t3 = ssc.new CacheManTask_Delivered_Msg();
ssc.executor.schedule(t3, deliveredMsgLiveTime, TimeUnit.SECONDS);
CacheManTask_Delivered_RemoteMsgId t4 = ssc.new CacheManTask_Delivered_RemoteMsgId();
ssc.executor.schedule(t4, deliveredMsgLiveTime, TimeUnit.SECONDS);
}
public static void stop() {
SmsSetCache ssc = SmsSetCache.getInstance();
ssc.isStarted = false;
ssc.executor.shutdown();
}
public void setUpdateMessagesInProcessListener(UpdateMessagesInProcessListener smscStatAggregator) {
this.smscStatAggregator = smscStatAggregator;
}
public TargetAddress addSmsSet(TargetAddress ta) {
synchronized (lstSmsSetUnderAtomicOper) {
TargetAddressContainer cont = lstSmsSetUnderAtomicOper.get(ta);
if (cont != null) {
cont.count++;
return cont.targetAddress;
} else {
cont = new TargetAddressContainer();
lstSmsSetUnderAtomicOper.put(ta, cont);
cont.count = 1;
cont.targetAddress = ta;
return ta;
}
}
}
public void removeSmsSet(TargetAddress ta) {
synchronized (lstSmsSetUnderAtomicOper) {
TargetAddressContainer cont = lstSmsSetUnderAtomicOper.get(ta);
if (cont != null && --cont.count <= 0)
lstSmsSetUnderAtomicOper.remove(ta);
}
}
public void incrementActivityCount() {
activityCount.incrementAndGet();
}
public void decrementActivityCount() {
activityCount.decrementAndGet();
}
public int getActivityCount() {
return activityCount.get();
}
public SmsSet getProcessingSmsSet(String targetId) {
synchronized (lstSmsSetInProcessing) {
return lstSmsSetInProcessing.get(targetId);
}
}
public SmsSet addProcessingSmsSet(String targetId, SmsSet smsSet, int processingSmsSetTimeout) {
this.processingSmsSetTimeout = processingSmsSetTimeout;
synchronized (lstSmsSetInProcessing) {
SmsSet res = lstSmsSetInProcessing.put(targetId, smsSet);
if (smscStatAggregator != null) {
smscStatAggregator.updateMaxMessagesInProcess(lstSmsSetInProcessing.size());
smscStatAggregator.updateMinMessagesInProcess(lstSmsSetInProcessing.size());
}
return res;
}
}
public void registerSmsSetWithBigMessageCount(String targetId, SmsSet smsSet) {
synchronized (lstSmsSetInProcessing) {
lstSmsSetWithBigMessageCount.put(targetId, smsSet);
}
}
public SmsSet removeProcessingSmsSet(String targetId) {
synchronized (lstSmsSetInProcessing) {
SmsSet smsSet = lstSmsSetInProcessing.remove(targetId);
lstSmsSetWithBigMessageCount.remove(targetId);
if (smscStatAggregator != null) {
smscStatAggregator.updateMaxMessagesInProcess(lstSmsSetInProcessing.size());
smscStatAggregator.updateMinMessagesInProcess(lstSmsSetInProcessing.size());
}
return smsSet;
}
}
public int getProcessingSmsSetSize() {
try {
int res = lstSmsSetInProcessing.size();
for (FastMap.Entry<String, SmsSet> n = this.lstSmsSetWithBigMessageCount.head(), end = this.lstSmsSetWithBigMessageCount
.tail(); (n = n.getNext()) != end && n != null;) {
res += n.getValue().getSmsCountWithoutDelivered();
}
return res;
} catch (Throwable e) {
// this block is not synchronized. We will return 0 in any Exception
return 0;
}
}
public String getLstSmsSetWithBigMessageCountState() {
if (this.lstSmsSetWithBigMessageCount.size() == 0)
return null;
try {
StringBuilder sb = new StringBuilder();
for (FastMap.Entry<String, SmsSet> n = this.lstSmsSetWithBigMessageCount.head(), end = this.lstSmsSetWithBigMessageCount
.tail(); (n = n.getNext()) != end && n != null;) {
SmsSet smsSet = n.getValue();
sb.append(smsSet.getTargetId());
sb.append(" - ");
sb.append(smsSet.getSmsCount());
sb.append(" - ");
sb.append(smsSet.getSmsCountWithoutDelivered());
sb.append("\n");
}
return sb.toString();
} catch (Throwable e) {
return "Exception when getLstSmsSetWithBigMessageCountState() = " + e.getMessage();
}
}
public void garbadeCollectProcessingSmsSet() {
synchronized (lstSmsSetInProcessing) {
Date limit = new Date(new Date().getTime() - processingSmsSetTimeout * 1000);
ArrayList<String> toDel = new ArrayList<String>();
for (Map.Entry<String, SmsSet> entry : lstSmsSetInProcessing.entrySet()) {
if (entry.getValue().getLastUpdateTime().before(limit)) {
toDel.add(entry.getKey());
}
}
for (String key : toDel) {
lstSmsSetInProcessing.remove(key);
}
toDel = new ArrayList<String>();
for (Map.Entry<String, SmsSet> entry : lstSmsSetWithBigMessageCount.entrySet()) {
if (entry.getValue().getLastUpdateTime().before(limit)) {
toDel.add(entry.getKey());
}
}
for (String key : toDel) {
lstSmsSetWithBigMessageCount.remove(key);
}
if (smscStatAggregator != null) {
smscStatAggregator.updateMaxMessagesInProcess(lstSmsSetInProcessing.size());
smscStatAggregator.updateMinMessagesInProcess(lstSmsSetInProcessing.size());
}
}
}
public void clearProcessingSmsSet() {
synchronized (lstSmsSetInProcessing) {
lstSmsSetInProcessing.clear();
lstSmsSetWithBigMessageCount.clear();
if (smscStatAggregator != null) {
smscStatAggregator.updateMaxMessagesInProcess(lstSmsSetInProcessing.size());
smscStatAggregator.updateMinMessagesInProcess(lstSmsSetInProcessing.size());
}
}
}
public void putCorrelationIdCacheElement(CorrelationIdValue elem, int correlationIdLiveTime) throws Exception {
this.correlationIdLiveTime = correlationIdLiveTime;
synchronized (this.correlationIdCacheSync) {
this.correlationIdCache1.put(elem.getCorrelationID(), elem);
}
}
public CorrelationIdValue getCorrelationIdCacheElement(String correlationID) throws Exception {
synchronized (this.correlationIdCacheSync) {
CorrelationIdValue res = this.correlationIdCache1.get(correlationID);
if (res == null)
res = this.correlationIdCache2.get(correlationID);
return res;
}
}
public void putSriResponseValue(SriResponseValue elem, int sriResponseLiveTime) throws Exception {
this.sriResponseLiveTime = sriResponseLiveTime;
if (sriResponseLiveTime > 0) {
synchronized (this.sriRespCacheSync) {
this.sriRespCache1.put(elem.getTargetID(), elem);
}
}
}
public SriResponseValue getSriResponseValue(String targetID) throws Exception {
if (sriResponseLiveTime == 0)
return null;
synchronized (this.sriRespCacheSync) {
SriResponseValue res = this.sriRespCache1.get(targetID);
if (res == null)
res = this.sriRespCache2.get(targetID);
return res;
}
}
public void putDeliveredMsgValue(Sms sms, int deliveredMsgLiveTime) {
this.deliveredMsgLiveTime = deliveredMsgLiveTime;
if (deliveredMsgLiveTime > 0) {
synchronized (this.deliveredMsgCacheSync) {
this.deliveredMsgCache1.put(sms.getMessageId(), sms);
}
}
}
public Sms getDeliveredMsgValue(Long messageId) {
if (deliveredMsgLiveTime == 0)
return null;
synchronized (this.deliveredMsgCacheSync) {
Sms res = this.deliveredMsgCache1.get(messageId);
if (res == null)
res = this.deliveredMsgCache2.get(messageId);
return res;
}
}
public static String generateRemoteMsgIdKey(String remoteMessageId, String destId) {
StringBuilder sb = new StringBuilder();
sb.append(remoteMessageId);
sb.append("_");
sb.append(destId);
return sb.toString();
}
public void putDeliveredRemoteMsgIdValue(String remoteMessageId, String destId, Long messageId, int deliveredMsgLiveTime) {
this.deliveredMsgLiveTime = deliveredMsgLiveTime;
if (deliveredMsgLiveTime > 0) {
synchronized (this.deliveredRemoteMsgIdCacheSync) {
this.deliveredRemoteMsgIdCache1.put(generateRemoteMsgIdKey(remoteMessageId, destId), messageId);
}
}
}
public Long getDeliveredRemoteMsgIdValue(String remoteMessageId, String destId) {
if (deliveredMsgLiveTime == 0)
return null;
String key = generateRemoteMsgIdKey(remoteMessageId, destId);
synchronized (this.deliveredRemoteMsgIdCacheSync) {
Long res = this.deliveredRemoteMsgIdCache1.get(key);
if (res == null)
res = this.deliveredRemoteMsgIdCache2.get(key);
return res;
}
}
private class CacheManTask implements Runnable {
public void run() {
try {
correlationIdCache2 = correlationIdCache1;
correlationIdCache1 = new FastMap<String, CorrelationIdValue>();
} finally {
if (isStarted) {
CacheManTask t = new CacheManTask();
executor.schedule(t, correlationIdLiveTime, TimeUnit.SECONDS);
}
}
}
}
private class CacheManTask_SRI_Resp implements Runnable {
public void run() {
try {
sriRespCache2 = sriRespCache1;
sriRespCache1 = new FastMap<String, SriResponseValue>();
} finally {
if (isStarted) {
CacheManTask_SRI_Resp t = new CacheManTask_SRI_Resp();
int time = sriResponseLiveTime;
// let's make delay 60 sec for "no caching option"
if (time <= 0)
time = 60;
executor.schedule(t, time, TimeUnit.SECONDS);
}
}
}
}
private class CacheManTask_Delivered_Msg implements Runnable {
public void run() {
try {
deliveredMsgCache2 = deliveredMsgCache1;
deliveredMsgCache1 = new FastMap<Long, Sms>();
} finally {
if (isStarted) {
CacheManTask_Delivered_Msg t = new CacheManTask_Delivered_Msg();
int time = deliveredMsgLiveTime;
// let's make delay 60 sec for "no caching option"
if (time <= 0)
time = 60;
executor.schedule(t, time, TimeUnit.SECONDS);
}
}
}
}
private class CacheManTask_Delivered_RemoteMsgId implements Runnable {
public void run() {
try {
deliveredRemoteMsgIdCache2 = deliveredRemoteMsgIdCache1;
deliveredRemoteMsgIdCache1 = new FastMap<String, Long>();
} finally {
if (isStarted) {
CacheManTask_Delivered_RemoteMsgId t = new CacheManTask_Delivered_RemoteMsgId();
int time = deliveredMsgLiveTime;
// let's make delay 60 sec for "no caching option"
if (time <= 0)
time = 60;
executor.schedule(t, time, TimeUnit.SECONDS);
}
}
}
}
}