/* * 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.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.URL; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; 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.naming.NamingTree; import org.restcomm.media.control.mgcp.endpoint.BaseEndpointImpl; import org.restcomm.media.control.mgcp.endpoint.factory.VirtualEndpointInstaller; import org.restcomm.media.control.mgcp.endpoint.naming.NamingService; import org.restcomm.media.control.mgcp.resources.ResourcesPool; import org.restcomm.media.control.mgcp.tx.GlobalTransactionManager; import org.restcomm.media.control.mgcp.tx.Transaction; import org.restcomm.media.network.deprecated.UdpManager; import org.restcomm.media.scheduler.PriorityQueueScheduler; import org.restcomm.media.scheduler.Scheduler; import org.restcomm.media.spi.ControlProtocol; import org.restcomm.media.spi.Endpoint; import org.restcomm.media.spi.EndpointInstaller; import org.restcomm.media.spi.ServerManager; import org.restcomm.media.spi.listener.TooManyListenersException; /** * The MGCP access point. * * @author yulian oifa * @author Henrique Rosa (henrique.rosa@telestax.com) */ public class Controller implements MgcpListener, ServerManager { private final static String HOME_DIR = "MMS_HOME"; private static final Logger logger = Logger.getLogger(Controller.class); // Core elements private UdpManager udpInterface; private PriorityQueueScheduler mediaScheduler; private Scheduler taskScheduler; private ResourcesPool resourcesPool; // MGCP elements private MgcpProvider mgcpProvider; private NamingTree namingTree = new NamingTree(); private Configurator configurator; protected GlobalTransactionManager txManager; // Ported from media server private final NamingService namingService; private final ArrayList<EndpointInstaller> installers; private final Map<String, Endpoint> endpoints; protected int port; protected int poolSize; private boolean active; public Controller() { this.poolSize = 10; this.namingService = new NamingService(); this.installers = new ArrayList<EndpointInstaller>(5); this.endpoints = new ConcurrentHashMap<>(); this.active = false; } /** * Assigns UDP network interface. * * @param udpInterface the UDP interface . */ public void setUdpInterface(UdpManager udpInterface) { this.udpInterface = udpInterface; } /** * Assigns pool size. * * @param poolSize the size of Transaction pool. */ public void setPoolSize(int poolSize) { this.poolSize = poolSize; } /** * Assigns MGCP port number. * * @param port the port number. */ public void setPort(int port) { this.port = port; } /** * Sets job scheduler. * * @param scheduler the scheduler instance. */ public void setMediaScheduler(PriorityQueueScheduler scheduler) { this.mediaScheduler = scheduler; } public void setTaskScheduler(Scheduler taskScheduler) { this.taskScheduler = taskScheduler; } public void setResourcesPool(ResourcesPool resourcesPool) { this.resourcesPool = resourcesPool; } /** * Modifies path to the configuration file. * The file must be present in the folder <MMS_HOME>/conf/ directory * * @param fileName the name of the file. * @throws Exception */ public void setConfiguration(String url) throws Exception { try { if (url != null) { //getting the full path to the configuration file String home = getHomeDir(); if (home == null) { throw new IOException(HOME_DIR + " not set"); } String path = home + "/conf/" + url; FileInputStream stream = new FileInputStream(path); configurator = new Configurator(stream); } } catch (Exception e) { logger.error("Could not configure MGCP controller", e); throw e; } } public void setConfigurationByURL(URL url) throws Exception { try { configurator = new Configurator(url.openStream()); } catch (Exception e) { logger.error("Could not configure MGCP controller", e); throw e; } } /** * Gets the Media Server Home directory. * * @TODO This method duplicates the logic in org.mobicents.media.server.bootstrap.Main * * @return the path to the home directory. */ private static String getHomeDir() { String mmsHomeDir = System.getProperty(HOME_DIR); if (mmsHomeDir == null) { mmsHomeDir = System.getenv(HOME_DIR); }; return mmsHomeDir; } public void createProvider() { mgcpProvider = new MgcpProvider(udpInterface, port); } public void createGlobalTransactionManager() { txManager = new GlobalTransactionManager(taskScheduler, mediaScheduler.getClock()); txManager.setPoolSize(poolSize); txManager.setNamingService(namingTree); txManager.setMgcpProvider(mgcpProvider); } /** * Installs endpoints defined by specified installer. * * @param installer the endpoints installer */ public void addInstaller(EndpointInstaller installer) { ((VirtualEndpointInstaller)installer).setController(this); installers.add(installer); } /** * Uninstalls endpoint defined by specified endpoint installer. * * @param installer the endpoints installer. */ public void removeInstaller(EndpointInstaller installer) { installers.remove(installer); installer.uninstall(); } /** * Starts the controller. */ public void start() { if (logger.isInfoEnabled()) { logger.info("Starting MGCP provider"); } createProvider(); mgcpProvider.activate(); try { mgcpProvider.addListener(this); } catch (TooManyListenersException e) { logger.error(e); } //initialize transaction subsystem createGlobalTransactionManager(); for (EndpointInstaller installer : installers) { installer.install(); } if (logger.isInfoEnabled()) { logger.info("Controller started"); } } /** * Stops the controller. */ public void stop() { if(mgcpProvider != null) { mgcpProvider.shutdown(); } if (logger.isInfoEnabled()) { logger.info("Controller stopped"); } } @Override public void process(MgcpEvent event) { // Find transaction int transactionId = event.getMessage().getTxID(); Transaction tx = findTransaction(event.getEventID(), transactionId, (InetSocketAddress) event.getAddress()); // Process transaction if (tx != null) { tx.process(event); } else { logger.warn("Could not find MGCP transaction id=" + transactionId); } } private Transaction findTransaction(int eventId, int transactionId, InetSocketAddress remoteAddress) { if (eventId == MgcpEvent.REQUEST) { return txManager.allocateNew(remoteAddress, transactionId); } else { // TODO find by transaction number - hrosa return txManager.find(remoteAddress, transactionId); } } @Override public void onStarted(Endpoint endpoint,EndpointInstaller installer) { try { MgcpEndpoint mgcpEndpoint = configurator.activate(endpoint, mgcpProvider, udpInterface.getLocalBindAddress(), port); mgcpEndpoint.setMgcpListener(this); namingTree.register(mgcpEndpoint,installer); logger.info("Endpoint restarted: " + endpoint.getLocalName()); } catch (Exception e) { logger.error("Could not register endpoint: " + endpoint.getLocalName()); } } @Override public void onStopped(Endpoint endpoint) { // TODO does nothing! } /** * Installs the specified endpoint. * * @param endpoint the endpoint to installed. */ public void install(Endpoint endpoint, EndpointInstaller installer) { // check endpoint first if (endpoint == null) { logger.error("Unknown endpoint"); return; } // The endpoint implementation must extend BaseEndpointImpl class BaseEndpointImpl baseEndpoint = null; try { baseEndpoint = (BaseEndpointImpl) endpoint; } catch (ClassCastException e) { logger.error("Unsupported endpoint implementation " + endpoint.getLocalName()); return; } // assign scheduler to the endpoint baseEndpoint.setScheduler(mediaScheduler); baseEndpoint.setResourcesPool(resourcesPool); if (logger.isInfoEnabled()) { logger.info("Installing " + endpoint.getLocalName()); } // starting endpoint try { endpoint.start(); } catch (Exception e) { logger.error("Couldn't start endpoint " + endpoint.getLocalName(), e); return; } // register endpoint with naming service try { namingService.register(endpoint); } catch (Exception e) { endpoint.stop(); logger.error("Could not register endpoint " + endpoint.getLocalName(), e); } // register endpoint localy endpoints.put(endpoint.getLocalName(), endpoint); // send notification to manager onStarted(endpoint, installer); } /** * Uninstalls the endpoint. * * @param name the local name of the endpoint to be uninstalled */ public void uninstall(String name) { // unregister locally Endpoint endpoint = endpoints.remove(name); onStopped(endpoint); try { // TODO: lookup irrespective of endpoint usage endpoint = namingService.lookup(name, true); if (endpoint != null) { endpoint.stop(); namingService.unregister(endpoint); } } catch (Exception e) { logger.error(e); } } @Override public void activate() throws IllegalStateException { if(!this.active) { this.active = true; start(); } else { throw new IllegalStateException("Controller is already active."); } } @Override public void deactivate() throws IllegalStateException { if (this.active) { this.active = false; stop(); } else { throw new IllegalStateException("Controller is already inactive."); } } @Override public boolean isActive() { return this.active; } @Override public ControlProtocol getControlProtocol() { return ControlProtocol.MGPC; } }