/*
* 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.endpoint.naming;
import java.util.ArrayList;
import org.restcomm.media.control.mgcp.endpoint.BaseEndpointImpl;
import org.restcomm.media.control.mgcp.endpoint.exception.UnknownEndpointException;
import org.restcomm.media.spi.Endpoint;
import org.restcomm.media.spi.EndpointState;
import org.restcomm.media.spi.ResourceUnavailableException;
/**
* Implements naming server for media server.
*
* The name of the endpoint has heirarhical structure and endpoints are
* stored in tree structure. The service allows register and unregister
* endpoints with this service and as well allows to lookup the endpoint using
* the name of endpoint in two styles: exclusive and non-exclusive
*
* The exclusive lookup is used when endpoint name is wildcarded and returned
* endpoint is not visible for future lookups. The user must explicit share
* the endpoint after usage to make visible again.
*
* @author yulian oifa
*/
public class NamingService {
//service entry point
private Node root = new Node(new String[]{""}, 0);
private final Object LOCK = new Object();
/**
* Registers endpoint.
* After registration the endpoint can be looked up using its name.
*
* @param endpoint the endpoint to be registered.
*/
public void register(Endpoint endpoint) {
synchronized(LOCK) {
EndpointName name = new EndpointName(endpoint.getLocalName());
Node node = getNode(name.getCategory(), true);
node.queue.add(endpoint);
}
}
/**
* Unregisters endpoint.
* Unregistered endpoint is not longer visible for lookups.
*
* @param endpoint the endpoint to be unregistered.
*/
public void unregister(Endpoint endpoint) {
synchronized(LOCK) {
EndpointName name = new EndpointName(endpoint.getLocalName());
Node node = getNode(name.getCategory(), true);
node.queue.remove(endpoint);
}
}
/**
* Gets the endpoint with specified name.
*
* @param name the name of the endpoint to lookup
* @param exclusive the mode of search. if true the returned endpoint won't
* be visible for future lookups unit it will be explicitly shared. *
* @return the endpoint with specified name
* @throws ResourceUnavailableException if name is wildcarded and no free endpoints now
* @throws UnknownEndpointException the name of endpoint is unknown.
*/
public Endpoint lookup(String name, boolean exclusive) throws ResourceUnavailableException, UnknownEndpointException {
synchronized(LOCK) {
EndpointName endpointName = new EndpointName(name);
Node node = getNode(endpointName.getCategory(), false);
if (node == null) return null;
return node.lookup(endpointName.getID(), exclusive);
}
}
/**
* Makes endpoint visible for lookups if it was looked up exclusively before
* @param endpoint the endpoint to share.
*/
public void share(Endpoint endpoint) {
synchronized(LOCK) {
((BaseEndpointImpl) endpoint).setState(EndpointState.READY);
}
}
/**
* Gets the node holding the endpoint.
*
* @param fqn1 the fully qualified name of the node
* @param allowNew allows create node if it is not exist yet
* @return the node with specified name.
*/
private Node getNode(String fqn1, boolean allowNew) {
String[] fqn = fqn1.split("/");
return root.getNode(fqn, 1, allowNew);
}
}
class Node {
private String name;
private ArrayList<Node> childs = new ArrayList<Node>();
protected ArrayList<Endpoint> queue = new ArrayList<Endpoint>();
protected Node(String[] fqn, int k) {
this.name = fqn[k];
}
protected Node getNode(String[] fqn, int k, boolean allowNew) {
if (k == fqn.length) return this;
for (int i = 0; i < childs.size(); i++) {
if (childs.get(i).name.equals(fqn[k])) {
if (k == fqn.length - 1) {
return childs.get(i);
} else {
return childs.get(i).getNode(fqn, k + 1, allowNew);
}
}
}
if (allowNew) {
Node node = new Node(fqn, k);
childs.add(node);
return node.getNode(fqn, k + 1, allowNew);
}
return null;
}
protected Endpoint lookup(String id, boolean exclusive) throws ResourceUnavailableException, UnknownEndpointException {
return id.equals("$") ? lookupAny(exclusive) : lookupConcrete(id);
}
private Endpoint lookupConcrete(String id) throws UnknownEndpointException {
for (int i = 0; i < queue.size(); i++) {
if (queue.get(i).getLocalName().endsWith(id)) return queue.get(i);
}
throw new UnknownEndpointException();
}
private Endpoint lookupAny(boolean exclusive) throws ResourceUnavailableException {
for (int i = 0; i < queue.size(); i++) {
if (queue.get(i).getState() == EndpointState.READY) {
Endpoint endpoint = queue.remove(i);
queue.add(endpoint);
return endpoint;
}
}
throw new ResourceUnavailableException();
}
}