/* * 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 javolution.util.FastList; import org.apache.log4j.Logger; import org.mobicents.protocols.ss7.m3ua.Asp; 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.message.MessageClass; 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.aspsm.HeartbeatAck; import org.mobicents.protocols.ss7.m3ua.parameter.ASPIdentifier; import org.mobicents.protocols.ss7.m3ua.parameter.ErrorCode; import org.mobicents.protocols.ss7.m3ua.parameter.TrafficModeType; /** * * @author amit bhayani * */ public class AspStateMaintenanceHandler extends MessageHandler { private static final Logger logger = Logger.getLogger(AspStateMaintenanceHandler.class); public AspStateMaintenanceHandler(AspFactoryImpl aspFactoryImpl) { super(aspFactoryImpl); } protected void handleAspUp(ASPUp aspUp) { if (aspFactoryImpl.getFunctionality() == Functionality.SGW || (aspFactoryImpl.getFunctionality() == Functionality.AS && aspFactoryImpl.getExchangeType() == ExchangeType.DE) || (aspFactoryImpl.getFunctionality() == Functionality.IPSP && aspFactoryImpl.getExchangeType() == ExchangeType.DE) || (aspFactoryImpl.getFunctionality() == Functionality.IPSP && aspFactoryImpl.getExchangeType() == ExchangeType.SE && aspFactoryImpl.getIpspType() == IPSPType.SERVER)) { ASPUpAck aspUpAck = (ASPUpAck) this.aspFactoryImpl.messageFactory.createMessage(MessageClass.ASP_STATE_MAINTENANCE, MessageType.ASP_UP_ACK); ASPIdentifier aspId = aspUp.getASPIdentifier(); aspUpAck.setASPIdentifier(aspId); this.aspFactoryImpl.write(aspUpAck); // If an ASP Up message is received and, internally, the remote ASP // is in the ASP-ACTIVE state, an ASP Up Ack message is returned, as // well as an Error message ("Unexpected Message"). In addition, the // remote ASP state is changed to ASP-INACTIVE in all relevant // Application Servers, and all registered Routing Keys are // considered deregistered. for (FastList.Node<Asp> n = this.aspFactoryImpl.aspList.head(), end = this.aspFactoryImpl.aspList.tail(); (n = n .getNext()) != end;) { AspImpl aspImpl = (AspImpl) n.getValue(); // Set the ASPIdentifier for each ASP aspImpl.setASPIdentifier(aspUp.getASPIdentifier()); FSM aspPeerFSM = aspImpl.getPeerFSM(); if (aspPeerFSM == null) { logger.error(String.format("Received ASPUP=%s for ASP=%s. But peer FSM is null.", aspUp, this.aspFactoryImpl.getName())); return; } if (AspState.getState(aspPeerFSM.getState().getName()) == AspState.ACTIVE) { // Check if ASP is in ACTIVE, its error state ErrorCode errorCodeObj = this.aspFactoryImpl.parameterFactory.createErrorCode(ErrorCode.Unexpected_Message); this.sendError(aspImpl.getAs().getRoutingContext(), errorCodeObj); // break; } try { // Signal ASP about state change aspPeerFSM.setAttribute(FSM.ATTRIBUTE_MESSAGE, aspUp); aspPeerFSM.signal(TransitionState.ASP_UP); // Signal corresponding AS about ASP's state transition FSM asLocalFSM = ((AsImpl) aspImpl.getAs()).getLocalFSM(); asLocalFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl); asLocalFSM.signal(TransitionState.ASP_UP); } catch (UnknownTransitionException e) { logger.error(e.getMessage(), e); } } } else { // TODO : Should we silently drop ASPUP_ACK? // ASPUP_ACK is unexpected in this state ErrorCode errorCodeObj = this.aspFactoryImpl.parameterFactory.createErrorCode(ErrorCode.Unexpected_Message); sendError(null, errorCodeObj); } } protected void handleAspUpAck(ASPUpAck aspUpAck) { if (!this.aspFactoryImpl.started) { // If management stopped this ASP, ignore ASPUpAck return; } if (aspFactoryImpl.getFunctionality() == Functionality.AS || (aspFactoryImpl.getFunctionality() == Functionality.SGW && aspFactoryImpl.getExchangeType() == ExchangeType.DE) || (aspFactoryImpl.getFunctionality() == Functionality.IPSP && aspFactoryImpl.getExchangeType() == ExchangeType.DE) || (aspFactoryImpl.getFunctionality() == Functionality.IPSP && aspFactoryImpl.getExchangeType() == ExchangeType.SE && aspFactoryImpl.getIpspType() == IPSPType.CLIENT)) { for (FastList.Node<Asp> n = this.aspFactoryImpl.aspList.head(), end = this.aspFactoryImpl.aspList.tail(); (n = n .getNext()) != end;) { AspImpl aspImpl = (AspImpl) n.getValue(); FSM aspLocalFSM = aspImpl.getLocalFSM(); if (aspLocalFSM == null) { logger.error(String.format("Received ASPUP_ACK=%s for ASP=%s. But local FSM is null.", aspUpAck, this.aspFactoryImpl.getName())); return; } boolean transToActive = this.activate(aspImpl); if (!transToActive) { // Transition to INACTIVE try { aspLocalFSM.signal(TransitionState.ASP_INACTIVE); } catch (UnknownTransitionException e) { logger.error(e.getMessage(), e); } } else { // Transition to ACTIVE_SENT try { aspLocalFSM.signal(TransitionState.ASP_ACTIVE_SENT); if (aspFactoryImpl.getFunctionality() == Functionality.IPSP) { // If its IPSP, we know NTFY will not be received, // so transition AS FSM here AsImpl asImpl = (AsImpl) aspImpl.getAs(); FSM asPeerFSM = asImpl.getPeerFSM(); if (asPeerFSM == null) { logger.error(String.format("Received ASPUP_ACK=%s for ASP=%s. But Peer FSM of AS=%s is null.", aspUpAck, this.aspFactoryImpl.getName(), asImpl)); return; } AsState asPeerFSMState = AsState.getState(asPeerFSM.getState().getName()); if (AsState.DOWN == asPeerFSMState) { // Transition to INACTIVE only if its DOWN asPeerFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl); asPeerFSM.signal(TransitionState.AS_STATE_CHANGE_INACTIVE); } } } catch (UnknownTransitionException e) { logger.error(e.getMessage(), e); } }// if..else }// for } else { // TODO : Should we silently drop ASPUP_ACK? // ASPUP_ACK is unexpected in this state ErrorCode errorCodeObj = this.aspFactoryImpl.parameterFactory.createErrorCode(ErrorCode.Unexpected_Message); sendError(null, errorCodeObj); } } protected void handleAspDown(ASPDown aspDown) { if (aspFactoryImpl.getFunctionality() == Functionality.SGW || (aspFactoryImpl.getFunctionality() == Functionality.AS && aspFactoryImpl.getExchangeType() == ExchangeType.DE) || (aspFactoryImpl.getFunctionality() == Functionality.IPSP && aspFactoryImpl.getExchangeType() == ExchangeType.DE) || (aspFactoryImpl.getFunctionality() == Functionality.IPSP && aspFactoryImpl.getExchangeType() == ExchangeType.SE && aspFactoryImpl.getIpspType() == IPSPType.SERVER)) { ASPDownAck aspDwnAck = (ASPDownAck) this.aspFactoryImpl.messageFactory.createMessage( MessageClass.ASP_STATE_MAINTENANCE, MessageType.ASP_DOWN_ACK); this.aspFactoryImpl.write(aspDwnAck); // ASPSM should be given to all ASP's for (FastList.Node<Asp> n = this.aspFactoryImpl.aspList.head(), end = this.aspFactoryImpl.aspList.tail(); (n = n .getNext()) != end;) { AspImpl aspImpl = (AspImpl) n.getValue(); FSM aspPeerFSM = aspImpl.getPeerFSM(); if (aspPeerFSM == null) { logger.error(String.format("Received ASPDOWN=%s for ASP=%s. But peer FSM is null.", aspDown, this.aspFactoryImpl.getName())); return; } try { // Signal ASP about state change aspPeerFSM.setAttribute(FSM.ATTRIBUTE_MESSAGE, aspDown); aspPeerFSM.signal(TransitionState.ASP_DOWN); // Signal corresponding AS about ASP's state transition FSM asLocalFSM = ((AsImpl) aspImpl.getAs()).getLocalFSM(); asLocalFSM.setAttribute(AsImpl.ATTRIBUTE_ASP, aspImpl); asLocalFSM.signal(TransitionState.ASP_DOWN); } catch (UnknownTransitionException e) { logger.error(e.getMessage(), e); } } } else { // TODO : Should we silently drop ASPUP_ACK? // ASPUP_ACK is unexpected in this state ErrorCode errorCodeObj = this.aspFactoryImpl.parameterFactory.createErrorCode(ErrorCode.Unexpected_Message); sendError(null, errorCodeObj); } } protected void handleAspDownAck(ASPDownAck aspUpAck) { if (!this.aspFactoryImpl.started) { boolean stopAssociation = true; for (FastList.Node<Asp> n = this.aspFactoryImpl.aspList.head(), end = this.aspFactoryImpl.aspList.tail(); (n = n .getNext()) != end;) { AspImpl aspImpl = (AspImpl) n.getValue(); FSM fsm = aspImpl.getLocalFSM(); if (fsm == null) { logger.error(String.format("Received ASPDOWN_ACK=%s for ASP=%s. But local FSM is null.", aspUpAck, this.aspFactoryImpl.getName())); continue; } FSM fsmPeer = aspImpl.getPeerFSM(); // If ASP's peer FSM is not null and it's state is not DOWN, // don't stop the underlying Association if (fsmPeer != null) { AspState aspPeerState = AspState.getState(fsmPeer.getState().getName()); if (aspPeerState != AspState.DOWN) { stopAssociation = false; } } try { fsm.signal(TransitionState.ASP_DOWN_ACK); } catch (UnknownTransitionException e) { logger.error(e.getMessage(), e); } }// for if (!stopAssociation) { logger.error(String .format("Ungracefully stopping the underlying Association=%s for AspFactory=%s. Atleast one of the ASP's is Double Exchange and Peer ASP state is still not down", this.aspFactoryImpl.association.getName(), this.aspFactoryImpl.getName())); } try { if (this.aspFactoryImpl.aspFactoryStopTimer != null) { this.aspFactoryImpl.aspFactoryStopTimer.cancel(); } this.aspFactoryImpl.transportManagement.stopAssociation(this.aspFactoryImpl.association.getName()); } catch (Exception e) { logger.error( String.format("Exception while starting the Association=%s", this.aspFactoryImpl.association.getName()), e); } } else { logger.error(String.format("Received ASPDOWN_ACK=%s for ASPFactory=%s. But Aspfactory is down.", aspUpAck, this.aspFactoryImpl.getName())); } } /** * If we receive Heartbeat, we send back response * * @param hrtBeat */ public void handleHeartbeat(Heartbeat hrtBeat) { HeartbeatAck hrtBeatAck = (HeartbeatAck) this.aspFactoryImpl.messageFactory.createMessage( MessageClass.ASP_STATE_MAINTENANCE, MessageType.HEARTBEAT_ACK); hrtBeatAck.setHeartbeatData(hrtBeat.getHeartbeatData()); this.aspFactoryImpl.write(hrtBeatAck); } private boolean activate(AspImpl aspImpl) { // If its loadshare, we want to send ASP_ACTIVE else if its // Override and there is already one ACTIVE, we leave this one // as INACTIVE AsImpl asImpl = (AsImpl) aspImpl.getAs(); // By default we assume Traffic Mode is Loadshare if (asImpl.getTrafficModeType() == null || asImpl.getTrafficModeType().getMode() == TrafficModeType.Loadshare) { // Activate this ASP this.aspFactoryImpl.sendAspActive(asImpl); return true; } else if (asImpl.getTrafficModeType().getMode() == TrafficModeType.Override) { for (FastList.Node<Asp> n = asImpl.appServerProcs.head(), end = asImpl.appServerProcs.tail(); (n = n.getNext()) != end;) { AspImpl asptemp = (AspImpl) n.getValue(); FSM fsm = asptemp.getLocalFSM(); AspState aspState = AspState.getState(fsm.getState().getName()); if (!asptemp.getName().equals(aspImpl.getName()) && (aspState == AspState.ACTIVE_SENT || aspState == AspState.ACTIVE)) { return false; } }// for this.aspFactoryImpl.sendAspActive(asImpl); return true; } return false; } }