package org.red5.server; /* * RED5 Open Source Flash Server - http://www.osflash.org/red5 * * Copyright (c) 2006-2009 by respective authors (see below). All rights reserved. * * This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import org.red5.server.api.IConnection; import org.red5.server.api.IGlobalScope; import org.red5.server.api.IScope; import org.red5.server.api.IServer; import org.red5.server.api.listeners.IConnectionListener; import org.red5.server.api.listeners.IScopeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.style.ToStringCreator; /** * Red5 server core class implementation. */ public class Server implements IServer, ApplicationContextAware { // Initialize Logging protected static Logger log = LoggerFactory.getLogger(Server.class); /** * List of global scopes */ protected ConcurrentMap<String, IGlobalScope> globals = new ConcurrentHashMap<String, IGlobalScope>(); /** * Mappings */ protected ConcurrentMap<String, String> mapping = new ConcurrentHashMap<String, String>(); /** * Spring application context */ protected ApplicationContext applicationContext; /** * Constant for slash */ protected static final String SLASH = "/"; /** * Constant for empty string */ protected static final String EMPTY = ""; public Set<IScopeListener> scopeListeners = new CopyOnWriteArraySet<IScopeListener>(); public Set<IConnectionListener> connectionListeners = new CopyOnWriteArraySet<IConnectionListener>(); /** * Setter for Spring application context * * @param applicationContext Application context */ public void setApplicationContext(ApplicationContext applicationContext) { log.debug("Setting application context"); this.applicationContext = applicationContext; } /** * Return scope key. Scope key consists of host name concatenated with * context path by slash symbol * * @param hostName Host name * @param contextPath Context path * @return Scope key as string */ protected String getKey(String hostName, String contextPath) { if (hostName == null) { hostName = EMPTY; } if (contextPath == null) { contextPath = EMPTY; } return hostName + SLASH + contextPath; } /** * Does global scope lookup for host name and context path * * @param hostName Host name * @param contextPath Context path * @return Global scope */ public IGlobalScope lookupGlobal(String hostName, String contextPath) { log.debug("{}", this); // Init mappings key String key = getKey(hostName, contextPath); // If context path contains slashes get complex key and look up for it // in mappings while (contextPath.indexOf(SLASH) != -1) { key = getKey(hostName, contextPath); log.debug("Check: {}", key); String globalName = mapping.get(key); if (globalName != null) { return getGlobal(globalName); } final int slashIndex = contextPath.lastIndexOf(SLASH); // Context path is substring from the beginning and till last slash // index contextPath = contextPath.substring(0, slashIndex); } // Get global scope key key = getKey(hostName, contextPath); log.debug("Check host and path: {}", key); // Look up for global scope switching keys if still not found String globalName = mapping.get(key); if (globalName != null) { return getGlobal(globalName); } key = getKey(EMPTY, contextPath); log.debug("Check wildcard host with path: {}", key); globalName = mapping.get(key); if (globalName != null) { return getGlobal(globalName); } key = getKey(hostName, EMPTY); log.debug("Check host with no path: {}", key); globalName = mapping.get(key); if (globalName != null) { return getGlobal(globalName); } key = getKey(EMPTY, EMPTY); log.debug("Check default host, default path: {}", key); return getGlobal(mapping.get(key)); } /** * Return global scope by name * * @param name Global scope name * @return Global scope */ public IGlobalScope getGlobal(String name) { if (name == null) { return null; } return globals.get(name); } /** * Register global scope * * @param scope Global scope to register */ public void registerGlobal(IGlobalScope scope) { log.info("Registering global scope: {}", scope.getName()); globals.put(scope.getName(), scope); } /** * Map key (host + / + context path) and global scope name * * @param hostName Host name * @param contextPath Context path * @param globalName Global scope name * @return true if mapping was added, false if already exist */ public boolean addMapping(String hostName, String contextPath, String globalName) { log.info("Add mapping global: {} host: {} context: {}", new Object[]{globalName, hostName, contextPath}); final String key = getKey(hostName, contextPath); log.debug("Add mapping: {} => {}", key, globalName); return (mapping.putIfAbsent(key, globalName) == null); } /** * Remove mapping with given key * * @param hostName Host name * @param contextPath Context path * @return true if mapping was removed, false if key doesn't exist */ public boolean removeMapping(String hostName, String contextPath) { log.info("Remove mapping host: {} context: {}", hostName, contextPath); final String key = getKey(hostName, contextPath); log.debug("Remove mapping: {}", key); return (mapping.remove(key) != null); } /** * Return mapping * * @return Map of "scope key / scope name" pairs */ public Map<String, String> getMappingTable() { return mapping; } /** * Return global scope names set iterator * * @return Iterator */ public Iterator<String> getGlobalNames() { return globals.keySet().iterator(); } /** * Return global scopes set iterator * * @return Iterator */ public Iterator<IGlobalScope> getGlobalScopes() { return globals.values().iterator(); } /** * String representation of server * * @return String representation of server */ @Override public String toString() { return new ToStringCreator(this).append(mapping).toString(); } /** {@inheritDoc} */ public void addListener(IScopeListener listener) { scopeListeners.add(listener); } /** {@inheritDoc} */ public void addListener(IConnectionListener listener) { connectionListeners.add(listener); } /** {@inheritDoc} */ public void removeListener(IScopeListener listener) { scopeListeners.remove(listener); } /** {@inheritDoc} */ public void removeListener(IConnectionListener listener) { connectionListeners.remove(listener); } /** * Notify listeners about a newly created scope. * * @param scope * the scope that was created */ protected void notifyScopeCreated(IScope scope) { for (IScopeListener listener : scopeListeners) { listener.notifyScopeCreated(scope); } } /** * Notify listeners that a scope was removed. * * @param scope * the scope that was removed */ protected void notifyScopeRemoved(IScope scope) { for (IScopeListener listener : scopeListeners) { listener.notifyScopeRemoved(scope); } } /** * Notify listeners that a new connection was established. * * @param conn * the new connection */ protected void notifyConnected(IConnection conn) { for (IConnectionListener listener : connectionListeners) { listener.notifyConnected(conn); } } /** * Notify listeners that a connection was disconnected. * * @param conn * the disconnected connection */ protected void notifyDisconnected(IConnection conn) { for (IConnectionListener listener : connectionListeners) { listener.notifyDisconnected(conn); } } }