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

org.restcomm.connect.mrb.MediaResourceBrokerGeneric Maven / Gradle / Ivy

/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2013, Telestax Inc and individual contributors
 * by the @authors tag.
 *
 * 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.restcomm.connect.mrb;

import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.configuration.Configuration;
import org.joda.time.DateTime;
import org.mobicents.protocols.mgcp.stack.JainMgcpStackImpl;
import org.restcomm.connect.commons.dao.Sid;
import org.restcomm.connect.commons.loader.ObjectFactory;
import org.restcomm.connect.dao.CallDetailRecordsDao;
import org.restcomm.connect.dao.ConferenceDetailRecordsDao;
import org.restcomm.connect.dao.DaoManager;
import org.restcomm.connect.dao.MediaServersDao;
import org.restcomm.connect.dao.entities.CallDetailRecord;
import org.restcomm.connect.dao.entities.ConferenceDetailRecord;
import org.restcomm.connect.dao.entities.ConferenceDetailRecordFilter;
import org.restcomm.connect.dao.entities.MediaServerEntity;
import org.restcomm.connect.mgcp.MediaResourceBrokerResponse;
import org.restcomm.connect.mgcp.PowerOnMediaGateway;
import org.restcomm.connect.mrb.api.GetConferenceMediaResourceController;
import org.restcomm.connect.mrb.api.GetMediaGateway;
import org.restcomm.connect.mrb.api.MediaGatewayForConference;
import org.restcomm.connect.mrb.api.StartMediaResourceBroker;

import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import jain.protocol.ip.mgcp.CreateProviderException;
import jain.protocol.ip.mgcp.JainMgcpProvider;
import jain.protocol.ip.mgcp.JainMgcpStack;

public class MediaResourceBrokerGeneric extends UntypedActor{

    private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);

    private Configuration configuration;
    private DaoManager storage;
    private ClassLoader loader;
    private ActorRef localMediaGateway;
    private String localMsId;
    private Map mediaGatewayMap;

    private JainMgcpStack mgcpStack;
    private JainMgcpProvider mgcpProvider;

    private MediaServerEntity localMediaServerEntity;

    public MediaResourceBrokerGeneric(){
        super();
    }

    @Override
    public void onReceive(Object message) throws Exception {
        final Class klass = message.getClass();
        final ActorRef sender = sender();
        ActorRef self = self();

        if (logger.isInfoEnabled()) {
            logger.info(" ********** MediaResourceBroker " + self().path() + " Processing Message: " + klass.getName());
        }
        if (StartMediaResourceBroker.class.equals(klass)) {
            onStartMediaResourceBroker((StartMediaResourceBroker)message, self, sender);
        } else if (GetMediaGateway.class.equals(klass)) {
            onGetMediaGateway((GetMediaGateway) message, self, sender);
        } else if (GetConferenceMediaResourceController.class.equals(klass)){
            sender.tell(new MediaResourceBrokerResponse(getConferenceMediaResourceController()), self);
        }
    }

    private void onStartMediaResourceBroker(StartMediaResourceBroker message, ActorRef self, ActorRef sender) throws UnknownHostException{
        this.configuration = message.configuration();
        this.storage = message.storage();
        this.loader = message.loader();

        localMediaServerEntity = uploadLocalMediaServersInDataBase();
        bindMGCPStack(localMediaServerEntity.getLocalIpAddress(), localMediaServerEntity.getLocalPort());
        this.localMediaGateway = turnOnMediaGateway(localMediaServerEntity);
        this.mediaGatewayMap = new HashMap();
        mediaGatewayMap.put(localMediaServerEntity.getMsId()+"", localMediaGateway);
    }

    private void bindMGCPStack(String ip, int port) throws UnknownHostException {
        mgcpStack = new JainMgcpStackImpl(InetAddress.getByName(ip), port);
        try {
            mgcpProvider = mgcpStack.createProvider();
        } catch (final CreateProviderException exception) {
            logger.error(exception, "Could not create a JAIN MGCP provider.");
        }
    }

    private ActorRef turnOnMediaGateway(MediaServerEntity mediaServerEntity) throws UnknownHostException {

        if (logger.isDebugEnabled()) {
            String mgcpServer = configuration.getString("mgcp-server[@class]");
            logger.debug("Will switch on media gateway: "+mgcpServer);
        }

        final ActorRef gateway = getContext().system().actorOf(new Props(new UntypedActorFactory() {
            private static final long serialVersionUID = 1L;

            @Override
            public UntypedActor create() throws Exception {
                final String classpath = configuration.getString("mgcp-server[@class]");
                return (UntypedActor) new ObjectFactory(loader).getObjectInstance(classpath);
            }
        }));

        final PowerOnMediaGateway.Builder builder = PowerOnMediaGateway.builder();
        builder.setName(configuration.getString("mgcp-server[@name]"));

        if(logger.isInfoEnabled())
            logger.info("turnOnMediaGateway local ip: "+localMediaServerEntity.getLocalIpAddress()+" local port: "+localMediaServerEntity.getLocalPort()
            +" remote ip: "+mediaServerEntity.getRemoteIpAddress()+" remote port: "+mediaServerEntity.getRemotePort());

        builder.setLocalIP(InetAddress.getByName(localMediaServerEntity.getLocalIpAddress()));
        builder.setLocalPort(localMediaServerEntity.getLocalPort());
        builder.setRemoteIP(InetAddress.getByName(mediaServerEntity.getRemoteIpAddress()));
        builder.setRemotePort(mediaServerEntity.getRemotePort());

        if (mediaServerEntity.getExternalAddress() != null) {
            builder.setExternalIP(InetAddress.getByName(mediaServerEntity.getExternalAddress()));
            builder.setUseNat(true);
        } else {
            builder.setUseNat(false);
        }

        builder.setTimeout(Long.parseLong(mediaServerEntity.getResponseTimeout()));
        builder.setStack(mgcpStack);
        builder.setProvider(mgcpProvider);

        final PowerOnMediaGateway powerOn = builder.build();
        gateway.tell(powerOn, null);

        return gateway;
    }

    private ActorRef getConferenceMediaResourceController() {
        return getContext().system().actorOf(new Props(new UntypedActorFactory() {
            private static final long serialVersionUID = 1L;

            @Override
            public UntypedActor create() throws Exception {
                return new ConferenceMediaResourceControllerGeneric(localMsId, localMediaGateway, configuration, storage, self());
            }
        }));
    }

    private void onGetMediaGateway(GetMediaGateway message, ActorRef self, ActorRef sender) throws Exception {
        final String conferenceName = message.conferenceName();
        final Sid callSid = message.callSid();

        // if its not request for conference return home media-gateway (media-server associated with this RC instance)
        if(conferenceName == null){
            updateMSIdinCallDetailRecord(localMsId, callSid);
            sender.tell(new MediaResourceBrokerResponse(localMediaGateway), self);
        }else{
            final Sid conferenceSid = addConferenceDetailRecord(conferenceName, callSid);
            sender.tell(new MediaResourceBrokerResponse(new MediaGatewayForConference(conferenceSid, localMediaGateway, null)), self);
        }
    }

    private void updateMSIdinCallDetailRecord(final String msId, final Sid callSid){
        if(callSid == null){
            logger.error("Call Id is not specisfied");
        }else{
            CallDetailRecordsDao dao = storage.getCallDetailRecordsDao();
            CallDetailRecord cdr = dao.getCallDetailRecord(callSid);
            if(cdr != null){
                cdr = cdr.setMsId(msId);
                dao.updateCallDetailRecord(cdr);
            }else{
                logger.warning("provided call id did not found");
            }
        }

    }

    private Sid addConferenceDetailRecord(final String conferenceName, final Sid callSid) throws Exception {
       Sid sid = null;
        if(conferenceName == null ){
            logger.error("provided conference name is null, this can lead to problems in future of this call");
        }else{
            CallDetailRecordsDao callDao = storage.getCallDetailRecordsDao();
            CallDetailRecord callRecord = callDao.getCallDetailRecord(callSid);
            if(callRecord != null){
                ConferenceDetailRecordsDao dao = storage.getConferenceDetailRecordsDao();

                // check if a conference with same name/account is running.
                final String[] cnfNameAndAccount = conferenceName.split(":");
                final String accountSid = cnfNameAndAccount[0];
                final String friendlyName = cnfNameAndAccount[1];

                ConferenceDetailRecordFilter filter = new ConferenceDetailRecordFilter(accountSid, "RUNNING%", null, null, friendlyName, 1, 0);
                List records = dao.getConferenceDetailRecords(filter);

                if(records != null && records.size()>0){
                    final ConferenceDetailRecord cdr = records.get(0);
                    sid = cdr.getSid();
                    if(logger.isInfoEnabled())
                        logger.info("A conference with same name is running. According to database record. given SID is: "+sid);
                }else{
                    // this is first record of this conference on all instances of
                    final ConferenceDetailRecord.Builder conferenceBuilder = ConferenceDetailRecord.builder();
                    sid = Sid.generate(Sid.Type.CONFERENCE);
                    conferenceBuilder.setSid(sid);
                    conferenceBuilder.setDateCreated(DateTime.now());

                    conferenceBuilder.setAccountSid(new Sid(accountSid));
                    conferenceBuilder.setStatus("RUNNING_INITIALIZING");
                    conferenceBuilder.setApiVersion(callRecord.getApiVersion());
                    final StringBuilder UriBuffer = new StringBuilder();
                    UriBuffer.append("/").append(callRecord.getApiVersion()).append("/Accounts/").append(accountSid).append("/Conferences/");
                    UriBuffer.append(sid);
                    final URI uri = URI.create(UriBuffer.toString());
                    conferenceBuilder.setUri(uri);
                    conferenceBuilder.setFriendlyName(friendlyName);
                    conferenceBuilder.setMasterMsId(localMsId);

                    ConferenceDetailRecord cdr = conferenceBuilder.build();
                    dao.addConferenceDetailRecord(cdr);

                    //getting CDR again as it is a conditional insert(select if exists or insert) to handle concurrency (incase another participant joins on another instance at very same time)
                    cdr = dao.getConferenceDetailRecords(filter).get(0);
                    sid = cdr.getSid();
                    if(logger.isInfoEnabled())
                        logger.info("addConferenceDetailRecord: SID: "+sid+" NAME: "+conferenceName);
                }
            }else{
                logger.error("call record is null");
            }
        }
        return sid;
    }

    private MediaServerEntity uploadLocalMediaServersInDataBase() {
        String localIpAdressForMediaGateway = configuration.getString("mgcp-server.local-address");
        int localPortAdressForMediaGateway = Integer.parseInt(configuration.getString("mgcp-server.local-port"));
        String remoteIpAddress = configuration.getString("mgcp-server.remote-address");
        int remotePort = Integer.parseInt(configuration.getString("mgcp-server.remote-port"));
        String responseTimeout = configuration.getString("mgcp-server.response-timeout");
        String externalAddress = configuration.getString("mgcp-server.external-address");

        final MediaServerEntity.Builder builder = MediaServerEntity.builder();
        builder.setLocalIpAddress(localIpAdressForMediaGateway);
        builder.setLocalPort(localPortAdressForMediaGateway);
        builder.setRemoteIpAddress(remoteIpAddress);
        builder.setRemotePort(remotePort);
        builder.setResponseTimeout(responseTimeout);
        builder.setExternalAddress(externalAddress);

        MediaServersDao dao = storage.getMediaServersDao();
        MediaServerEntity mediaServerEntity = builder.build();
        final List existingMediaServersForSameIP = dao.getMediaServerEntityByIP(remoteIpAddress);

        if(existingMediaServersForSameIP == null || existingMediaServersForSameIP.size()==0){
            dao.addMediaServer(mediaServerEntity);
            final List newMediaServerEntity = dao.getMediaServerEntityByIP(remoteIpAddress);
            this.localMsId = newMediaServerEntity.get(0).getMsId()+"";
        }else{
            this.localMsId = existingMediaServersForSameIP.get(0).getMsId()+"";
            mediaServerEntity = mediaServerEntity.setMsId(Integer.parseInt(this.localMsId));
            dao.updateMediaServer(mediaServerEntity);
            if(existingMediaServersForSameIP.size()>1)
                logger.error("in DB: there are multiple media servers registered for same IP address");
        }
        return mediaServerEntity;
    }

    @Override
    public void postStop() {
        // Cleanup resources
        cleanup();

        // Terminate actor
        getContext().stop(self());
    }

    protected void cleanup() {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy