package org.red5.server;

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

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;
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.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 (red5@osflash.org)
 */
public class Scope extends BasicScope implements IScope, IScopeStatistics,
		ScopeMBean {

	private static final Logger logger = Logger.getLogger(Scope.class);

	/**
	 * 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();
		}

		/**
		 * {@inheritDoc}
		 */
		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;
		}

		/**
		 * {@inheritDoc}
		 */
		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;
		}

		/**
		 * {@inheritDoc}
		 */
		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;
		}

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

		/** {@inheritDoc} */
		public String next() {
			if (next != null) {
				final String result = next;
				next = null;
				return result.substring(prefix.length());
			}
			if (hasNext()) {
				return next();
			} else {
				return null;
			}
		}

		/** {@inheritDoc} */
		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

	/**
	 * Logger
	 */
	protected static Log log = LogFactory.getLog(Scope.class.getName());

	/**
	 * 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 Map<String, IBasicScope> children = new ConcurrentHashMap<String, IBasicScope>();

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

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

	/**
	 * Statistics about connections to the scope.
	 */
	private 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 Map<String, Object> serviceHandlers;

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

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

	/**
	 * Creates unnamed scope
	 */
	public Scope() {
		this(null);
		creationTime = System.currentTimeMillis();
	}

	/**
	 * 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)) {
			if (log.isDebugEnabled()) {
				log
						.debug("Failed to add child scope: " + scope + " to "
								+ this);
			}
			return false;
		}
		if (scope instanceof IScope) {
			// start the scope
			if (hasHandler() && !getHandler().start((IScope) scope)) {
				if (log.isDebugEnabled()) {
					log.debug("Failed to start child scope: " + scope + " in "
							+ this);
				}
				return false;
			}
		}
		if (scope instanceof IScope) {
			final IServer server = getServer();
			if (server instanceof Server) {
				((Server) server).notifyScopeCreated((IScope) scope);
			}
		}
		if (log.isDebugEnabled()) {
			log.debug("Add child scope: " + scope + " to " + 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
	 * params. Client associated with connection is added to scope clients set,
	 * connection is registred as scope event listener.
	 * 
	 * @param conn
	 *            Connection object
	 * @param params
	 *            Params passed with connection
	 * @return <code>true</code> on success, <code>false</code> otherwise
	 */
	public synchronized boolean connect(IConnection conn, Object[] params) {
		// log.debug("Connect: "+conn+" to "+this);
		// log.debug("has handler? "+hasHandler());
		if (hasParent() && !parent.connect(conn, params)) {
			return false;
		}
		if (hasHandler() && !getHandler().connect(conn, this, params)) {
			return false;
		}
		final IClient client = conn.getClient();
		// log.debug("connected to: "+this);
		if (!clients.containsKey(client)) {
			// log.debug("Joining: "+this);
			if (hasHandler() && !getHandler().join(client, this)) {
				return false;
			}
			final Set<IConnection> conns = new HashSet<IConnection>();
			conns.add(conn);
			clients.put(conn.getClient(), conns);
			log.debug("adding client");
			clientStats.increment();
		} else {
			final Set<IConnection> conns = clients.get(client);
			conns.add(conn);
		}
		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 destory() {
		if (hasParent()) {
			parent.removeChildScope(this);
		}
		if (hasHandler()) {
			handler.stop(this);
			// TODO: kill all child scopes
		}
	}

	/**
	 * Disconnect connection from scope
	 * 
	 * @param conn
	 *            Connection object
	 */
	public synchronized 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 && clients.containsKey(client)) {
			final Set<IConnection> conns = clients.get(client);
			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 "
									+ conn + " on handler " + 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 "
								+ client + " on handler " + 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);
		}
	}

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

	/** {@inheritDoc} */
	public int getActiveClients() {
		return clients.size();
	}

	/** {@inheritDoc} */
	public int getActiveConnections() {
		return connectionStats.getCurrent();
	}

	/** {@inheritDoc} */
	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() {
		return Thread.currentThread().getContextClassLoader();
	}

	/**
	 * 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;
		}
	}

	/** {@inheritDoc} */
	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;
		}
	}

	/** {@inheritDoc} */
	public int getMaxClients() {
		return clientStats.getMax();
	}

	/** {@inheritDoc} */
	public int getMaxConnections() {
		return connectionStats.getMax();
	}

	/** {@inheritDoc} */
	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) {
		return (IScope) children.get(TYPE + SEPARATOR + 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)
				return null;

			// Only synchronize if potentially needs to be created
			synchronized (this) {
				if (serviceHandlers == null) {
					serviceHandlers = new ConcurrentHashMap<String, Object>();
				}
			}
		}
		return serviceHandlers;
	}

	/** {@inheritDoc} */
	public IScopeStatistics getStatistics() {
		return this;
	}

	/** {@inheritDoc} */
	public int getTotalClients() {
		return clientStats.getTotal();
	}

	/** {@inheritDoc} */
	public int getTotalConnections() {
		return connectionStats.getTotal();
	}

	/** {@inheritDoc} */
	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) {
		if (log.isDebugEnabled()) {
			log.debug("Has child scope? " + name + " in " + this);
		}
		// log.debug("Children: " + children);
		return children.containsKey(TYPE + SEPARATOR + name);
	}

	/**
	 * 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) {
		return children.containsKey(type + SEPARATOR + name);
	}

	/**
	 * 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() {
		if (hasParent()) {
			if (!parent.hasChildScope(name)) {
				if (!parent.addChildScope(this)) {
					return;
				}
			}
		}
		if (autoStart) {
			start();
		}
	}

	/**
	 * Uninitialize scope and unregister from parent.
	 */
	public void uninit() {
		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;
	}

	/**
	 * 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;
	}

	/**
	 * 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) {
		if (scope instanceof IScope) {
			if (hasHandler()) {
				getHandler().stop((IScope) scope);
			}
			subscopeStats.decrement();
		}
		children.remove(scope.getType() + SEPARATOR + 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) {
		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) {
		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) {
		if (oName != null) {
			JMXAgent.unregisterMBean(oName);
			oName = null;
		}
		this.name = name;

		if (name != null) {
			try {
				oName = new ObjectName(JMXFactory.getDefaultDomain() + ":type="
						+ getClass().getName() + ",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) {
		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() {
		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
				if (log.isDebugEnabled()) {
					log.debug("Scope " + this
							+ " has no handler, allowing start.");
				}
				result = true;
			}
			running = result;
		}
		return result;
	}

	/**
	 * Stops scope
	 */
	public synchronized void stop() {
		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;
	}

	/**
	 * {@inheritDoc}
	 */
	@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;
		}
	}

}
