/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.slee.container.management;

import java.io.ObjectStreamException;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.management.ObjectName;
import javax.slee.InvalidArgumentException;
import javax.slee.InvalidStateException;
import javax.slee.SLEEException;
import javax.slee.SbbID;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.management.DependencyException;
import javax.slee.management.DeploymentException;
import javax.slee.management.LinkNameAlreadyBoundException;
import javax.slee.management.ResourceAdaptorEntityAlreadyExistsException;
import javax.slee.management.ResourceAdaptorEntityNotification;
import javax.slee.management.ResourceAdaptorEntityState;
import javax.slee.management.ResourceManagementMBean;
import javax.slee.management.UnrecognizedLinkNameException;
import javax.slee.management.UnrecognizedResourceAdaptorEntityException;
import javax.slee.management.UnrecognizedResourceAdaptorException;
import javax.slee.resource.ConfigProperties;
import javax.slee.resource.InvalidConfigurationException;
import javax.slee.resource.ResourceAdaptorID;
import javax.slee.resource.ResourceAdaptorTypeID;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;

import org.apache.log4j.Logger;
import org.mobicents.slee.container.AbstractSleeContainerModule;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.component.ComponentRepository;
import org.mobicents.slee.container.component.classloading.ReplicationClassLoader;
import org.mobicents.slee.container.component.ra.ResourceAdaptorComponent;
import org.mobicents.slee.container.component.ratype.ResourceAdaptorTypeComponent;
import org.mobicents.slee.container.component.sbb.ResourceAdaptorEntityBindingDescriptor;
import org.mobicents.slee.container.component.sbb.ResourceAdaptorTypeBindingDescriptor;
import org.mobicents.slee.container.component.sbb.SbbComponent;
import org.mobicents.slee.container.management.jmx.ResourceUsageMBean;
import org.mobicents.slee.container.resource.ResourceAdaptorEntity;
import org.mobicents.slee.container.resource.ResourceAdaptorObjectState;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.mobicents.slee.resource.ActivityHandleReferenceFactory;
import org.mobicents.slee.resource.ResourceAdaptorEntityImpl;
import org.mobicents.slee.resource.deployment.ResourceAdaptorClassCodeGenerator;
import org.mobicents.slee.resource.deployment.ResourceAdaptorTypeClassCodeGenerator;

/**
 *
 * Manages deployed resource adaptor components.
 *
 * @author Eduardo Martins
 *
 */
public final class ResourceManagementImpl extends AbstractSleeContainerModule implements ResourceManagement {

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

	/**
	 * the resource adaptor entities mapped by name
	 */
	private final ConcurrentHashMap<String, ResourceAdaptorEntityImpl> resourceAdaptorEntities;

	/**
	 * the set of resource adaptor entities aggregated per ra type, this is a runtime cache for optimal performance on ra type activity context factories
	 */
	private final ConcurrentHashMap<ResourceAdaptorTypeID, Set<ResourceAdaptorEntity>> entitiesPerType = new ConcurrentHashMap<ResourceAdaptorTypeID, Set<ResourceAdaptorEntity>>();

	/**
	 * the resource adaptor entity name links
	 */
	private final ConcurrentHashMap<String, String> resourceAdaptorEntityLinks;

	/**
	 * singleton
	 */
	private static final ResourceManagementImpl INSTANCE = new ResourceManagementImpl();

	private ActivityHandleReferenceFactory handleReferenceFactory;

	public static ResourceManagementImpl getInstance(){
		return INSTANCE;
	}

	private ResourceManagementImpl() {
		super();
		resourceAdaptorEntities = new ConcurrentHashMap<String, ResourceAdaptorEntityImpl>();
		resourceAdaptorEntityLinks = new ConcurrentHashMap<String, String>();
	}

	private Object readResolve() throws ObjectStreamException {
		// fools serialization
		return INSTANCE;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@Override
	protected Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	// --- MBEAN methods

	/**
	 * @see ResourceManagementMBean#createResourceAdaptorEntity(ResourceAdaptorID,
	 *      String, ConfigProperties)
	 */
	public void createResourceAdaptorEntity(ResourceAdaptorID id,
			String entityName, ConfigProperties properties)
	throws NullPointerException, InvalidArgumentException,
	UnrecognizedResourceAdaptorException,
	ResourceAdaptorEntityAlreadyExistsException,
	InvalidConfigurationException {

		if (logger.isDebugEnabled()) {
			logger.debug("Creating RA Entity. Id: " + id + ", name: "
					+ entityName + ", properties: " + properties);
		}

		if (id == null) {
			throw new NullPointerException("null ra id");
		}

		if (properties == null) {
			throw new NullPointerException("null config properties");
		}

		/*
		 * javax.slee.InvalidArgumentExceptionThis exception is thrown if
		 * the resource adaptor entity name argument is not a valid. A
		 * resource adaptor entity name cannot be null or zero-length, and
		 * may only contain letter or digit characters as defined by
		 * java.lang.Character.isLetterOrDigit. Additionally, any other
		 * character in the Unicode range 0x0020-0x007e may be included in a
		 * resource adaptor entity name.
		 */
		if (entityName == null) {
			throw new InvalidArgumentException("entityName is null");
		}
		if (entityName.length() == 0) {
			throw new InvalidArgumentException("entityName is zero length");
		}
		validateNewEntityOrLinkName(entityName);

		ResourceAdaptorComponent component = getResourceAdaptorComponent(id);
		if (component == null) {
			throw new UnrecognizedResourceAdaptorException(
					"Failed to create RA Entity. RA ID: " + id
					+ " not found.");
		}
		else {
			final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			Thread.currentThread().setContextClassLoader(component.getClassLoader());
			try {
				if (this.resourceAdaptorEntities.containsKey(entityName)) {
					throw new ResourceAdaptorEntityAlreadyExistsException(
							"Failed to create RA Entity. Resource Adpator Entity Name: "
							+ entityName + " already exists! RA ID: " + id);
				}

				TraceManagement traceMBeanImpl = sleeContainer.getTraceManagement();
				ResourceAdaptorEntityNotification notificationSource = new ResourceAdaptorEntityNotification(entityName);
				traceMBeanImpl.registerNotificationSource(notificationSource);

				// create resource usage mbean
				ResourceUsageMBean usageMBean = null;
				if (component.getUsageParametersInterface() != null) {
					try {
						usageMBean = sleeContainer.getUsageParametersManagement().newResourceUsageMBean(entityName, component);
					} catch (Throwable e) {
						if (usageMBean != null) {
							usageMBean.remove();
						}
						throw new SLEEException("failed to create and register entity resource usage mbean",e);
					}
				}

				ResourceAdaptorEntityImpl raEntity =null;
				try {
					raEntity = new ResourceAdaptorEntityImpl(
							entityName, component, properties, this,notificationSource, usageMBean);
				}
				catch (InvalidConfigurationException e) {
					traceMBeanImpl.deregisterNotificationSource(notificationSource);
					throw e;
				}
				catch (InvalidArgumentException e) {
					traceMBeanImpl.deregisterNotificationSource(notificationSource);
					throw e;
				}
				catch (SLEEException e) {
					traceMBeanImpl.deregisterNotificationSource(notificationSource);
					throw e;
				}
				catch (Throwable e) {
					traceMBeanImpl.deregisterNotificationSource(notificationSource);
					throw new SLEEException(e.getMessage(),e);
				}

				for (ResourceAdaptorTypeID resourceAdaptorTypeID : component.getSpecsDescriptor().getResourceAdaptorTypes()) {
					Set<ResourceAdaptorEntity> set = entitiesPerType.get(resourceAdaptorTypeID);
					if (set == null) {
						throw new SLEEException("there is no set of ra entities for "+resourceAdaptorTypeID);
					}
					else {
						set.add(raEntity);
					}
				}
				this.resourceAdaptorEntities.put(entityName, raEntity);

				logger.info("Created Resource Adaptor Entity "+entityName+" for " + id+" Config Properties: " + properties);
			}
			finally {
				Thread.currentThread().setContextClassLoader(classLoader);
			}
		}

	}

	/**
	 * @see ResourceManagementMBean#activateResourceAdaptorEntity(String)
	 */
	public void activateResourceAdaptorEntity(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException, InvalidStateException {

		if (logger.isDebugEnabled()) {
			logger.debug("Activating RA Entity " + entityName);
		}

		if (entityName == null) {
			throw new NullPointerException("null entity name");
		}

		final ResourceAdaptorEntity raEntity = this.resourceAdaptorEntities
		.get(entityName);
		if (raEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(
					"Resource Adaptor Entity " + entityName + " not found.");
		} else {
			final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			Thread.currentThread().setContextClassLoader(raEntity.getComponent().getClassLoader());
			try {
				raEntity.activate();
				logger.info("Activated RA Entity " + entityName);
			}
			finally {
				Thread.currentThread().setContextClassLoader(classLoader);
			}
		}
	}

	/**
	 * @see ResourceManagementMBean#deactivateResourceAdaptorEntity(String)
	 */
	public void deactivateResourceAdaptorEntity(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException, InvalidStateException {

		if (logger.isDebugEnabled()) {
			logger.debug("Deactivating RA Entity " + entityName);
		}

		if (entityName == null) {
			throw new NullPointerException("null entity name");
		}

		final ResourceAdaptorEntity raEntity = this.resourceAdaptorEntities
		.get(entityName);
		if (raEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(
					"Resource Adaptor Entity " + entityName + " not found.");
		} else {
			final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			Thread.currentThread().setContextClassLoader(raEntity.getComponent().getClassLoader());
			boolean rollback = true;
			try {
				sleeContainer.getTransactionManager().begin();
				raEntity.deactivate();
				rollback = false;
			} catch (NotSupportedException e) {
				throw new SLEEException(e.getMessage(),e);
			} catch (SystemException e) {
				throw new SLEEException(e.getMessage(),e);
			} catch (TransactionRequiredLocalException e) {
				throw new SLEEException(e.getMessage(),e);
			}
			finally {
				try {
					if (rollback) {
						sleeContainer.getTransactionManager().rollback();
					}
					else {
						sleeContainer.getTransactionManager().commit();
					}
				}
				catch (Throwable e) {
					throw new SLEEException(e.getMessage(),e);
				}
				finally {
					Thread.currentThread().setContextClassLoader(classLoader);
				}
			}
			logger.info("Deactivated RA Entity " + entityName);
		}

	}

	/**
	 * @see ResourceManagementMBean#removeResourceAdaptorEntity(String)
	 */
	public void removeResourceAdaptorEntity(String entityName)
			throws java.lang.NullPointerException,
			UnrecognizedResourceAdaptorEntityException, InvalidStateException,
			DependencyException {

		if (logger.isDebugEnabled()) {
			logger.debug("Removing RA Entity " + entityName);
		}

		if (entityName == null) {
			throw new NullPointerException("null entity name");
		}

		final ResourceAdaptorEntityImpl raEntity = this.resourceAdaptorEntities
		.get(entityName);
		if (raEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(
					"Resource Adaptor Entity " + entityName + " not found.");
		}
		else {
			final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			Thread.currentThread().setContextClassLoader(raEntity.getComponent().getClassLoader());
			try {
				if (this.resourceAdaptorEntityLinks.containsValue(entityName)) {
					throw new DependencyException("entity name has link(s)");
				}

				raEntity.remove();

				this.resourceAdaptorEntities.remove(entityName);

				for (ResourceAdaptorTypeID resourceAdaptorTypeID : raEntity.getComponent().getSpecsDescriptor().getResourceAdaptorTypes()) {
					Set<ResourceAdaptorEntity> set = entitiesPerType.get(resourceAdaptorTypeID);
					if (set == null) {
						throw new SLEEException("there is no set of ra entities for "+resourceAdaptorTypeID);
					}
					else {
						set.remove(raEntity);
					}
				}

				ResourceUsageMBean resourceUsageMBeanImpl = raEntity.getResourceUsageMBean();
				if (resourceUsageMBeanImpl != null) {
					resourceUsageMBeanImpl.remove();
				}

				logger.info("Removed RA Entity " + entityName);
			}
			finally {
				Thread.currentThread().setContextClassLoader(classLoader);
			}

		}
	}

	/**
	 *
	 * @see ResourceManagementMBean#updateConfigurationProperties(String,
	 *      ConfigProperties)
	 */
	public void updateConfigurationProperties(String entityName,
			ConfigProperties properties) throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException, InvalidStateException,
			InvalidConfigurationException {

		if (logger.isDebugEnabled()) {
			logger.debug("Updating RA Entity with properties: " + properties);
		}

		if (entityName == null) {
			throw new NullPointerException("null entity name");
		}

		if (properties == null) {
			throw new NullPointerException("null config properties");
		}

		final ResourceAdaptorEntity raEntity = this.resourceAdaptorEntities
				.get(entityName);
		if (raEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(
					"Resource Adaptor Entity " + entityName + " not found.");
		} else {
			final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			Thread.currentThread().setContextClassLoader(raEntity.getComponent().getClassLoader());
			try {
				raEntity.updateConfigurationProperties(properties);
				logger.info("Updated RA Entity with properties: " + properties);
			}
			finally {
				Thread.currentThread().setContextClassLoader(classLoader);
			}
		}
	}

	/**
	 * @see ResourceManagementMBean#bindLinkName(String, String)
	 */
	public void bindLinkName(String linkName, String entityName)
			throws NullPointerException, InvalidArgumentException,
			UnrecognizedResourceAdaptorEntityException,
			LinkNameAlreadyBoundException {

		if (logger.isDebugEnabled()) {
			logger.debug("Binding link between RA Entity " + entityName
					+ " and Name " + linkName);
		}
		if (linkName == null) {
			throw new NullPointerException("null link name");
		}
		if (entityName == null) {
			throw new NullPointerException("null entity name");
		}

		if (this.resourceAdaptorEntityLinks.containsKey(linkName)) {
			throw new LinkNameAlreadyBoundException(linkName);
		}
		if (!this.resourceAdaptorEntities.containsKey(entityName)) {
			throw new UnrecognizedResourceAdaptorEntityException(entityName);
		}
		validateNewEntityOrLinkName(linkName);
		this.resourceAdaptorEntityLinks.put(linkName, entityName);
		logger.info("Bound link between RA Entity " + entityName
				+ " and Name " + linkName);

	}

	/**
	 * @see ResourceManagementMBean#unbindLinkName(String)
	 */
	public void unbindLinkName(String linkName) throws NullPointerException,
			UnrecognizedLinkNameException, DependencyException {

		if (logger.isDebugEnabled()) {
			logger.debug("Unbinding RA Entity Link " + linkName);
		}

		if (sleeContainer.getServiceManagement().isRAEntityLinkNameReferenced(linkName)) {
			throw new DependencyException(linkName + " link name is still used by sbbs");
		} else {
			this.resourceAdaptorEntityLinks.remove(linkName);
			logger.info("Unbound RA Entity Link " + linkName);
		}

	}

	/**
	 *
	 * @see ResourceManagementMBean#getResourceAdaptorEntities(ResourceAdaptorEntityState)
	 */
	public String[] getResourceAdaptorEntities(ResourceAdaptorEntityState state)
			throws NullPointerException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA Entities with state " + state);
		}
		if (state == null) {
			throw new NullPointerException("null entity state");
		}
		HashSet<String> resultEntityNames = new HashSet<String>();
		for (ResourceAdaptorEntity resourceAdaptorEntity : resourceAdaptorEntities
				.values()) {
			if (resourceAdaptorEntity.getState() == state) {
				resultEntityNames.add(resourceAdaptorEntity.getName());
			}
		}
		String[] resultEntityNamesArray = new String[resultEntityNames.size()];
		resultEntityNamesArray = resultEntityNames
				.toArray(resultEntityNamesArray);
		if (logger.isTraceEnabled()) {
			logger.trace("Got RA Entities with state " + state + " : "
					+ resultEntityNames);
		}
		return resultEntityNamesArray;
	}

	/**
	 * @see ResourceManagementMBean#getLinkNames()
	 */
	public String[] getLinkNames() {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA link names");
		}
		String[] linkNames = getLinkNamesSet().toArray(
				new String[0]);
		if (logger.isTraceEnabled()) {
			logger.trace("Got RA link names : " + Arrays.asList(linkNames));
		}
		return linkNames;

	}

	/**
	 * Retrieves a copy of the current set of ra entity links
	 * @return
	 */
	public Set<String> getLinkNamesSet() {
		return Collections.unmodifiableSet(resourceAdaptorEntityLinks.keySet());
	}

	/**
	 * @see ResourceManagementMBean#getLinkNames(String)
	 */
	public String[] getLinkNames(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA link names for entity name " + entityName);
		}

		if (entityName == null)
			throw new NullPointerException("null entity name");

		if (!this.resourceAdaptorEntities.containsKey(entityName)) {
			throw new UnrecognizedResourceAdaptorEntityException(entityName);
		}

		HashSet<String> linkNames = new HashSet<String>();
		for (String linkName : resourceAdaptorEntityLinks.keySet()) {
			if (resourceAdaptorEntityLinks.get(linkName).equals(entityName)) {
				linkNames.add(linkName);
			}
		}

		String[] linkNamesArray = new String[linkNames.size()];
		linkNamesArray = linkNames.toArray(linkNamesArray);

		if (logger.isTraceEnabled()) {
			logger.trace("Got RA link names for entity " + entityName + " : "
					+ linkNames);
		}
		return linkNamesArray;

	}

	/**
	 * @see ResourceManagementMBean#getBoundSbbs(String)
	 */
	public SbbID[] getBoundSbbs(String linkName) throws NullPointerException,
			UnrecognizedLinkNameException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting sbbs bound to link name " + linkName);
		}

		if (linkName == null) {
			throw new NullPointerException("null link name");
		}

		if (!this.resourceAdaptorEntityLinks.containsKey(linkName)) {
			throw new UnrecognizedLinkNameException(linkName);
		}
		final Set<SbbID> boundSbbsSet = new HashSet<SbbID>();
		final ComponentRepository componentRepository = sleeContainer
		.getComponentRepository();
		for (SbbID sbbID : componentRepository.getSbbIDs()) {
			SbbComponent sbbComponent = componentRepository
			.getComponentByID(sbbID);
			for (ResourceAdaptorTypeBindingDescriptor raTypeBinding : sbbComponent
					.getDescriptor().getResourceAdaptorTypeBindings()) {
				for (ResourceAdaptorEntityBindingDescriptor raEntityBinding : raTypeBinding
						.getResourceAdaptorEntityBinding()) {
					if (raEntityBinding.getResourceAdaptorEntityLink()
							.equals(linkName)) {
						boundSbbsSet.add(sbbID);
					}
				}
			}
		}
		SbbID[] result = boundSbbsSet
		.toArray(new SbbID[boundSbbsSet.size()]);
		if (logger.isTraceEnabled()) {
			logger.trace("Got sbbs bound to link name " + linkName + " : "
					+ boundSbbsSet);
		}
		return result;

	}

	/**
	 * @see ResourceManagementMBean#getConfigurationProperties(ResourceAdaptorID)
	 */
	public ConfigProperties getConfigurationProperties(ResourceAdaptorID id)
			throws NullPointerException, UnrecognizedResourceAdaptorException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting configuration properties for RA with id "
					+ id);
		}

		if (id == null)
			throw new NullPointerException("null resource adaptor id");

		ResourceAdaptorComponent component = getResourceAdaptorComponent(id);
		if (component == null) {
			throw new UnrecognizedResourceAdaptorException(
					"unrecognized resource adaptor " + id.toString());
		} else {
			return component.getDefaultConfigPropertiesInstance();
		}
	}

	/**
	 * @see ResourceManagementMBean#getConfigurationProperties(String)
	 */
	public ConfigProperties getConfigurationProperties(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting configuration properties for RA entity with name "
							+ entityName);
		}

		if (entityName == null)
			throw new NullPointerException("null entity name");

		final ResourceAdaptorEntity resourceAdaptorEntity = this.resourceAdaptorEntities
				.get(entityName);
		if (resourceAdaptorEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(entityName);
		} else {
			return resourceAdaptorEntity.getConfigurationProperties();
		}
	}

	/**
	 * @see ResourceManagementMBean#getResourceAdaptor(String)
	 */
	public ResourceAdaptorID getResourceAdaptor(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA ID for RA entity with name " + entityName);
		}

		if (entityName == null)
			throw new NullPointerException("null entity name");

		ResourceAdaptorEntity resourceAdaptorEntity = (ResourceAdaptorEntity) resourceAdaptorEntities
				.get(entityName);
		if (resourceAdaptorEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException("Entity "
					+ entityName + " not found");
		} else {
			return resourceAdaptorEntity.getResourceAdaptorID();
		}
	}

	/**
	 * @see ResourceManagementMBean#getResourceAdaptorEntities()
	 */
	public String[] getResourceAdaptorEntities() {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA entity names");
		}

		return resourceAdaptorEntities.keySet().toArray(new String[0]);
	}

	/**
	 * @see ResourceManagementMBean#getResourceAdaptorEntities(ResourceAdaptorID)
	 */
	public String[] getResourceAdaptorEntities(
			ResourceAdaptorID resourceAdaptorID) throws NullPointerException,
			UnrecognizedResourceAdaptorException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA entity names for ra with ID "
					+ resourceAdaptorID);
		}
		if (resourceAdaptorID == null) {
			throw new NullPointerException("null resource adaptor");
		}

		ResourceAdaptorComponent component = getResourceAdaptorComponent(resourceAdaptorID);
		if (component == null) {
			throw new UnrecognizedResourceAdaptorException(
					resourceAdaptorID.toString());
		} else {
			Set<String> entityNameSet = new HashSet<String>();
			for (ResourceAdaptorEntity raEntity : resourceAdaptorEntities.values()) {
				if (raEntity.getResourceAdaptorID().equals(resourceAdaptorID)) {
					entityNameSet.add(raEntity.getName());
				}
			}
			String[] entityNames = entityNameSet
			.toArray(new String[entityNameSet.size()]);
			if (logger.isTraceEnabled()) {
				logger.trace("Got RA entity names : "
						+ Arrays.asList(entityNames));
			}
			return entityNames;
		}

	}

	/**
	 * @see ResourceManagementMBean#getResourceAdaptorEntities(String[])
	 */
	public String[] getResourceAdaptorEntities(String[] linkNames)
			throws NullPointerException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA entity name for link names : "
					+ Arrays.asList(linkNames));
		}

		if (linkNames == null)
			throw new NullPointerException("null link names");

		String[] resultEntityNames = new String[linkNames.length];
		for (int i = 0; i < linkNames.length; i++) {
			String entityName = resourceAdaptorEntityLinks
			.get(linkNames[i]);
			resultEntityNames[i] = entityName;
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Got RA entity names : "
					+ Arrays.asList(resultEntityNames));
		}

		return resultEntityNames;

	}

	/**
	 * @see ResourceManagementMBean#getResourceAdaptorEntity(String linkName)
	 */
	public String getResourceAdaptorEntityName(String linkName)
			throws NullPointerException, UnrecognizedLinkNameException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting RA entity name for link name " + linkName);
		}

		if (linkName == null)
			throw new NullPointerException("null link name");

		final String entityName = resourceAdaptorEntityLinks.get(linkName);
		if (entityName == null)
			throw new UnrecognizedLinkNameException(linkName);

		if (logger.isTraceEnabled()) {
			logger.trace("Got RA entity name " + entityName + " for link name "
					+ linkName);
		}

		return entityName;
	}

	/**
	 * @see ResourceManagementMBean#getState(String)
	 */
	public ResourceAdaptorEntityState getState(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException {

		if (logger.isTraceEnabled()) {
			logger.trace("Getting state for RA entity with name " + entityName);
		}

		if (entityName == null)
			throw new NullPointerException("null entity name");

		final ResourceAdaptorEntity resourceAdaptorEntity = this.resourceAdaptorEntities
				.get(entityName);
		if (resourceAdaptorEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(entityName);
		} else {
			return resourceAdaptorEntity.getState();
		}
	}

	/**
	 * @see ResourceManagementMBean#getResourceUsageMBean(String)
	 */
	public ObjectName getResourceUsageMBean(String entityName)
			throws NullPointerException,
			UnrecognizedResourceAdaptorEntityException,
			InvalidArgumentException {

		if (entityName == null)
			throw new NullPointerException("null entity name");

		final ResourceAdaptorEntityImpl resourceAdaptorEntity = this.resourceAdaptorEntities
				.get(entityName);

		if (resourceAdaptorEntity == null) {
			throw new UnrecognizedResourceAdaptorEntityException(entityName);
		} else {
			ResourceUsageMBean mbean = resourceAdaptorEntity.getResourceUsageMBean();
			if (mbean == null) {
				throw new InvalidArgumentException(" Resource Adaptor entity "+entityName+" related resource adaptor does not defines a usage paramters interface. See section 14.2 in SLEE 1.1 specs for more info");
			}
			else {
				// ensure it is open
				mbean.open();
				// return its object name
				return mbean.getObjectName();
			}
		}
	}

	// --- additonal operations

	/**
	 * Retrieves the {@link ResourceAdaptorEntity} with the specified entity
	 * name.
	 *
	 */
	public ResourceAdaptorEntityImpl getResourceAdaptorEntity(String entityName) {
		return this.resourceAdaptorEntities
				.get(entityName);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.management.ResourceManagement#getResourceAdaptorEntitiesPerType(javax.slee.resource.ResourceAdaptorTypeID)
	 */
	public Set<ResourceAdaptorEntity> getResourceAdaptorEntitiesPerType(ResourceAdaptorTypeID resourceAdaptorTypeID) {
		return entitiesPerType.get(resourceAdaptorTypeID);
	}

	private ResourceAdaptorComponent getResourceAdaptorComponent(
			ResourceAdaptorID id) {
		return sleeContainer.getComponentRepository().getComponentByID(id);
	}

	private void validateNewEntityOrLinkName(String name)
			throws InvalidArgumentException {
		for (int i = 0; i < name.length(); i++) {
			char c = name.charAt(i);
			if (!Character.isLetterOrDigit(c)) {
				if (c < '\u0020' || c > '\u007e') {
					throw new InvalidArgumentException(
							name
									+ " includes char "
									+ c
									+ " ,which is not a letter or digit or in unicode range 0x0020-0x007e");
				}
			}
		}
	}

	@Override
	public String toString() {
		return "Resource Management: "
			+ "\n+-- Resource Adaptor Entities: " + resourceAdaptorEntities.keySet()
			+ "\n+-- Resource Adaptor Entity Links: " + resourceAdaptorEntityLinks.keySet()
			+ "\n+-- Resource Adaptor Entities per RA Type size: "	+ entitiesPerType.size()
			+ "\n+-- Activity Handle Reference Factory: "	+ handleReferenceFactory;

	}

	/**
	 * Installs the specified {@link ResourceAdaptorTypeComponent} in the container
	 * @param component
	 * @throws DeploymentException
	 */
	public void installResourceAdaptorType(
			ResourceAdaptorTypeComponent component) throws DeploymentException {
		if (logger.isDebugEnabled()) {
			logger.debug("Installing " + component);

		}
		// generate code for aci factory
		new ResourceAdaptorTypeClassCodeGenerator().process(component);
		// create instance of aci factory and store it in the
		// component
		if (component.getActivityContextInterfaceFactoryConcreteClass() != null) {
			try {
				Constructor<?> constructor = component
						.getActivityContextInterfaceFactoryConcreteClass()
						.getConstructor(
								new Class[] { SleeContainer.class,
										ResourceAdaptorTypeID.class });
				Object aciFactory = constructor.newInstance(new Object[] {
						sleeContainer, component.getResourceAdaptorTypeID() });
				component.setActivityContextInterfaceFactory(aciFactory);
			} catch (Throwable e) {
				throw new SLEEException(
						"unable to create ra type aci factory instance", e);
			}
		}
		final ResourceAdaptorTypeID resourceAdaptorTypeID = component.getResourceAdaptorTypeID();
		entitiesPerType.put(
				resourceAdaptorTypeID,
				new HashSet<ResourceAdaptorEntity>());
		TransactionalAction action = new TransactionalAction() {
			public void execute() {
				entitiesPerType.remove(resourceAdaptorTypeID);

			}
		};
		sleeContainer.getTransactionManager().getTransactionContext().getAfterRollbackActions().add(action);
	}

	/**
	 * Installs the specified {@link ResourceAdaptorComponent} in the container
	 * @param component
	 * @throws DeploymentException
	 */
	public void installResourceAdaptor(final ResourceAdaptorComponent component) throws DeploymentException {
		if (logger.isDebugEnabled()) {
			logger.debug("Installing " + component);

		}
		new ResourceAdaptorClassCodeGenerator().process(component);
		// if we are in cluster mode we need to add the RA class loader domain to the replication class loader
		if (!sleeContainer.getCluster().getMobicentsCache().isLocalMode()) {
			final ReplicationClassLoader replicationClassLoader = sleeContainer.getReplicationClassLoader();
			replicationClassLoader.addDomain(component.getClassLoaderDomain());
			final TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
			if (txContext != null) {
				TransactionalAction action = new TransactionalAction() {
					public void execute() {
						replicationClassLoader.removeDomain(component.getClassLoaderDomain());
					}
				};
				txContext.getAfterRollbackActions().add(action);
			}
		}
	}

	/**
	 * Uninstalls the specified {@link ResourceAdaptorTypeComponent} from the container
	 * @param component
	 */
	public void uninstallResourceAdaptorType(final ResourceAdaptorTypeComponent component) {
		if (logger.isDebugEnabled()) {
			logger.debug("Uninstalling " + component);

		}
		final ResourceAdaptorTypeID resourceAdaptorTypeID = component.getResourceAdaptorTypeID();
		final TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
		TransactionalAction action1 = new TransactionalAction() {
			public void execute() {
				entitiesPerType.remove(resourceAdaptorTypeID);
			}
		};
		if (txContext != null) {
			txContext.getAfterCommitActions().add(action1);
		}
		else {
			action1.execute();
		}

		// if we are in cluster mode we need to remove the RA class loader domain from the replication class loader
		if (!sleeContainer.getCluster().getMobicentsCache().isLocalMode()) {
			final ReplicationClassLoader replicationClassLoader = sleeContainer.getReplicationClassLoader();
			TransactionalAction action2 = new TransactionalAction() {
				public void execute() {
					replicationClassLoader.removeDomain(component.getClassLoaderDomain());
				}
			};
			if (txContext != null) {
				txContext.getAfterCommitActions().add(action2);
			}
			else {
				action2.execute();
			}
		}

	}

	/**
	 * Uninstalls the specified {@link ResourceAdaptorComponent} from the container
	 * @param component
	 * @throws DependencyException
	 */
	public void uninstallResourceAdaptor(ResourceAdaptorComponent component) throws DependencyException {
		if (logger.isDebugEnabled()) {
			logger.debug("Uninstalling " + component);

		}
		for (ResourceAdaptorEntity raEntity : resourceAdaptorEntities.values()) {
			if (raEntity.getResourceAdaptorID().equals(component.getResourceAdaptorID())) {
				throw new DependencyException("can't uninstall "+component.getResourceAdaptorID()+" since ra entity "+raEntity.getName()+" refers it");
			}
		}
	}

	@Override
	public void sleeInitialization() {
		if (!sleeContainer.getCluster().getMobicentsCache().isLocalMode()) {
			handleReferenceFactory = new ActivityHandleReferenceFactory(this);
		}
	}

	@Override
	public void sleeStarting() {
		if (handleReferenceFactory != null) {
			handleReferenceFactory.init();
		}
	}

	@Override
	public void sleeRunning() {
		final Thread currentThread = Thread.currentThread();
		final ClassLoader currentThreadClassLoader = currentThread.getContextClassLoader();
		for (ResourceAdaptorEntity raEntity : resourceAdaptorEntities.values()) {
			try {
				currentThread.setContextClassLoader(raEntity.getComponent().getClassLoader());
				raEntity.sleeRunning();
			} catch (Exception e) {
				if (logger.isDebugEnabled()) {
					logger.debug(e.getMessage(), e);
				}
			}
		}
		currentThread.setContextClassLoader(currentThreadClassLoader);
	}

	@Override
	public void sleeStopping() {

		logger.info("Stopping all active resource adaptors ...");

		// inform all ra entities that we are stopping the container
		final Thread currentThread = Thread.currentThread();
		final ClassLoader currentThreadClassLoader = currentThread.getContextClassLoader();
		for (ResourceAdaptorEntity raEntity : resourceAdaptorEntities.values()) {
			try {
				currentThread.setContextClassLoader(raEntity.getComponent().getClassLoader());
				raEntity.sleeStopping();
			} catch (Exception e) {
				logger.error(e.getMessage(),e);
			}
		}
		currentThread.setContextClassLoader(currentThreadClassLoader);

		// wait till all ra entity objects are stopped
		boolean loop;
		boolean sleepInLastLoop = false;
		do {
			loop = false;
			for (ResourceAdaptorEntity raEntity : resourceAdaptorEntities.values()) {
				try {
					if (raEntity.getResourceAdaptorObject()
							.getState() == ResourceAdaptorObjectState.STOPPING) {
						logger.info("Waiting for ra entity "+raEntity.getName()+" to stop...");
						loop = true;
						sleepInLastLoop = true;
					}
				} catch (Exception e) {
					if (logger.isDebugEnabled()) {
						logger.debug(e.getMessage(), e);
					}
				}
			}
			if (loop || sleepInLastLoop) {
				try {
					// wait a sec
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					logger.error(e.getMessage(), e);
				}
			}
		} while (loop);
		if (handleReferenceFactory != null) {
			handleReferenceFactory.remove();
		}
	}

	/**
	 *
	 * @return
	 */
	public ActivityHandleReferenceFactory getHandleReferenceFactory() {
		return handleReferenceFactory;
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/*
 * Created on Jul 14, 2004
 *
 * The source code contained in this file is in in the public domain.
 * It can be used in any project, or product without prior permission,
 * license or royalty payments. There is no claim of correctness and
 * NO WARRANTY OF ANY KIND provided with this code.
 */
package org.mobicents.slee.runtime.sbbentity;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.slee.CreateException;
import javax.slee.SLEEException;
import javax.slee.SbbLocalObject;
import javax.slee.TransactionRequiredLocalException;
import javax.transaction.SystemException;

import org.apache.log4j.Logger;
import org.mobicents.slee.SbbLocalObjectExt;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.component.sbb.GetChildRelationMethodDescriptor;
import org.mobicents.slee.container.sbbentity.ChildRelation;
import org.mobicents.slee.container.sbbentity.SbbEntity;
import org.mobicents.slee.container.sbbentity.SbbEntityID;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.runtime.sbb.SbbLocalObjectImpl;

/**
 *
 * Implements the ChildRelation interface
 *
 * @author F.Moggia
 * @author M. Ranganathan
 * @author Ralf Siedow
 * @author eduardomartins
 *
 *
 */
public class ChildRelationImpl implements ChildRelation {

    /**
	 *
	 */
	private static final long serialVersionUID = 1L;

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

	private static final SleeContainer sleeContainer = SleeContainer.lookupFromJndi();

	private GetChildRelationMethodDescriptor getChildRelationMethod;
    private SbbEntityImpl sbbEntity;

    private HashSet<SbbLocalObject> getLocalObjects() {
        HashSet<SbbLocalObject> localObjects = new HashSet<SbbLocalObject>();
        SbbEntity childSbbEntity = null;
        for (SbbEntityID sbbEntityId : sbbEntity.cacheData.getChildRelationSbbEntities(getChildRelationMethod.getChildRelationMethodName())) {
            childSbbEntity = sleeContainer.getSbbEntityFactory().getSbbEntity(sbbEntityId, false);
        	if (childSbbEntity != null) {
        		localObjects.add(childSbbEntity.getSbbLocalObject());
        	}
        }
        return localObjects;
    }

    /**
     * Creates a new instance of a child relation
     * @param getChildRelationMethod the child relation method
     * @param sbbEntity the sbb entity that owns this child relation
     */
    public ChildRelationImpl(GetChildRelationMethodDescriptor getChildRelationMethod,
             SbbEntityImpl sbbEntity) {

        if (getChildRelationMethod == null)
            throw new NullPointerException("null getChildRelationMethod");

        this.sbbEntity = sbbEntity;
        this.getChildRelationMethod = getChildRelationMethod;
    }

    @SuppressWarnings("rawtypes")
	public Iterator iterator() {
        return new ChildRelationIterator();
    }

    /**
     * The contains method. This method returns true if the SBB entity
     * represented by the SBB local object specified by the input argument is a
     * member of this child relation. If the method argument is not an SBB local
     * object, is an invalid SBB local object, or is an SBB local object whose
     * underlying SBB entity is not a member of this child relation, then this
     * method returns false.
     *
     */
    public boolean contains(Object object) {

    	if (!(object instanceof SbbLocalObject))
            return false;

    	final SbbLocalObjectImpl sbblocal = (SbbLocalObjectImpl) object;
        final SbbEntityID sbbEntityId = sbblocal.getSbbEntityId();
        if(!idBelongsToChildRelation(sbbEntityId)) {
        	return false;
        }

        return new SbbEntityCacheData(sbbEntityId,sleeContainer.getCluster().getMobicentsCache()).exists();
    }

    private boolean idBelongsToChildRelation(SbbEntityID sbbEntityID) {
    	 if(sbbEntityID.isRootSbbEntity()) {
         	return false;
         }
         if(!sbbEntityID.getParentChildRelation().equals(getChildRelationMethod.getChildRelationMethodName())) {
         	return false;
         }
         if(!sbbEntityID.getParentSBBEntityID().equals(sbbEntity.getSbbEntityId())) {
         	return false;
         }
         return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.slee.ChildRelation#create()
     */
    public SbbLocalObject create() throws CreateException,
            TransactionRequiredLocalException, SLEEException {
    	return create(sleeContainer.getUuidGenerator().createUUID());
    }

    /*
     * (non-Javadoc)
     *
     * @see java.util.Collection#size()
     */
    public int size() {
        return sbbEntity.cacheData.getChildRelationSbbEntities(getChildRelationMethod.getChildRelationMethodName()).size();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.util.Collection#clear()
     */
    @SuppressWarnings("rawtypes")
	public void clear() {
    	sleeContainer.getTransactionManager().mandateTransaction();
    	for (Iterator it = iterator();it.hasNext();) {
    		it.next();
    		it.remove();
    	}
    }

    /*
     * (non-Javadoc)
     *
     * @see java.util.Collection#isEmpty()
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.util.Collection#toArray()
     */
    public Object[] toArray() {

    	sleeContainer.getTransactionManager().mandateTransaction();

        return this.getLocalObjects().toArray();
    }

    /**
     * This operation is not supported ( see page 62 ) of the spec. The add
     * methods add and addAll. Any attempts to add to the child relation using
     * these methods result in a java.lang. UnsupportedOperationException. For
     * example, the SLEE must throw the UnsupportedOperationException when these
     * methods are invoked with arguments that add to the collection. o An
     * invocation of an add method that has no effect on the collection, such as
     * invoking the addAll method with an empty collection may or may not throw
     * an UnsupportedOperationException . The create method of the ChildRelation
     * interface should be used to create a child SBB entity. This causes the
     * SLEE to automatically add an SBB local object that represents the newly
     * created SBB entity to the collection.
     */
    public boolean add(Object object) {
        if (object == null)
            throw new NullPointerException("null arg! ");
        else
            throw new UnsupportedOperationException("Operation not supported !");
    }

    /**
     * Spec page 62 The remove methods: clear, remove, removeAll, and retainAll.
     * o These methods may remove SBB entities from the child relation. The
     * input argument specifies which SBB entities will be removed from the
     * child relation or retained in the child relation by specifying the SBB
     * local object or collection of SBB local objects that represent the SBB
     * entities to be removed or retained. o Removing an SBB entity from a child
     * relation initiates a cascading removal of the SBB entity tree rooted by
     * the SBB entity, similar to invoking the remove method on an SBB local
     * object that represents the SBB entity.
     *
     */
    public boolean remove(Object object) {

    	sleeContainer.getTransactionManager().mandateTransaction();

    	if(logger.isDebugEnabled()) {
        	logger.debug("removing sbb local object " + object);
        }

        if (object == null)
            throw new NullPointerException("Null arg for remove ");

        if (!(object instanceof SbbLocalObject))
            return false;

    	final SbbLocalObjectImpl sbbLocalObjectImpl = (SbbLocalObjectImpl) object;
        if(!idBelongsToChildRelation(sbbLocalObjectImpl.getSbbEntityId()) && !sbbLocalObjectImpl.getSbbEntity().isRemoved()) {
        	return false;
        }
        else {
        	sbbLocalObjectImpl.remove();
        	return true;
        }
    }

    /**
     * This operation is not supported ( see page 62 ) of the spec. The add
     * methods add and addAll. Any attempts to add to the child relation using
     * these methods result in a java.lang. UnsupportedOperationException. For
     * example, the SLEE must throw the UnsupportedOperationException when these
     * methods are invoked with arguments that add to the collection. o An
     * invocation of an add method that has no effect on the collection, such as
     * invoking the addAll method with an empty collection may or may not throw
     * an UnsupportedOperationException . The create method of the ChildRelation
     * interface should be used to create a child SBB entity. This causes the
     * SLEE to automatically add an SBB local object that represents the newly
     * created SBB entity to the collection.
     */
    @SuppressWarnings("rawtypes")
	public boolean addAll(Collection c) {
        if (c == null)
            throw new NullPointerException("Null arg!");
        else
            throw new UnsupportedOperationException("Operation not supported !");
    }

    /**
     * This method returns true if all SBB entities represented by the SBB local
     * objects in the collection specified by the input argument are members of
     * this child relation. If the collection contains an object that is not an
     * SBB local object, an SBB local object that is invalid, or an SBB local
     * object whose underlying SBB entity is not a member of this child
     * relation, then this method returns false.
     */
    @SuppressWarnings("rawtypes")
    public boolean containsAll(Collection c) {

    	if (c == null)
            throw new NullPointerException("null collection!");

        for (Iterator it = c.iterator(); it.hasNext(); ) {
            if (!contains(it.next())) {
            	return false;
            }
        }

        if(logger.isDebugEnabled()) {
        	logger.debug("containsAll : collection = " + c + " > all in child relation");
        }
        return true;
    }

    /**
     * Removing an SBB entity from a child relation initiates a cascading
     * removal of the SBB entity tree rooted by the SBB entity, similar to
     * invoking the remove method on an SBB local object that represents the SBB
     * entity.
     *
     */
    @SuppressWarnings("rawtypes")
    public boolean removeAll(Collection c) {
        boolean flag = true;
        if (c == null)
            throw new NullPointerException(" null collection ! ");
        for ( Iterator it = c.iterator(); it.hasNext();	) {
           flag &= this.remove( it.next());

        }
        return flag;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.util.Collection#retainAll(java.util.Collection)
     */
    @SuppressWarnings("rawtypes")
    public boolean retainAll(Collection c) {
        boolean flag = false;
        if (c == null)
            throw new NullPointerException(" null arg! ");
        for ( Iterator it = this.iterator(); it.hasNext();) {
            if ( ! c.contains(it.next())) {
                flag = true;
                it.remove();
            }

        }
        return flag;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.util.Collection#toArray(java.lang.Object[])
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Object[] toArray(Object[] a) {
        if (a == null)
            throw new NullPointerException("null arg!");
        HashSet localObjects = this.getLocalObjects();
        return localObjects.toArray(a);
    }

    public Set<SbbEntityID> getSbbEntitySet(){
    	return new HashSet<SbbEntityID>(sbbEntity.cacheData.getChildRelationSbbEntities(getChildRelationMethod.getChildRelationMethodName()));
    }

	// --- ITERATOR

	/*
     * JAIN SLEE 1.0 Specification, Final Release Page 62 The iterator method
     * and the Iterator objects returned by this method. Chapter 6 The SBB
     * Abstract Class
     *
     * The SLEE must implement the methods of the Iterator objects returned by
     * the iterator method by following the contract defined by the
     * java.util.Iterator interface. o The next method of the Iterator object
     * returns an object that implements the SBB local interface of the child
     * SBB of the child relation. Java typecast can be used to narrow the
     * returned object reference to the SBB local interface of the child SBB. o
     * The remove method of the Iterator object removes the SBB entity that is
     * represented by the last SBB local object returned by the next method of
     * the Iterator object from the child relation. Removing an SBB entity from
     * a child relation by invoking this remove method initiates a cascading
     * removal of the SBB entity tree rooted by the SBB entity, similar to
     * invoking the remove method on an SBB local object that represents the SBB
     * entity. The behavior of the Iterator object is unspecified if the
     * underlying child relation is modified while the iteration is in progress
     * in any way other than by calling this remove method.
     */
    @SuppressWarnings("rawtypes")
    class ChildRelationIterator implements Iterator {

        private Iterator<SbbEntityID> myIterator;
        private SbbEntityID nextEntity;
        private SbbLocalObject nextSbbLocalObject;

        public ChildRelationIterator() {
        	this.myIterator = getSbbEntitySet().iterator();
        }

        public void remove() {
        	if (nextSbbLocalObject != null) {
        		myIterator.remove();
            	nextSbbLocalObject.remove();
        	}
        	else {
        		throw new IllegalStateException();
        	}
        }

        public boolean hasNext() {
            return myIterator.hasNext();
        }

        public Object next() {
            nextEntity = myIterator.next();
            SbbEntity childSbbEntity = sleeContainer.getSbbEntityFactory().getSbbEntity(nextEntity, false);
            if (childSbbEntity != null) {
            	this.nextSbbLocalObject =  childSbbEntity.getSbbLocalObject();
            	sbbEntity.addChildWithSbbObject(childSbbEntity);
            	return nextSbbLocalObject;
            }
            else {
            	return next();
            }
        }

    }

    // extension methods

    private void validateChildName(String childName) throws IllegalArgumentException, NullPointerException {
    	if (childName == null) {
    		throw new NullPointerException("null child name");
    	}
    	if (childName.isEmpty()) {
    		throw new IllegalArgumentException("empty child name");
    	}
    }

	@Override
	public SbbLocalObjectExt create(String childName) throws CreateException,
			IllegalArgumentException, NullPointerException,
			TransactionRequiredLocalException, SLEEException {

		if (logger.isDebugEnabled())
			logger.debug("Creating child "+childName+" from relation "+getChildRelationMethod.getChildRelationMethodName()+" of entity "+sbbEntity.getSbbEntityId());

		validateChildName(childName);

		final SleeTransactionManager sleeTransactionManager = sleeContainer
				.getTransactionManager();

		sleeTransactionManager.mandateTransaction();

		final SbbEntity childSbbEntity = sleeContainer.getSbbEntityFactory()
				.createNonRootSbbEntity(sbbEntity.getSbbEntityId(),
						getChildRelationMethod.getChildRelationMethodName(),
						childName);

		if (logger.isDebugEnabled())
			logger.debug("Created child Sbb Entity: "
					+ childSbbEntity.getSbbEntityId());

		childSbbEntity.setPriority(getChildRelationMethod.getDefaultPriority());

		/*
		 * Exception handling here must follow Sec. 9.12.1 of spec This is a
		 * non-slee originated method invocation
		 *
		 * If a non-SLEE originated method invocation returns by throwing a
		 * checked exception, then the following occurs: � The state of the
		 * transaction is unaffected � The checked exception is propagated to
		 * the caller. It is expected that the caller will have the appropriate
		 * logic to handle the exception. If a non-SLEE originated method
		 * invocation returns by throwing a RuntimeException, then: � The
		 * transaction is marked for rollback. � The SBB object that was invoked
		 * is discarded, i.e. is moved to the Does Not Exist state. � A
		 * javax.slee.TransactionRolledBackLocalException is propagated to the
		 * caller. The transaction will eventually be rolled back when the
		 * highest level SLEE originated invocation re-turns as described in
		 * Section 9.12.2. The sbbRolledBack method is not invoked for an SBB
		 * originated method transaction because the transa ction is only marked
		 * for rollback and will only be rolled back when the highest level SLEE
		 * originated invocation returns. The sbbRolledBack method is only
		 * invoked on the SBB entity in-voked by the highest level SLEE
		 * originated invocation. If the RuntimeException propagates to the
		 * highest level (i.e. the SLEE originated method invocation returns by
		 * throwing a RuntimeException) the SLEE originated method invocation
		 * exception handling mechanism is init iated.
		 */

		if (System.getSecurityManager()!=null) {
            try {
            	AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                public Object run() throws CreateException {
                	assignSbbObjectToChild(childSbbEntity,sleeTransactionManager);
                    return null;
                }
            });
            }
            catch (PrivilegedActionException e) {
				final Throwable t = e.getCause();
				if (t instanceof CreateException) {
					throw (CreateException) t;
				}
				else {
					throw new SLEEException(t.getMessage(),t);
				}
			}
    	}
        else {
        	assignSbbObjectToChild(childSbbEntity,sleeTransactionManager);
        }
    	sbbEntity.addChildWithSbbObject(childSbbEntity);
		return childSbbEntity.getSbbLocalObject();
	}

	private void assignSbbObjectToChild(final SbbEntity childSbbEntity, final SleeTransactionManager sleeTransactionManager) throws CreateException {
    	final Thread t = Thread.currentThread();
    	final ClassLoader cl = t.getContextClassLoader();
    	t.setContextClassLoader(childSbbEntity.getSbbComponent().getClassLoader());
    	try {
			// All checked exceptions (i.e. CreateException) are propagated to
			// the caller
			childSbbEntity.assignSbbObject();

		} catch (CreateException e) {
			// All RuntimeExceptions are dealt with here
			if (logger.isDebugEnabled())
				logger.error("Caught CreateException in creating child entity",
						e);

			childSbbEntity.trashObject();
			// Propagate exception to caller.
			throw e;
		} catch (Exception e) {
			// All RuntimeExceptions are dealt with here
			logger.error("Caught RuntimeException in creating child entity", e);
			try {
				sleeTransactionManager.setRollbackOnly();
			} catch (SystemException e1) {
				logger.error("Failed to set rollbackonly", e);
			}
			childSbbEntity.trashObject();
		}
        finally {
        	t.setContextClassLoader(cl);
        }
    }

	@Override
	public SbbLocalObjectExt get(String childName)
			throws IllegalArgumentException, NullPointerException,
			TransactionRequiredLocalException, SLEEException {

		if (logger.isTraceEnabled())
			logger.trace("Retreiving child "+childName+" from relation "+getChildRelationMethod.getChildRelationMethodName()+" of entity "+sbbEntity.getSbbEntityId());

		validateChildName(childName);

		SleeTransactionManager sleeTransactionManager = sleeContainer
				.getTransactionManager();

		sleeTransactionManager.mandateTransaction();

		SbbEntity childSbbEntity = sleeContainer.getSbbEntityFactory()
				.getSbbEntity(
						new NonRootSbbEntityID(sbbEntity.getSbbEntityId(),
								getChildRelationMethod
										.getChildRelationMethodName(),
								childName), false);

		if (logger.isDebugEnabled())
			logger.debug("Retrieved child "+childName+" from relation "+getChildRelationMethod.getChildRelationMethodName()+" of entity "+sbbEntity.getSbbEntityId()+" -> "+childSbbEntity);

		return childSbbEntity == null ? null : childSbbEntity
				.getSbbLocalObject();
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright XXXX, Red Hat Middleware LLC, and individual contributors as indicated
 * by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License, v. 2.0.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */
package org.mobicents.tools.twiddle.jslee;

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import org.jboss.console.twiddle.command.CommandContext;
import org.jboss.console.twiddle.command.CommandException;
import org.jboss.logging.Logger;
import org.mobicents.tools.twiddle.AbstractSleeCommand;
import org.mobicents.tools.twiddle.Utils;
import org.mobicents.tools.twiddle.op.AbstractOperation;

import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import java.io.PrintWriter;
import java.util.Arrays;

/**
 * @author baranowb
 *
 */
public class SleeCommand extends AbstractSleeCommand {

	/**
	 *
	 */
	public SleeCommand() {
		super("slee", "This command performs operations on JSLEE SleeManagementMBean.");
		// TODO Auto-generated constructor stub
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.mobicents.slee.tools.twiddle.AbstractSleeCommand#displayHelp()
	 */
	@Override
	public void displayHelp() {
		PrintWriter out = context.getWriter();

		out.println(desc);
		out.println();
		out.println("usage: " + name + " <-operation>");
		out.println();
		out.println("operation:");
		out.println("    -r, --start                     Starts container.");
		out.println("    -s, --stopt                     Stops container.");
		out.println("    -d, --shutdown                  Shutdowns container.");
		out.println("    -i, --info                      Displays information about SLEE container(vendor, version, etc.).");
		//no more supported, since we dont have subsystems?
		out.flush();

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.tools.twiddle.AbstractSleeCommand#processArguments
	 * (java.lang.String[])
	 */
	@Override
	protected void processArguments(String[] args) throws CommandException {
		String sopts = ":rsdi";

		LongOpt[] lopts = {
				new LongOpt("start", LongOpt.NO_ARGUMENT, null, 'r'),
				new LongOpt("stopt", LongOpt.NO_ARGUMENT, null, 's'),
				new LongOpt("shutdown", LongOpt.NO_ARGUMENT, null, 'd'),
				new LongOpt("info", LongOpt.NO_ARGUMENT, null, 'i'),

				 };

		Getopt getopt = new Getopt(null, args, sopts, lopts);
		// getopt.setOpterr(false);

		int code;
		while ((code = getopt.getopt()) != -1) {
			switch (code) {
			case ':':
				throw new CommandException("Option requires an argument: " + args[getopt.getOptind() - 1]);

			case '?':
				throw new CommandException("Invalid (or ambiguous) option: " + args[getopt.getOptind() - 1]);

			case 'r':

				super.operation = new StartOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);
				break;

			case 's':

				super.operation = new StopOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);
				break;
			case 'd':

				super.operation = new ShutdownOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);
				break;
			case 'i':

				super.operation = new InfoOperation(super.context, super.log, this);
				super.operation.buildOperation(getopt, args);
				break;

			default:
				throw new CommandException("Command: \"" + getName() + "\", found unexpected opt: " + args[getopt.getOptind() - 1]);

			}
		}

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.mobicents.slee.tools.twiddle.AbstractSleeCommand#getBeanOName()
	 */
	@Override
	public ObjectName getBeanOName() throws MalformedObjectNameException, NullPointerException {
		return new ObjectName(Utils.SLEE_MANAGEMENT);
	}

	private class StartOperation extends AbstractOperation
	{
		private static final String OPERATION_start= "start";
		public StartOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName =OPERATION_start;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			// TODO Auto-generated method stub

		}
	}
	private class StopOperation extends AbstractOperation
	{
		private static final String OPERATION_stop = "stop";

		public StopOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_stop;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			// TODO Auto-generated method stub

		}
	}
	private class ShutdownOperation extends AbstractOperation
	{
		private static final String OPERATION_shutdown = "shutdown";
		public ShutdownOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			super.operationName = OPERATION_shutdown;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			// TODO Auto-generated method stub

		}
	}

	private class InfoOperation extends AbstractOperation
	{
		private final String[] OPS=new String[]{
				"SleeName",
				"SleeVendor",
				"SleeVersion",
				"Subsystems",
				"State",
		};
		public InfoOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			//no name, since its complicated op. It performs more than one call.
			super.operationName ="complicated!";
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			// TODO Auto-generated method stub

		}

		/* (non-Javadoc)
		 * @see org.mobicents.slee.tools.twiddle.Operation#invoke()
		 */
		@Override
		public void invoke() throws CommandException {
			try {
				// TODO: to optimize invoke method and replace StringBuffer with StringBuilder
				ObjectName on = sleeCommand.getBeanOName();
				MBeanServerConnection conn = context.getServer();
				Object[] parms = getOpArguments().toArray();
				String[] sig = new String[getOpSignature().size()];
				sig = getOpSignature().toArray(sig);
				//operationResult = conn.invoke(on, this.operationName, parms, sig);
				StringBuffer sb = new StringBuffer();
				sb.append("Info[");
				for(int index = 0;index<OPS.length;index++)
				{
					String op = OPS[index];
					sb.append(op);
					sb.append("=");

					Object res = conn.invoke(on, "get"+op, parms, sig);

					if(res instanceof String[])
					{
						String[] result = (String[]) res;
						sb.append(Arrays.toString(result));
					}else
					{
						sb.append(res);
					}

					if(index+1!=OPS.length)
					{
						sb.append(",");
					}
				}
				sb.append("]");
				super.operationResult = sb.toString();
				displayResult();
			}catch(javax.management.InstanceNotFoundException infe)
			{
				//this means no slee running/reployed
				super.operationResult = "No container deployed.";
				displayResult();
			}
			catch (Exception e) {
				//add handle error here?
				throw new CommandException("Failed to invoke \"" + this.operationName + "\" due to: ", e);
			}
		}
	}

}


package javax.slee.resource;

/**
 * This class defines the standard reason error codes for event processing failures reported to
 * resource adaptor objects by the {@link ResourceAdaptor#eventProcessingFailed eventProcessingFailed}
 * callback method.
 *

 * A SLEE vendor may define any implementation-specific reason codes that may be useful
 * or appropriate by extending this class.  The integer representation of
 * implementation-specific event codes must be positive numbers greater than zero.  The
 * OTHER REASON reason code has the reserved value <tt>0</tt>, while negative numbers are
 * reserved for the remainder of the standard reason codes defined in this class.
 *

 * A singleton instance of each enumerated value defined in this class is guaranteed (via
 * an implementation of readResolve() - refer
 * {@link java.io.Serializable java.io.Serializable}), so that equality tests using
 * == are always evaluated correctly.  (This equality test is only guaranteed
 * if this class is loaded in the application's boot class path, rather than dynamically
 * loaded at runtime.)  However, it must also be noted that for any vendor-defined reason
 * code, the {@link #isOtherReason} will return true, while the
 * FailureReason (or subclass of FailureReason) object representing
 * the vendor-defined reason code will not be equal, using the ==
 * operator or the equals method, to {@link FailureReason#OTHER_REASON}.
 * @since SLEE 1.1
 */
public class FailureReason {
    /**
     * An integer representation of {@link #OTHER_REASON}.
     */
    public static final int REASON_OTHER_REASON = 0;

    /**
     * An integer representation of {@link #EVENT_QUEUE_FULL}.
     */
    public static final int REASON_EVENT_QUEUE_FULL = -1;

    /**
     * An integer representation of {@link #EVENT_QUEUE_TIMEOUT}.
     */
    public static final int REASON_EVENT_QUEUE_TIMEOUT = -2;

    /**
     * An integer representation of {@link #SYSTEM_OVERLOAD}.
     */
    public static final int REASON_SYSTEM_OVERLOAD = -3;

    /**
     * An integer representation of {@link #EVENT_MARSHALING_ERROR}.
     */
    public static final int REASON_EVENT_MARSHALING_ERROR = -4;

    /**
     * An integer representation of {@link #FIRING_TRANSACTION_ROLLED_BACK}.
     */
    public static final int REASON_FIRING_TRANSACTION_ROLLED_BACK = -5;

    /**
     * Event failure reason indicating that the event could not be processed for an other reason.
     */
    public static final FailureReason OTHER_REASON = new FailureReason(REASON_OTHER_REASON);

    /**
     * Event failure reason indicating the event could not be enqueued for processing because the
     * event queue is full.
     */
    public static final FailureReason EVENT_QUEUE_FULL = new FailureReason(REASON_EVENT_QUEUE_FULL);

    /**
     * Event failure reason indicating the event timeout period for processing of the event (if
     * supported by the SLEE) expired before the event could be processed.  Typically this means
     * the SLEE is overloaded.
     */
    public static final FailureReason EVENT_QUEUE_TIMEOUT = new FailureReason(REASON_EVENT_QUEUE_TIMEOUT);

    /**
     * Event failure reason indicating the event could not processed due to generic system
     * overload conditions.
     */
    public static final FailureReason SYSTEM_OVERLOAD = new FailureReason(REASON_SYSTEM_OVERLOAD);

    /**
     * Event failure reason indicating the event could not be marshaled by the resource adaptor
     * entity's {@link javax.slee.resource.Marshaler}.
     */
    public static final FailureReason EVENT_MARSHALING_ERROR = new FailureReason(REASON_EVENT_MARSHALING_ERROR);

    /**
     * Event failure reason indicating the transaction in which the event was fired
     * rolled back.
     */
    public static final FailureReason FIRING_TRANSACTION_ROLLED_BACK = new FailureReason(REASON_FIRING_TRANSACTION_ROLLED_BACK);

    /**
     * Get a FailureReason object from an integer value.
     *

     * This method returns the singleton objects for the failure reasons defined in
     * this class.  For all vendor-defined reason codes (those greater than 0 in value),
     * a non-unique FailureReason object is return encapsulating that value.
     * @param reason the reason as an integer.
     * @return a FailureReason object corresponding to reason.
     * @throws IllegalArgumentException if reason is less that 0 and does not
     *         correspond to a SLEE-defined reason code.
     */
    public static FailureReason fromInt(int reason) throws IllegalArgumentException {
        switch (reason) {
            case REASON_OTHER_REASON: return OTHER_REASON;
            case REASON_EVENT_QUEUE_FULL: return EVENT_QUEUE_FULL;
            case REASON_EVENT_QUEUE_TIMEOUT: return EVENT_QUEUE_TIMEOUT;
            case REASON_SYSTEM_OVERLOAD: return SYSTEM_OVERLOAD;
            case REASON_EVENT_MARSHALING_ERROR: return EVENT_MARSHALING_ERROR;
            case REASON_FIRING_TRANSACTION_ROLLED_BACK: return FIRING_TRANSACTION_ROLLED_BACK;
            default:
                if (reason > 0)
                    return new FailureReason(reason);
                else
                    throw new IllegalArgumentException("Invalid failure reason: " + reason);
        }
    }

    /**
     * Get an integer value representation for this FailureReason object.
     * @return an integer value representation for this FailureReason object.
     */
    public final int toInt() {
        return reason;
    }

    /**
     * Determine if this FailureReason object represents the OTHER REASON reason,
     * or represents a vendor-defined reason.
     *

     * This method returns true if the FailureReason object
     * is equal to {@link #OTHER_REASON}, or if the reason code is a vendor-defined
     * reason code, ie. is greater that 0.  This allows fallback to the generic reason
     * code if the consumer of the FailureReason object is not familiar with
     * vendor-defined codes.
     * @return true if this object represents the OTHER REASON reason,
     *       or represents a vendor-defined reason code, false otherwise.
     */
    public final boolean isOtherReason() {
        // REASON_OTHER_REASON == 0
        // vendor-defined codes > 0
        return reason >= 0;
    }

    /**
     * Determine if this FailureReason object represents the EVENT QUEUE FULL reason.
     *

     * This method is effectively equivalent to the conditional test:
     * (this == EVENT_QUEUE_FULL), ie. the code:
     *

     *     if (reason.isEventQueueFull()) ...
     *

     * is interchangeable with the code:
     *

     *    if (reason == FailureReason.EVENT_QUEUE_FULL) ...
     *

     * @return true if this object represents the EVENT QUEUE FULL reason,
     *       false otherwise.
     */
    public final boolean isEventQueueFull() {
        return reason == REASON_EVENT_QUEUE_FULL;
    }

    /**
     * Determine if this FailureReason object represents the EVENT QUEUE TIMEOUT reason.
     *

     * This method is effectively equivalent to the conditional test:
     * (this == EVENT_QUEUE_TIMEOUT), ie. the code:
     *

     *     if (reason.isEventQueueTimeout()) ...
     *

     * is interchangeable with the code:
     *

     *    if (reason == FailureReason.EVENT_QUEUE_TIMEOUT) ...
     *

     * @return true if this object represents the EVENT QUEUE TIMEOUT reason,
     *       false otherwise.
     */
    public final boolean isEventQueueTimeout() {
        return reason == REASON_EVENT_QUEUE_TIMEOUT;
    }

    /**
     * Determine if this FailureReason object represents the SYSTEM OVERLOAD reason.
     *

     * This method is effectively equivalent to the conditional test:
     * (this == SYSTEM_OVERLOAD), ie. the code:
     *

     *     if (reason.isSystemOverload()) ...
     *

     * is interchangeable with the code:
     *

     *    if (reason == FailureReason.SYSTEM_OVERLOAD) ...
     *

     * @return true if this object represents the SYSTEM OVERLOAD reason,
     *       false otherwise.
     */
    public final boolean isSystemOverLoad() {
        return reason == REASON_SYSTEM_OVERLOAD;
    }

    /**
     * Determine if this FailureReason object represents the EVENT MARSHALING ERROR reason.
     *

     * This method is effectively equivalent to the conditional test:
     * (this == EVENT_MARSHALING_ERROR), ie. the code:
     *

     *     if (reason.isEventMarshalingError()) ...
     *

     * is interchangeable with the code:
     *

     *    if (reason == FailureReason.EVENT_MARSHALING_ERROR) ...
     *

     * @return true if this object represents the EVENT MARSHALING ERROR reason,
     *       false otherwise.
     */
    public final boolean isEventMarshalingError() {
        return reason == REASON_EVENT_MARSHALING_ERROR;
    }

    /**
     * Determine if this FailureReason object represents the FIRING TRANSACTION ROLLED BACK reason.
     *

     * This method is effectively equivalent to the conditional test:
     * (this == FIRING_TRANSACTION_ROLLED_BACK), ie. the code:
     *

     *     if (reason.isFiringTransactionRolledBack()) ...
     *

     * is interchangeable with the code:
     *

     *    if (reason == FailureReason.FIRING_TRANSACTION_ROLLED_BACK) ...
     *

     * @return true if this object represents the FIRING TRANSACTION ROLLED BACK reason,
     *       false otherwise.
     */
    public final boolean isFiringTransactionRolledBack() {
        return reason == REASON_FIRING_TRANSACTION_ROLLED_BACK;
    }

    /**
     * Compare this failure reason for equality with another.
     * @param obj the object to compare this with.
     * @return true if obj is an instance of this class
     *        representing the same failure reason as this, false otherwise.
     */
    public boolean equals(Object obj) {
        if (obj == this) return true;

        return (obj instanceof FailureReason) && ((FailureReason)obj).reason == reason;
    }

    /**
     * Get a hash code value for this failure reason error code.
     * @return a hash code value.
     */
    public final int hashCode() {
        return reason;
    }

    /**
     * Get the textual representation of the FailureReason object.
     * @return the textual representation of the FailureReason object.
     */
    public String toString() {
        switch (reason) {
            case REASON_OTHER_REASON: return "Other reason";
            case REASON_EVENT_QUEUE_FULL: return "Event queue full";
            case REASON_EVENT_QUEUE_TIMEOUT: return "Event timed out while queued";
            case REASON_SYSTEM_OVERLOAD: return "System overload";
            case REASON_EVENT_MARSHALING_ERROR: return "Event marshaling error";
            case REASON_FIRING_TRANSACTION_ROLLED_BACK: return "Firing transaction rolled back";
            default: return "Vendor-defined reason: " + reason;
        }
    }

    /**
     * Protected constructor that allows subclasses to extend this class with additional
     * vendor-defined reason codes, while preventing unauthorized object creation.
     * @param reason the reason code.
     * @throws IllegalArgumentException if a vendor-defined reason code is less than or
     *        equal to 0.
     */
    protected FailureReason(int reason) {
        if (reason <= 0 && !getClass().equals(FailureReason.class))
            throw new IllegalArgumentException("Invalid vendor-defined reason code: " + reason);

        this.reason = reason;
    }

    /**
     * Get the reason code provided in the constructor.  This method allows subclasses
     * to implement a {@link #fromInt} method and override the {@link #toString} method
     * if desired.
     * @return the reason code.
     */
    protected final int getReason() {
        return reason;
    }

    /**
     * Resolve deserialisation references so that the singleton property of each
     * defined enumerated object is preserved.
     */
    private Object readResolve() {
        return fromInt(reason);
    }

    /**
     * The internal state representation of the enumerated type.
     */
    private final int reason;
}


package javax.slee.management;

import javax.management.ObjectName;

/**
 * This class identifies a notification such as an {@link AlarmNotification alarm} or
 * {@link TraceNotification trace} notification as being generated from some internal
 * subsystem in the SLEE.
 * @since SLEE 1.1
 */
public final class SubsystemNotification extends AbstractNotificationSource implements NotificationSource {
    /**
     * The JMX notification type of alarm notifications that are generated in response
     * to an internal component or subsystem of the SLEE interacting with the
     * {@link javax.slee.facilities.AlarmFacility} (or a similar internal interface).
     *

     * The notification type is equal to the string "javax.slee.management.alarm.subsystem".
     */
    public static final String ALARM_NOTIFICATION_TYPE = "javax.slee.management.alarm.subsystem";

    /**
     * The JMX notification type of trace notifications that are generated in response
     * to an internal component or subsystem of the SLEE interacting with a
     * {@link javax.slee.facilities.Tracer} object (or a similar internal interface).
     *

     * The notification type is equal to the string "javax.slee.management.trace.subsystem".
     */
    public static final String TRACE_NOTIFICATION_TYPE = "javax.slee.management.trace.subsystem";

    /**
     * The JMX notification type of usage notifications that are generated by a
     * {@link javax.slee.usage.UsageMBean} containing a SubsystemNotification
     * as a notification source.
     *

     * The notification type is equal to the string "javax.slee.management.usage.subsystem".
     */
    public static final String USAGE_NOTIFICATION_TYPE = "javax.slee.management.usage.subsystem";

    /**
     * The JMX Object Name property key that identifies the name of the internal subsystem
     * in a Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "subsystemName".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @since SLEE 1.1
     */
    public static final String SUBSYSTEM_NAME_KEY = "subsystemName";

    /**
     * Create a new SubsystemNotification object that uniquely identifies an
     * internal component or subsystem of the SLEE.
     * @param subsystemName the name of the SLEE internal component or subsystem.  Subsystem
     *        names may be any vendor-defined string.
     * @throws NullPointerException if subsystemName is null.
     */
    public SubsystemNotification(String subsystemName) {
        if (subsystemName == null) throw new NullPointerException("entityName is null");
        this.subsystemName = subsystemName;
    }

    /**
     * Get the name of the internal component or subsystem of the SLEE identified by this
     * notification source.
     * @return the name of the internal component or subsystem.
     */
    public String getSubsystemName() {
        return subsystemName;
    }

    /**
     * Get the JMX notification type of alarm notifications generated in response
     * to a SLEE internal component or subsystem interacting with the Alarm Facility.
     * @return the string defined by {@link #ALARM_NOTIFICATION_TYPE}.
     */
    public String getAlarmNotificationType() {
        return ALARM_NOTIFICATION_TYPE;
    }

    /**
     * Get the JMX notification type of trace notifications generated in response
     * to a SLEE internal component or subsystem interacting with the Trace Facility.
     * @return the string defined by {@link #TRACE_NOTIFICATION_TYPE}.
     */
    public String getTraceNotificationType() {
        return TRACE_NOTIFICATION_TYPE;
    }

    /**
     * Get the JMX notification type of usage notifications generated in response
     * to a SLEE internal component or subsystem interacting with its usage parameters.
     * @return the string defined by {@link #USAGE_NOTIFICATION_TYPE}.
     */
    public String getUsageNotificationType() {
        return USAGE_NOTIFICATION_TYPE;
    }

    /**
     * Get a JMX Object Name property string that uniquely identifies the specified SLEE
     * internal component or subsystem, suitable for inclusion in the Object Name of a Usage
     * MBean. This method makes use of the {@link javax.management.ObjectName#quote}
     * method to ensure that the name is valid for inclusion as property values in an Object
     * Name.
     *

     * This method can be used as follows to manually construct a complete Object Name
     * for a Usage MBean:
     *

     *     ObjectName name = new ObjectName(

     *         {@link javax.slee.usage.UsageMBean#BASE_OBJECT_NAME} + "," +

     *         {@link javax.slee.usage.UsageMBean#USAGE_PARAMETER_SET_NAME_KEY} + "=" + ObjectName.quote(paramSetName) + ","   // optional

     *         {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY} + "=" + {@link #USAGE_NOTIFICATION_TYPE SubsystemNotification.USAGE_NOTIFICATION_TYPE} + "," +

     *         {@link #getUsageMBeanProperties(String) SubsystemNotification.getUsageMBeanProperties(subsystemName)}

     *     );
     *

     * @param subsystemName the name of the SLEE internal component or subsystem.
     * @return an Object Name property string that uniquely identifies the specified
     *        internal component or subsystem.
     * @throws NullPointerException if subsystem is null.
     */
    public static String getUsageMBeanProperties(String subsystemName) {
        if (subsystemName == null) throw new NullPointerException("subsystemName is null");
        return SUBSYSTEM_NAME_KEY + '=' + ObjectName.quote(subsystemName);
    }

    /**
     * Get a JMX Object Name property string that uniquely identifies the SLEE internal
     * component or subsystem of this notification source, suitable for inclusion in
     * the Object Name of a Usage MBean.
     *

     * This method is equivalent to {@link #getUsageMBeanProperties(String) getUsageMBeanProperties(getSubsystemName())}.
     * @return an Object Name property string that uniquely identifies the
     *        internal component or subsystem.
     * @return an Object Name property string that uniquely identifies the SLEE
     *        internal component or subsystem of this notification source.
     */
    public String getUsageMBeanProperties() {
        return getUsageMBeanProperties(subsystemName);
    }

    /**
     * Compare this notification source for equality with another object.
     * @param obj the object to compare this with.
     * @return true if obj is an instance of this class and
     *        contains the subsystem name as this, false otherwise.
     */
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof SubsystemNotification)) return false;

        return this.subsystemName.equals(((SubsystemNotification)obj).subsystemName);
    }

    /**
     * Get a hash code value for this notification source.
     * @return a hash code value for this notification source.
     */
    public int hashCode() {
        return subsystemName.hashCode();
    }

    /**
     * Get a string representation for this notification source.
     * @return a string representation for this notification source.
     * @see Object#toString()
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("SubsystemNotification[subsystem=").append(subsystemName).append(']');
        return buf.toString();
    }

    /**
     * Compare this notification source with the specified object for order.
     * Returns a negative integer, zero, or a positive integer if this object
     * is less than, equal to, or greater than the specified object.
     *

     * If obj is a SubsystemNotification, order is
     * determined by comparing the encapsulated subsystem name.  Otherwise, if
     * obj is a NotificationSource, ordering is determined
     * by comparing the class name of this class with the class name of obj.
     *

     * @param obj the object to compare this with.
     * @return a negative integer, zero, or a positive integer if this notification
     *        source is considered less than, equal to, or greater than the
     *        specified object.
     * @throws ClassCastException if obj does not implement the
     *        {@link NotificationSource} interface.
     * @see Comparable#compareTo(Object)
     */
    public int compareTo(Object obj) {
        // can't compare with null
        if (obj == null) throw new NullPointerException("obj is null");
        if (obj == this) return 0;

        if (obj instanceof SubsystemNotification) {
            // compare the profile table name
            SubsystemNotification that = (SubsystemNotification)obj;
            return this.subsystemName.compareTo(that.subsystemName);
        }
        else {
            return super.compareTo(TYPE, obj);
        }
    }

    // protected

    protected String getClassName() {
        return TYPE;
    }

    private final String subsystemName;

    // constant to avoid expensive getClass() invocations at runtime
    private static final String TYPE = SubsystemNotification.class.getName();
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.slee.container.deployment.profile.jpa;

import java.io.Serializable;

import javax.slee.Address;

import org.mobicents.slee.container.profile.entity.ProfileEntity;

/**
 * Abstract base class of a profile entity array attribute value (jpa entity) wrapper.
 *
 * @author martins
 *
 */
public abstract class ProfileEntityArrayAttributeValue {

	// FIXME replace long with uuid to work on cluster env
	/**
	 * the id of the jpa entity
	 */
	protected long id;

	/**
	 * holds binary elements of the array
	 */
	protected Serializable serializable;

	/**
	 * holds all primitive, primitive wrappers + string and address elements of
	 * an array, those can be sued in queries by attr value
	 */
	protected String string;

	/**
	 * Retrieves the id of the jpa entity
	 * @return
	 */
	public long getId() {
		return id;
	}

	/**
	 * Sets the id of the jpa entity
	 * @param id
	 */
	public void setId(long id) {
		this.id = id;
	}

	/**
	 * Retrieves the serializable binary field of the entity
	 * @return
	 */
	public Serializable getSerializable() {
		return serializable;
	}

	/**
	 * Sets the serializable binary field of the entity
	 * @param serializable
	 */
	public void setSerializable(Serializable serializable) {
		this.serializable = serializable;
	}

	/**
	 * Retrieves the string field of the entity
	 * @return
	 */
	public String getString() {
		return string;
	}

	/**
	 * Sets the string field of the entity
	 * @param string
	 */
	public void setString(String string) {
		this.string = string;
	}

	/**
	 * NOTE: when adding an instance of this class to a profile entity, it is
	 * required to invoke this method with the owner, otherwise the insert on
	 * jpa is created by hibernate with the entity ids as null.
	 *
	 * @param profileEntity
	 */
	public abstract void setProfileEntity(ProfileEntity profileEntity);

	// --- AUX METHODS to avoid autoboxing

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public boolean getboolean() {
		return Boolean.parseBoolean(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setboolean(boolean attrValue) {
		setString(Boolean.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public byte getbyte() {
		return Byte.parseByte(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setbyte(byte attrValue) {
		setString(Byte.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public char getchar() {
		return getString().charAt(0);
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setchar(char attrValue) {
		setString(Character.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public double getdouble() {
		return Double.parseDouble(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setdouble(double attrValue) {
		setString(Double.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public float getfloat() {
		return Float.parseFloat(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setfloat(float attrValue) {
		setString(Float.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public int getint() {
		return Integer.parseInt(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setint(int attrValue) {
		setString(Integer.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public long getlong() {
		return Long.parseLong(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setlong(long attrValue) {
		setString(Long.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public short getshort() {
		return Short.parseShort(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setshort(short attrValue) {
		setString(Short.toString(attrValue));
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Boolean getBoolean() {
		return Boolean.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setBoolean(Boolean attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Byte getByte() {
		return Byte.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setByte(Byte attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Character getCharacter() {
		return Character.valueOf(getString().charAt(0));
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setCharacter(Character attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Double getDouble() {
		return Double.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setDouble(Double attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Float getFloat() {
		return Float.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setFloat(Float attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Integer getInteger() {
		return Integer.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setInteger(Integer attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Long getLong() {
		return Long.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setLong(Long attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Short getShort() {
		return Short.valueOf(getString());
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setShort(Short attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
	}

	/**
	 * Convenient method to retrieve a specific type stored as string.
	 *
	 * Note: there are no guarantees of type safety, those should be used by
	 * generate code, free of type checking/safety issues
	 *
	 * @return
	 */
	public Address getAddress() {
		return (Address) getSerializable();
	}

	/**
	 * Stores the attr value, converting and storing in the string field of the
	 * entity
	 *
	 * @param attrValue
	 */
	public void setAddress(Address attrValue) {
		if (attrValue != null) {
			setString(attrValue.toString());
		}
		else {
			setString(null);
		}
		setSerializable(attrValue);
	}

	@Override
	public int hashCode() {
		return (int)id;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj != null && obj.getClass() == this.getClass()) {
			return ((ProfileEntityArrayAttributeValue)obj).id == this.id;
		}
		else {
			return false;
		}
	}

	@Override
	public String toString() {
		return "ProfileEntityArrayAttributeValue[ id = "+getId()+" , string = "+getString()+" , serializable = "+getSerializable()+" ]";
	}

}