/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. 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.restcomm.media.control.mgcp.controller;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
import org.restcomm.media.concurrent.ConcurrentCyclicFIFO;
import org.restcomm.media.concurrent.ConcurrentMap;
import org.restcomm.media.control.mgcp.MgcpEvent;
import org.restcomm.media.control.mgcp.MgcpListener;
import org.restcomm.media.control.mgcp.MgcpProvider;
import org.restcomm.media.control.mgcp.controller.signal.MgcpPackage;
import org.restcomm.media.spi.Connection;
import org.restcomm.media.spi.ConnectionType;
import org.restcomm.media.spi.Endpoint;
import org.restcomm.media.spi.ResourceUnavailableException;
import org.restcomm.media.spi.TooManyConnectionsException;
import org.restcomm.media.spi.utils.Text;
/**
* Represents media endpoint on MGCP controller side.
*
* @author yulian oifa
*/
public class MgcpEndpoint {
public static AtomicInteger txID = new AtomicInteger(1);
//The size of the connection's pool
private final static int N = 15;
public final static int STATE_LOCKED = 1;
public final static int STATE_FREE = 2;
public final static int STATE_BUSY = 3;
//native endpoint
private Endpoint endpoint;
protected Text fullName;
//the state of this activity
private AtomicInteger state = new AtomicInteger(STATE_FREE);
//Request executor associated with endpoint
Request request;
//pool of connection activities, limited to 15
private ConcurrentCyclicFIFO<MgcpConnection> connections = new ConcurrentCyclicFIFO<MgcpConnection>();
//list of active connections
private ConcurrentMap<MgcpConnection> activeConnections=new ConcurrentMap<MgcpConnection>();
private Iterator<Integer> keyIterator;
protected MgcpProvider mgcpProvider;
private MgcpListener listener;
private MgcpEndpointStateListener stateListener;
/**
* Constructs new endpoint activity.
*
* @param endpoint native endpoint.
*/
public MgcpEndpoint(Endpoint endpoint, MgcpProvider mgcpProvider, String domainName, int port, Collection<MgcpPackage> packages) {
this.endpoint = endpoint;
this.mgcpProvider = mgcpProvider;
this.fullName = new Text(endpoint.getLocalName() + "@" + domainName + ":" + port);
//create request executor
request=new Request(this, packages);
for (int i = 0; i < N; i++) {
connections.offer(new MgcpConnection());
}
}
/**
* Gets the MGCP name of this activity.
*
* @return the name of activity
*/
public String getName() {
return endpoint.getLocalName();
}
public Text getFullName() {
return fullName;
}
/**
* Provides the access to the native endpoint.
*
* @return the native endpoint.
*/
public Endpoint getEndpoint() {
return endpoint;
}
public void setMgcpListener(MgcpListener listener) {
this.listener = listener;
}
public void setMgcpEndpointStateListener(MgcpEndpointStateListener listener) {
this.stateListener = listener;
}
/**
* Gets the current state of this activity.
*
* @return the state indicator.
*/
public int getState() {
return state.get();
}
/**
* Gets the access to the request executor.
*
* @return request executor.
*/
public Request getRequest() {
return this.request;
}
/**
* Exclusively locks this activity.
*/
public void lock() {
this.state.set(STATE_LOCKED);
}
/**
* Unlocks this activity if it is not busy
*/
public void share() {
if (this.state.get() == STATE_LOCKED) {
this.state.set(STATE_FREE);
if(this.stateListener!=null)
this.stateListener.onFreed(this);
}
}
/**
* Creates new RTP connection.
*
* Creation of connection makes endpoint activity busy.
*
* @return connection activity.
*/
public MgcpConnection createConnection(MgcpCall call, ConnectionType type,boolean isLocal) throws TooManyConnectionsException, ResourceUnavailableException {
//create connection
Connection connection = endpoint.createConnection(type,isLocal);
//wrap connection with relative activity
MgcpConnection mgcpConnection = connections.poll();
if(mgcpConnection==null)
mgcpConnection=new MgcpConnection();
mgcpConnection.wrap(this, call, connection);
//put connection activity into active list
activeConnections.put(mgcpConnection.id,mgcpConnection);
//change state to BUSY what means that this connection has at least one
//connection
this.state.set(STATE_BUSY);
return mgcpConnection;
}
/**
* Deletes connection.
*
* @param id the identifier of the relative connection activity.
*/
public void deleteConnection(Integer id) {
MgcpConnection mgcpConnection=activeConnections.remove(id);
//connection not found?
if (mgcpConnection == null) {
//TODO: throw exception
return;
}
//remove activity from list and terminate
mgcpConnection.release();
endpoint.deleteConnection(mgcpConnection.connection);
//return object to pool
connections.offer(mgcpConnection);
//update state
if (activeConnections.isEmpty()) {
int oldValue=this.state.getAndSet(STATE_FREE);
if(oldValue!=STATE_FREE && this.stateListener!=null)
this.stateListener.onFreed(this);
}
this.request.cancel();
}
public void deleteAllConnections() {
keyIterator = activeConnections.keysIterator();
while(keyIterator.hasNext())
connections.offer(activeConnections.remove(keyIterator.next()));
endpoint.deleteAllConnections();
int oldValue=this.state.getAndSet(STATE_FREE);
if(oldValue!=STATE_FREE && this.stateListener!=null)
this.stateListener.onFreed(this);
this.request.cancel();
}
public MgcpConnection getConnection(Integer connectionID) {
return activeConnections.get(connectionID);
}
protected void send(MgcpEvent message, SocketAddress address) {
listener.process(message);
}
/**
* Asks mgcp connection object from pool.
*
* @param call the call to which connection belongs
* @return MgcpConnection object.
*/
protected MgcpConnection poll(MgcpCall call) {
//take first from pool and put into list of active
MgcpConnection mgcpConnection = connections.poll();
if(mgcpConnection==null)
mgcpConnection=new MgcpConnection();
activeConnections.put(mgcpConnection.id,mgcpConnection);
//assign call
mgcpConnection.setCall(call);
return mgcpConnection;
}
/**
* Reclaims used connection object.
*
* @param connection the object to be reclaimed
*/
protected void offer(MgcpConnection mgcpConnection) {
//remove from active list
activeConnections.remove(mgcpConnection.id);
//reclaim
mgcpConnection.release();
endpoint.deleteConnection(mgcpConnection.connection);
//back to pool
connections.offer(mgcpConnection);
if (activeConnections.isEmpty()) {
int oldValue=this.state.getAndSet(STATE_FREE);
if(oldValue!=STATE_FREE && this.stateListener!=null)
this.stateListener.onFreed(this);
}
}
public void configure(boolean isALaw)
{
endpoint.configure(isALaw);
}
}