package org.red5.server.stream; /* * 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.io.IOException; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.red5.server.BasicScope; import org.red5.server.api.IScope; import org.red5.server.messaging.IConsumer; import org.red5.server.messaging.IMessage; import org.red5.server.messaging.IPipeConnectionListener; import org.red5.server.messaging.IProvider; import org.red5.server.messaging.InMemoryPushPushPipe; import org.red5.server.messaging.OOBControlMessage; import org.red5.server.messaging.PipeConnectionEvent; /** * Scope type for publishing that deals with pipe connection events, * like async message listening in JMS */ public class BroadcastScope extends BasicScope implements IBroadcastScope, IPipeConnectionListener { /** * Simple in memory push pipe, triggered by an active provider to push messages to consumer */ private InMemoryPushPushPipe pipe; /** * Number of components. */ private int compCounter; /** * Remove flag */ private boolean hasRemoved; /** * Lock for critical sections, to prevent concurrent modification. * A "fairness" policy is used wherein the longest waiting thread * will be granted access before others. */ protected Lock lock = new ReentrantLock(true); /** * Creates broadcast scope * @param parent Parent scope * @param name Scope name */ public BroadcastScope(IScope parent, String name) { super(parent, TYPE, name, false); pipe = new InMemoryPushPushPipe(); pipe.addPipeConnectionListener(this); compCounter = 0; hasRemoved = false; keepOnDisconnect = true; } /** * Register pipe connection event listener with this scope's pipe. * A listener that wants to listen to events when * provider/consumer connects to or disconnects from * a specific pipe. * @param listener Pipe connection event listener * * @see org.red5.server.messaging.IPipeConnectionListener */ public void addPipeConnectionListener(IPipeConnectionListener listener) { pipe.addPipeConnectionListener(listener); } /** * Unregisters pipe connection event listener with this scope's pipe * @param listener Pipe connection event listener * * @see org.red5.server.messaging.IPipeConnectionListener */ public void removePipeConnectionListener(IPipeConnectionListener listener) { pipe.removePipeConnectionListener(listener); } /** * Pull message from pipe * @return Message object * * @see org.red5.server.messaging.IMessage */ public IMessage pullMessage() { return pipe.pullMessage(); } /** * Pull message with timeout * @param wait Timeout * @return Message object * * @see org.red5.server.messaging.IMessage */ public IMessage pullMessage(long wait) { return pipe.pullMessage(wait); } /** * Connect scope's pipe to given consumer * * @param consumer Consumer * @param paramMap Parameters passed with connection * @return <code>true</code> on success, <code>false</code> otherwise */ public boolean subscribe(IConsumer consumer, Map<?, ?> paramMap) { lock(); try { return !hasRemoved && pipe.subscribe(consumer, paramMap); } finally { unlock(); } } /** * Disconnects scope's pipe from given consumer * @param consumer Consumer * @return <code>true</code> on success, <code>false</code> otherwise */ public boolean unsubscribe(IConsumer consumer) { return pipe.unsubscribe(consumer); } /** * Getter for pipe consumers * @return Pipe consumers */ public List<IConsumer> getConsumers() { return pipe.getConsumers(); } /** * Send out-of-band ("special") control message * * @param consumer Consumer, may be used in concrete implementations * @param oobCtrlMsg Out-of-band control message */ public void sendOOBControlMessage(IConsumer consumer, OOBControlMessage oobCtrlMsg) { pipe.sendOOBControlMessage(consumer, oobCtrlMsg); } /** * Push a message to this output endpoint. May block * the pusher when output can't handle the message at * the time. * @param message Message to be pushed. * @throws IOException If message could not be pushed. */ public void pushMessage(IMessage message) throws IOException { pipe.pushMessage(message); } /** * Connect scope's pipe with given provider * @param provider Provider * @param paramMap Parameters passed on connection * @return <code>true</code> on success, <code>false</code> otherwise */ public boolean subscribe(IProvider provider, Map<?, ?> paramMap) { lock(); try { return !hasRemoved && pipe.subscribe(provider, paramMap); } finally { unlock(); } } /** * Disconnects scope's pipe from given provider * @param provider Provider * @return <code>true</code> on success, <code>false</code> otherwise */ public boolean unsubscribe(IProvider provider) { lock(); try { return pipe.unsubscribe(provider); } finally { unlock(); } } /** * Getter for providers list * @return List of providers */ public List<IProvider> getProviders() { return pipe.getProviders(); } /** * Send out-of-band ("special") control message * * @param provider Provider, may be used in concrete implementations * @param oobCtrlMsg Out-of-band control message */ public void sendOOBControlMessage(IProvider provider, OOBControlMessage oobCtrlMsg) { pipe.sendOOBControlMessage(provider, oobCtrlMsg); } /** * Pipe connection event handler * @param event Pipe connection event */ public void onPipeConnectionEvent(PipeConnectionEvent event) { // Switch event type switch (event.getType()) { case PipeConnectionEvent.CONSUMER_CONNECT_PULL: case PipeConnectionEvent.CONSUMER_CONNECT_PUSH: case PipeConnectionEvent.PROVIDER_CONNECT_PULL: case PipeConnectionEvent.PROVIDER_CONNECT_PUSH: compCounter++; break; case PipeConnectionEvent.CONSUMER_DISCONNECT: case PipeConnectionEvent.PROVIDER_DISCONNECT: compCounter--; if (compCounter <= 0) { // XXX should we synchronize parent before removing? if (hasParent()) { IProviderService providerService = (IProviderService) getParent() .getContext().getBean(IProviderService.BEAN_NAME); providerService.unregisterBroadcastStream(getParent(), getName()); } hasRemoved = true; } break; default: throw new UnsupportedOperationException("Event type not supported: "+event.getType()); } } public void lock() { lock.lock(); } public void unlock() { lock.unlock(); } }