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.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; import javax.management.ObjectName; import org.red5.server.api.IBWControllable; import org.red5.server.api.IBandwidthConfigure; import org.red5.server.api.IClient; import org.red5.server.api.IConnection; import org.red5.server.api.IScope; import org.red5.server.api.persistence.IPersistable; import org.red5.server.jmx.JMXAgent; import org.red5.server.jmx.JMXFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Client is an abstraction representing user connected to Red5 application. * Clients are tied to connections and registred in ClientRegistry */ public class Client extends AttributeStore implements IClient, ClientMBean { /** * Logger */ protected static Logger log = LoggerFactory.getLogger(Client.class); /** * Name of connection attribute holding the permissions. */ protected static final String PERMISSIONS = IPersistable.TRANSIENT_PREFIX + "_red5_permissions"; /** * Scopes this client connected to */ protected ConcurrentMap<IConnection, IScope> connToScope = new ConcurrentHashMap<IConnection, IScope>(); /** * Creation time as Timestamp */ protected long creationTime; /** * Clients identificator */ protected String id; /** * MBean object name used for de/registration purposes. */ private ObjectName oName; /** * Client registry where Client is registred */ protected ClientRegistry registry; public Client() { //here for jmx reference only log.debug("Default ctor called"); } /** * Creates client, sets creation time and registers it in ClientRegistry * * @param id Client id * @param registry ClientRegistry */ public Client(String id, ClientRegistry registry) { this.id = id; this.registry = registry; this.creationTime = System.currentTimeMillis(); //create a new mbean for this instance oName = JMXFactory.createObjectName("type", "Client", "id", id); JMXAgent.registerMBean(this, this.getClass().getName(), ClientMBean.class, oName); } /** * Disconnects client from Red5 application */ public void disconnect() { log.debug("Disconnect - id: {}, closing {} connections", id, getConnections().size()); // Close all associated connections for (IConnection con : getConnections()) { con.close(); } } /** * Check clients equality by id * * @param obj Object to check against * @return true if clients ids are the same, false otherwise */ @Override public boolean equals(Object obj) { if (!(obj instanceof Client)) { return false; } return ((Client) obj).getId().equals(id); } /** * Return bandwidth configuration context, that is, broadcasting bandwidth and quality settings for this client * @return Bandwidth configuration context */ public IBandwidthConfigure getBandwidthConfigure() { // TODO implement it return null; } /** * Return set of connections for this client * * @return Set of connections */ public Set<IConnection> getConnections() { return connToScope.keySet(); } /** * Return client connections to given scope * * @param scope Scope * @return Set of connections for that scope */ public Set<IConnection> getConnections(IScope scope) { if (scope == null) { return getConnections(); } Set<IConnection> result = new HashSet<IConnection>(); for (Entry<IConnection, IScope> entry : connToScope.entrySet()) { if (scope.equals(entry.getValue())) { result.add(entry.getKey()); } } return result; } /** * * @return creation time */ public long getCreationTime() { return creationTime; } public void setId(String id) { this.id = id; } /** * Returns the client id * @return client id */ public String getId() { return id; } /** * Parent flow controllable object, that is, parent object that is used to determine client broadcast bandwidth * settings. In case of base Client class parent is host. * * @return IFlowControllable instance */ public IBWControllable getParentBWControllable() { // parent is host return null; } /** * * @return scopes on this client */ public Collection<IScope> getScopes() { return connToScope.values(); } /** * if overriding equals then also do hashCode * @return a has code */ @Override public int hashCode() { return Integer.valueOf(id); } /** * Iterate through the scopes and their attributes. * Used by JMX * * @return list of scope attributes */ public List<String> iterateScopeNameList() { log.debug("iterateScopeNameList called"); List<String> scopeNames = new ArrayList<String>(); log.debug("Scopes: {}", connToScope.values().size()); for (IScope scope : connToScope.values()) { log.debug("Client scope: {}", scope); for (Map.Entry<String, Object> entry : scope.getAttributes().entrySet()) { log.debug("Client scope attr: {} = {}", entry.getKey(), entry.getValue()); } } return scopeNames; } /** * Associate connection with client * @param conn Connection object */ protected void register(IConnection conn) { log.debug("Registering connection for this client {}", id); if (conn != null) { if (conn.getScope() != null) { connToScope.put(conn, conn.getScope()); } else { log.warn("Clients scope is null. Id: {}", id); } } else { log.warn("Clients connection is null. Id: {}", id); } } /** * Set new bandwidth configuration context * @param config Bandwidth configuration context */ public void setBandwidthConfigure(IBandwidthConfigure config) { // TODO implement it } /** * * @return string representation of client */ @Override public String toString() { return "Client: " + id; } /** * Removes client-connection association for given connection * @param conn Connection object */ protected void unregister(IConnection conn) { // Remove connection from connected scopes list connToScope.remove(conn); // If client is not connected to any scope any longer then remove if (connToScope.isEmpty()) { if (registry != null) { // This client is not connected to any scopes, remove from registry. registry.removeClient(this); } // deregister with jmx JMXAgent.unregisterMBean(oName); } } /** {@inheritDoc} */ @SuppressWarnings("unchecked") public Collection<String> getPermissions(IConnection conn) { Collection<String> result = (Collection<String>) conn.getAttribute(PERMISSIONS); if (result == null) { result = Collections.EMPTY_SET; } return result; } /** {@inheritDoc} */ public boolean hasPermission(IConnection conn, String permissionName) { final Collection<String> permissions = getPermissions(conn); return permissions.contains(permissionName); } /** {@inheritDoc} */ public void setPermissions(IConnection conn, Collection<String> permissions) { if (permissions == null) { conn.removeAttribute(PERMISSIONS); } else { conn.setAttribute(PERMISSIONS, permissions); } } }