package org.red5.server.messaging; /* * 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.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Abstract pipe that books providers/consumers and listeners. * Aim to ease the implementation of concrete pipes. For more * information on what pipe is, see IPipe interface documentation. * * @author The Red5 Project (red5@osflash.org) * @author Steven Gong (steven.gong@gmail.com) * * @see org.red5.server.messaging.IPipe */ public abstract class AbstractPipe implements IPipe { /** * Logger */ private static final Logger log = LoggerFactory.getLogger(AbstractPipe.class); /** * Pipe consumers list */ protected volatile List<IConsumer> consumers = new CopyOnWriteArrayList<IConsumer>(); /** * Pipe providers list */ protected volatile List<IProvider> providers = new CopyOnWriteArrayList<IProvider>(); /** * Event listeners */ protected volatile List<IPipeConnectionListener> listeners = new CopyOnWriteArrayList<IPipeConnectionListener>(); /** * Connect consumer to this pipe. Doesn't allow to connect one consumer twice. * Does register event listeners if instance of IPipeConnectionListener is given. * * @param consumer Consumer * @param paramMap Parameters passed with connection, used in concrete pipe implementations * @return <code>true</code> if consumer was added, <code>false</code> otherwise */ public boolean subscribe(IConsumer consumer, Map<?, ?> paramMap) { // Pipe is possibly used by dozens of Threads at once (like many subscribers for one server stream) // so make it synchronized synchronized (consumers) { // Can't add one consumer twice if (consumers.contains(consumer)) { return false; } consumers.add(consumer); } // If consumer is listener object register it as listener if (consumer instanceof IPipeConnectionListener) { listeners.add((IPipeConnectionListener) consumer); } return true; } /** * Connect provider to this pipe. Doesn't allow to connect one provider twice. * Does register event listeners if instance of IPipeConnectionListener is given. * * @param provider Provider * @param paramMap Parameters passed with connection, used in concrete pipe implementations * @return <code>true</code> if provider was added, <code>false</code> otherwise */ public boolean subscribe(IProvider provider, Map<?, ?> paramMap) { // Shared between possibly dozens of Threads so make it synchronized synchronized (providers) { if (providers.contains(provider)) { return false; } providers.add(provider); } // Register event listener if given if (provider instanceof IPipeConnectionListener) { listeners.add((IPipeConnectionListener) provider); } return true; } /** * Disconnects provider from this pipe. Fires pipe connection event. * @param provider Provider that should be removed * @return <code>true</code> on success, <code>false</code> otherwise */ public boolean unsubscribe(IProvider provider) { if (!providers.remove(provider)) { return false; } fireProviderConnectionEvent(provider, PipeConnectionEvent.PROVIDER_DISCONNECT, null); if (provider instanceof IPipeConnectionListener) { listeners.remove(provider); } return true; } /** * Disconnects consumer from this pipe. Fires pipe connection event. * @param consumer Consumer that should be removed * @return <code>true</code> on success, <code>false</code> otherwise */ public boolean unsubscribe(IConsumer consumer) { if (!consumers.remove(consumer)) { return false; } fireConsumerConnectionEvent(consumer, PipeConnectionEvent.CONSUMER_DISCONNECT, null); if (consumer instanceof IPipeConnectionListener) { listeners.remove(consumer); } return true; } /** * Registers pipe connect events listener * @param listener Listener */ public void addPipeConnectionListener(IPipeConnectionListener listener) { listeners.add(listener); } /** * Removes pipe connection listener * @param listener Listener */ public void removePipeConnectionListener(IPipeConnectionListener listener) { listeners.remove(listener); } /** * Send out-of-band ("special") control message to all consumers * * @param provider Provider, may be used in concrete implementations * @param oobCtrlMsg Out-of-band control message */ public void sendOOBControlMessage(IProvider provider, OOBControlMessage oobCtrlMsg) { for (IConsumer consumer : consumers) { try { consumer.onOOBControlMessage(provider, this, oobCtrlMsg); } catch (Throwable t) { log .error( "exception when passing OOBCM from provider to consumers", t); } } } /** * Send out-of-band ("special") control message to all providers * * @param consumer Consumer, may be used in concrete implementations * @param oobCtrlMsg Out-of-band control message */ public void sendOOBControlMessage(IConsumer consumer, OOBControlMessage oobCtrlMsg) { for (IProvider provider : providers) { try { provider.onOOBControlMessage(consumer, this, oobCtrlMsg); } catch (Throwable t) { log .error( "exception when passing OOBCM from consumer to providers", t); } } } /** * Getter for pipe connection events listeners * * @return Listeners */ public List<IPipeConnectionListener> getListeners() { return listeners; } /** * Setter for pipe connection events listeners * * @param listeners Listeners */ public void setListeners(List<IPipeConnectionListener> listeners) { this.listeners = listeners; } /** * Getter for providers * * @return Providers list */ public List<IProvider> getProviders() { return providers; } /** * Getter for consumers * * @return consumers list */ public List<IConsumer> getConsumers() { return consumers; } /** * Broadcast consumer connection event * * @param consumer Consumer that has connected * @param type Event type * @param paramMap Parameters passed with connection */ protected void fireConsumerConnectionEvent(IConsumer consumer, int type, Map<?, ?> paramMap) { // Create event object PipeConnectionEvent event = new PipeConnectionEvent(this); // Fill it up event.setConsumer(consumer); event.setType(type); event.setParamMap(paramMap); // Fire it firePipeConnectionEvent(event); } /** * Broadcast provider connection event * @param provider Provider that has connected * @param type Event type * @param paramMap Parameters passed with connection */ protected void fireProviderConnectionEvent(IProvider provider, int type, Map<?, ?> paramMap) { PipeConnectionEvent event = new PipeConnectionEvent(this); event.setProvider(provider); event.setType(type); event.setParamMap(paramMap); firePipeConnectionEvent(event); } /** * Fire any pipe connection event and run all it's tasks * @param event Pipe connection event */ protected void firePipeConnectionEvent(PipeConnectionEvent event) { for (IPipeConnectionListener element : listeners) { try { element.onPipeConnectionEvent(event); } catch (Throwable t) { log.error("exception when handling pipe connection event", t); } } // Run all of event's tasks for (Runnable task : event.getTaskList()) { try { task.run(); } catch (Throwable t) { } } // Clear event's tasks list event.getTaskList().clear(); } }