/*
* 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.m3ua.impl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.ReferenceCountUtil;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.xml.XMLFormat;
import javolution.xml.XMLSerializable;
import javolution.xml.stream.XMLStreamException;
import org.apache.log4j.Logger;
import org.mobicents.protocols.api.Association;
import org.mobicents.protocols.api.AssociationListener;
import org.mobicents.protocols.api.IpChannelType;
import org.mobicents.protocols.api.Management;
import org.mobicents.protocols.ss7.m3ua.Asp;
import org.mobicents.protocols.ss7.m3ua.AspFactory;
import org.mobicents.protocols.ss7.m3ua.ExchangeType;
import org.mobicents.protocols.ss7.m3ua.Functionality;
import org.mobicents.protocols.ss7.m3ua.IPSPType;
import org.mobicents.protocols.ss7.m3ua.impl.fsm.FSM;
import org.mobicents.protocols.ss7.m3ua.impl.fsm.UnknownTransitionException;
import org.mobicents.protocols.ss7.m3ua.impl.message.M3UAMessageImpl;
import org.mobicents.protocols.ss7.m3ua.impl.message.MessageFactoryImpl;
import org.mobicents.protocols.ss7.m3ua.impl.oam.M3UAOAMMessages;
import org.mobicents.protocols.ss7.m3ua.impl.parameter.ParameterFactoryImpl;
import org.mobicents.protocols.ss7.m3ua.message.M3UAMessage;
import org.mobicents.protocols.ss7.m3ua.message.MessageClass;
import org.mobicents.protocols.ss7.m3ua.message.MessageFactory;
import org.mobicents.protocols.ss7.m3ua.message.MessageType;
import org.mobicents.protocols.ss7.m3ua.message.aspsm.ASPDown;
import org.mobicents.protocols.ss7.m3ua.message.aspsm.ASPDownAck;
import org.mobicents.protocols.ss7.m3ua.message.aspsm.ASPUp;
import org.mobicents.protocols.ss7.m3ua.message.aspsm.ASPUpAck;
import org.mobicents.protocols.ss7.m3ua.message.aspsm.Heartbeat;
import org.mobicents.protocols.ss7.m3ua.message.asptm.ASPActive;
import org.mobicents.protocols.ss7.m3ua.message.asptm.ASPActiveAck;
import org.mobicents.protocols.ss7.m3ua.message.asptm.ASPInactive;
import org.mobicents.protocols.ss7.m3ua.message.asptm.ASPInactiveAck;
import org.mobicents.protocols.ss7.m3ua.message.mgmt.Notify;
import org.mobicents.protocols.ss7.m3ua.message.ssnm.DestinationAvailable;
import org.mobicents.protocols.ss7.m3ua.message.ssnm.DestinationRestricted;
import org.mobicents.protocols.ss7.m3ua.message.ssnm.DestinationStateAudit;
import org.mobicents.protocols.ss7.m3ua.message.ssnm.DestinationUPUnavailable;
import org.mobicents.protocols.ss7.m3ua.message.ssnm.DestinationUnavailable;
import org.mobicents.protocols.ss7.m3ua.message.ssnm.SignallingCongestion;
import org.mobicents.protocols.ss7.m3ua.message.transfer.PayloadData;
import org.mobicents.protocols.ss7.m3ua.parameter.ASPIdentifier;
import org.mobicents.protocols.ss7.m3ua.parameter.ParameterFactory;
import org.mobicents.protocols.ss7.mtp.Mtp3EndCongestionPrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3StatusCause;
import org.mobicents.protocols.ss7.mtp.Mtp3StatusPrimitive;
/**
*
* @author amit bhayani
*
*/
public class AspFactoryImpl implements AssociationListener, XMLSerializable, AspFactory {
private static final Logger logger = Logger.getLogger(AspFactoryImpl.class);
private static long ASP_ID_COUNT = 1L;
private static final int SCTP_PAYLOAD_PROT_ID_M3UA = 3;
private static final String NAME = "name";
private static final String STARTED = "started";
private static final String ASSOCIATION_NAME = "assocName";
private static final String MAX_SEQUENCE_NUMBER = "maxseqnumber";
private static final String ASP_ID = "aspid";
private static final String HEART_BEAT = "heartbeat";
protected String name;
protected boolean started = false;
protected Association association = null;
protected String associationName = null;
protected FastList<Asp> aspList = new FastList<Asp>();
private ByteBuffer txBuffer = ByteBuffer.allocateDirect(8192);
// data buffer for incoming TCP data
private CompositeByteBuf tcpIncBuffer;
protected Management transportManagement = null;
protected M3UAManagementImpl m3UAManagementImpl = null;
private ASPIdentifier aspid;
protected ParameterFactory parameterFactory = new ParameterFactoryImpl();
protected MessageFactory messageFactory = new MessageFactoryImpl();
private TransferMessageHandler transferMessageHandler = new TransferMessageHandler(this);
private SignalingNetworkManagementHandler signalingNetworkManagementHandler = new SignalingNetworkManagementHandler(this);
private ManagementMessageHandler managementMessageHandler = new ManagementMessageHandler(this);
private AspStateMaintenanceHandler aspStateMaintenanceHandler = new AspStateMaintenanceHandler(this);
private AspTrafficMaintenanceHandler aspTrafficMaintenanceHandler = new AspTrafficMaintenanceHandler(this);
private RoutingKeyManagementHandler routingKeyManagementHandler = new RoutingKeyManagementHandler(this);
protected Functionality functionality = null;
protected IPSPType ipspType = null;
protected ExchangeType exchangeType = null;
private long aspupSentTime = 0L;
private int maxSequenceNumber = M3UAManagementImpl.MAX_SEQUENCE_NUMBER;
private int[] slsTable = null;
private int maxOutboundStreams;
protected AspFactoryStopTimer aspFactoryStopTimer = null;
protected HeartBeatTimer heartBeatTimer = null;
private boolean isHeartBeatEnabled = false;
private FastMap<Integer, AtomicInteger> congDpcList = new FastMap<Integer, AtomicInteger>().shared();
public AspFactoryImpl() {
// clean transmission buffer
txBuffer.clear();
txBuffer.rewind();
txBuffer.flip();
this.heartBeatTimer = new HeartBeatTimer(this);
}
public AspFactoryImpl(String name, int maxSequenceNumber, long aspId, boolean isHeartBeatEnabled) {
this();
this.name = name;
this.maxSequenceNumber = maxSequenceNumber;
this.slsTable = new int[this.maxSequenceNumber];
this.aspid = parameterFactory.createASPIdentifier(aspId);
this.isHeartBeatEnabled = isHeartBeatEnabled;
}
/**
* @return the aspid
*/
public ASPIdentifier getAspid() {
return aspid;
}
/**
* @return the isHeartBeatEnabled
*/
public boolean isHeartBeatEnabled() {
return isHeartBeatEnabled;
}
public void setM3UAManagement(M3UAManagementImpl m3uaManagement) {
this.m3UAManagementImpl = m3uaManagement;
this.transferMessageHandler.setM3UAManagement(m3uaManagement);
}
public M3UAManagementImpl getM3UAManagement() {
return m3UAManagementImpl;
}
protected void start() throws Exception {
this.transportManagement.startAssociation(this.association.getName());
this.started = true;
}
protected void stop() throws Exception {
this.started = false;
if (this.association == null)
return;
if (this.isHeartBeatEnabled()) {
this.heartBeatTimer.cancel();
}
if (this.functionality == Functionality.AS
|| (this.functionality == Functionality.SGW && this.exchangeType == ExchangeType.DE)
|| (this.functionality == Functionality.IPSP && this.exchangeType == ExchangeType.DE)
|| (this.functionality == Functionality.IPSP && this.exchangeType == ExchangeType.SE && this.ipspType == IPSPType.CLIENT)) {
if (this.association.isConnected()) {
ASPDown aspDown = (ASPDown) this.messageFactory.createMessage(MessageClass.ASP_STATE_MAINTENANCE,
MessageType.ASP_DOWN);
this.write(aspDown);
for (FastList.Node<Asp> n = aspList.head(), end = aspList.tail(); (n = n.getNext()) != end;) {
AspImpl aspImpl = (AspImpl) n.getValue();
try {
FSM aspLocalFSM = aspImpl.getLocalFSM();
aspLocalFSM.signal(TransitionState.ASP_DOWN_SENT);
AsImpl peerAs = (AsImpl) aspImpl.getAs();
FSM asPeerFSM = peerAs.getPeerFSM();
asPeerFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl);
asPeerFSM.signal(TransitionState.ASP_DOWN);
} catch (UnknownTransitionException e) {
logger.error(e.getMessage(), e);
}
}
// Start the timer to kill the underlying transport Association
aspFactoryStopTimer = new AspFactoryStopTimer(this);
this.m3UAManagementImpl.m3uaScheduler.execute(aspFactoryStopTimer);
} else {
for (FastList.Node<Asp> n = aspList.head(), end = aspList.tail(); (n = n.getNext()) != end;) {
AspImpl aspImpl = (AspImpl) n.getValue();
try {
FSM aspLocalFSM = aspImpl.getLocalFSM();
aspLocalFSM.signal(TransitionState.COMM_DOWN);
AsImpl peerAs = (AsImpl) aspImpl.getAs();
FSM asPeerFSM = peerAs.getPeerFSM();
asPeerFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl);
asPeerFSM.signal(TransitionState.ASP_DOWN);
} catch (UnknownTransitionException e) {
logger.error(e.getMessage(), e);
}
}
// Finally stop the association
this.transportManagement.stopAssociation(this.association.getName());
}
} else {
// if (this.association.isConnected()) {
// throw new
// Exception("Still few ASP's are connected. Bring down the ASP's first");
// }
this.transportManagement.stopAssociation(this.association.getName());
}
}
public boolean getStatus() {
return this.started;
}
// public boolean isConnected() {
// return started && up;
// }
public Functionality getFunctionality() {
return functionality;
}
protected void setFunctionality(Functionality functionality) {
this.functionality = functionality;
}
public IPSPType getIpspType() {
return ipspType;
}
protected void setIpspType(IPSPType ipspType) {
this.ipspType = ipspType;
}
public ExchangeType getExchangeType() {
return exchangeType;
}
protected void setExchangeType(ExchangeType exchangeType) {
this.exchangeType = exchangeType;
}
protected void setTransportManagement(Management transportManagement) {
this.transportManagement = transportManagement;
}
public Association getAssociation() {
return this.association;
}
protected void setAssociation(Association association) {
// Unset the listener to previous association
if (this.association != null) {
this.association.setAssociationListener(null);
}
this.association = association;
this.associationName = this.association.getName();
// Set the listener for new association
this.association.setAssociationListener(this);
}
protected void unsetAssociation() throws Exception {
if (this.association != null) {
if (this.association.isStarted()) {
throw new Exception(String.format("Association=%s is still started. Stop first", this.association.getName()));
}
this.association.setAssociationListener(null);
this.association = null;
}
}
public String getName() {
return this.name;
}
protected void read(M3UAMessage message) {
switch (message.getMessageClass()) {
case MessageClass.MANAGEMENT:
switch (message.getMessageType()) {
case MessageType.ERROR:
this.managementMessageHandler
.handleError((org.mobicents.protocols.ss7.m3ua.message.mgmt.Error) message);
break;
case MessageType.NOTIFY:
Notify notify = (Notify) message;
this.managementMessageHandler.handleNotify(notify);
break;
default:
logger.error(String.format("Rx : MGMT with invalid MessageType=%d message=%s",
message.getMessageType(), message));
break;
}
break;
case MessageClass.TRANSFER_MESSAGES:
switch (message.getMessageType()) {
case MessageType.PAYLOAD:
PayloadData payload = (PayloadData) message;
this.transferMessageHandler.handlePayload(payload);
break;
default:
logger.error(String.format("Rx : Transfer message with invalid MessageType=%d message=%s",
message.getMessageType(), message));
break;
}
break;
case MessageClass.SIGNALING_NETWORK_MANAGEMENT:
switch (message.getMessageType()) {
case MessageType.DESTINATION_UNAVAILABLE:
DestinationUnavailable duna = (DestinationUnavailable) message;
this.signalingNetworkManagementHandler.handleDestinationUnavailable(duna);
break;
case MessageType.DESTINATION_AVAILABLE:
DestinationAvailable dava = (DestinationAvailable) message;
this.signalingNetworkManagementHandler.handleDestinationAvailable(dava);
break;
case MessageType.DESTINATION_STATE_AUDIT:
DestinationStateAudit daud = (DestinationStateAudit) message;
this.signalingNetworkManagementHandler.handleDestinationStateAudit(daud);
break;
case MessageType.SIGNALING_CONGESTION:
SignallingCongestion scon = (SignallingCongestion) message;
this.signalingNetworkManagementHandler.handleSignallingCongestion(scon);
break;
case MessageType.DESTINATION_USER_PART_UNAVAILABLE:
DestinationUPUnavailable dupu = (DestinationUPUnavailable) message;
this.signalingNetworkManagementHandler.handleDestinationUPUnavailable(dupu);
break;
case MessageType.DESTINATION_RESTRICTED:
DestinationRestricted drst = (DestinationRestricted) message;
this.signalingNetworkManagementHandler.handleDestinationRestricted(drst);
break;
default:
logger.error(String.format("Received SSNM with invalid MessageType=%d message=%s",
message.getMessageType(), message));
break;
}
break;
case MessageClass.ASP_STATE_MAINTENANCE:
switch (message.getMessageType()) {
case MessageType.ASP_UP:
ASPUp aspUp = (ASPUp) message;
this.aspStateMaintenanceHandler.handleAspUp(aspUp);
break;
case MessageType.ASP_UP_ACK:
ASPUpAck aspUpAck = (ASPUpAck) message;
this.aspStateMaintenanceHandler.handleAspUpAck(aspUpAck);
break;
case MessageType.ASP_DOWN:
ASPDown aspDown = (ASPDown) message;
this.aspStateMaintenanceHandler.handleAspDown(aspDown);
break;
case MessageType.ASP_DOWN_ACK:
ASPDownAck aspDownAck = (ASPDownAck) message;
this.aspStateMaintenanceHandler.handleAspDownAck(aspDownAck);
break;
case MessageType.HEARTBEAT:
Heartbeat hrtBeat = (Heartbeat) message;
this.aspStateMaintenanceHandler.handleHeartbeat(hrtBeat);
break;
case MessageType.HEARTBEAT_ACK:
// Nothing to do
break;
default:
logger.error(String.format("Received ASPSM with invalid MessageType=%d message=%s",
message.getMessageType(), message));
break;
}
break;
case MessageClass.ASP_TRAFFIC_MAINTENANCE:
switch (message.getMessageType()) {
case MessageType.ASP_ACTIVE:
ASPActive aspActive = (ASPActive) message;
this.aspTrafficMaintenanceHandler.handleAspActive(aspActive);
break;
case MessageType.ASP_ACTIVE_ACK:
ASPActiveAck aspAciveAck = (ASPActiveAck) message;
this.aspTrafficMaintenanceHandler.handleAspActiveAck(aspAciveAck);
break;
case MessageType.ASP_INACTIVE:
ASPInactive aspInactive = (ASPInactive) message;
this.aspTrafficMaintenanceHandler.handleAspInactive(aspInactive);
break;
case MessageType.ASP_INACTIVE_ACK:
ASPInactiveAck aspInaciveAck = (ASPInactiveAck) message;
this.aspTrafficMaintenanceHandler.handleAspInactiveAck(aspInaciveAck);
break;
default:
logger.error(String.format("Received ASPTM with invalid MessageType=%d message=%s",
message.getMessageType(), message));
break;
}
break;
case MessageClass.ROUTING_KEY_MANAGEMENT:
break;
default:
logger.error(String.format("Received message with invalid MessageClass=%d message=%s",
message.getMessageClass(), message));
break;
}
}
protected void write(M3UAMessage message) {
try {
ByteBufAllocator byteBufAllocator = this.association.getByteBufAllocator();
ByteBuf byteBuf;
if (byteBufAllocator != null) {
byteBuf = byteBufAllocator.buffer();
} else {
byteBuf = Unpooled.buffer();
}
((M3UAMessageImpl) message).encode(byteBuf);
org.mobicents.protocols.api.PayloadData payloadData = null;
if (this.m3UAManagementImpl.isSctpLibNettySupport()) {
switch (message.getMessageClass()) {
case MessageClass.ASP_STATE_MAINTENANCE:
case MessageClass.MANAGEMENT:
case MessageClass.ROUTING_KEY_MANAGEMENT:
payloadData = new org.mobicents.protocols.api.PayloadData(byteBuf.readableBytes(), byteBuf, true, true,
SCTP_PAYLOAD_PROT_ID_M3UA, 0);
break;
case MessageClass.TRANSFER_MESSAGES:
PayloadData payload = (PayloadData) message;
int seqControl = payload.getData().getSLS();
payloadData = new org.mobicents.protocols.api.PayloadData(byteBuf.readableBytes(), byteBuf, true,
false, SCTP_PAYLOAD_PROT_ID_M3UA, this.slsTable[seqControl]);
break;
default:
payloadData = new org.mobicents.protocols.api.PayloadData(byteBuf.readableBytes(), byteBuf, true, true,
SCTP_PAYLOAD_PROT_ID_M3UA, 0);
break;
}
this.association.send(payloadData);
// congestion control - we will send MTP-PAUSE every 8 messages
int congLevel = this.association.getCongestionLevel();
if (message instanceof PayloadData) {
PayloadData payloadData2 = (PayloadData) message;
if (congLevel > 0) {
sendCongestionInfoToMtp3Users(congLevel, payloadData2.getData().getDpc());
} else {
sendCongestionEndInfoToMtp3Users(congLevel, payloadData2.getData().getDpc());
}
}
} else {
byte[] bf = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bf);
synchronized (txBuffer) {
switch (message.getMessageClass()) {
case MessageClass.ASP_STATE_MAINTENANCE:
case MessageClass.MANAGEMENT:
case MessageClass.ROUTING_KEY_MANAGEMENT:
payloadData = new org.mobicents.protocols.api.PayloadData(byteBuf.readableBytes(), bf, true, true,
SCTP_PAYLOAD_PROT_ID_M3UA, 0);
break;
case MessageClass.TRANSFER_MESSAGES:
PayloadData payload = (PayloadData) message;
int seqControl = payload.getData().getSLS();
payloadData = new org.mobicents.protocols.api.PayloadData(byteBuf.readableBytes(), bf, true, false,
SCTP_PAYLOAD_PROT_ID_M3UA, this.slsTable[seqControl]);
break;
default:
payloadData = new org.mobicents.protocols.api.PayloadData(byteBuf.readableBytes(), bf, true, true,
SCTP_PAYLOAD_PROT_ID_M3UA, 0);
break;
}
this.association.send(payloadData);
}
}
} catch (Throwable e) {
logger.error(String.format("Error while trying to send PayloadData to SCTP layer. M3UAMessage=%s", message), e);
}
}
private void sendCongestionInfoToMtp3Users(int congLevel, int dpc) {
AtomicInteger ai = congDpcList.get(dpc);
if (ai == null) {
ai = new AtomicInteger();
congDpcList.put(dpc, ai);
}
if (ai.incrementAndGet() % 8 == 0) {
Mtp3StatusPrimitive statusPrimitive = new Mtp3StatusPrimitive(dpc, Mtp3StatusCause.SignallingNetworkCongested,
congLevel, 0);
this.m3UAManagementImpl.sendStatusMessageToLocalUser(statusPrimitive);
}
}
private void sendCongestionEndInfoToMtp3Users(int congLevel, int dpc) {
AtomicInteger ai = congDpcList.get(dpc);
if (ai == null) {
return;
}
ai = new AtomicInteger();
congDpcList.remove(dpc);
Mtp3EndCongestionPrimitive endCongestionPrimitive = new Mtp3EndCongestionPrimitive(dpc);
this.m3UAManagementImpl.sendEndCongestionMessageToLocalUser(endCongestionPrimitive);
}
protected AspImpl createAsp() {
AspImpl remAsp = new AspImpl(this.name, this);
// We set ASP IP only if its AS or IPSP Client side
if (this.getFunctionality() == Functionality.AS
|| (this.getFunctionality() == Functionality.IPSP && this.getIpspType() == IPSPType.CLIENT)) {
remAsp.setASPIdentifier(aspid);
}
this.aspList.add(remAsp);
return remAsp;
}
protected boolean destroyAsp(AspImpl aspImpl) {
aspImpl.aspFactoryImpl = null;
return this.aspList.remove(aspImpl);
}
public List<Asp> getAspList() {
return this.aspList.unmodifiable();
}
protected AspImpl getAsp(long rc) {
for (FastList.Node<Asp> n = aspList.head(), end = aspList.tail(); (n = n.getNext()) != end;) {
Asp aspImpl = n.getValue();
if (aspImpl.getAs().getRoutingContext() != null
&& aspImpl.getAs().getRoutingContext().getRoutingContexts()[0] == rc) {
return (AspImpl) aspImpl;
}
}
return null;
}
protected void sendAspActive(AsImpl asImpl) {
ASPActive aspActive = (ASPActive) this.messageFactory.createMessage(MessageClass.ASP_TRAFFIC_MAINTENANCE,
MessageType.ASP_ACTIVE);
aspActive.setRoutingContext(asImpl.getRoutingContext());
aspActive.setTrafficModeType(asImpl.getTrafficModeType());
this.write(aspActive);
}
protected static long generateId() {
ASP_ID_COUNT++;
if (ASP_ID_COUNT == 4294967295L) {
ASP_ID_COUNT = 1L;
}
return ASP_ID_COUNT;
}
private void handleCommDown() {
if (this.isHeartBeatEnabled()) {
this.heartBeatTimer.cancel();
}
for (FastList.Node<Asp> n = aspList.head(), end = aspList.tail(); (n = n.getNext()) != end;) {
AspImpl aspImpl = (AspImpl) n.getValue();
try {
FSM aspLocalFSM = aspImpl.getLocalFSM();
if (aspLocalFSM != null) {
aspLocalFSM.signal(TransitionState.COMM_DOWN);
}
FSM aspPeerFSM = aspImpl.getPeerFSM();
if (aspPeerFSM != null) {
aspPeerFSM.signal(TransitionState.COMM_DOWN);
}
AsImpl asImpl = (AsImpl) aspImpl.getAs();
FSM asLocalFSM = asImpl.getLocalFSM();
if (asLocalFSM != null) {
asLocalFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl);
asLocalFSM.signal(TransitionState.ASP_DOWN);
}
FSM asPeerFSM = asImpl.getPeerFSM();
if (asPeerFSM != null) {
asPeerFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl);
asPeerFSM.signal(TransitionState.ASP_DOWN);
}
} catch (UnknownTransitionException e) {
logger.error(e.getMessage(), e);
}
}
}
protected void sendAspUp() {
// TODO : Possibility of race condition?
long now = System.currentTimeMillis();
if ((now - aspupSentTime) > 2000) {
ASPUp aspUp = (ASPUp) this.messageFactory.createMessage(MessageClass.ASP_STATE_MAINTENANCE, MessageType.ASP_UP);
aspUp.setASPIdentifier(this.aspid);
this.write(aspUp);
aspupSentTime = now;
}
}
private void handleCommUp() {
if (this.isHeartBeatEnabled()) {
this.heartBeatTimer.start();
this.heartBeatTimer.reset();
this.m3UAManagementImpl.m3uaScheduler.execute(this.heartBeatTimer);
}
if (this.functionality == Functionality.AS
|| (this.functionality == Functionality.SGW && this.exchangeType == ExchangeType.DE)
|| (this.functionality == Functionality.IPSP && this.exchangeType == ExchangeType.DE)
|| (this.functionality == Functionality.IPSP && this.exchangeType == ExchangeType.SE && this.ipspType == IPSPType.CLIENT)) {
this.aspupSentTime = 0L;
this.sendAspUp();
}
for (FastList.Node<Asp> n = aspList.head(), end = aspList.tail(); (n = n.getNext()) != end;) {
AspImpl aspImpl = (AspImpl) n.getValue();
try {
FSM aspLocalFSM = aspImpl.getLocalFSM();
if (aspLocalFSM != null) {
aspLocalFSM.signal(TransitionState.COMM_UP);
}
FSM aspPeerFSM = aspImpl.getPeerFSM();
if (aspPeerFSM != null) {
aspPeerFSM.signal(TransitionState.COMM_UP);
}
} catch (UnknownTransitionException e) {
logger.error(e.getMessage(), e);
}
}
}
/**
* XML Serialization/Deserialization
*/
protected static final XMLFormat<AspFactoryImpl> ASP_FACTORY_XML = new XMLFormat<AspFactoryImpl>(AspFactoryImpl.class) {
@Override
public void read(javolution.xml.XMLFormat.InputElement xml, AspFactoryImpl aspFactoryImpl) throws XMLStreamException {
aspFactoryImpl.name = xml.getAttribute(NAME, "");
aspFactoryImpl.associationName = xml.getAttribute(ASSOCIATION_NAME, "");
aspFactoryImpl.started = xml.getAttribute(STARTED).toBoolean();
aspFactoryImpl.maxSequenceNumber = xml.getAttribute(MAX_SEQUENCE_NUMBER, M3UAManagementImpl.MAX_SEQUENCE_NUMBER);
aspFactoryImpl.slsTable = new int[aspFactoryImpl.maxSequenceNumber];
// For backward compatible
long aspIdTemp = xml.getAttribute(ASP_ID, aspFactoryImpl.generateId());
aspFactoryImpl.aspid = aspFactoryImpl.parameterFactory.createASPIdentifier(aspIdTemp);
aspFactoryImpl.isHeartBeatEnabled = xml.getAttribute(HEART_BEAT, false);
}
@Override
public void write(AspFactoryImpl aspFactoryImpl, javolution.xml.XMLFormat.OutputElement xml) throws XMLStreamException {
xml.setAttribute(NAME, aspFactoryImpl.name);
xml.setAttribute(ASSOCIATION_NAME, aspFactoryImpl.associationName);
xml.setAttribute(STARTED, aspFactoryImpl.started);
xml.setAttribute(MAX_SEQUENCE_NUMBER, aspFactoryImpl.maxSequenceNumber);
xml.setAttribute(ASP_ID, aspFactoryImpl.aspid.getAspId());
xml.setAttribute(HEART_BEAT, aspFactoryImpl.isHeartBeatEnabled);
}
};
/**
* AssociationListener methods
*/
@Override
public void onCommunicationLost(Association association) {
logger.warn(String.format("Communication channel lost for AspFactroy=%s Association=%s", this.name,
association.getName()));
this.handleCommDown();
}
@Override
public void onCommunicationRestart(Association association) {
logger.warn(String.format("Communication channel restart for AspFactroy=%s Association=%s", this.name,
association.getName()));
//TODO : Is this correct way to handle?
try {
this.transportManagement.stopAssociation(this.associationName);
} catch (Exception e) {
logger.warn(String.format("Error while trying to stop underlying Association for AspFactpry=%s",
this.getName()), e);
}
try {
this.transportManagement.startAssociation(this.associationName);
} catch (Exception e) {
logger.error(String.format("Error while trying to start underlying Association for AspFactpry=%s",
this.getName()), e);
}
}
@Override
public void onCommunicationShutdown(Association association) {
logger.warn(String.format("Communication channel shutdown for AspFactroy=%s Association=%s", this.name,
association.getName()));
this.handleCommDown();
}
@Override
public void onCommunicationUp(Association association, int maxInboundStreams, int maxOutboundStreams) {
this.maxOutboundStreams = maxOutboundStreams;
// Recreate SLS table. Minimum of two is correct?
this.createSLSTable(Math.min(maxInboundStreams, maxOutboundStreams) - 1);
this.handleCommUp();
}
protected void createSLSTable(int minimumBoundStream) {
if (minimumBoundStream == 0) { // special case - only 1 stream
for (int i = 0; i < this.maxSequenceNumber; i++) {
slsTable[i] = 0;
}
} else {
// SCTP Stream 0 is for management messages, we start from 1
int stream = 1;
for (int i = 0; i < this.maxSequenceNumber; i++) {
if (stream > minimumBoundStream) {
stream = 1;
}
slsTable[i] = stream++;
}
}
}
@Override
public void onPayload(Association association, org.mobicents.protocols.api.PayloadData payloadData) {
try {
M3UAMessage m3UAMessage;
if (this.m3UAManagementImpl.sctpLibNettySupport) {
ByteBuf byteBuf = payloadData.getByteBuf();
processPayload(association.getIpChannelType(), byteBuf);
} else {
byte[] m3uadata = payloadData.getData();
ByteBuf byteBuf = Unpooled.wrappedBuffer(m3uadata);
processPayload(association.getIpChannelType(), byteBuf);
}
} catch (Throwable e) {
logger.error(
String.format("Error while trying to process PayloadData from SCTP layer. payloadData=%s", payloadData), e);
}
}
private void processPayload(IpChannelType ipChannelType, ByteBuf byteBuf) {
M3UAMessage m3UAMessage;
if (ipChannelType == IpChannelType.SCTP) {
try {
// TODO where is streamNumber stored?
m3UAMessage = this.messageFactory.createMessage(byteBuf);
if (this.isHeartBeatEnabled()) {
this.heartBeatTimer.reset();
}
this.read(m3UAMessage);
} finally {
ReferenceCountUtil.release(byteBuf);
}
} else {
if (tcpIncBuffer == null) {
tcpIncBuffer = byteBuf.alloc().compositeBuffer();
}
tcpIncBuffer.addComponent(byteBuf);
tcpIncBuffer.writerIndex(tcpIncBuffer.capacity());
while (true) {
m3UAMessage = this.messageFactory.createMessage(tcpIncBuffer);
if (m3UAMessage == null)
break;
if (this.isHeartBeatEnabled()) {
this.heartBeatTimer.reset();
}
this.read(m3UAMessage);
}
tcpIncBuffer.discardReadBytes();
}
}
/*
* (non-Javadoc)
*
* @see org.mobicents.protocols.api.AssociationListener#inValidStreamId(org.mobicents .protocols.api.PayloadData)
*/
@Override
public void inValidStreamId(org.mobicents.protocols.api.PayloadData payloadData) {
logger.error(String
.format("Tx : PayloadData with streamNumber=%d which is greater than or equal to maxSequenceNumber=%d. Droping PayloadData=%s",
payloadData.getStreamNumber(), this.maxOutboundStreams, payloadData));
}
public void show(StringBuffer sb) {
sb.append(M3UAOAMMessages.SHOW_ASP_NAME).append(this.name).append(M3UAOAMMessages.SHOW_ASPID)
.append(this.aspid.getAspId()).append(M3UAOAMMessages.SHOW_HEARTBEAT_ENABLED).append(this.isHeartBeatEnabled())
.append(M3UAOAMMessages.SHOW_SCTP_ASSOC).append(this.associationName).append(M3UAOAMMessages.SHOW_STARTED)
.append(this.started);
sb.append(M3UAOAMMessages.NEW_LINE);
sb.append(M3UAOAMMessages.SHOW_ASSIGNED_TO);
for (FastList.Node<Asp> n = aspList.head(), end = aspList.tail(); (n = n.getNext()) != end;) {
AspImpl aspImpl = (AspImpl) n.getValue();
sb.append(M3UAOAMMessages.TAB).append(M3UAOAMMessages.SHOW_AS_NAME).append(aspImpl.getAs().getName())
.append(M3UAOAMMessages.SHOW_FUNCTIONALITY).append(this.functionality).append(M3UAOAMMessages.SHOW_MODE)
.append(this.exchangeType);
if (this.functionality == Functionality.IPSP) {
sb.append(M3UAOAMMessages.SHOW_IPSP_TYPE).append(this.ipspType);
}
if (aspImpl.getLocalFSM() != null) {
sb.append(M3UAOAMMessages.SHOW_LOCAL_FSM_STATE).append(aspImpl.getLocalFSM().getState());
}
if (aspImpl.getPeerFSM() != null) {
sb.append(M3UAOAMMessages.SHOW_PEER_FSM_STATE).append(aspImpl.getPeerFSM().getState());
}
sb.append(M3UAOAMMessages.NEW_LINE);
}
}
}