Hi Guys,

Recently in my project I was having issues where clients could connect to a
room scope whilst roomStop() was happening to that scope. After roomStop()
had completed Red5 would still have clients connected to the old Scope
object instance and when further clients connected Red5 would get into a
very funny state where two Scope object instances for the same scope. Even
worse when clients disconnected from the old scope roomStop() would get
called again screwing up the new scope. Anyway it was defiantly not good :-)

I think others have not come across bug because their roomStop() method
generally executes quickly and clients don't reconnect in time. My
roomStop() method saves a whole bunch of stuff that can take some time.

Anyway I resolved my issue by adding synchronization code to Scope class.
The code works as follows:

While a child scope is being removed and stopped the following will block
(only for that scope name so as minimise performance impact):

Scope#hasChildScope(String name)
Scope#hasChildScope(String type, String name)
Scope#getScope(String name)

This has fixed my issues.

I've attached the code so that Red5 developers may take lessons learned (or
just the new code) and integrate it into the trunk. The changes I've made
are marked with comment bars (feel free to remove them they are just there
for reference).

Cheers,

Nathan
package org.red5.server;

/*
 * RED5 Open Source Flash Server - http://www.osflash.org/red5
 *
 * Copyright (c) 2006-2008 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.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.apache.commons.lang.StringUtils;
import org.red5.server.api.IBasicScope;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IContext;
import org.red5.server.api.IGlobalScope;
import org.red5.server.api.IScope;
import org.red5.server.api.IScopeAware;
import org.red5.server.api.IScopeHandler;
import org.red5.server.api.IServer;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.persistence.PersistenceUtils;
import org.red5.server.api.statistics.IScopeStatistics;
import org.red5.server.api.statistics.support.StatisticsCounter;
import org.red5.server.jmx.JMXAgent;
import org.red5.server.jmx.JMXFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.style.ToStringCreator;

/**
 * The scope object.
 *
 * A statefull object shared between a group of clients connected to the same
 * context path. Scopes are arranged in a hierarchical way, so its possible for
 * a scope to have a parent. If a client is connect to a scope then they are
 * also connected to its parent scope. The scope object is used to access
 * resources, shared object, streams, etc.
 *
 * The following are all names for scopes: application, room, place, lobby.
 *
 * @author The Red5 Project ([EMAIL PROTECTED])
 */
public class Scope extends BasicScope implements IScope, IScopeStatistics,
		ScopeMBean {

	/**
	 * Iterates through connections
	 */
	class ConnectionIterator implements Iterator<IConnection> {
		/**
		 * Connections iterator
		 */
		private Iterator<IConnection> connIterator;

		/**
		 * Current connection
		 */
		private IConnection current;

		/**
		 * Set iterator
		 */
		private final Iterator<Set<IConnection>> setIterator;

		/**
		 * Creates connection iterator
		 */
		public ConnectionIterator() {
			setIterator = clients.values().iterator();
		}

		/**
		 * [EMAIL PROTECTED]
		 */
		public boolean hasNext() {
			if (connIterator != null && connIterator.hasNext()) {
				// More connections for this client
				return true;
			}

			if (!setIterator.hasNext()) {
				// No more clients
				return false;
			}

			connIterator = setIterator.next().iterator();
			while (connIterator != null) {
				if (connIterator.hasNext()) {
					// Found client with connections
					return true;
				}

				if (!setIterator.hasNext()) {
					// No more clients
					return false;
				}

				// Advance to next client
				connIterator = setIterator.next().iterator();
			}
			return false;
		}

		/**
		 * [EMAIL PROTECTED]
		 */
		public IConnection next() {
			if (connIterator == null || !connIterator.hasNext()) {
				if (!setIterator.hasNext()) {
					// No more clients
					throw new NoSuchElementException();
				}

				connIterator = setIterator.next().iterator();
				while (!connIterator.hasNext()) {
					// Client has no connections, search next one
					if (!setIterator.hasNext()) {
						// No more clients
						throw new NoSuchElementException();
					}

					connIterator = setIterator.next().iterator();
				}
			}
			// Always of type IConnection, no need to cast
			current = connIterator.next();
			return current;
		}

		/**
		 * [EMAIL PROTECTED]
		 */
		public void remove() {
			if (current != null) {
				disconnect(current);
			}
		}

	}

	/**
	 * Iterator that filters strings by given prefix
	 */
	class PrefixFilteringStringIterator implements Iterator<String> {
		/**
		 * Iterator
		 */
		private final Iterator<String> iterator;

		/**
		 * Next object
		 */
		private String next;

		/**
		 * Prefix
		 */
		private final String prefix;

		/**
		 * Creates prefix filtering string iterator from iterator and prefix
		 *
		 * @param iterator
		 *            Iterator
		 * @param prefix
		 *            Prefix
		 */
		public PrefixFilteringStringIterator(Iterator<String> iterator,
				String prefix) {
			this.iterator = iterator;
			this.prefix = prefix;
		}

		/** [EMAIL PROTECTED] */
		public boolean hasNext() {
			if (next != null) {
				return true;
			}
			do {
				next = (iterator.hasNext()) ? iterator.next() : null;
			} while (next != null && !next.startsWith(prefix));
			return next != null;
		}

		/** [EMAIL PROTECTED] */
		public String next() {
			if (next != null) {
				final String result = next;
				next = null;
				return result.substring(prefix.length());
			}
			if (hasNext()) {
				return next();
			} else {
				return null;
			}
		}

		/** [EMAIL PROTECTED] */
		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

	/**
	 * Logger
	 */
	protected static Logger log = LoggerFactory.getLogger(Scope.class);

	/**
	 * Scope type constant
	 */
	private static final String TYPE = "scope";

	/**
	 * Unset flag constant
	 */
	private static final int UNSET = -1;

	/**
	 * Autostart flag
	 */
	private boolean autoStart = true;

	/**
	 * Child scopes map (child scopes are named)
	 */
	private final ConcurrentMap<String, IBasicScope> children = new ConcurrentHashMap<String, IBasicScope>();

	// -------------------------------------
	// Nathan Smith
	// Synchronize removal and retrieval of child scopes
	/**
	 * Map of locks used to synchronize childen (removal and retrieval)
	 */
	private final Map<String, Object> childrenLocks = new ConcurrentHashMap<String, Object>();

	// -------------------------------------

	/**
	 * Clients and connection map
	 */
	private final ConcurrentMap<IClient, Set<IConnection>> clients = new ConcurrentHashMap<IClient, Set<IConnection>>();

	/**
	 * Statistics about clients connected to the scope.
	 */
	protected final StatisticsCounter clientStats = new StatisticsCounter();

	/**
	 * Statistics about connections to the scope.
	 */
	protected final StatisticsCounter connectionStats = new StatisticsCounter();

	/**
	 * Scope context
	 */
	private IContext context;

	/**
	 * Timestamp the scope was created.
	 */
	private long creationTime;

	/**
	 * Scope nesting depth, unset by default
	 */
	private int depth = UNSET;

	/**
	 * Whether scope is enabled
	 */
	private boolean enabled = true;

	/**
	 * Scope handler
	 */
	private IScopeHandler handler;

	/**
	 * Whether scope is running
	 */
	private boolean running;

	/**
	 * Registered service handlers for this scope. The map is created on-demand
	 * only if it's accessed for writing.
	 */
	private volatile ConcurrentMap<String, Object> serviceHandlers;

	/**
	 * Statistics about subscopes.
	 */
	protected final StatisticsCounter subscopeStats = new StatisticsCounter();

	/**
	 * Mbean object name.
	 */
	protected ObjectName oName;

	/**
	 * Creates unnamed scope
	 */
	public Scope() {
		this(null);
	}

	/**
	 * Creates scope with given name
	 *
	 * @param name
	 *            Scope name
	 */
	public Scope(String name) {
		super(null, TYPE, name, false);
		creationTime = System.currentTimeMillis();
	}

	/**
	 * Add child scope to this scope
	 *
	 * @param scope
	 *            Child scope
	 * @return <code>true</code> on success (if scope has handler and it
	 *         accepts child scope addition), <code>false</code> otherwise
	 */
	public boolean addChildScope(IBasicScope scope) {
		if (scope.getStore() == null) {
			// Child scope has no persistence store, use same class as parent.
			try {
				if (scope instanceof Scope) {
					((Scope) scope).setPersistenceClass(this.persistenceClass);
				}
			} catch (Exception error) {
				log.error("Could not set persistence class. {}", error);
			}
		}
		if (hasHandler() && !getHandler().addChildScope(scope)) {
			log.debug("Failed to add child scope: {} to {}", scope, this);
			return false;
		}
		if (scope instanceof IScope) {
			// start the scope
			if (hasHandler() && !getHandler().start((IScope) scope)) {
				log.debug("Failed to start child scope: {} in {}", scope, this);
				return false;
			}
		}
		if (scope instanceof IScope) {
			final IServer server = getServer();
			if (server instanceof Server) {
				((Server) server).notifyScopeCreated((IScope) scope);
			}
		}
		log.debug("Add child scope: {} to {}", scope, this);
		children.put(scope.getType() + SEPARATOR + scope.getName(), scope);
		return true;
	}

	/**
	 * Connect to scope
	 *
	 * @param conn
	 *            Connection object
	 * @return <code>true</code> on success, <code>false</code> otherwise
	 */
	public boolean connect(IConnection conn) {
		return connect(conn, null);
	}

	/**
	 * Connect to scope with parameters. To successfully connect to scope it
	 * must have handler that will accept this connection with given set of
	 * parameters. Client associated with connection is added to scope clients set,
	 * connection is registered as scope event listener.
	 *
	 * @param conn
	 *            Connection object
	 * @param params
	 *            Parameters passed with connection
	 * @return <code>true</code> on success, <code>false</code> otherwise
	 */
	public boolean connect(IConnection conn, Object[] params) {
		//		logger.debug("Has connection: {}", (conn != null));
		//		logger.debug("Has handler: {}", (handler != null));
		//		logger.debug("Has parent: {}", (parent != null));
		if (hasParent() && !parent.connect(conn, params)) {
			return false;
		}
		if (hasHandler() && !getHandler().connect(conn, this, params)) {
			return false;
		}
		final IClient client = conn.getClient();
		if (!conn.isConnected()) {
			// Timeout while connecting client
			return false;
		}
		//we would not get this far if there is no handler
		if (hasHandler() && !getHandler().join(client, this)) {
			//if (!getHandler().join(client, this)) {
			return false;
		}
		//checking the connection again? why?
		if (!conn.isConnected()) {
			// Timeout while connecting client
			return false;
		}

		Set<IConnection> conns = clients.get(client);
		if (conns == null) {
			conns = new CopyOnWriteArraySet<IConnection>();
			clients.put(client, conns);
		}
		conns.add(conn);

		clientStats.increment();
		addEventListener(conn);
		connectionStats.increment();

		if (this.equals(conn.getScope())) {
			final IServer server = getServer();
			if (server instanceof Server) {
				((Server) server).notifyConnected(conn);
			}
		}
		return true;
	}

	/**
	 * Create child scope with given name
	 *
	 * @param name
	 *            Child scope name
	 * @return <code>true</code> on success, <code>false</code> otherwise
	 */
	public boolean createChildScope(String name) {
		final Scope scope = new Scope(name);
		scope.setParent(this);
		subscopeStats.increment();
		return addChildScope(scope);
	}

	/**
	 * Destroys scope
	 */
	public void destroy() {
		log.debug("Destroy scope");
		if (hasParent()) {
			parent.removeChildScope(this);
		}
		if (hasHandler()) {
			//--------------------------------
			// Nathan Smith
			// Changed from:
			// handler.stop(this)
			// to:
			// getHandler.stop(this)
			// Because handler can be null when there is a parent handler
			getHandler().stop(this);
			//--------------------------------
		}
		// TODO: kill all child scopes
		Set<Map.Entry<String, IBasicScope>> entries = children.entrySet();
		for (Map.Entry<String, IBasicScope> entry : entries) {
			log.debug("Stopping child scope: {}", entry.getKey());
			IBasicScope basic = entry.getValue();
			if (basic instanceof Scope) {
				((Scope) basic).uninit();
			}
		}
	}

	/**
	 * Disconnect connection from scope
	 *
	 * @param conn
	 *            Connection object
	 */
	public void disconnect(IConnection conn) {
		// We call the disconnect handlers in reverse order they were called
		// during connection, i.e. roomDisconnect is called before
		// appDisconnect.
		final IClient client = conn.getClient();
		if (client == null) {
			// Early bail out
			removeEventListener(conn);
			connectionStats.decrement();
			if (hasParent()) {
				parent.disconnect(conn);
			}
			return;
		}

		final Set<IConnection> conns = clients.get(client);
		if (conns != null) {
			conns.remove(conn);
			IScopeHandler handler = null;
			if (hasHandler()) {
				handler = getHandler();
				try {
					handler.disconnect(conn, this);
				} catch (Exception e) {
					log
							.error(
									"Error while executing \"disconnect\" for connection {} on handler {}. {}",
									new Object[] { conn, handler, e });
				}
			}
			if (conns.isEmpty()) {
				clients.remove(client);
				clientStats.decrement();
				if (handler != null) {
					try {
						// there may be a timeout here ?
						handler.leave(client, this);
					} catch (Exception e) {
						log
								.error(
										"Error while executing \"leave\" for client {} on handler {}. {}",
										new Object[] { conn, handler, e });
					}
				}
			}
			removeEventListener(conn);
			connectionStats.decrement();

			if (this.equals(conn.getScope())) {
				final IServer server = getServer();
				if (server instanceof Server) {
					((Server) server).notifyDisconnected(conn);
				}
			}
		}
		if (hasParent()) {
			parent.disconnect(conn);
		}
	}

	/** [EMAIL PROTECTED] */
	@Override
	public void dispatchEvent(IEvent event) {
		Iterator<IConnection> conns = getConnections();
		while (conns.hasNext()) {
			try {
				conns.next().dispatchEvent(event);
			} catch (RuntimeException e) {
				log.error("{}", e);
			}
		}
	}

	/** [EMAIL PROTECTED] */
	public int getActiveClients() {
		return clients.size();
	}

	/** [EMAIL PROTECTED] */
	public int getActiveConnections() {
		return connectionStats.getCurrent();
	}

	/** [EMAIL PROTECTED] */
	public int getActiveSubscopes() {
		return subscopeStats.getCurrent();
	}

	/**
	 * Return base scope of given type with given name
	 *
	 * @param type
	 *            Scope type
	 * @param name
	 *            Scope name
	 * @return Basic scope object
	 */
	public IBasicScope getBasicScope(String type, String name) {
		return children.get(type + SEPARATOR + name);
	}

	/**
	 * Return basic scope names iterator
	 *
	 * @param type
	 *            Scope type
	 * @return Iterator
	 */
	public Iterator<String> getBasicScopeNames(String type) {
		if (type == null) {
			return children.keySet().iterator();
		} else {
			return new PrefixFilteringStringIterator(children.keySet()
					.iterator(), type + SEPARATOR);
		}
	}

	/**
	 * Return current thread context classloader
	 *
	 * @return Current thread context classloader
	 */
	public ClassLoader getClassLoader() {
		//System.out.println(">>>>> scope: " + Thread.currentThread().getContextClassLoader());
		//System.out.println(">>>>> scope (context): " + getContext().getClassLoader());
		return getContext().getClassLoader();
	}

	/**
	 * Return set of clients
	 *
	 * @return Set of clients bound to scope
	 */
	public Set<IClient> getClients() {
		return clients.keySet();
	}

	/**
	 * Return connection iterator
	 *
	 * @return Connections iterator
	 */
	public Iterator<IConnection> getConnections() {
		return new ConnectionIterator();
	}

	/**
	 * Return scope context. If scope doesn't have context, parent's context is
	 * returns, and so forth.
	 *
	 * @return Scope context or parent context
	 */
	public IContext getContext() {
		if (!hasContext() && hasParent()) {
			//log.debug("returning parent context");
			return parent.getContext();
		} else {
			//log.debug("returning context");
			return context;
		}
	}

	/**
	 * Return scope context path
	 *
	 * @return Scope context path
	 */
	public String getContextPath() {
		if (hasContext()) {
			return "";
		} else if (hasParent()) {
			return parent.getContextPath() + '/' + name;
		} else {
			return null;
		}
	}

	/** [EMAIL PROTECTED] */
	public long getCreationTime() {
		return creationTime;
	}

	/**
	 * return scope depth
	 *
	 * @return Scope depth
	 */
	@Override
	public int getDepth() {
		if (depth == UNSET) {
			if (hasParent()) {
				depth = parent.getDepth() + 1;
			} else {
				depth = 0;
			}
		}
		return depth;
	}

	/**
	 * Return scope handler or parent's scope handler if this scope doesn't have
	 * one
	 *
	 * @return Scope handler (or parent's one)
	 */
	public IScopeHandler getHandler() {
		if (handler != null) {
			return handler;
		} else if (hasParent()) {
			return getParent().getHandler();
		} else {
			return null;
		}
	}

	/** [EMAIL PROTECTED] */
	public int getMaxClients() {
		return clientStats.getMax();
	}

	/** [EMAIL PROTECTED] */
	public int getMaxConnections() {
		return connectionStats.getMax();
	}

	/** [EMAIL PROTECTED] */
	public int getMaxSubscopes() {
		return subscopeStats.getMax();
	}

	/**
	 * Return parent scope
	 *
	 * @return Parent scope
	 */
	@Override
	public IScope getParent() {
		return parent;
	}

	/**
	 * Return scope path calculated from parent path and parent scope name
	 *
	 * @return Scope path
	 */
	@Override
	public String getPath() {
		if (hasParent()) {
			return parent.getPath() + '/' + parent.getName();
		} else {
			return "";
		}
	}

	/**
	 * Return resource located at given path
	 *
	 * @param path
	 *            Resource path
	 * @return Resource
	 */
	public Resource getResource(String path) {
		if (hasContext()) {
			return context.getResource(path);
		}
		return getContext().getResource(getContextPath() + '/' + path);
	}

	/**
	 * Return array of resources from path string, usually used with pattern
	 * path
	 *
	 * @param path
	 *            Resources path
	 * @return Resources
	 * @throws IOException
	 *             I/O exception
	 */
	public Resource[] getResources(String path) throws IOException {
		if (hasContext()) {
			return context.getResources(path);
		}
		return getContext().getResources(getContextPath() + '/' + path);
	}

	/**
	 * Return child scope by name
	 *
	 * @param name
	 *            Scope name
	 * @return Child scope with given name
	 */
	public IScope getScope(String name) {
		// -------------------------------------
		// Nathan Smith
		// Synchronize removal and retrieval of child scopes
		IScope scope;

		// Get the child lock
		Object lock = getChildLock(name);

		// Synchronize retrieval of the child scope (with removal)
		synchronized (lock) {

			// Get the scope
			scope = (IScope) children.get(TYPE + SEPARATOR + name);
		}

		return scope;
		// -------------------------------------
	}


	// -------------------------------------
	// Nathan Smith
	// Synchronize removal and retrieval of child scopes

	/**
	 * Get lock object to use as mutex for a child scope.
	 */
	private synchronized Object getChildLock(String name) {

		Object lock = childrenLocks.get(name);

		if (lock == null) {
			lock = new Object();
			childrenLocks.put(name, lock);
		}

		return lock;

	}

	/**
	 * Remove lock object to use as mutex (for GC purposes)
	 */
	private void removeChildLock(String name) {
		childrenLocks.remove(name);
	}
	// -------------------------------------

	/**
	 * Return child scope names iterator
	 *
	 * @return Child scope names iterator
	 */
	public Iterator<String> getScopeNames() {
		return new PrefixFilteringStringIterator(children.keySet().iterator(),
				"scope");
	}

	/**
	 * Return service handler by name
	 *
	 * @param name
	 *            Handler name
	 * @return Service handler with given name
	 */
	public Object getServiceHandler(String name) {
		Map<String, Object> serviceHandlers = getServiceHandlers(false);
		if (serviceHandlers == null) {
			return null;
		}
		return serviceHandlers.get(name);
	}

	/**
	 * Return set of service handler names. Removing entries from the set
	 * unregisters the corresponding service handler.
	 *
	 * @return Set of service handler names
	 */
	@SuppressWarnings("unchecked")
	public Set<String> getServiceHandlerNames() {
		Map<String, Object> serviceHandlers = getServiceHandlers(false);
		if (serviceHandlers == null) {
			return Collections.EMPTY_SET;
		}
		return serviceHandlers.keySet();
	}

	/**
	 * Return map of service handlers. The map is created if it doesn't exist
	 * yet.
	 *
	 * @return Map of service handlers
	 */
	protected Map<String, Object> getServiceHandlers() {
		return getServiceHandlers(true);
	}

	/**
	 * Return map of service handlers and optionally created it if it doesn't
	 * exist.
	 *
	 * @param allowCreate
	 *            Should the map be created if it doesn't exist?
	 * @return Map of service handlers
	 */
	protected Map<String, Object> getServiceHandlers(boolean allowCreate) {
		if (serviceHandlers == null) {
			if (allowCreate) {
				serviceHandlers = new ConcurrentHashMap<String, Object>();
			}
		}
		return serviceHandlers;
	}

	/** [EMAIL PROTECTED] */
	public IScopeStatistics getStatistics() {
		return this;
	}

	/** [EMAIL PROTECTED] */
	public int getTotalClients() {
		return clientStats.getTotal();
	}

	/** [EMAIL PROTECTED] */
	public int getTotalConnections() {
		return connectionStats.getTotal();
	}

	/** [EMAIL PROTECTED] */
	public int getTotalSubscopes() {
		return subscopeStats.getTotal();
	}

	/**
	 * Handles event. To be implemented in subclasses.
	 *
	 * @param event
	 *            Event to handle
	 * @return <code>true</code> on success, <code>false</code> otherwise
	 */
	@Override
	public boolean handleEvent(IEvent event) {
		return false;
	}

	/**
	 * Check whether scope has child scope with given name
	 *
	 * @param name
	 *            Child scope name
	 * @return <code>true</code> if scope has child node with given name,
	 *         <code>false</code> otherwise
	 */
	public boolean hasChildScope(String name) {
		log.debug("Has child scope? {} in {}", name, this);

		// -------------------------------------
		// Nathan Smith
		// Synchronize removal and retrieval of child scopes
		boolean has;

		// Get the child lock
		Object lock = getChildLock(name);

		// Synchronize retrieval of the child scope (with removal)
		synchronized (lock) {
			has = children.containsKey(TYPE + SEPARATOR + name);
		}

		return has;
		// -------------------------------------
	}

	/**
	 * Check whether scope has child scope with given name and type
	 *
	 * @param type
	 *            Child scope type
	 * @param name
	 *            Child scope name
	 * @return <code>true</code> if scope has child node with given name and
	 *         type, <code>false</code> otherwise
	 */
	public boolean hasChildScope(String type, String name) {

		// -------------------------------------
		// Nathan Smith
		// Synchronize removal and retrieval of child scopes
		boolean has;

		// Get the child lock
		Object lock = getChildLock(name);

		// Synchronize retrieval of the child scope (with removal)
		synchronized (lock) {
			has = children.containsKey(type + SEPARATOR + name);
		}

		return has;
		// -------------------------------------
	}

	/**
	 * Check if scope has a context
	 *
	 * @return <code>true</code> if scope has context, <code>false</code>
	 *         otherwise
	 */
	public boolean hasContext() {
		return context != null;
	}

	/**
	 * Check if scope or it's parent has handler
	 *
	 * @return <code>true</code> if scope or it's parent scope has a handler,
	 *         <code>false</code> otherwise
	 */
	public boolean hasHandler() {
		return (handler != null || (hasParent() && getParent().hasHandler()));
	}

	/**
	 * Check if scope has parent scope
	 *
	 * @return <code>true</code> if scope has parent scope, <code>false</code>
	 *         otherwise`
	 */
	@Override
	public boolean hasParent() {
		return (parent != null);
	}

	/**
	 * Initialization actions, start if autostart is set to <code>true</code>
	 */
	public void init() {
		log.debug("Init scope");
		if (hasParent()) {
			if (!parent.hasChildScope(name)) {
				if (!parent.addChildScope(this)) {
					return;
				}
			}
		}
		if (autoStart) {
			start();
		}
	}

	/**
	 * Uninitialize scope and unregister from parent.
	 */
	public void uninit() {
		log.debug("Un-init scope");
		for (IBasicScope child : children.values()) {
			if (child instanceof Scope) {
				((Scope) child).uninit();
			}
		}
		stop();
		if (hasParent()) {
			if (parent.hasChildScope(name)) {
				parent.removeChildScope(this);
			}
		}
	}

	/**
	 * Check if scope is enabled
	 *
	 * @return <code>true</code> if scope is enabled, <code>false</code>
	 *         otherwise
	 */
	public boolean isEnabled() {
		return enabled;
	}

	/**
	 * Here for JMX only, uses isEnabled()
	 */
	public boolean getEnabled() {
		return isEnabled();
	}

	/**
	 * Check if scope is in running state
	 *
	 * @return <code>true</code> if scope is in running state,
	 *         <code>false</code> otherwise
	 */
	public boolean isRunning() {
		return running;
	}

	/**
	 * Here for JMX only, uses isEnabled()
	 */
	public boolean getRunning() {
		return isRunning();
	}

	/**
	 * Child scopes iterator
	 *
	 * @return Child scopes iterator
	 */
	@Override
	public Iterator<IBasicScope> iterator() {
		return children.values().iterator();
	}

	/**
	 * Looks up connections for client
	 *
	 * @param client
	 *            Client
	 * @return Connection
	 */
	public Set<IConnection> lookupConnections(IClient client) {
		return clients.get(client);
	}

	/**
	 * Register service handler by name
	 *
	 * @param name
	 *            Service handler name
	 * @param handler
	 *            Service handler
	 */
	public void registerServiceHandler(String name, Object handler) {
		Map<String, Object> serviceHandlers = getServiceHandlers();
		serviceHandlers.put(name, handler);
	}

	/**
	 * Removes child scope
	 *
	 * @param scope
	 *            Child scope to remove
	 */
	public void removeChildScope(IBasicScope scope) {

		//  -------------------------------------
		// Nathan Smith
		// Synchronize removal and retrieval of child scopes

		// Get the child lock
		Object lock = getChildLock(scope.getName());

		// Synchronize retrieval of the child scope (with removal)
		synchronized (lock) {

			// -------------------------------------
			// Nathan Smith
			// Don't remove if reference if we have another one
			if (hasChildScope(scope.getName())
					&& getScope(scope.getName()) != scope) {
				log.warn("Being asked to remove wrong scope reference "
						+ "child scope is {} not {}", new Object[] {
						getScope(scope.getName()), scope });
				return;
			}
			// -------------------------------------

			log.debug("Remove child scope: {}", scope);
			if (scope instanceof IScope) {
				if (hasHandler()) {
					getHandler().stop((IScope) scope);
				}
				subscopeStats.decrement();
			}
			children.remove(scope.getType() + SEPARATOR + scope.getName());
		}

		// Can remove lock now as getScope() will no longer resolve to this child
		removeChildLock(scope.getName());

		// -------------------------------------

		if (hasHandler()) {
			log.debug("Remove child scope");
			getHandler().removeChildScope(scope);
		}
		scope.setStore(null);

		if (scope instanceof IScope) {
			final IServer server = getServer();
			if (server instanceof Server) {
				((Server) server).notifyScopeRemoved((IScope) scope);
			}
		}
	}

	/**
	 * Setter for autostart flag
	 *
	 * @param autoStart
	 *            Autostart flag value
	 */
	public void setAutoStart(boolean autoStart) {
		this.autoStart = autoStart;
	}

	/**
	 * Setter for child load path. Should be implemented in subclasses?
	 *
	 * @param pattern
	 *            Load path pattern
	 */
	public void setChildLoadPath(String pattern) {

	}

	/**
	 * Setter for context
	 *
	 * @param context
	 *            Context object
	 */
	public void setContext(IContext context) {
		log.debug("Set context: {}", context);
		this.context = context;
	}

	/**
	 * Set scope depth
	 *
	 * @param depth
	 *            Scope depth
	 */
	public void setDepth(int depth) {
		this.depth = depth;
	}

	/**
	 * Enable or disable scope by setting enable flag
	 *
	 * @param enabled
	 *            Enable flag value
	 */
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	/**
	 * Setter for scope event handler
	 *
	 * @param handler
	 *            Event handler
	 */
	public void setHandler(IScopeHandler handler) {
		log.debug("Set handler: {}", handler);
		this.handler = handler;
		if (handler instanceof IScopeAware) {
			((IScopeAware) handler).setScope(this);
		}
	}

	/**
	 * Setter for scope name
	 *
	 * @param name
	 *            Scope name
	 */
	@Override
	public void setName(String name) {
		log.debug("Set name: {}", name);
		if (oName != null) {
			JMXAgent.unregisterMBean(oName);
			oName = null;
		}
		this.name = name;

		if (StringUtils.isNotBlank(name)) {
			try {
				String className = getClass().getName();
				if (className.indexOf('.') != -1) {
					//strip package stuff
					className = className
							.substring(className.lastIndexOf('.') + 1);
				}
				oName = new ObjectName(JMXFactory.getDefaultDomain() + ":type="
						+ className + ",name=" + name);
			} catch (MalformedObjectNameException e) {
				log.error("Invalid object name. {}", e);
			}
			JMXAgent.registerMBean(this, this.getClass().getName(),
					ScopeMBean.class, oName);
		}
	}

	/**
	 * Setter for parent scope
	 *
	 * @param parent
	 *            Parent scope
	 */
	public void setParent(IScope parent) {
		log.debug("Set parent scope: {}", parent);
		this.parent = parent;
	}

	/**
	 * Set scope persistence class
	 *
	 * @param persistenceClass
	 *            Scope's persistence class
	 * @throws Exception
	 *             Exception
	 */
	public void setPersistenceClass(String persistenceClass) throws Exception {
		this.persistenceClass = persistenceClass;
		if (persistenceClass != null) {
			setStore(PersistenceUtils.getPersistenceStore(this,
					persistenceClass));
		} else {
			setStore(null);
		}
	}

	/**
	 * Starts scope
	 *
	 * @return <code>true</code> if scope has handler and it's start method
	 *         returned true, <code>false</code> otherwise
	 */
	public synchronized boolean start() {
		log.debug("Start scope");
		boolean result = false;
		if (enabled && !running) {
			if (hasHandler()) {
				// Only start if scope handler allows it
				try {
					// if we dont have a handler of our own dont try to start it
					if (handler != null) {
						result = handler.start(this);
					}
				} catch (Throwable e) {
					log.error("Could not start scope {}. {}", this, e);
				}
			} else {
				// Always start scopes without handlers
				log.debug("Scope {} has no handler, allowing start.", this);
				result = true;
			}
			running = result;
		}
		return result;
	}

	/**
	 * Stops scope
	 */
	public synchronized void stop() {
		log.debug("Stop scope");
		if (enabled && running && hasHandler()) {
			try {
				// if we dont have a handler of our own dont try to stop it
				if (handler != null) {
					handler.stop(this);
				}
			} catch (Throwable e) {
				log.error("Could not stop scope " + this, e);
			}
		}
		running = false;
	}

	/**
	 * [EMAIL PROTECTED]
	 */
	@Override
	public String toString() {
		final ToStringCreator tsc = new ToStringCreator(this);
		return tsc.append("Depth", getDepth()).append("Path", getPath())
				.append("Name", getName()).toString();
	}

	/**
	 * Unregisters service handler by name
	 *
	 * @param name
	 *            Service handler name
	 */
	public void unregisterServiceHandler(String name) {
		Map<String, Object> serviceHandlers = getServiceHandlers(false);
		if (serviceHandlers == null) {
			return;
		}
		serviceHandlers.remove(name);
	}

	/**
	 * Return the server instance connected to this scope.
	 *
	 * @return the server instance
	 */
	public IServer getServer() {
		if (!hasParent()) {
			return null;
		}
		final IScope parent = getParent();
		if (parent instanceof Scope) {
			return ((Scope) parent).getServer();
		} else if (parent instanceof IGlobalScope) {
			return ((IGlobalScope) parent).getServer();
		} else {
			return null;
		}
	}

}
_______________________________________________
osflash mailing list
[email protected]
http://osflash.org/mailman/listinfo/osflash_osflash.org

Reply via email to