/*
* 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.protocols.ss7.mtp;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.mobicents.ss7.congestion.ExecutorCongestionMonitor;
import org.mobicents.ss7.congestion.ExecutorCongestionMonitorImpl;
// lic dep 1
/**
*
* @author amit bhayani
* @author sergey vetyutnev
*
*/
public abstract class Mtp3UserPartBaseImpl implements Mtp3UserPart {
private static final Logger logger = Logger.getLogger(Mtp3UserPartBaseImpl.class);
private static final String LICENSE_PRODUCT_NAME = "Mobicents-jSS7";
protected static final String ROUTING_LABEL_FORMAT = "routingLabelFormat"; // we do not store this value
protected static final String USE_LSB_FOR_LINKSET_SELECTION = "useLsbForLinksetSelection";
private int maxSls = 32;
private int slsFilter = 0x1F;
// The count of threads that will be used for message delivering to
// Mtp3UserPartListener's
// For single thread model this value should be equal 1
protected int deliveryTransferMessageThreadCount = Runtime.getRuntime().availableProcessors() * 2;
// RoutingLabeFormat option
private RoutingLabelFormat routingLabelFormat = RoutingLabelFormat.ITU;
// If set to true, lowest bit of SLS is used for loadbalancing between Linkset else highest bit of SLS is used.
private boolean useLsbForLinksetSelection = false;
protected boolean isStarted = false;
private CopyOnWriteArrayList<Mtp3UserPartListener> userListeners = new CopyOnWriteArrayList<Mtp3UserPartListener>();
// a thread pool for delivering Mtp3TransferMessage messages
private ExecutorService[] msgDeliveryExecutors;
// a thread for delivering PAUSE, RESUME and STATUS messages
private ScheduledExecutorService msgDeliveryExecutorSystem;
private int[] slsTable = null;
private ExecutorCongestionMonitorImpl executorCongestionMonitor = null;
private Mtp3TransferPrimitiveFactory mtp3TransferPrimitiveFactory = null;
private final String productName;
public Mtp3UserPartBaseImpl(String productName) {
if(productName == null){
this.productName = LICENSE_PRODUCT_NAME;
} else {
this.productName = productName;
}
}
public int getDeliveryMessageThreadCount() {
return this.deliveryTransferMessageThreadCount;
}
public void setDeliveryMessageThreadCount(int deliveryMessageThreadCount) throws Exception {
if (deliveryMessageThreadCount > 0 && deliveryMessageThreadCount <= 100)
this.deliveryTransferMessageThreadCount = deliveryMessageThreadCount;
}
@Override
public void addMtp3UserPartListener(Mtp3UserPartListener listener) {
this.userListeners.add(listener);
}
@Override
public void removeMtp3UserPartListener(Mtp3UserPartListener listener) {
this.userListeners.remove(listener);
}
@Override
public RoutingLabelFormat getRoutingLabelFormat() {
return this.routingLabelFormat;
}
@Override
public void setRoutingLabelFormat(RoutingLabelFormat routingLabelFormat) throws Exception {
if (routingLabelFormat != null)
this.routingLabelFormat = routingLabelFormat;
}
@Override
public boolean isUseLsbForLinksetSelection() {
return useLsbForLinksetSelection;
}
@Override
public void setUseLsbForLinksetSelection(boolean useLsbForLinksetSelection) throws Exception {
this.useLsbForLinksetSelection = useLsbForLinksetSelection;
}
/*
* For classic MTP3 this value is maximum SIF length minus routing label length. This method should be overloaded if
* different message length is supported.
*/
@Override
public int getMaxUserDataLength(int dpc) {
switch (this.routingLabelFormat) {
case ITU:
// For PC_FORMAT_14, the MTP3 Routing Label takes 4 bytes - OPC/DPC
// = 16 bits each and SLS = 4 bits
return 272 - 4;
case ANSI_Sls8Bit:
// For PC_FORMAT_24, the MTP3 Routing Label takes 6 bytes - OPC/DPC
// = 24 bits each and SLS = 8 bits
return 272 - 7;
default:
// TODO : We don't support rest just yet
return -1;
}
}
@Override
public Mtp3TransferPrimitiveFactory getMtp3TransferPrimitiveFactory() {
return this.mtp3TransferPrimitiveFactory;
}
@Override
public ExecutorCongestionMonitor getExecutorCongestionMonitor() {
return executorCongestionMonitor;
}
public void start() throws Exception {
// lic dep 2
if (this.isStarted)
return;
if (!(this.routingLabelFormat == RoutingLabelFormat.ITU || this.routingLabelFormat == RoutingLabelFormat.ANSI_Sls8Bit)) {
throw new Exception("Invalid PointCodeFormat set. We support only ITU or ANSI now");
}
switch (this.routingLabelFormat) {
case ITU:
this.maxSls = 16;
this.slsFilter = 0x0f;
break;
case ANSI_Sls5Bit:
this.maxSls = 32;
this.slsFilter = 0x1f;
break;
case ANSI_Sls8Bit:
this.maxSls = 256;
this.slsFilter = 0xff;
break;
default:
throw new Exception("Invalid SLS length");
}
this.slsTable = new int[maxSls];
this.mtp3TransferPrimitiveFactory = new Mtp3TransferPrimitiveFactory(this.routingLabelFormat);
this.createSLSTable(this.deliveryTransferMessageThreadCount);
this.msgDeliveryExecutors = new ExecutorService[this.deliveryTransferMessageThreadCount];
for (int i = 0; i < this.deliveryTransferMessageThreadCount; i++) {
this.msgDeliveryExecutors[i] = Executors.newFixedThreadPool(1, new DefaultThreadFactory("Mtp3-DeliveryExecutor-"
+ i));
}
this.msgDeliveryExecutorSystem = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
"Mtp3-DeliveryExecutorSystem"));
this.executorCongestionMonitor = new ExecutorCongestionMonitorImpl(productName, msgDeliveryExecutors);
this.isStarted = true;
this.startThreadMonitoring();
}
public void stop() throws Exception {
if (!this.isStarted)
return;
this.isStarted = false;
for (ExecutorService es : this.msgDeliveryExecutors) {
es.shutdown();
}
this.msgDeliveryExecutorSystem.shutdown();
this.executorCongestionMonitor = null;
}
private void startThreadMonitoring() {
ExecutorCongestionMonitorImpl monitor = this.executorCongestionMonitor;
if (isStarted && monitor != null) {
monitor.monitor();
ExecutorCongestionMonitorHandler handler = new ExecutorCongestionMonitorHandler();
this.msgDeliveryExecutorSystem.schedule(handler, 500, TimeUnit.MILLISECONDS);
}
}
/**
* Deliver an incoming message to the local user
*
* @param msg
* @param effectiveSls For the thread selection (for message delivering)
*/
protected void sendTransferMessageToLocalUser(Mtp3TransferPrimitive msg, int seqControl) {
if (this.isStarted) {
MsgTransferDeliveryHandler hdl = new MsgTransferDeliveryHandler(msg);
seqControl = seqControl & slsFilter;
this.msgDeliveryExecutors[this.slsTable[seqControl]].execute(hdl);
} else {
logger.error(String.format(
"Received Mtp3TransferPrimitive=%s but Mtp3UserPart is not started. Message will be dropped", msg));
}
}
protected void sendPauseMessageToLocalUser(Mtp3PausePrimitive msg) {
if (this.isStarted) {
MsgSystemDeliveryHandler hdl = new MsgSystemDeliveryHandler(msg);
this.msgDeliveryExecutorSystem.execute(hdl);
} else {
logger.error(String.format(
"Received Mtp3PausePrimitive=%s but MTP3 is not started. Message will be dropped", msg));
}
}
protected void sendResumeMessageToLocalUser(Mtp3ResumePrimitive msg) {
if (this.isStarted) {
MsgSystemDeliveryHandler hdl = new MsgSystemDeliveryHandler(msg);
this.msgDeliveryExecutorSystem.execute(hdl);
} else {
logger.error(String.format(
"Received Mtp3ResumePrimitive=%s but MTP3 is not started. Message will be dropped", msg));
}
}
protected void sendStatusMessageToLocalUser(Mtp3StatusPrimitive msg) {
if (this.isStarted) {
MsgSystemDeliveryHandler hdl = new MsgSystemDeliveryHandler(msg);
this.msgDeliveryExecutorSystem.execute(hdl);
} else {
logger.error(String.format(
"Received Mtp3StatusPrimitive=%s but MTP3 is not started. Message will be dropped", msg));
}
}
protected void sendEndCongestionMessageToLocalUser(Mtp3EndCongestionPrimitive msg) {
if (this.isStarted) {
MsgSystemDeliveryHandler hdl = new MsgSystemDeliveryHandler(msg);
this.msgDeliveryExecutorSystem.execute(hdl);
} else {
logger.error(String.format(
"Received Mtp3EndCongestionPrimitive=%s but MTP3 is not started. Message will be dropped", msg));
}
}
private void createSLSTable(int minimumBoundThread) {
int stream = 0;
for (int i = 0; i < maxSls; i++) {
if (stream >= minimumBoundThread) {
stream = 0;
}
slsTable[i] = stream++;
}
}
private class MsgTransferDeliveryHandler implements Runnable {
private Mtp3TransferPrimitive msg;
public MsgTransferDeliveryHandler(Mtp3TransferPrimitive msg) {
this.msg = msg;
}
@Override
public void run() {
if (isStarted) {
try {
for (Mtp3UserPartListener lsn : userListeners) {
lsn.onMtp3TransferMessage(this.msg);
}
} catch (Exception e) {
logger.error("Exception while delivering a system messages to the MTP3-user: " + e.getMessage(), e);
}
} else {
logger.error(String.format(
"Received Mtp3TransferPrimitive=%s but Mtp3UserPart is not started. Message will be dropped", msg));
}
}
}
private class MsgSystemDeliveryHandler implements Runnable {
Mtp3Primitive msg;
public MsgSystemDeliveryHandler(Mtp3Primitive msg) {
this.msg = msg;
}
@Override
public void run() {
if (isStarted) {
try {
for (Mtp3UserPartListener lsn : userListeners) {
if (this.msg.getType() == Mtp3Primitive.PAUSE)
lsn.onMtp3PauseMessage((Mtp3PausePrimitive) this.msg);
if (this.msg.getType() == Mtp3Primitive.RESUME)
lsn.onMtp3ResumeMessage((Mtp3ResumePrimitive) this.msg);
if (this.msg.getType() == Mtp3Primitive.STATUS)
lsn.onMtp3StatusMessage((Mtp3StatusPrimitive) this.msg);
if (this.msg.getType() == Mtp3Primitive.END_CONGESTION)
lsn.onMtp3EndCongestionMessage((Mtp3EndCongestionPrimitive) this.msg);
}
} catch (Exception e) {
logger.error("Exception while delivering a payload messages to the MTP3-user: " + e.getMessage(), e);
}
} else {
logger.error(String.format(
"Received Mtp3Primitive=%s but Mtp3UserPart is not started. Message will be dropped", msg));
}
}
}
private class ExecutorCongestionMonitorHandler implements Runnable {
@Override
public void run() {
startThreadMonitoring();
}
}
}