/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2014, Telestax Inc and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * This file incorporates work covered by the following copyright contributed under the GNU LGPL : Copyright 2007-2011 Red Hat.
 */
package org.mobicents.slee.runtime.eventrouter.stats;

import javax.slee.EventTypeID;

import org.mobicents.slee.container.eventrouter.EventRouterExecutor;
import org.mobicents.slee.container.eventrouter.stats.EventRouterExecutorStatistics;
import org.mobicents.slee.container.eventrouter.stats.EventRouterStatistics;
import org.mobicents.slee.container.eventrouter.stats.EventTypeRoutingStatistics;
import org.mobicents.slee.runtime.eventrouter.EventRouterImpl;

/**
 * @author martins
 *
 */
public class EventRouterStatisticsImpl implements EventRouterStatistics {

	private final EventRouterImpl eventRouter;

	public EventRouterStatisticsImpl(EventRouterImpl eventRouter) {
		this.eventRouter = eventRouter;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.eventrouter.stats.EventRouterStatisticsMBean#getActivitiesMapped()
	 */
	public int getActivitiesMapped() {
		int result = 0;
		for (int i = 0; i < getExecutors().length; i++) {
			result += getActivitiesMapped(i);
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.core.eventrouter.stats.EventRouterStatisticsMBean#getActivitiesMapped(int)
	 */
	public int getActivitiesMapped(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getActivitiesMapped();
	}

	private EventRouterExecutor[] getExecutors() throws IllegalStateException {
		return eventRouter.getExecutors();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatistics#getEventRouterExecutorStatistics(int)
	 */
	public EventRouterExecutorStatistics getEventRouterExecutorStatistics(
			int executor) {
		return getExecutors()[executor].getStatistics();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterExecutorStatistics#getAverageEventRoutingTime()
	 */
	public long getAverageEventRoutingTime() {
		long time = 0L;
		long events = 0L;
		for (EventTypeID eventTypeID : eventRouter.getSleeContainer().getComponentManagement().getComponentRepository().getEventComponentIDs()) {
			for (int i = 0; i < getExecutors().length; i++) {
				final EventRouterExecutorStatistics eventRouterExecutorStatistics = getEventRouterExecutorStatistics(i);
                if (eventRouterExecutorStatistics != null) {
                    EventTypeRoutingStatistics eventTypeRoutingStatistics = eventRouterExecutorStatistics.getEventTypeRoutingStatistics(eventTypeID);
                    if (eventTypeRoutingStatistics != null) {
                        time += eventTypeRoutingStatistics.getRoutingTime();
                        events += eventTypeRoutingStatistics.getEventsRouted();
                    }
                }
			}
		}
		return time == 0L ? 0L : time / events;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterExecutorStatistics#getAverageEventRoutingTime(javax.slee.EventTypeID)
	 */
	public long getAverageEventRoutingTime(EventTypeID eventTypeID) {
		long time = 0L;
		long events = 0L;
		for (int i = 0; i < getExecutors().length; i++) {
            final EventRouterExecutorStatistics eventRouterExecutorStatistics = getEventRouterExecutorStatistics(i);
            if (eventRouterExecutorStatistics != null) {
                EventTypeRoutingStatistics eventTypeRoutingStatistics = eventRouterExecutorStatistics.getEventTypeRoutingStatistics(eventTypeID);
                if (eventTypeRoutingStatistics != null) {
                    time += eventTypeRoutingStatistics.getRoutingTime();
                    events += eventTypeRoutingStatistics.getEventsRouted();
                }
            }
		}
		return time == 0L ? 0L : time / events;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterStatistics#getAverageEventRoutingTime(int)
	 */
	public long getAverageEventRoutingTime(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getAverageEventRoutingTime();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterStatistics#getAverageEventRoutingTime(int, javax.slee.EventTypeID)
	 */
	public long getAverageEventRoutingTime(int executor, EventTypeID eventTypeID) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getAverageEventRoutingTime(eventTypeID);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getEventsRouted(javax.slee.EventTypeID)
	 */
	public long getEventsRouted(EventTypeID eventTypeID) {
		long result = 0L;
		for (int i = 0; i < getExecutors().length; i++) {
			result += getEventsRouted(i,eventTypeID);
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getEventsRouted(int, javax.slee.EventTypeID)
	 */
	public long getEventsRouted(int executor, EventTypeID eventTypeID) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getEventsRouted(eventTypeID);
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getExecutedTasks()
	 */
	public long getExecutedTasks() {
		long result = 0L;
		for (int i = 0; i < getExecutors().length; i++) {
			result += getExecutedTasks(i);
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getExecutedTasks(int)
	 */
	public long getExecutedTasks(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getExecutedTasks();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getExecutingTime(int)
	 */
	public long getExecutingTime(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getExecutingTime();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getIdleTime(int)
	 */
	public long getIdleTime(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getIdleTime();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getMiscTasksExecuted()
	 */
	public long getMiscTasksExecuted() {
		long result = 0L;
		for (int i = 0; i < getExecutors().length; i++) {
			result += getMiscTasksExecuted(i);
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getMiscTasksExecuted(int)
	 */
	public long getMiscTasksExecuted(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getMiscTasksExecuted();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getMiscTasksExecutingTime(int)
	 */
	public long getMiscTasksExecutingTime(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getMiscTasksExecutingTime();
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getRoutingTime(javax.slee.EventTypeID)
	 */
	public long getRoutingTime(EventTypeID eventTypeID) {
		long result = 0L;
		for (int i = 0; i < getExecutors().length; i++) {
			result += getRoutingTime(i,eventTypeID);
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterStatisticsMBean#getRoutingTime(int, javax.slee.EventTypeID)
	 */
	public long getRoutingTime(int executor, EventTypeID eventTypeID) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getRoutingTime(eventTypeID);
	}

	@Override
	public int getWorkingQueueSize() {
		int result = 0;
		for (int i = 0; i < getExecutors().length; i++) {
			result += getWorkingQueueSize(i);
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterStatistics#getWorkingQueueSize(int)
	 */
	@Override
	public int getWorkingQueueSize(int executor) {
		final EventRouterExecutorStatistics executorStats = getExecutors()[executor].getStatistics();
		return executorStats == null ? 0 : executorStats.getWorkingQueueSize();
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Global Statistics\n");
		sb.append("\tActivities mapped: ").append(getActivitiesMapped()).append('\n');
		sb.append("\tAverage event routing time: ").append(getAverageEventRoutingTime()).append('\n');
		sb.append("\tExecuted Tasks: ").append(getExecutedTasks()).append('\n');
		sb.append("\tMisc Tasks Executed: ").append(getMiscTasksExecuted()).append('\n');
		sb.append("\tWorking Queue Size: ").append(getWorkingQueueSize()).append('\n');
		for (EventTypeID eventTypeID : eventRouter.getSleeContainer().getComponentManagement().getComponentRepository().getEventComponentIDs()) {
			sb.append('\n');
			sb.append(eventTypeID).append(" statistics:\n");
			sb.append("\tAverage event routing time: ").append(getAverageEventRoutingTime(eventTypeID)).append('\n');
			sb.append("\tEvent routing time: ").append(getRoutingTime(eventTypeID)).append('\n');
			sb.append("\tEvents routed: ").append(getEventsRouted(eventTypeID)).append('\n');
		}
		for (int i = 0; i < getExecutors().length; i++) {
			sb.append("\nExecutor ").append(i).append(" statistics:\n");
			sb.append(getEventRouterExecutorStatistics(i));
		}
		return sb.toString();
	}
}


/*
 * 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.jmx;

import java.util.concurrent.ConcurrentHashMap;

import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.slee.management.ManagementException;
import javax.slee.management.NotificationSource;
import javax.slee.usage.UsageNotificationManagerMBean;

import org.mobicents.slee.container.component.SleeComponentWithUsageParametersInterface;
import org.mobicents.slee.container.component.UsageParameterDescriptor;

/**
 * Base implementation of the {@link UsageNotificationManagerMBean} interface
 * from SLEE 1.1 specs. Concrete classes for the user specified interfaces must
 * extend this class.
 *
 * @author martins
 *
 */
public class UsageNotificationManagerMBeanImpl extends StandardMBean implements
		UsageNotificationManagerMBean {

	private final NotificationSource notificationSource;
	private ObjectName objectName;
	private final ConcurrentHashMap<String, Boolean> paramNames = new ConcurrentHashMap<String, Boolean>();

	private boolean isSlee11 = false;

	public UsageNotificationManagerMBeanImpl(Class<?> interfaceName,
			NotificationSource notificationSource, SleeComponentWithUsageParametersInterface component)
			throws NotCompliantMBeanException, ClassNotFoundException {
		super(interfaceName);
		this.notificationSource = notificationSource;
		if(!component.isSlee11())
		{
			this.isSlee11=false;
			return;
		}else
		{
			for(UsageParameterDescriptor param:component.getUsageParametersList())
			{
				this.setNotificationsEnabled(param.getName(), param.getNotificationsEnabled());
			}
		}

	}

	public void close() throws ManagementException {
		// ignore
	}

	public NotificationSource getNotificationSource()
			throws ManagementException {
		return notificationSource;
	}

	/**
	 * Indicates if notifications are enabled for the specified parameter name
	 *
	 * @param paramName
	 * @return
	 */
	public boolean getNotificationsEnabled(String paramName) {

		Boolean areNotificationsEnabled = paramNames.get(paramName);

		if(!isSlee11)
		{
			if (areNotificationsEnabled == null
				|| areNotificationsEnabled.booleanValue()) {
			// considering that notifications are enabled, by default, for each
			// param
				return true;
			} else {
				return false;
			}
		}else
		{
			if (areNotificationsEnabled != null
					&& areNotificationsEnabled.booleanValue()) {
				// considering that notifications are enabled, by default, for each
				// param
					return true;
				} else {
					return false;
				}
		}
	}

	public void setNotificationsEnabled(String paramName, boolean enabled) {

		paramNames.put(paramName, Boolean.valueOf(enabled));
	}

	public ObjectName getObjectName() {
		return objectName;
	}

	public void setObjectName(ObjectName objectName) {
		this.objectName = objectName;
	}

}


/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2014, Telestax Inc and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * This file incorporates work covered by the following copyright contributed under the GNU LGPL : Copyright 2007-2011 Red Hat.
 */

package org.mobicents.slee.container.management;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.ObjectName;
import javax.slee.InvalidArgumentException;
import javax.slee.InvalidStateException;
import javax.slee.SLEEException;
import javax.slee.SbbID;
import javax.slee.ServiceID;
import javax.slee.UnrecognizedServiceException;
import javax.slee.management.InvalidLinkNameBindingStateException;
import javax.slee.management.ManagementException;
import javax.slee.management.SbbNotification;
import javax.slee.management.ServiceState;
import javax.slee.management.SleeState;
import javax.slee.management.UnrecognizedResourceAdaptorEntityException;
import javax.slee.resource.ActivityFlags;
import javax.transaction.SystemException;

import org.apache.log4j.Logger;
import org.mobicents.slee.container.AbstractSleeContainerModule;
import org.mobicents.slee.container.activity.ActivityContext;
import org.mobicents.slee.container.activity.ActivityContextFactory;
import org.mobicents.slee.container.activity.ActivityContextHandle;
import org.mobicents.slee.container.activity.ActivityType;
import org.mobicents.slee.container.component.ComponentRepository;
import org.mobicents.slee.container.component.event.EventTypeComponent;
import org.mobicents.slee.container.component.sbb.EventEntryDescriptor;
import org.mobicents.slee.container.component.service.ServiceComponent;
import org.mobicents.slee.container.management.jmx.MobicentsManagement;
import org.mobicents.slee.container.management.jmx.ServiceUsageMBean;
import org.mobicents.slee.container.service.ServiceActivityContextHandle;
import org.mobicents.slee.container.service.ServiceActivityContextInterfaceFactoryImpl;
import org.mobicents.slee.container.service.ServiceActivityFactory;
import org.mobicents.slee.container.service.ServiceActivityFactoryImpl;
import org.mobicents.slee.container.service.ServiceActivityHandle;
import org.mobicents.slee.container.service.ServiceActivityHandleImpl;
import org.mobicents.slee.container.service.ServiceCacheData;
import org.mobicents.slee.container.service.ServiceStartedEventImpl;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.mobicents.slee.runtime.facilities.NotificationSourceWrapperImpl;
import org.mobicents.slee.runtime.sbbentity.RootSbbEntitiesRemovalTask;
import org.mobicents.slee.util.concurrent.SleeThreadFactory;

/**
 *
 * @author martins
 *
 */
public class ServiceManagementImpl extends AbstractSleeContainerModule
		implements ServiceManagement {

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

	private SleeTransactionManager transactionManager;
	private ComponentRepository componentRepositoryImpl;

	private ServiceActivityFactory serviceActivityFactory;
	private ServiceActivityContextInterfaceFactoryImpl serviceActivityContextInterfaceFactory;

	private final ConcurrentHashMap<ServiceID, ScheduledFuture<?>> activityEndingTasks = new ConcurrentHashMap<ServiceID, ScheduledFuture<?>>();

    private final static SleeThreadFactory SLEE_THREAD_FACTORY = new SleeThreadFactory("SLEE-ServiceManagement");

	/*
	 * (non-Javadoc)
	 *
	 * @see org.mobicents.slee.container.management.ServiceManagement#
	 * getServiceActivityFactory()
	 */
	public ServiceActivityFactory getServiceActivityFactory() {
		return serviceActivityFactory;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.mobicents.slee.container.management.ServiceManagement#
	 * getServiceActivityContextInterfaceFactory()
	 */
	public ServiceActivityContextInterfaceFactoryImpl getServiceActivityContextInterfaceFactory() {
		return serviceActivityContextInterfaceFactory;
	}

	@Override
	public void sleeInitialization() {
		transactionManager = sleeContainer.getTransactionManager();
		componentRepositoryImpl = sleeContainer.getComponentManagement()
				.getComponentRepository();
		serviceActivityFactory = new ServiceActivityFactoryImpl(this);
		serviceActivityContextInterfaceFactory = new ServiceActivityContextInterfaceFactoryImpl(
				sleeContainer);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#getState(javax.slee.ServiceID
	 * )
	 */
	public ServiceState getState(ServiceID serviceID)
			throws NullPointerException, UnrecognizedServiceException,
			ManagementException {

		if (logger.isDebugEnabled()) {
			logger.debug("Service.getState " + serviceID);
		}

		if (serviceID == null)
			throw new NullPointerException("Null service ID!");

		final ServiceComponent serviceComponent = componentRepositoryImpl
				.getComponentByID(serviceID);

		if (serviceComponent == null)
			throw new UnrecognizedServiceException(serviceID.toString());

		return serviceComponent.getServiceState();

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.slee.management.ServiceManagementMBean#getServices(javax.slee.
	 * management.ServiceState)
	 */
	public ServiceID[] getServices(ServiceState serviceState)
			throws NullPointerException, ManagementException {

		if (serviceState == null)
			throw new NullPointerException("Passed a null state");

		try {
			ArrayList<ServiceID> retval = new ArrayList<ServiceID>();
			for (ServiceID serviceID : componentRepositoryImpl.getServiceIDs()) {
				ServiceComponent service = componentRepositoryImpl
						.getComponentByID(serviceID);
				if (service == null) {
					throw new UnrecognizedServiceException(serviceID.toString());
				}
				if (service.getServiceState().equals(serviceState)) {
					retval.add(serviceID);
				}
			}
			return retval.toArray(new ServiceID[retval.size()]);
		} catch (Exception e) {
			throw new ManagementException("Error getting services by state!", e);
		}
	}

	/**
	 * Retrieves the set of ra entity link names referenced by the service
	 * componen, which do not exist
	 *
	 * @param serviceComponent
	 */
	public Set<String> getReferencedRAEntityLinksWhichNotExists(
			ServiceComponent serviceComponent) {
		Set<String> result = new HashSet<String>();
		Set<String> raLinkNames = sleeContainer.getResourceManagement()
				.getLinkNamesSet();
		for (String raLink : serviceComponent
				.getResourceAdaptorEntityLinks(componentRepositoryImpl)) {
			if (!raLinkNames.contains(raLink)) {
				result.add(raLink);
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.management.ServiceManagement#activate(javax.slee.ServiceID)
	 */
	public void activate(ServiceID serviceID) throws NullPointerException,
			UnrecognizedServiceException, InvalidStateException,
			InvalidLinkNameBindingStateException {
		activate(serviceID,null);
	}

	private void activate(final ServiceID serviceID, final ServiceID oldServiceID)
			throws NullPointerException, UnrecognizedServiceException,
			InvalidStateException, InvalidLinkNameBindingStateException {

		if (logger.isDebugEnabled()) {
			logger.debug("Activating " + serviceID);
		}

		if (serviceID == null)
			throw new NullPointerException("null service id");

		synchronized (sleeContainer.getManagementMonitor()) {

			final ServiceComponent serviceComponent = componentRepositoryImpl
					.getComponentByID(serviceID);
			if (serviceComponent == null) {
				throw new UnrecognizedServiceException("Unrecognized service "
						+ serviceID);
			}

			if (serviceComponent.getServiceState() == ServiceState.ACTIVE) {
				throw new InvalidStateException("Service " + serviceID
						+ " already active");
			}

			// check sbb ra entity links
			Set<String> referencedRAEntityLinksWhichNotExists = getReferencedRAEntityLinksWhichNotExists(serviceComponent);
			if (!referencedRAEntityLinksWhichNotExists.isEmpty()) {
				throw new InvalidLinkNameBindingStateException(
						referencedRAEntityLinksWhichNotExists.iterator().next());
			}

			// set old version (used for smooth service version upgrade
			if (oldServiceID != null) {
				serviceComponent.setOldVersion(oldServiceID);
			}

			// change service state
			serviceComponent.setServiceState(ServiceState.ACTIVE);

			// lets cache some info in the event components this service refer
			for (EventEntryDescriptor mEventEntry : serviceComponent
					.getRootSbbComponent().getDescriptor().getEventEntries()
					.values()) {
				if (mEventEntry.isInitialEvent()) {
					EventTypeComponent eventTypeComponent = componentRepositoryImpl
							.getComponentByID(mEventEntry.getEventReference());
					eventTypeComponent
							.activatedServiceWhichDefineEventAsInitial(serviceComponent);
				}
			}

			// only create activity if slee is already running and single node
			// in cluster, otherwise
			// slee will do it by itself when state changes
			if (sleeContainer.getSleeState() == SleeState.RUNNING
					&& sleeContainer.getCluster().isHeadMember()) {
				startActivity(serviceComponent);
			}

			// notifying the resource adaptors about service state change
			final ResourceManagement resourceManagement = sleeContainer
					.getResourceManagement();
			for (String raEntityName : resourceManagement
					.getResourceAdaptorEntities()) {
				resourceManagement.getResourceAdaptorEntity(raEntityName)
						.serviceActive(serviceID);
			}

			logger.info("Activated " + serviceID);
		}
	}

	private void startActivity(final ServiceComponent serviceComponent) {

		// create ac for the activity
		ActivityContextHandle ach = new ServiceActivityContextHandle(
				new ServiceActivityHandleImpl(serviceComponent.getServiceID()));
		ActivityContext ac = sleeContainer.getActivityContextFactory()
				.createActivityContext(ach, ActivityFlags.NO_FLAGS);

		if (logger.isDebugEnabled()) {
			logger.debug("Starting " + serviceComponent.getServiceID()
					+ " activity.");
		}

		// ensure the service cache data exists
		new ServiceCacheData(serviceComponent.getServiceID(), sleeContainer
				.getCluster().getMobicentsCache()).create();
		serviceComponent.setActivityEnded(false);

		// fire slee 1.0 and 1.1 service started events
		ServiceStartedEventImpl event = new ServiceStartedEventImpl(
				serviceComponent.getServiceID());
		ac.fireEvent(ServiceStartedEventImpl.SLEE_10_EVENT_TYPE_ID, event,
				null, null, null, null, null);
		ac.fireEvent(ServiceStartedEventImpl.SLEE_11_EVENT_TYPE_ID, event,
				null, event.getService(), null, null, null);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#activate(javax.slee.ServiceID
	 * [])
	 */
	public void activate(ServiceID[] serviceIDs) throws NullPointerException,
			InvalidArgumentException, UnrecognizedServiceException,
			InvalidStateException, ManagementException {
		if (serviceIDs.length == 0) {
			throw new InvalidArgumentException("InvalidArgumentException");
		}

		for (int i = 0; i < serviceIDs.length; i++) {
			if (serviceIDs[i] == null) {
				throw new InvalidArgumentException("InvalidArgumentException");
			}
		}
		for (int i = 0; i < serviceIDs.length - 1; i++)
			for (int j = i + 1; j < serviceIDs.length; j++)
				if (serviceIDs[i] == (serviceIDs[j])) {
					throw new InvalidArgumentException(
							"InvalidArgumentException");
				}

		for (int i = 0; i < serviceIDs.length; i++) {
			activate(serviceIDs[i]);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#deactivate(javax.slee.ServiceID
	 * )
	 */
	public void deactivate(final ServiceID serviceID)
			throws NullPointerException, UnrecognizedServiceException,
			InvalidStateException, ManagementException {

		if (logger.isDebugEnabled()) {
			logger.debug("Deactivating " + serviceID);
		}

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

		synchronized (sleeContainer.getManagementMonitor()) {

			final ServiceComponent serviceComponent = componentRepositoryImpl
					.getComponentByID(serviceID);

			if (serviceComponent == null)
				throw new UnrecognizedServiceException("Service not found for "
						+ serviceID);

			if (logger.isDebugEnabled())
				logger.debug(serviceID.toString() + " state = "
						+ serviceComponent.getServiceState());

			final SleeState sleeState = sleeContainer.getSleeState();

			if (serviceComponent.getServiceState() == ServiceState.STOPPING)
				throw new InvalidStateException("Service is STOPPING");

			if (serviceComponent.getServiceState() == ServiceState.INACTIVE) {
				throw new InvalidStateException("Service already deactivated");
			}

			serviceComponent.setServiceState(ServiceState.STOPPING);

			// warn ra entities about state change
			final ResourceManagement resourceManagement = sleeContainer
					.getResourceManagement();
			for (String raEntityName : resourceManagement
					.getResourceAdaptorEntities()) {
				resourceManagement.getResourceAdaptorEntity(raEntityName)
						.serviceStopping(serviceID);
			}

			// only end activity if slee was running and is single node in
			// cluster, otherwise not needed (cluster) or
			// slee already did it
			if (sleeContainer.getCluster().isSingleMember() && (sleeState == SleeState.RUNNING || sleeState == SleeState.STOPPING)) {
				if (sleeState == SleeState.RUNNING) {
					endServiceActivity(serviceID);
				}
				else {
					// chance of concurrency with activity end
					synchronized (serviceComponent) {
						if(serviceComponent.isActivityEnded()) {
							// activity already ended but service was not in stopping state
							completeServiceStop(serviceComponent);
						}
					}
				}
			} else {
				serviceComponent.setServiceState(ServiceState.INACTIVE);
				// warn ra entities about state change
				for (String raEntityName : resourceManagement
						.getResourceAdaptorEntities()) {
					resourceManagement.getResourceAdaptorEntity(raEntityName)
							.serviceInactive(serviceID);
				}
				logger.info("Deactivated " + serviceID);
			}

			// remove runtime cache related with this service
			for (EventEntryDescriptor mEventEntry : serviceComponent
					.getRootSbbComponent().getDescriptor().getEventEntries()
					.values()) {
				if (mEventEntry.isInitialEvent()) {
					EventTypeComponent eventTypeComponent = componentRepositoryImpl
							.getComponentByID(mEventEntry.getEventReference());
					eventTypeComponent
							.deactivatedServiceWhichDefineEventAsInitial(serviceComponent);
				}
			}

		}
	}

	public void endServiceActivity(final ServiceID serviceID) {

		final ActivityContextHandle ach = new ServiceActivityContextHandle(
				new ServiceActivityHandleImpl(serviceID));
		if (logger.isDebugEnabled()) {
			logger.debug("Ending " + serviceID + " activity.");
		}
		ActivityContext ac = sleeContainer.getActivityContextFactory()
				.getActivityContext(ach);
		if (ac == null) {
			logger.warn("unable to find and end ac " + ach);

			logger.warn("WORKAROUND USAGE: ENDING SERVICE");
			sleeContainer.getActivityContextFactory().WAremove("ACH=SERVICE");

			return;
		}
		if (!activityEndingTasks.contains(serviceID)) {
			// schedule a task to force the ending of the activity after 30 secs
			Runnable r = new Runnable() {
				public void run() {
					try {
						activityEndingTasks.remove(serviceID);
						if (sleeContainer.getSleeState() != null) {
							final ActivityContext ac = sleeContainer
									.getActivityContextFactory()
									.getActivityContext(ach);
							if (ac != null) {
								// if (logger.isDebugEnabled()) {
								logger.info("Forcing the end of " + ach);
								// }
								ac.activityEnded();
							}
						}
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
					}
				}
			};
			ScheduledFuture<?> scheduledFuture = sleeContainer
					.getNonClusteredScheduler().schedule(r, 30L,
							TimeUnit.SECONDS);
			activityEndingTasks.put(serviceID, scheduledFuture);
		}
		// end the activity
		if (!ac.isEnding()) {
			ac.endActivity();
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#deactivate(javax.slee.ServiceID
	 * [])
	 */
	public void deactivate(ServiceID[] arg0) throws NullPointerException,
			InvalidArgumentException, UnrecognizedServiceException,
			InvalidStateException, ManagementException {

		if (arg0.length == 0) {
			throw new InvalidArgumentException("InvalidArgumentException");
		}

		for (int i = 0; i < arg0.length; i++) {
			if (arg0[i] == null) {
				throw new InvalidArgumentException("InvalidArgumentException");
			}
		}
		for (int i = 0; i < arg0.length - 1; i++)
			for (int j = i + 1; j < arg0.length; j++)
				if (arg0[i] == (arg0[j])) {
					throw new InvalidArgumentException(
							"InvalidArgumentException");
				}
		try {
			for (int i = 0; i < arg0.length; i++) {
				deactivate(arg0[i]);
			}
		} catch (InvalidStateException ise) {
			throw ise;
		} catch (Exception ex) {
			throw new ManagementException("system exception starting service",
					ex);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#deactivateAndActivate(javax
	 * .slee.ServiceID, javax.slee.ServiceID)
	 */
	public void deactivateAndActivate(ServiceID arg0, ServiceID arg1)
			throws NullPointerException, InvalidArgumentException,
			UnrecognizedServiceException, InvalidStateException,
			ManagementException {
		if (logger.isInfoEnabled())
			logger.debug("deactivateAndActivate (" + arg0 + " , " + arg1);
		if (arg0 == arg1)
			throw new InvalidArgumentException(
					"Activating and deactivating the same service!");
		if ((arg0 == null) || (arg1 == null))
			throw new InvalidArgumentException("The service(s) are null!");
		try {
			ServiceComponent serviceToDeactivate = componentRepositoryImpl
					.getComponentByID(arg0);
			if (serviceToDeactivate == null) {
				throw new UnrecognizedServiceException();
			} else {
				activate(arg1,arg0);
				deactivate(arg0);
			}
		} catch (InvalidStateException ise) {
			throw ise;
		} catch (Exception ex) {
			throw new ManagementException(
					"exception in deactivating/activating service ! ");
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#deactivateAndActivate(javax
	 * .slee.ServiceID[], javax.slee.ServiceID[])
	 */
	public void deactivateAndActivate(ServiceID[] arg0, ServiceID[] arg1)
			throws NullPointerException, InvalidArgumentException,
			UnrecognizedServiceException, InvalidStateException,
			ManagementException {

		if (arg0.length == 0 || arg1.length == 0)
			throw new InvalidArgumentException("The parameter array(s) must not be empty.");

		if (arg0.length != arg1.length)
			throw new InvalidArgumentException("The parameter arrays must have same lenght.");

		Set<ServiceID> services = new HashSet<ServiceID>();

		for (int i = 0; i < arg0.length - 1; i++) {
			if (arg0[i] == null || arg1[i] == null) {
				throw new InvalidArgumentException("Null entry found in parameter array(s).");
			}
			if (!services.add(arg0[i]) || !services.add(arg1[i])) {
				throw new InvalidArgumentException("Repeated entry found in parameter array(s).");
			}
		}

		try {
			for (int i = 0; i < arg0.length; i++) {
				deactivateAndActivate(arg0[i],arg1[i]);
			}
		} catch (InvalidStateException ise) {
			throw ise;
		} catch (ManagementException me) {
			throw me;
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.slee.management.ServiceManagementMBean#getServiceUsageMBean(javax
	 * .slee.ServiceID)
	 */
	public ObjectName getServiceUsageMBean(ServiceID serviceID)
			throws NullPointerException, UnrecognizedServiceException,
			ManagementException {

		if (logger.isDebugEnabled()) {
			logger.debug("getServiceUsageMBean " + serviceID);
		}

		final ServiceComponent serviceComponent = componentRepositoryImpl
				.getComponentByID(serviceID);
		if (serviceComponent != null) {
			return serviceComponent.getServiceUsageMBean().getObjectName();
		} else {
			throw new UnrecognizedServiceException(serviceID.toString());
		}
	}

	// --- non JMX

	/**
	 * Install a service into SLEE
	 *
	 * @param serviceComponent
	 * @throws Exception
	 */
	public void installService(final ServiceComponent serviceComponent)
			throws Exception {

		if (logger.isDebugEnabled()) {
			logger.debug("Installing Service " + serviceComponent);
		}

		// creates and registers the service usage mbean
		final ServiceUsageMBean serviceUsageMBean = sleeContainer
				.getUsageParametersManagement().newServiceUsageMBean(
						serviceComponent);
		// add rollback action to remove state created
		TransactionalAction action = new TransactionalAction() {
			public void execute() {
				try {
					serviceUsageMBean.remove();
				} catch (Throwable e) {
					logger.error(e.getMessage(), e);
				}
			}
		};
		final TransactionContext txContext = sleeContainer
				.getTransactionManager().getTransactionContext();
		txContext.getAfterRollbackActions().add(action);

		// register notification sources for all sbbs
		//
		final TraceManagement traceMBeanImpl = sleeContainer
				.getTraceManagement();
		for (final SbbID sbbID : serviceComponent
				.getSbbIDs(componentRepositoryImpl)) {

			// Tracer must be available for both 1.1 and 1.0 sbb components
			// SbbComponent sbbComponent =
			// componentRepositoryImpl.getComponentByID(sbbID);
			// if(sbbComponent.isSlee11())
			{
				traceMBeanImpl.registerNotificationSource(new SbbNotification(
						serviceComponent.getServiceID(), sbbID));

				// add rollback action to remove state created
				action = new TransactionalAction() {
					public void execute() {
						// remove notification sources for all sbbs
						traceMBeanImpl
								.deregisterNotificationSource(new SbbNotification(
										serviceComponent.getServiceID(), sbbID));
					}
				};
				txContext.getAfterRollbackActions().add(action);
			}

			// this might be used not only by 1.1 sbbs...
			NotificationSourceWrapperImpl sbbMNotificationSource = new NotificationSourceWrapperImpl(
					new SbbNotification(serviceComponent.getServiceID(), sbbID));
			serviceComponent.getAlarmNotificationSources().putIfAbsent(sbbID,
					sbbMNotificationSource);
		}

		sleeContainer.getSbbManagement().serviceInstall(serviceComponent);
	}

	/**
	 * uninstall a service.
	 *
	 * @throws SystemException
	 * @throws UnrecognizedServiceException
	 * @throws MBeanRegistrationException
	 * @throws InstanceNotFoundException
	 * @throws UnrecognizedResourceAdaptorEntityException
	 * @throws NullPointerException
	 * @throws InvalidStateException
	 *
	 */
	public void uninstallService(final ServiceComponent serviceComponent)
			throws SystemException, UnrecognizedServiceException,
			InstanceNotFoundException, MBeanRegistrationException,
			NullPointerException, UnrecognizedResourceAdaptorEntityException,
			ManagementException, InvalidStateException {

		if (logger.isDebugEnabled()) {
			logger.debug("Uninstalling service with id "
					+ serviceComponent.getServiceID());
		}

		if (serviceComponent.getServiceState().isStopping()) {
			// let's be friendly and give it a few secs
			for (int i = 0; i < 15; i++) {
				try {
					Thread.sleep(1000);
					logger.info("Waiting for "
							+ serviceComponent.getServiceID()
							+ " to stop, current state is "
							+ serviceComponent.getServiceState());
					if (serviceComponent.getServiceState().isInactive()) {
						break;
					}
				} catch (Throwable e) {
					logger.error(e.getMessage(), e);
				}
			}
		}
		if (!serviceComponent.getServiceState().isInactive()) {
			throw new InvalidStateException(serviceComponent.toString()
					+ " is not inactive");
		}

		final TransactionContext txContext = sleeContainer
				.getTransactionManager().getTransactionContext();

		if (logger.isDebugEnabled()) {
			logger.debug("Closing Usage MBean of service "
					+ serviceComponent.getServiceID());
		}

		ServiceUsageMBean serviceUsageMBean = serviceComponent.getServiceUsageMBean();
		if (serviceUsageMBean != null) {
			serviceUsageMBean.remove();
			// add rollback action to re-create the mbean
			// FIXME this doesn't make sense, this restore looses all old data,
			// it shoudl only remove on
			// commit but as it is right now, the needed sbb components are
			// already removed
			TransactionalAction action = new TransactionalAction() {
				public void execute() {
					try {
						sleeContainer.getUsageParametersManagement()
								.newServiceUsageMBean(serviceComponent);
					} catch (Throwable e) {
						logger.error(e.getMessage(), e);
					}
				}
			};
			txContext.getAfterRollbackActions().add(action);
		}

		// register notification sources for all sbbs
		final TraceManagement traceMBeanImpl = sleeContainer
				.getTraceManagement();
		for (final SbbID sbbID : serviceComponent
				.getSbbIDs(componentRepositoryImpl)) {

			// Tracer must be available for both 1.1 and 1.0 sbb components
			// SbbComponent sbbComponent =
			// componentRepositoryImpl.getComponentByID(sbbID);
			// if(sbbComponent.isSlee11())
			{
				traceMBeanImpl
						.deregisterNotificationSource(new SbbNotification(
								serviceComponent.getServiceID(), sbbID));

				// add rollback action to re-add state removed
				TransactionalAction action = new TransactionalAction() {
					public void execute() {
						// remove notification sources for all sbbs
						traceMBeanImpl
								.registerNotificationSource(new SbbNotification(
										serviceComponent.getServiceID(), sbbID));
					}

				};
				txContext.getAfterRollbackActions().add(action);
			}
		}

		// warn sbb management that the service is being uninstalled, giving it
		// the option to clear any related resources
		sleeContainer.getSbbManagement().serviceUninstall(serviceComponent);

	}

	@Override
	public void sleeRunning() {
		if (sleeContainer.getCluster().isHeadMember()) {
			try {
				startActiveServicesActivities();
			} catch (Throwable e) {
				throw new SLEEException(e.getMessage(), e);
			}
		}
	}

	@Override
	public void sleeStopping() {
		if (sleeContainer.getCluster().isSingleMember()) {
			stopAllServiceActivities();
		}
	}

	private void stopAllServiceActivities() {

		logger.info("Ending all service activities...");

		ActivityContextFactory acf = sleeContainer.getActivityContextFactory();

		try {
			for (ActivityContextHandle ach : acf
					.getAllActivityContextsHandles()) {
				if (ach.getActivityType() == ActivityType.SERVICE) {
					endServiceActivity(((ServiceActivityContextHandle) ach)
							.getActivityHandle().getServiceID());
				}
			}
		} catch (Exception e) {
			logger.error("Exception while ending all service activities", e);
		}

		// give 35 secs for all activities to end
		for (int i = 0; i < 35; i++) {
			// check if there is still any service activity
			boolean noActivities = true;
			try {
				for (ActivityContextHandle ach : acf
						.getAllActivityContextsHandles()) {
					if (ach.getActivityType() == ActivityType.SERVICE) {
						logger.info("Waiting for " + ach.getActivityHandle()
								+ " to stop...");
						noActivities = false;
						break;
					}
				}
			} catch (Exception e) {
				if (logger.isDebugEnabled()) {
					logger.debug(e.getMessage(), e);
				}
			}
			if (noActivities) {
				break;
			} else {
				try {
					// wait a sec
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					logger.error(e.getMessage(), e);
				}
			}
		}

		logger.info("All service activities ended.");
	}

	public void startActiveServicesActivities() throws NullPointerException,
			ManagementException, UnrecognizedServiceException, SystemException {

		// create a set of services to activated, ordered by creation date
		// such order for activation is not mandatory but may overcome not ideal
		// multi service
		// design
		final Comparator<ServiceComponent> comparator = new Comparator<ServiceComponent>() {
			public int compare(ServiceComponent o1, ServiceComponent o2) {
				if (o2.getCreationTime() > o1.getCreationTime()) {
					return 1;
				} else {
					return -1;
				}
			}
		};

		final SortedSet<ServiceComponent> orderedSet = new TreeSet<ServiceComponent>(
				comparator);
		for (ServiceID serviceID : componentRepositoryImpl.getServiceIDs()) {
			ServiceComponent serviceComponent = componentRepositoryImpl
					.getComponentByID(serviceID);
			if (serviceComponent != null
					&& serviceComponent.getServiceState() == ServiceState.ACTIVE) {
				orderedSet.add(serviceComponent);
			}
		}

		ActivityContextFactory acf = sleeContainer.getActivityContextFactory();
		for (ServiceComponent serviceComponent : orderedSet) {
			ActivityContext ac = acf
					.getActivityContext(new ServiceActivityContextHandle(
							new ServiceActivityHandleImpl(serviceComponent
									.getServiceID())));
			if (ac != null) {
				ac.activityEnded();
			}
			startActivity(serviceComponent);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}

	/**
	 * Verifies if the specified ra entity link name is referenced by a non
	 * inactive service.
	 *
	 * @param raLinkName
	 * @return
	 */
	public boolean isRAEntityLinkNameReferenced(String raLinkName) {
		if (raLinkName == null) {
			throw new NullPointerException("null ra link name");
		}

		boolean b = false;
		try {
			b = transactionManager.requireTransaction();
			for (ServiceID serviceID : componentRepositoryImpl.getServiceIDs()) {
				ServiceComponent serviceComponent = componentRepositoryImpl
						.getComponentByID(serviceID);
				if (serviceComponent.getServiceState() != ServiceState.INACTIVE
						&& serviceComponent.getResourceAdaptorEntityLinks(
								componentRepositoryImpl).contains(raLinkName)) {
					return true;
				}
			}
			return false;
		} finally {
			try {
				transactionManager.requireTransactionEnd(b, false);
			} catch (Throwable ex) {
				throw new SLEEException(ex.getMessage(), ex);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.container.management.ServiceManagement#activityEnded
	 * (org.mobicents.slee.container.service.ServiceActivityHandle)
	 */
	public void activityEnded(final ServiceActivityHandle activityHandle) {
		// do this only on tx commit and in a new thread, to escape the tx context
		final Runnable r = new Runnable() {
			public void run() {
				final ServiceID serviceID = activityHandle.getServiceID();
				if (logger.isDebugEnabled()) {
					logger.debug("Activity end for " + serviceID);
				}
				// remove and cancel the timer task to force activity ending
				ScheduledFuture<?> scheduledFuture = activityEndingTasks.remove(serviceID);
				if (scheduledFuture != null) {
					scheduledFuture.cancel(true);
				}
				// get stopping service
				final ServiceComponent serviceComponent = componentRepositoryImpl
				.getComponentByID(serviceID);
				if (serviceComponent != null) {
					synchronized (serviceComponent) {
						if (logger.isDebugEnabled()) {
							logger.debug("Service is in "
									+ serviceComponent.getServiceState() + " state.");
						}
						if (serviceComponent.getServiceState().isStopping()) {
							completeServiceStop(serviceComponent);
						}
						serviceComponent.setActivityEnded(true);
					}
				}
				else {
					if (logger.isDebugEnabled()) {
						logger.debug(serviceID.toString()+ " activity ended, but component not found, removed concurrently?");
					}
				}
			}
		};
		final ExecutorService executorService = Executors.newSingleThreadExecutor(SLEE_THREAD_FACTORY);
		TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
		if (txContext != null) {
			TransactionalAction txAction = new TransactionalAction() {
				public void execute() {
					try {
						executorService.execute(r);
					}
					catch (Exception e) {
						logger.error("failed to execute task to complete service deactivation",e);
					}
					executorService.shutdown();
				}
			};
			txContext.getAfterCommitActions().add(txAction);
		}
		else {
			try {
				executorService.execute(r);
			}
			catch (Exception e) {
				logger.error("failed to execute task to complete service deactivation",e);
			}
			executorService.shutdown();
		}
	}

	protected void completeServiceStop(ServiceComponent serviceComponent) {
		boolean noRootSbbEntities = false;
		int entitiesRemovalDelay = MobicentsManagement.entitiesRemovalDelay * 60;
		int waitingTime = 0;
		while(true) {
			try {
				if (sleeContainer.getSbbEntityFactory().getRootSbbEntityIDs(serviceComponent.getServiceID()).isEmpty()) {
					noRootSbbEntities = true;
					break;
				}
				Thread.sleep(1000);
				if (logger.isDebugEnabled()) {
					logger.debug("Waiting for service "+serviceComponent+" root sbb entities to end.");
				}

				logger.warn("WORKAROUND USAGE: ENDING ROOT SBB");
				sleeContainer.getSbbEntityFactory().WAremove();
			}
			catch (Exception e) {
				logger.error("failure waiting for the ending of all sbb entities from "+serviceComponent.getServiceID(),e);
			}
			waitingTime += 1;
			if(entitiesRemovalDelay > 0 && entitiesRemovalDelay<= waitingTime) {
				break;
			}
		}
		if (!noRootSbbEntities) {
			// force the removal of all sbb entities
			new RootSbbEntitiesRemovalTask(serviceComponent).run();
		}
		// ensure service cache data is removed
		ServiceCacheData serviceCacheData = new ServiceCacheData(serviceComponent.getServiceID(), sleeContainer
				.getCluster().getMobicentsCache());
		if (serviceCacheData.exists()) {
			serviceCacheData.remove();
		}
		// change state
		serviceComponent.setServiceState(ServiceState.INACTIVE);
		// notifying the resource adaptors about service state change
		final ResourceManagement resourceManagement = sleeContainer
		.getResourceManagement();
		for (String raEntityName : resourceManagement
				.getResourceAdaptorEntities()) {
			resourceManagement.getResourceAdaptorEntity(raEntityName)
			.serviceInactive(serviceComponent.getServiceID());
		}
		logger.info("Deactivated " + serviceComponent.getServiceID());

	}
}


/*
 * 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.runtime.eventrouter.stats;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.slee.EventTypeID;

import org.mobicents.slee.container.activity.ActivityContextHandle;
import org.mobicents.slee.container.eventrouter.stats.EventRouterExecutorStatistics;
import org.mobicents.slee.container.eventrouter.stats.EventTypeRoutingStatistics;

/**
 * Impl of {@link EventRouterExecutorStatistics}. This class is not thread safe
 * due to performance overhead introduced when collecting stats, i.e., errors in
 * values due to multiple threads accessing the statistics state are not
 * important.
 *
 * @author martins
 *
 */
public class EventRouterExecutorStatisticsImpl implements
		EventRouterExecutorStatistics {

	private AtomicInteger activitiesMapped = new AtomicInteger(0);

	private final Map<EventTypeID, EventTypeRoutingStatisticsImpl> eventTypeRoutingStatisticsMap = new HashMap<EventTypeID, EventTypeRoutingStatisticsImpl>();

	private long miscTasksExecuted = 0L;
	private long miscTaskExecutingTime = 0L;

	private long tasksExecuted = 0L;
	private long taskExecutingTime = 0L;

	private final long startTime = System.nanoTime();

	private final Collection<Runnable> executorWorkingQueue;

	public EventRouterExecutorStatisticsImpl(Collection<Runnable> executorWorkingQueue) {
		this.executorWorkingQueue = executorWorkingQueue;
	}

	private void taskExecuted(long executionTime) {
		tasksExecuted++;
		taskExecutingTime += executionTime;
	}

	/**
	 * Indicates that the activity with the specified handle was mapped to the executor.
	 * @param ach
	 */
	public void activityMapped(ActivityContextHandle ach) {
		activitiesMapped.incrementAndGet();
	}

	/**
	 * Indicates that the activity with the specified handle was unmapped to the executor.
	 * @param ach
	 */
	public void activityUnmapped(ActivityContextHandle ach) {
		activitiesMapped.decrementAndGet();
	}

	/**
	 * Adds the time for an event routing with a specific {@link EventTypeID}.
	 *
	 * @param eventTypeID
	 *            the id of the event type
	 * @param routingTime
	 *            the time spent to route the event, in milliseconds
	 */
	public void eventRouted(EventTypeID eventTypeID, long routingTime) {
		EventTypeRoutingStatisticsImpl eventTypeRoutingStatistics = eventTypeRoutingStatisticsMap.get(eventTypeID);
		if (eventTypeRoutingStatistics == null) {
			synchronized (eventTypeRoutingStatisticsMap) {
				eventTypeRoutingStatistics = new EventTypeRoutingStatisticsImpl(eventTypeID);
				eventTypeRoutingStatisticsMap.put(eventTypeID, eventTypeRoutingStatistics);
			}
		}
		eventTypeRoutingStatistics.eventRouted(routingTime);
		taskExecuted(routingTime);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #addEventTypeRoutingStatistics(javax.slee.EventTypeID)
	 */
	public void addEventTypeRoutingStatistics(EventTypeID eventTypeID) {
		eventTypeRoutingStatisticsMap.put(eventTypeID,
				new EventTypeRoutingStatisticsImpl(eventTypeID));
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics#getActivitiesMapped()
	 */
	public int getActivitiesMapped() {
		return activitiesMapped.get();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterExecutorStatistics#getAverageEventRoutingTime()
	 */
	public long getAverageEventRoutingTime() {
		long time = 0L;
		long events = 0L;
		for(EventTypeRoutingStatistics eventTypeRoutingStatistics : eventTypeRoutingStatisticsMap.values()) {
			time += eventTypeRoutingStatistics.getRoutingTime();
			events += eventTypeRoutingStatistics.getEventsRouted();
		}
		return time == 0L ? 0L : time / events;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.eventrouter.stats.EventRouterExecutorStatistics#getAverageEventRoutingTime(javax.slee.EventTypeID)
	 */
	public long getAverageEventRoutingTime(EventTypeID eventTypeID) {
		final EventTypeRoutingStatistics eventTypeRoutingStatistics = getEventTypeRoutingStatistics(eventTypeID);
		return eventTypeRoutingStatistics == null ? 0L
				: eventTypeRoutingStatistics.getAverageEventRoutingTime();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getEventTypeRoutingStatistics(javax.slee.EventTypeID)
	 */
	public EventTypeRoutingStatisticsImpl getEventTypeRoutingStatistics(
			EventTypeID eventTypeID) {
		return eventTypeRoutingStatisticsMap.get(eventTypeID);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #removeEventTypeRoutingStatistics(javax.slee.EventTypeID)
	 */
	public void removeEventTypeRoutingStatistics(EventTypeID eventTypeID) {
		eventTypeRoutingStatisticsMap.remove(eventTypeID);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getEventsRouted(javax.slee.EventTypeID)
	 */
	public long getEventsRouted(EventTypeID eventTypeID) {
		final EventTypeRoutingStatistics eventTypeRoutingStatistics = getEventTypeRoutingStatistics(eventTypeID);
		return eventTypeRoutingStatistics == null ? 0L
				: eventTypeRoutingStatistics.getEventsRouted();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getExecutedTasks()
	 */
	public long getExecutedTasks() {
		return tasksExecuted;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getExecutingTime()
	 */
	public long getExecutingTime() {
		return taskExecutingTime;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getIdleTime()
	 */
	public long getIdleTime() {
		return (System.nanoTime() - startTime) - getExecutingTime();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getMiscTasksExecuted()
	 */
	public long getMiscTasksExecuted() {
		return miscTasksExecuted;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getMiscTasksExecutingTime()
	 */
	public long getMiscTasksExecutingTime() {
		return miscTaskExecutingTime;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.runtime.eventrouter.stats.EventRouterExecutorStatistics
	 * #getRoutingTime(javax.slee.EventTypeID)
	 */
	public long getRoutingTime(EventTypeID eventTypeID) {
		final EventTypeRoutingStatistics eventTypeRoutingStatistics = getEventTypeRoutingStatistics(eventTypeID);
		return eventTypeRoutingStatistics == null ? 0L
				: eventTypeRoutingStatistics.getRoutingTime();
	}

	/**
	 * Adds the time for a misc task execution.
	 *
	 * @param executionTime
	 *            the time spent to execute the misc task
	 */
	public void miscTaskExecuted(long executionTime) {
		miscTasksExecuted++;
		miscTaskExecutingTime += executionTime;
		taskExecuted(executionTime);
	}

	@Override
	public int getWorkingQueueSize() {
		return executorWorkingQueue.size();
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Activities mapped: ").append(getActivitiesMapped()).append('\n');
		for (EventTypeRoutingStatistics eventTypeRoutingStatistics : eventTypeRoutingStatisticsMap.values()) {
			sb.append(eventTypeRoutingStatistics).append('\n');
		}
		sb.append("Average event routing time: ").append(getAverageEventRoutingTime()).append('\n');
		sb.append("Executed Tasks: ").append(getExecutedTasks()).append('\n');
		sb.append("Executing Time: ").append(getExecutingTime()).append('\n');
		sb.append("Idle Time: ").append(getIdleTime()).append('\n');
		sb.append("Misc Tasks Executed: ").append(getMiscTasksExecuted()).append('\n');
		sb.append("Misc Tasks Executing Time: ").append(getMiscTasksExecutingTime()).append('\n');
		sb.append("Working Queue Size: ").append(getWorkingQueueSize()).append('\n');
		return sb.toString();
	}
}


package javax.slee.facilities;

import java.io.Serializable;
import java.io.StreamCorruptedException;

/**
 * This class defines an enumerated type for the alarm levels supported by the Alarm Facility.
 * The alarm levels match those defined by ITU X.733.
 *

 * A singleton instance of each enumerated value 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.)
 *

 * The alarm levels, in order of relative severity, are:
 *

 *
CLEAR
 *
CRITICAL
 *
MAJOR
 *
WARNING
 *
INDETERMINATE
 *
MINOR
 *

 * @since SLEE 1.1
 */
public final class AlarmLevel implements Serializable {
    /**
     * An integer representation of {@link #CLEAR}.
     */
    public static final int LEVEL_CLEAR = 0;

    /**
     * An integer representation of {@link #CRITICAL}.
     */
    public static final int LEVEL_CRITICAL = 1;

    /**
     * An integer representation of {@link #MAJOR}.
     */
    public static final int LEVEL_MAJOR = 2;

    /**
     * An integer representation of {@link #WARNING}.
     */
    public static final int LEVEL_WARNING = 3;

    /**
     * An integer representation of {@link #INDETERMINATE}.
     */
    public static final int LEVEL_INDETERMINATE = 4;

    /**
     * An integer representation of {@link #MINOR}.
     */
    public static final int LEVEL_MINOR = 5;

    /**
     * A string representation of {@link #CLEAR}.
     */
    public static final String CLEAR_STRING = "Clear";

    /**
     * A string representation of {@link #CRITICAL}.
     */
    public static final String CRITICAL_STRING = "Critical";

    /**
     * A string representation of {@link #MAJOR}.
     */
    public static final String MAJOR_STRING = "Major";

    /**
     * A string representation of {@link #WARNING}.
     */
    public static final String WARNING_STRING = "Warning";

    /**
     * A string representation of {@link #INDETERMINATE}.
     */
    public static final String INDETERMINATE_STRING = "Indeterminate";

    /**
     * A string representation of {@link #MINOR}.
     */
    public static final String MINOR_STRING = "Minor";

    /**
     * Alarm level used when an error condition has cleared.
     */
    public static final AlarmLevel CLEAR = new AlarmLevel(LEVEL_CLEAR);

    /**
     * Alarm level used for critical error conditions.  The critical severity level
     * indicates that a condition affecting service has occurred and immediate
     * corrective action is required.  For example, a monitored resource has become
     * totally incapacitated and its full capability must be restored.
     */
    public static final AlarmLevel CRITICAL = new AlarmLevel(LEVEL_CRITICAL);

    /**
     * Alarm level used for error conditions of major severity.  The major severity
     * level indicates that a condition affecting service has occurred and urgent
     * corrective action is required.  For example, a monitored resource has experienced
     * severe degradation in its capability and full capability must be restored.
     */
    public static final AlarmLevel MAJOR = new AlarmLevel(LEVEL_MAJOR);

    /**
     * Alarm level used to indicate alarm warning conditions.  The warning severity
     * level indicates the detection of a potential or impending fault that may
     * affect service, before any significant effects have been encountered.  Action
     * should be taken to further diagnose (if necessary) and correct the problem in
     * order to prevent a more serious (eg. service-affecting) fault.
     */
    public static final AlarmLevel WARNING = new AlarmLevel(LEVEL_WARNING);

    /**
     * Alarm level used when the severity of the error condition is unknown.
     */
    public static final AlarmLevel INDETERMINATE = new AlarmLevel(LEVEL_INDETERMINATE);

    /**
     * Alarm level used for error conditions of minor severity.  The minor severity
     * level indicates that a condition has occurred that does not directly affect
     * service, however corrective action should be taken in order to prevent a more
     * serious (eg. service-affecting) fault.  An alarm of minor severity could be
     * reported, for example, when the detected alarm condition is not currently
     * degrading the capability of the monitored resource.
     */
    public static final AlarmLevel MINOR = new AlarmLevel(LEVEL_MINOR);

    /**
     * Get an AlarmLevel object from an integer value.
     * @param level the level as an integer.
     * @return an AlarmLevel object corresponding to level.
     * @throws IllegalArgumentException if level is not a valid alarm
     *        level value.
     */
    public static AlarmLevel fromInt(int level) throws IllegalArgumentException {
        switch (level) {
            case LEVEL_CLEAR: return CLEAR;
            case LEVEL_CRITICAL: return CRITICAL;
            case LEVEL_MAJOR: return MAJOR;
            case LEVEL_WARNING: return WARNING;
            case LEVEL_INDETERMINATE: return INDETERMINATE;
            case LEVEL_MINOR: return MINOR;
            default: throw new IllegalArgumentException("Invalid level: " + level);
        }
    }

    /**
     * Get an AlarmLevel object from a string value.
     * @param level the level as a string, for example as returned by the {@link #toString()}
     *        method (case insensitive).
     * @return an AlarmLevel object corresponding to level.
     * @throws NullPointerException if level is null.
     * @throws IllegalArgumentException if level is not a valid alarm
     *        level string.
     */
    public static AlarmLevel fromString(String level) throws NullPointerException, IllegalArgumentException {
        if (level == null) throw new NullPointerException("level is null");
        if (level.equalsIgnoreCase(CLEAR_STRING)) return CLEAR;
        if (level.equalsIgnoreCase(CRITICAL_STRING)) return CRITICAL;
        if (level.equalsIgnoreCase(MAJOR_STRING)) return MAJOR;
        if (level.equalsIgnoreCase(WARNING_STRING)) return WARNING;
        if (level.equalsIgnoreCase(INDETERMINATE_STRING)) return INDETERMINATE;
        if (level.equalsIgnoreCase(MINOR_STRING)) return MINOR;
        throw new IllegalArgumentException("Invalid level: " + level);
    }

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

    /**
     * Determine if this AlarmLevel object represents the CLEAR alarm level.
     *

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

     *     if (alarmLevel.isClear()) ...
     *

     * is interchangeable with the code:
     *

     *    if (alarmLevel == AlarmLevel.CLEAR) ...
     *

     * @return true if this object represents the CLEAR alarm level,
     *       false otherwise.
     */
    public boolean isClear() {
        return level == LEVEL_CLEAR;
    }

    /**
     * Determine if this AlarmLevel object represents the CRITICAL alarm level.
     *

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

     *     if (alarmLevel.isCritical()) ...
     *

     * is interchangeable with the code:
     *

     *    if (alarmLevel == AlarmLevel.CRITICAL) ...
     *

     * @return true if this object represents the CRITICAL alarm level,
     *       false otherwise.
     */
    public boolean isCritical() {
        return level == LEVEL_CRITICAL;
    }

    /**
     * Determine if this AlarmLevel object represents the MAJOR alarm level.
     *

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

     *     if (alarmLevel.isMajor()) ...
     *

     * is interchangeable with the code:
     *

     *    if (alarmLevel == AlarmLevel.MAJOR) ...
     *

     * @return true if this object represents the MAJOR alarm level,
     *       false otherwise.
     */
    public boolean isMajor() {
        return level == LEVEL_MAJOR;
    }

    /**
     * Determine if this AlarmLevel object represents the WARNING alarm level.
     *

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

     *     if (alarmLevel.isWarning()) ...
     *

     * is interchangeable with the code:
     *

     *    if (alarmLevel == AlarmLevel.WARNING) ...
     *

     * @return true if this object represents the WARNING alarm level,
     *       false otherwise.
     */
    public boolean isWarning() {
        return level == LEVEL_WARNING;
    }

    /**
     * Determine if this AlarmLevel object represents the INDETERMINATE alarm level.
     *

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

     *     if (alarmLevel.isIndeterminate()) ...
     *

     * is interchangeable with the code:
     *

     *    if (alarmLevel == AlarmLevel.INDETERMINATE) ...
     *

     * @return true if this object represents the INDETERMINATE alarm level,
     *       false otherwise.
     */
    public boolean isIndeterminate() {
        return level == LEVEL_INDETERMINATE;
    }

    /**
     * Determine if this AlarmLevel object represents the MINOR alarm level.
     *

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

     *     if (alarmLevel.isMinor()) ...
     *

     * is interchangeable with the code:
     *

     *    if (alarmLevel == AlarmLevel.MINOR) ...
     *

     * @return true if this object represents the MINOR alarm level,
     *       false otherwise.
     */
    public boolean isMinor() {
        return level == LEVEL_MINOR;
    }

    /**
     * Determine if this AlarmLevel object represents a level that is higher than some other
     * AlarmLevel object.  For the purposes of the comparison the following order, from highest
     * to lowest severity, is assumed for alarm levels:
     *

     *
CLEAR
     *
CRITICAL
     *
MAJOR
     *
WARNING
     *
INDETERMINATE
     *
MINOR
     *

     * @param other the AlarmLevel object to compare this with.
     * @return true if the level represented by this AlarmLevel
     *        object is considered a higher level than the level represented by the specified
     *        AlarmLevel object, false otherwise.
     * @throws NullPointerException if other is null.
     */
    public boolean isHigherLevel(AlarmLevel other) throws NullPointerException {
        if (other == null) throw new NullPointerException("other is null");

        return this.level < other.level;
    }

    /**
     * Compare this alarm level 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 alarm level as this, false otherwise.
     */
    public boolean equals(Object obj) {
        if (obj == this) return true;

        return (obj instanceof AlarmLevel) && ((AlarmLevel)obj).level == level;
    }

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

    /**
     * Get the textual representation of this alarm level object.
     * @return the textual representation of this alarm level object.
     */
    public String toString() {
        switch (level) {
            case LEVEL_CLEAR: return CLEAR_STRING;
            case LEVEL_CRITICAL: return CRITICAL_STRING;
            case LEVEL_MAJOR: return MAJOR_STRING;
            case LEVEL_WARNING: return WARNING_STRING;
            case LEVEL_INDETERMINATE: return INDETERMINATE_STRING;
            case LEVEL_MINOR: return MINOR_STRING;
            default: return "AlarmLevel in Unknown and Invalid State";
        }
    }

    /**
     * Private constructor to prevent unauthorized object creation.
     */
    private AlarmLevel(int level) {
        this.level = level;
    }

    /**
     * Resolve deserialisation references so that the singleton property of each
     * enumerated object is preserved.
     */
    private Object readResolve() throws StreamCorruptedException {
        try {
            return fromInt(level);
        }
        catch (IllegalArgumentException iae) {
            throw new StreamCorruptedException("Invalid internal state found");
        }
    }

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


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2003-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.console.server.mbeans;

import java.util.ArrayList;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.slee.ComponentID;
import javax.slee.EventTypeID;
import javax.slee.SbbID;
import javax.slee.ServiceID;
import javax.slee.management.ComponentDescriptor;
import javax.slee.management.DeployableUnitDescriptor;
import javax.slee.management.DeployableUnitID;
import javax.slee.management.LibraryID;
import javax.slee.profile.ProfileSpecificationID;
import javax.slee.resource.ResourceAdaptorID;
import javax.slee.resource.ResourceAdaptorTypeID;

import org.mobicents.slee.container.management.console.client.ManagementConsoleException;
import org.mobicents.slee.container.management.console.server.Logger;

/**
 *
 * @author Stefano Zappaterra
 * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
 */
public class DeploymentMBeanUtils {

  private MBeanServerConnection mbeanServer;

  private ObjectName deploymentMBean;

  public DeploymentMBeanUtils(MBeanServerConnection mbeanServer, ObjectName sleeManagementMBean) throws ManagementConsoleException {
    this.mbeanServer = mbeanServer;
    try {
      deploymentMBean = (ObjectName) mbeanServer.getAttribute(sleeManagementMBean, "DeploymentMBean");
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  // This code does not work! Not all components are found.
  // public ComponentDescriptor[] getDescriptors()
  // throws ManagementConsoleException {
  // try {
  // ArrayList<ComponentID> arrayComponentIDs = new ArrayList<ComponentID>();
  //
  // DeployableUnitID[] deployableUnitIDs = (DeployableUnitID[]) mbeanServer
  // .getAttribute(deploymentMBean, "DeployableUnits");
  //
  // DeployableUnitDescriptor[] deployableUnitDescriptors =
  // (DeployableUnitDescriptor[]) mbeanServer
  // .invoke(deploymentMBean, "getDescriptors",
  // new Object[] { deployableUnitIDs },
  // new String[] { DeployableUnitID[].class.getName() });
  //
  // for (int i = 0; i < deployableUnitDescriptors.length; i++) {
  // // Logger.info(deployableUnitDescriptors[i].getURL());
  // DeployableUnitDescriptor deployableUnitDescriptor =
  // deployableUnitDescriptors[i];
  // ComponentID[] partialComponentIDs = deployableUnitDescriptor
  // .getComponents();
  //
  // for (int j = 0; j < partialComponentIDs.length; j++) {
  // // Logger.info(" " + partialComponentIDs[j].toString());
  // arrayComponentIDs.add(partialComponentIDs[j]);
  // }
  // }
  //
  // ComponentID[] componentIDs = new ComponentID[arrayComponentIDs
  // .size()];
  // componentIDs = (ComponentID[]) arrayComponentIDs
  // .toArray(componentIDs);
  // ManagementConsole.getInstance().getComponentIDMap().put(componentIDs);
  //
  // // This code does not work!
  // //ComponentDescriptor[] componentDescriptors =
  // //(ComponentDescriptor[]) mbeanServer .invoke(deploymentMBean,
  // //"getDescriptors", new Object[] { componentIDs }, new String[] {
  // //ComponentID[].class.getName() });
  //
  //
  // ComponentDescriptor[] componentDescriptors = new
  // ComponentDescriptor[componentIDs.length];
  // for (int i = 0; i < componentIDs.length; i++) {
  // componentDescriptors[i] = (ComponentDescriptor) mbeanServer
  // .invoke(deploymentMBean, "getDescriptor",
  // new Object[] { componentIDs[i] },
  // new String[] { ComponentID.class.getName() });
  // }
  // return componentDescriptors;
  // } catch (Exception e) {
  // e.printStackTrace();
  // throw new ManagementConsoleException("Cannot obtain Components");
  // }
  // }

  public EventTypeID[] getEventTypes() throws ManagementConsoleException {
    try {
      EventTypeID[] IDs = (EventTypeID[]) mbeanServer.getAttribute(deploymentMBean, "EventTypes");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ProfileSpecificationID[] getProfileSpecifications() throws ManagementConsoleException {
    try {
      ProfileSpecificationID[] IDs = (ProfileSpecificationID[]) mbeanServer.getAttribute(deploymentMBean, "ProfileSpecifications");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ResourceAdaptorTypeID[] getResourceAdaptorTypes() throws ManagementConsoleException {
    try {
      ResourceAdaptorTypeID[] IDs = (ResourceAdaptorTypeID[]) mbeanServer.getAttribute(deploymentMBean, "ResourceAdaptorTypes");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ResourceAdaptorID[] getResourceAdaptors() throws ManagementConsoleException {
    try {
      ResourceAdaptorID[] IDs = (ResourceAdaptorID[]) mbeanServer.getAttribute(deploymentMBean, "ResourceAdaptors");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public SbbID[] getSbbs() throws ManagementConsoleException {
    try {
      SbbID[] IDs = (SbbID[]) mbeanServer.getAttribute(deploymentMBean, "Sbbs");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ServiceID[] getServices() throws ManagementConsoleException {
    try {
      ServiceID[] IDs = (ServiceID[]) mbeanServer.getAttribute(deploymentMBean, "Services");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public LibraryID[] getLibraries() throws ManagementConsoleException {
    try {
      LibraryID[] IDs = (LibraryID[]) mbeanServer.getAttribute(deploymentMBean, "Libraries");
      // ManagementConsole.getInstance().getComponentIDMap().put(IDs);
      return IDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ComponentDescriptor getDescriptor(ComponentID id) throws ManagementConsoleException {
    try {
      return (ComponentDescriptor) mbeanServer.invoke(deploymentMBean, "getDescriptor", new Object[] { id }, new String[] { ComponentID.class.getName() });
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ComponentDescriptor[] getDescriptors(ComponentID[] ids) throws ManagementConsoleException {
    ComponentDescriptor[] componentDescriptors = new ComponentDescriptor[ids.length];

    for (int i = 0; i < ids.length; i++)
      componentDescriptors[i] = getDescriptor(ids[i]);

    return componentDescriptors;
  }

  public String[] getEnvEntries(ComponentID id) throws ManagementConsoleException {
     try {
       return (String[]) mbeanServer.invoke(deploymentMBean, "getEnvEntries", new Object[] { id }, new String[] { ComponentID.class.getName() });
     }
     catch (Exception e) {
       e.printStackTrace();
       throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
     }
   }
  private void addComponentIDToArrayList(ArrayList<ComponentID> componentIDArrayList, ComponentID[] componentIDs) {
    for (int i = 0; i < componentIDs.length; i++)
      componentIDArrayList.add(componentIDs[i]);
  }

  public ComponentDescriptor[] getComponentDescriptors() throws ManagementConsoleException {
    ArrayList<ComponentID> componentIDArrayList = new ArrayList<ComponentID>();

    addComponentIDToArrayList(componentIDArrayList, getEventTypes());
    addComponentIDToArrayList(componentIDArrayList, getProfileSpecifications());
    addComponentIDToArrayList(componentIDArrayList, getSbbs());
    addComponentIDToArrayList(componentIDArrayList, getResourceAdaptors());
    addComponentIDToArrayList(componentIDArrayList, getResourceAdaptorTypes());
    addComponentIDToArrayList(componentIDArrayList, getServices());
    addComponentIDToArrayList(componentIDArrayList, getLibraries());

    ComponentID[] componentIDs = new ComponentID[componentIDArrayList.size()];
    componentIDs = componentIDArrayList.toArray(componentIDs);

    return getDescriptors(componentIDs);
  }

  public DeployableUnitID[] getDeployableUnits() throws ManagementConsoleException {
    try {
      return (DeployableUnitID[]) mbeanServer.getAttribute(deploymentMBean, "DeployableUnits");
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public DeployableUnitDescriptor getDescriptor(DeployableUnitID id) throws ManagementConsoleException {
    try {
      DeployableUnitDescriptor deployableUnitDescriptor = (DeployableUnitDescriptor) mbeanServer.invoke(deploymentMBean, "getDescriptor", new Object[] { id },
          new String[] { DeployableUnitID.class.getName() });
      // ammendonca DeployableUnitDescriptorEx deployableUnitDescriptorEx = new DeployableUnitDescriptorEx(deployableUnitDescriptor, id);
      return deployableUnitDescriptor;
    }
    catch (Exception e) {
      // e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  // The returned array is not granted to have the same size of the input one
  // This beacause some DeployableUnitIDs may not have a correspondent
  // DeployableUnitDescriptor
  public DeployableUnitDescriptor[] getDescriptors(DeployableUnitID[] ids) throws ManagementConsoleException {
    ArrayList<DeployableUnitDescriptor> deployabelUnitDescriptorArray = new ArrayList<DeployableUnitDescriptor>(ids.length);

    for (int i = 0; i < ids.length; i++) {
      try {
        deployabelUnitDescriptorArray.add(getDescriptor(ids[i]));
      }
      catch (Exception e) {
        Logger.error(e.getMessage());
      }
    }

    DeployableUnitDescriptor[] deployableUnitDescriptors = new DeployableUnitDescriptor[deployabelUnitDescriptorArray.size()];
    deployableUnitDescriptors = deployabelUnitDescriptorArray.toArray(deployableUnitDescriptors);

    return deployableUnitDescriptors;
  }

  public DeployableUnitDescriptor[] getDeployableUnitDescriptors() throws ManagementConsoleException {
    DeployableUnitID[] deployableUnitIDs = getDeployableUnits();
    return getDescriptors(deployableUnitIDs);
  }

  public DeployableUnitID install(String url) throws ManagementConsoleException {
    try {
      return (DeployableUnitID) mbeanServer.invoke(deploymentMBean, "install", new Object[] { url }, new String[] { String.class.getName() });
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public void uninstall(DeployableUnitID id) throws ManagementConsoleException {
    try {
      mbeanServer.invoke(deploymentMBean, "uninstall", new Object[] { id }, new String[] { DeployableUnitID.class.getName() });
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }
  }

  public ComponentID[] getReferringComponents(ComponentID id) throws ManagementConsoleException {
    try {
      ComponentID[] componentIDs = (ComponentID[]) mbeanServer.invoke(deploymentMBean, "getReferringComponents", new Object[] { id },
          new String[] { ComponentID.class.getName() });
      // ManagementConsole.getInstance().getComponentIDMap().put(componentIDs);
      return componentIDs;
    }
    catch (Exception e) {
      e.printStackTrace();
      throw new ManagementConsoleException(SleeManagementMBeanUtils.doMessage(e));
    }

  }

}


/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2015, Telestax Inc and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * This file incorporates work covered by the following copyright contributed under the GNU LGPL : Copyright 2007-2011 Red Hat.
 */

package org.mobicents.slee.runtime.activity;

import org.apache.log4j.Logger;
import org.infinispan.tree.Fqn;
import org.infinispan.tree.Node;
import org.mobicents.slee.container.AbstractSleeContainerModule;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.activity.ActivityContext;
import org.mobicents.slee.container.activity.ActivityContextFactory;
import org.mobicents.slee.container.activity.ActivityContextHandle;
import org.mobicents.slee.container.activity.ActivityType;
import org.mobicents.slee.container.eventrouter.EventRouterExecutor;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.restcomm.cache.FqnWrapper;
import org.restcomm.cluster.DataRemovalListener;

import javax.slee.SLEEException;
import javax.slee.resource.ActivityAlreadyExistsException;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Activity context factory -- return an activity context given an activity or
 * create one and put it in the table. This also implements the activity context
 * naming facility for the SLEE.
 *
 * @author F.Moggia
 * @author M. Ranganathan
 * @author Tim Fox
 * @author eduardomartins second version
 * @version 2.0
 *
 *
 */
public class ActivityContextFactoryImpl extends AbstractSleeContainerModule implements ActivityContextFactory {

	private static Logger logger = Logger.getLogger(ActivityContextFactoryImpl.class);

	/**
	 * a map with the local resources related with an activity context, which hold all runtime structures related to the activity
	 */
	private final ConcurrentHashMap<ActivityContextHandle, LocalActivityContextImpl> localActivityContexts = new ConcurrentHashMap<ActivityContextHandle, LocalActivityContextImpl>();

	private ActivityContextFactoryCacheData cacheData;

	private final ActivityManagementConfiguration configuration;

	private final static boolean doTraceLogs = logger.isTraceEnabled();

	public ActivityContextFactoryImpl(ActivityManagementConfiguration configuration) {
		this.configuration = configuration;
	}

	/**
	 *
	 * @return
	 */
	public ActivityManagementConfiguration getConfiguration() {
		return configuration;
	}

	@Override
	public void sleeInitialization() {
		sleeContainer.getCluster().addDataRemovalListener(new DataRemovalClusterListener());
	}

	@Override
	public void sleeStarting() {
		cacheData = new ActivityContextFactoryCacheData(sleeContainer.getCluster());
		cacheData.create();
	}

	public void WAremove(String type) {
		cacheData.WAremove(type);
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.AbstractSleeContainerModule#getSleeContainer()
	 */
	public SleeContainer getSleeContainer() {
		return sleeContainer;
	}

	LocalActivityContextImpl getLocalActivityContext(ActivityContextImpl ac) {
		final ActivityContextHandle ach = ac.getActivityContextHandle();
		LocalActivityContextImpl localActivityContext = localActivityContexts.get(ach);
		if (localActivityContext == null) {
			final LocalActivityContextImpl newLocalActivityContext = new LocalActivityContextImpl(ach, ac.getActivityFlags(), ac.getStringID(), this);
			localActivityContext = localActivityContexts.putIfAbsent(ach,newLocalActivityContext);
			if (localActivityContext == null) {
				localActivityContext = newLocalActivityContext;
				final EventRouterExecutor executor = sleeContainer.getEventRouter().getEventRouterExecutorMapper().getExecutor(ach);
				localActivityContext.setExecutorService(executor);
				executor.activityMapped(ach);
				TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
				if (txContext != null) {
					TransactionalAction txAction = new TransactionalAction() {
						@Override
						public void execute() {
							try {
								Runnable r = new Runnable() {
									@Override
									public void run() {
										if (getActivityContext(ach) == null) {
											localActivityContexts.remove(ach);
											executor.activityUnmapped(ach);
										}
									}
								};
								executor.execute(r);
							}
							catch (Throwable e) {
								logger.error("Failed to rollback removal of AC local resources",e);
							}

						}
					};
					txContext.getAfterRollbackActions().add(txAction);
				}
			}
		}
		return localActivityContext;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.activity.ActivityContextFactory#createActivityContext(org.mobicents.slee.container.activity.ActivityContextHandle, int)
	 */
	public ActivityContextImpl createActivityContext(final ActivityContextHandle ach, int activityFlags) throws ActivityAlreadyExistsException {

		if (sleeContainer.getCongestionControl().refuseStartActivity()) {
			throw new SLEEException("congestion control refused activity start");
		}

		// create ac
		ActivityContextCacheData activityContextCacheData = new ActivityContextCacheData(ach, sleeContainer.getCluster());
		if (activityContextCacheData.exists()) {
			throw new ActivityAlreadyExistsException(ach.toString());
		}

		ActivityContextImpl ac = new ActivityContextImpl(ach,activityContextCacheData,tracksIdleTime(ach,true),Integer.valueOf(activityFlags),this);
		if (logger.isDebugEnabled()) {
			logger.debug("Created activity context with handle "+ach);
		}
		return ac;
	}

	private boolean tracksIdleTime(ActivityContextHandle ach, boolean updateLastAccessTime) {
		if(!updateLastAccessTime) {
			return false;
		}
		if (configuration.getTimeBetweenLivenessQueries() < 1) {
			return false;
		}
		return ach.getActivityType() == ActivityType.RA;
	}

	@Override
	public ActivityContextImpl getActivityContext(ActivityContextHandle ach) {
		return getActivityContext(ach,false);
	}

	@Override
	public ActivityContextImpl getActivityContext(ActivityContextHandle ach, boolean updateLastAccessTime) {
		ActivityContextCacheData activityContextCacheData = new ActivityContextCacheData(ach, sleeContainer.getCluster());
		if (activityContextCacheData.exists()) {
			try {
				return new ActivityContextImpl(ach,activityContextCacheData,tracksIdleTime(ach, updateLastAccessTime),this);
			}
			catch (Throwable e) {
				logger.error("Failed to load AC.",e);
				// force cache data & local resources removal
				final LocalActivityContextImpl localActivityContext = localActivityContexts.remove(ach);
				if (localActivityContext != null) {
					localActivityContext.getExecutorService().activityUnmapped(ach);
				}
				activityContextCacheData.remove();
				return null;
			}
		}
		else {
			return null;
		}
	}

	@Override
	public ActivityContext getActivityContext(String sid) {
		return getActivityContext(sid, false);
	}

	@Override
	public ActivityContext getActivityContext(String sid,
			boolean updateLastAccessTime) {
		// TODO add alternative strategy where there is a cache data mapping from sid to
		// ach, and configuration option to select strategy
		ActivityContextImpl ac = null;
		for (ActivityContextHandle ach : getAllActivityContextsHandles()) {
			ac = getActivityContext(ach, updateLastAccessTime);
			if (sid.equals(ac.getStringID(false))) {
				break;
			}
		}
		return ac;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.activity.ActivityContextFactory#getAllActivityContextsHandles()
	 */
	public Set<ActivityContextHandle> getAllActivityContextsHandles() {
		return cacheData.getActivityContextHandles();
	}

	public void removeActivityContext(final ActivityContextImpl ac) {

		if (doTraceLogs) {
			logger.trace("Removing activity context "+ac.getActivityContextHandle());
		}

		// remove runtime resources
		final LocalActivityContextImpl localActivityContext = localActivityContexts.remove(ac.getActivityContextHandle());
		if (localActivityContext != null) {
			localActivityContext.getExecutorService().activityUnmapped(ac.getActivityContextHandle());
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Removed activity context with handle "+ac.getActivityContextHandle());
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.activity.ActivityContextFactory#getActivityContextCount()
	 */
	public int getActivityContextCount() {
		return getAllActivityContextsHandles().size();
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.activity.ActivityContextFactory#activityContextExists(org.mobicents.slee.container.activity.ActivityContextHandle)
	 */
	public boolean activityContextExists(ActivityContextHandle ach) {
		return new ActivityContextCacheData(ach, sleeContainer.getCluster()).exists();
	}

	@Override
	public String toString() {
		return "ActivityContext Factory: "
			+ "\n+-- Local ACs: " + (localActivityContexts.size() > 20 ? localActivityContexts.size() : localActivityContexts.keySet())
			+ "\n+-- ACs: " + (getActivityContextCount() > 20 ? getActivityContextCount() : getAllActivityContextsHandles());
	}

	private class DataRemovalClusterListener implements DataRemovalListener {

		@SuppressWarnings("rawtypes")
		public void dataRemoved(FqnWrapper dataFqnWrapper) {
			final Fqn dataFqn = dataFqnWrapper.getFqn();
			final ActivityContextHandle ach = (ActivityContextHandle) dataFqn.getLastElement();
			final LocalActivityContextImpl localActivityContext = localActivityContexts.remove(ach);
			if(localActivityContext != null) {
				final EventRouterExecutor executor = localActivityContext.getExecutorService();
				if (executor != null) {
					executor.activityUnmapped(localActivityContext.getActivityContextHandle());
				}
				if(doTraceLogs) {
					logger.trace("Remotely removed local AC for "+ach);
				}
			}
		}

		@SuppressWarnings("rawtypes")
		public FqnWrapper getBaseFqn() {
			return new FqnWrapper(ActivityContextFactoryCacheData.NODE_FQN);
		}

	}
}