All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.javax.media.mscontrol.container.Link Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source
 * Copyright XXXX, Red Hat Middleware LLC, and individual contributors as indicated
 * by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License, v. 2.0.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

package org.mobicents.javax.media.mscontrol.container;

import jain.protocol.ip.mgcp.JainMgcpCommandEvent;
import jain.protocol.ip.mgcp.JainMgcpListener;
import jain.protocol.ip.mgcp.JainMgcpResponseEvent;
import jain.protocol.ip.mgcp.message.CreateConnection;
import jain.protocol.ip.mgcp.message.CreateConnectionResponse;
import jain.protocol.ip.mgcp.message.DeleteConnection;
import jain.protocol.ip.mgcp.message.ModifyConnection;
import jain.protocol.ip.mgcp.message.ModifyConnectionResponse;
import jain.protocol.ip.mgcp.message.parms.ConflictingParameterException;
import jain.protocol.ip.mgcp.message.parms.ConnectionIdentifier;
import jain.protocol.ip.mgcp.message.parms.ConnectionMode;
import jain.protocol.ip.mgcp.message.parms.GainControl;
import jain.protocol.ip.mgcp.message.parms.LocalOptionExtension;
import jain.protocol.ip.mgcp.message.parms.LocalOptionValue;
import jain.protocol.ip.mgcp.message.parms.ReturnCode;
import java.io.Serializable;
import java.util.concurrent.ScheduledExecutorService;
import javax.media.mscontrol.MediaErr;
import javax.media.mscontrol.join.Joinable.Direction;
import org.mobicents.fsm.FSM;
import org.mobicents.fsm.State;
import org.mobicents.fsm.StateEventHandler;
import org.mobicents.fsm.TransitionHandler;
import org.mobicents.fsm.UnknownTransitionException;
import org.mobicents.javax.media.mscontrol.mixer.MediaMixerImpl;
import org.mobicents.javax.media.mscontrol.networkconnection.NetworkConnectionImpl;
import javax.media.mscontrol.networkconnection.NetworkConnection;

/**
 *
 * @author kulikov
 */
public class Link extends FSM {
    
    private ContainerImpl[] containers = new ContainerImpl[2];
    protected ConnectionIdentifier[] connections = new ConnectionIdentifier[2];
    
    protected Direction direction;
    protected Serializable context;
    
    private LinkListener listener;
    
    public Link(ScheduledExecutorService scheduler, ContainerImpl a, ContainerImpl b) {
        super(scheduler);
        
        //remember link ends
        containers[0] = a;
        containers[1] = b;
        
        //define states
        this.createState("NULL").setOnEnter(new OnDeactivate(this));
        this.createState("CONNECTING");
        this.createState("CONNECTED");
        this.createState("ACTIVE").setOnEnter(new OnActivate(this));
        this.createState("DISCONNECTING").setOnEnter(new UnjoinRequest());
        this.createState("DISCONNECTED_1");
        this.createState("CANCELED");
        this.createState("STARTING");
        this.createState("STARTED_1");
        
        //define transitions
        
        //---------------------------------------------------------------------
        //             State NULL
        //---------------------------------------------------------------------
        this.createTransition("join", "NULL", "CONNECTING").setHandler(new JoinRequest());
        this.createTransition("success", "NULL", "CONNECTED"); 
        //---------------------------------------------------------------------
        //             State CONNECTING
        //---------------------------------------------------------------------
        this.createTransition("success", "CONNECTING", "CONNECTED").setHandler(new InitiateTransmission());
        this.createTransition("failure", "CONNECTING", "NULL");
        this.createTransition("release", "CONNECTING", "CANCELED");
        this.createTimeoutTransition("CONNECTING", "NULL", 5000);
        
        //---------------------------------------------------------------------
        //             State CONNECTED
        //---------------------------------------------------------------------
        this.createTransition("join", "CONNECTED", "STARTING").setHandler(new StartRequest());
        this.createTransition("release", "CONNECTED", "DISCONNECTING");
        this.createTransition("success", "CONNECTED", "CONNECTED"); 
        
        //---------------------------------------------------------------------
        //             State DISCONNECTING
        //---------------------------------------------------------------------
        this.createTransition("success", "DISCONNECTING", "DISCONNECTED_1");
        this.createTransition("failure", "DISCONNECTING", "DISCONNECTING");
        
        //---------------------------------------------------------------------
        //             State DISCONNECTED_1
        //---------------------------------------------------------------------
        this.createTransition("success", "DISCONNECTED_1", "NULL");

                
        //---------------------------------------------------------------------
        //             State CANCELED
        //---------------------------------------------------------------------
        this.createTransition("success", "CANCELED", "DISCONNECTING");
        this.createTransition("failure", "CANCELED", "NULL");
        this.createTimeoutTransition("CANCELED", "NULL", 5000);
        
        
        //---------------------------------------------------------------------
        //             State STARTING
        //---------------------------------------------------------------------
        this.createTransition("success", "STARTING", "STARTED_1");
        this.createTransition("failure", "STARTING", "DISCONNECTING");
        this.createTimeoutTransition("STARTING", "DISCONNECTING", 5000);
        
        //---------------------------------------------------------------------
        //             State STARTED_1
        //---------------------------------------------------------------------
        this.createTransition("success", "STARTED_1", "ACTIVE");
        this.createTransition("failure", "STARTED_1", "DISCONNECTING");
        this.createTimeoutTransition("STARTED_1", "DISCONNECTING", 5000);

        
        //---------------------------------------------------------------------
        //             State ACTIVE
        //---------------------------------------------------------------------
        this.createTransition("join", "ACTIVE", "STARTING").setHandler(new StartRequest());
        this.createTransition("release", "ACTIVE", "DISCONNECTING");
        
        setStart("NULL");
        setEnd("NULL");
    }
    
    public ContainerImpl getContainer(int i) {
        return containers[i];
    }
    
    public void setListener(LinkListener listener) {
        this.listener = listener;
    }
    
    public Direction getDirection() {
        return this.direction;
    }
    
    private ConnectionMode getMode0(ContainerImpl container) {
        if (container instanceof MediaMixerImpl) {
            return ConnectionMode.Confrnce;
        } else switch (direction) {
            case DUPLEX:
                return ConnectionMode.SendRecv;
            case RECV :
                return ConnectionMode.RecvOnly;
            default :
                return ConnectionMode.SendOnly;
        }
    }

    private ConnectionMode getMode1(ContainerImpl container) {
        if (container instanceof MediaMixerImpl) {
            return ConnectionMode.Confrnce;
        } else switch (direction) {
            case DUPLEX:
                return ConnectionMode.SendRecv;
            case RECV :
                return ConnectionMode.SendOnly;
            default :
                return ConnectionMode.RecvOnly;
        }
    }
    
    private Direction inversion(Direction direction) {
        switch (direction) {
            case SEND :
                return Direction.RECV;
            case RECV :
                return Direction.SEND;
            default :
                return Direction.DUPLEX;
        }
    }
    
    private class InitiateTransmission implements TransitionHandler {

        public void process(State state) {
            signalAsync("join");
        }
        
    }
    
    private class JoinRequest implements TransitionHandler {

        public void process(State state) {
            //wait if another transaction is in progress for endpoint containers[0]
            if (containers[0].endpoint.concreteNameExpectedSoon()) {
                try {
                    containers[0].endpoint.await();
                } catch (InterruptedException e) {
                    signalAsync("failure");
                    return;
                }
            }
            
            //block others who want to access containers[0]
            if (!containers[0].endpoint.hasConcreteName()) {
                containers[0].endpoint.expectingConcreteName();
            }

            
            //wait if another transaction is in progress for endpoint containers[1]
            if (containers[1].endpoint.concreteNameExpectedSoon()) {
                try {
                    containers[1].endpoint.await();
                } catch (InterruptedException e) {
                    signalAsync("failure");
                    return;
                }
            }
            
            //block others who want to access containers[0]
            if (!containers[1].endpoint.hasConcreteName()) {
                containers[1].endpoint.expectingConcreteName();
            }
            
            
            //prepare and send request for creating two connections.
            int txID = containers[0].session.getDriver().getNextTxID();

            CreateConnection crcx = new CreateConnection(this, containers[0].session.getCallID(),
                    containers[0].endpoint.getIdentifier(), ConnectionMode.Inactive);
            crcx.setTransactionHandle(txID);
            crcx.setNotifiedEntity(containers[0].session.getDriver().getCallAgent());

            try {
                crcx.setSecondEndpointIdentifier(containers[1].endpoint.getIdentifier());
            } catch (ConflictingParameterException e) {
            }

            containers[0].session.getDriver().attach(txID, new JoinResponse());
            containers[0].session.getDriver().send(crcx);
        }        
    }
    
    public class JoinResponse implements JainMgcpListener {

        public void processMgcpCommandEvent(JainMgcpCommandEvent evt) {
        }

        public void processMgcpResponseEvent(JainMgcpResponseEvent evt) {
            CreateConnectionResponse resp = (CreateConnectionResponse) evt;
            switch (resp.getReturnCode().getValue()) {
                case ReturnCode.TRANSACTION_BEING_EXECUTED:
                    return;
                case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:
                    containers[0].setConcreteName(resp.getSpecificEndpointIdentifier());
                    containers[1].setConcreteName(resp.getSecondEndpointIdentifier());
                    
                    connections[0] = resp.getConnectionIdentifier();
                    connections[1] = resp.getSecondConnectionIdentifier();
                                        
                    try {
                        signal("success");
                    } catch (Exception e) {
                    }
                    
                    break;
                default:
                    try {
                        signal("failure");
                    } catch (Exception e) {
                    }
            }
        }
        
    }

    private class StartRequest implements TransitionHandler {

        public void process(State state) {
            //first leg
            int txID = containers[0].session.getDriver().getNextTxID();

            ConnectionMode mode0 = getMode0(containers[0]);
            ModifyConnection mdcx = new ModifyConnection(this, containers[0].session.getCallID(),
                    containers[0].endpoint.getIdentifier(), connections[0]);
            mdcx.setMode(mode0);
            mdcx.setTransactionHandle(txID);

            if(containers[0] instanceof MediaMixerImpl && ((MediaMixerImpl)containers[0]).hasDtmfClamp())
            {
            	LocalOptionExtension currExtention=new LocalOptionExtension("x-dc","true");
            	mdcx.setLocalConnectionOptions(new LocalOptionValue[]{currExtention});
            }
            
            containers[0].session.getDriver().attach(txID, new StartResponse());
            containers[0].session.getDriver().send(mdcx);
            
            
            //second leg
            txID = containers[1].session.getDriver().getNextTxID();
            ConnectionMode mode1 = getMode1(containers[1]);

            mdcx = new ModifyConnection(this, containers[1].session.getCallID(),
                    containers[1].endpoint.getIdentifier(), connections[1]);
            mdcx.setMode(mode1);
            mdcx.setTransactionHandle(txID);

            if(containers[1] instanceof MediaMixerImpl && ((MediaMixerImpl)containers[1]).hasDtmfClamp())
            {
            	LocalOptionExtension currExtention=new LocalOptionExtension("x-dc","true");
            	mdcx.setLocalConnectionOptions(new LocalOptionValue[]{currExtention});
            }
            
            containers[0].session.getDriver().attach(txID, new StartResponse());
            containers[0].session.getDriver().send(mdcx);
        }
    }

    public class StartResponse implements JainMgcpListener {

        private MediaErr error;
        private String errorMsg;

        public StartResponse() {
        }

        public void processMgcpCommandEvent(JainMgcpCommandEvent evt) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        public void processMgcpResponseEvent(JainMgcpResponseEvent evt) {
            ModifyConnectionResponse resp = (ModifyConnectionResponse) evt;
            switch (resp.getReturnCode().getValue()) {
                case ReturnCode.TRANSACTION_BEING_EXECUTED:
                    return;
                case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:                    
                    try {
                        signal("success");
                    } catch (UnknownTransitionException e) {
                    }
                    break;
                default:
                    try {
                        signal("failure");
                    } catch (UnknownTransitionException e) {
                    }
                    break;
            }
        }
    }

    private class UnjoinRequest implements StateEventHandler {

        public void onEvent(State state) {
            //first leg
            if (connections[0] != null) {
                int txID = containers[0].getMediaSession().getDriver().getNextTxID();
                DeleteConnection req = new DeleteConnection(this,
                        containers[0].session.getCallID(), containers[0].endpoint.getIdentifier(),
                        connections[0]);
                req.setTransactionHandle(txID);

                containers[0].session.getDriver().attach(txID, new UnjoinResponse());
                containers[0].session.getDriver().send(req);
            } else {
                signalAsync("success");
            }

            //second leg
            if (connections[1] != null) {
                int txID = containers[1].getMediaSession().getDriver().getNextTxID();
                DeleteConnection req = new DeleteConnection(this,
                        containers[1].session.getCallID(), containers[1].endpoint.getIdentifier(),
                        connections[1]);
                req.setTransactionHandle(txID);

                containers[1].session.getDriver().attach(txID, new UnjoinResponse());
                containers[1].session.getDriver().send(req);
            } else {
                signalAsync("success");
            }
        }
    }

    private class UnjoinResponse implements JainMgcpListener {

        public void processMgcpCommandEvent(JainMgcpCommandEvent evt) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        public void processMgcpResponseEvent(JainMgcpResponseEvent evt) {
            try {
                signal("success");
            } catch (UnknownTransitionException e) {
            }
        }
    }
    
    public class OnActivate implements StateEventHandler {
        private Link link;
        
        public OnActivate(Link link) {
            this.link = link;
        }
        
        public void onEvent(State state) {
            if (listener != null)  listener.joined(link);
        }
    }
    
    public class OnDeactivate implements StateEventHandler {
        private Link link;
        
        public OnDeactivate(Link link) {
            this.link = link;
        }
        
        public void onEvent(State state) {
            if (listener != null)  listener.unjoined(link);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy