All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.red5.server.Client Maven / Gradle / Ivy

The newest version!
/*
 * RED5 Open Source Media Server - https://github.com/Red5/
 * 
 * Copyright 2006-2016 by respective authors (see below). All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.red5.server;

import java.beans.ConstructorProperties;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
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.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.management.openmbean.CompositeData;

import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.persistence.IPersistable;
import org.red5.server.api.scope.IScope;
import org.red5.server.stream.bandwidth.ClientServerDetection;
import org.red5.server.stream.bandwidth.ServerClientDetection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Client is an abstraction representing user connected to Red5 application. Clients are tied to connections and registered in ClientRegistry
 */
public class Client extends AttributeStore implements IClient {

    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";

    /**
     * Client registry where Client is registered
     */
    protected transient WeakReference registry;

    /**
     * Connections this client is associated with.
     */
    protected transient CopyOnWriteArraySet connections = new CopyOnWriteArraySet();

    /**
     * Creation time as Timestamp
     */
    protected final long creationTime;

    /**
     * Clients identifier
     */
    protected final String id;

    /**
     * Whether or not the bandwidth has been checked.
     */
    protected boolean bandwidthChecked;

    /**
     * Disconnected state.
     */
    protected AtomicBoolean disconnected = new AtomicBoolean(false);

    /**
     * Creates client, sets creation time and registers it in ClientRegistry.
     *
     * @param id
     *            Client id
     * @param registry
     *            ClientRegistry
     */
    @ConstructorProperties({ "id", "registry" })
    public Client(String id, ClientRegistry registry) {
        super();
        if (id != null) {
            this.id = id;
        } else {
            this.id = registry.nextId();
        }
        this.creationTime = System.currentTimeMillis();
        // use a weak reference to prevent any hard-links to the registry
        this.registry = new WeakReference(registry);
    }

    /**
     * Creates client, sets creation time and registers it in ClientRegistry.
     *
     * @param id
     *            Client id
     * @param creationTime
     *            Creation time
     * @param registry
     *            ClientRegistry
     */
    @ConstructorProperties({ "id", "creationTime", "registry" })
    public Client(String id, Long creationTime, ClientRegistry registry) {
        super();
        if (id != null) {
            this.id = id;
        } else {
            this.id = registry.nextId();
        }
        if (creationTime != null) {
            this.creationTime = creationTime;
        } else {
            this.creationTime = System.currentTimeMillis();
        }
        // use a weak reference to prevent any hard-links to the registry
        this.registry = new WeakReference(registry);
    }

    /**
     * Disconnects client from Red5 application
     */
    public void disconnect() {
        if (disconnected.compareAndSet(false, true)) {
            log.debug("Disconnect - id: {}", id);
            if (connections != null && !connections.isEmpty()) {
                log.debug("Closing {} scope connections", connections.size());
                // close all connections held to Red5 by client
                for (IConnection con : getConnections()) {
                    try {
                        con.close();
                    } catch (Exception e) {
                        // closing a connection calls into application code, so exception possible
                        log.error("Unexpected exception closing connection {}", e);
                    }
                }
            } else {
                log.debug("Connection map is empty or null");
            }
            // unregister client
            removeInstance();
        }
    }

    /**
     * Return set of connections for this client
     *
     * @return Set of connections
     */
    public Set getConnections() {
        return Collections.unmodifiableSet(connections);
    }

    /**
     * Return client connections to given scope
     *
     * @param scope
     *            Scope
     * @return Set of connections for that scope
     */
    public Set getConnections(IScope scope) {
        if (scope == null) {
            return getConnections();
        }
        Set scopeClients = scope.getClients();
        if (scopeClients.contains(this)) {
            for (IClient cli : scopeClients) {
                if (this.equals(cli)) {
                    return cli.getConnections();
                }
            }
        }
        return Collections.emptySet();
    }

    /**
     * Returns the time at which the client was created.
     * 
     * @return creation time
     */
    public long getCreationTime() {
        return creationTime;
    }

    /**
     * Returns the client id.
     * 
     * @return client id
     */
    public String getId() {
        return id;
    }

    /**
     *
     * @return scopes on this client
     */
    public Collection getScopes() {
        Set scopes = new HashSet();
        for (IConnection conn : connections) {
            scopes.add(conn.getScope());
        }
        return scopes;
    }

    /**
     * Iterate through the scopes and their attributes. Used by JMX
     *
     * @return list of scope attributes
     */
    public List iterateScopeNameList() {
        log.debug("iterateScopeNameList called");
        Collection scopes = getScopes();
        log.debug("Scopes: {}", scopes.size());
        List scopeNames = new ArrayList(scopes.size());
        for (IScope scope : scopes) {
            log.debug("Client scope: {}", scope);
            scopeNames.add(scope.getName());
            if (log.isDebugEnabled()) {
                for (Map.Entry entry : scope.getAttributes().entrySet()) {
                    log.debug("Client scope attr: {} = {}", entry.getKey(), entry.getValue());
                }
            }
        }
        return scopeNames;
    }

    /**
     * Returns registration status of given connection.
     * 
     * @param conn
     *            connection
     * @return true if registered and false otherwise
     */
    public boolean isRegistered(IConnection conn) {
        return connections.contains(conn);
    }

    /**
     * Associate connection with client
     * 
     * @param conn
     *            Connection object
     */
    protected void register(IConnection conn) {
        if (log.isDebugEnabled()) {
            if (conn == null) {
                log.debug("Register null connection, client id: {}", id);
            } else {
                log.debug("Register connection ({}:{}) client id: {}", conn.getRemoteAddress(), conn.getRemotePort(), id);
            }
        }
        if (conn != null) {
            IScope scope = conn.getScope();
            if (scope != null) {
                log.debug("Registering for scope: {}", scope);
                connections.add(conn);
            } else {
                log.warn("Clients scope is null. Id: {}", id);
            }
        } else {
            log.warn("Clients connection is null. Id: {}", id);
        }
    }

    /**
     * Removes client-connection association for given connection
     * 
     * @param conn
     *            Connection object
     */
    protected void unregister(IConnection conn) {
        unregister(conn, true);
    }

    /**
     * Removes client-connection association for given connection
     * 
     * @param conn
     *            Connection object
     * @param deleteIfNoConns
     *            Whether to delete this client if it no longer has any connections
     */
    protected void unregister(IConnection conn, boolean deleteIfNoConns) {
        log.debug("Unregister connection ({}:{}) client id: {}", conn.getRemoteAddress(), conn.getRemotePort(), id);
        // remove connection from connected scopes list
        connections.remove(conn);
        // If client is not connected to any scope any longer then remove
        if (deleteIfNoConns && connections.isEmpty()) {
            // TODO DW dangerous the way this is called from BaseConnection.initialize(). Could we unexpectedly pop a Client out of the registry?
            removeInstance();
        }
    }

    /** {@inheritDoc} */
    public boolean isBandwidthChecked() {
        return bandwidthChecked;
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public Collection getPermissions(IConnection conn) {
        Collection result = (Collection) conn.getAttribute(PERMISSIONS);
        if (result == null) {
            result = Collections.emptySet();
        }
        return result;
    }

    /** {@inheritDoc} */
    public boolean hasPermission(IConnection conn, String permissionName) {
        final Collection permissions = getPermissions(conn);
        return permissions.contains(permissionName);
    }

    /** {@inheritDoc} */
    public void setPermissions(IConnection conn, Collection permissions) {
        if (permissions == null) {
            conn.removeAttribute(PERMISSIONS);
        } else {
            conn.setAttribute(PERMISSIONS, permissions);
        }
    }

    /** {@inheritDoc} */
    public void checkBandwidth() {
        log.debug("Check bandwidth");
        bandwidthChecked = true;
        //do something to check the bandwidth, Dan what do you think?
        ServerClientDetection detection = new ServerClientDetection();
        detection.checkBandwidth(Red5.getConnectionLocal());
    }

    /** {@inheritDoc} */
    public Map checkBandwidthUp(Object[] params) {
        if (log.isDebugEnabled()) {
            log.debug("Check bandwidth: {}", Arrays.toString(params));
        }
        bandwidthChecked = true;
        //do something to check the bandwidth, Dan what do you think?
        ClientServerDetection detection = new ClientServerDetection();
        // if dynamic bw is turned on, we switch to a higher or lower
        return detection.checkBandwidth(params);
    }

    /**
     * Allows for reconstruction via CompositeData.
     *
     * @param cd
     *            composite data
     * @return Client class instance
     */
    public static Client from(CompositeData cd) {
        Client instance = null;
        if (cd.containsKey("id")) {
            String id = (String) cd.get("id");
            instance = new Client(id, (Long) cd.get("creationTime"), null);
            instance.setAttribute(PERMISSIONS, cd.get(PERMISSIONS));
        }
        if (cd.containsKey("attributes")) {
            AttributeStore attrs = (AttributeStore) cd.get("attributes");
            instance.setAttributes(attrs);
        }
        return instance;
    }

    /**
     * Removes this instance from the client registry.
     */
    private void removeInstance() {
        // unregister client
        ClientRegistry ref = registry.get();
        if (ref != null) {
            ref.removeClient(this);
        } else {
            log.warn("Client registry reference was not accessable, removal failed");
            // TODO: attempt to lookup the registry via the global.clientRegistry
        }
    }

    @Override
    public int hashCode() {
        if (id == null) {
            return -1;
        }
        return id.hashCode();
    }

    /**
     * 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 ((Client) obj).getId().equals(id);
        }
        return false;
    }

    /**
     *
     * @return string representation of client
     */
    @Override
    public String toString() {
        return "Client: " + id;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy