/*
 * 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 org.apache.log4j.Logger;

/**
 * @author martins
 *
 */
public class TimerFacilityConfiguration implements
		TimerFacilityConfigurationMBean {

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

	private Integer timerThreads;

	private Integer purgePeriod;

	private boolean taskExecutionWaitsForTxCommitConfirmation = true;

	@Override
	public boolean getTaskExecutionWaitsForTxCommitConfirmation() {
		return taskExecutionWaitsForTxCommitConfirmation;
	}

	@Override
	public void setTaskExecutionWaitsForTxCommitConfirmation(boolean value) {
		taskExecutionWaitsForTxCommitConfirmation = value;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.container.management.jmx.TimerFacilityConfigurationMBean
	 * #getTimerThreads()
	 */
	public int getTimerThreads() {
		return timerThreads != null ? timerThreads.intValue() : 0;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.mobicents.slee.container.management.jmx.TimerFacilityConfigurationMBean
	 * #setTimerThreads(int)
	 */
	public void setTimerThreads(int value) {
		if (this.timerThreads == null) {
			logger
			.info("SLEE Timer facility initiated with "
					+ value
					+ " threads.");
		}
		else {
			logger
			.warn("Setting timer facility threads to "
					+ value
					+ ". If called with server running a stop and start is need to apply changes.");
		}
		this.timerThreads = value;
	}

	@Override
	public int getPurgePeriod() {
		return purgePeriod != null ? purgePeriod.intValue() : 0;
	}

	@Override
	public void setPurgePeriod(int value) {
		if (this.purgePeriod == null) {
			logger
			.info("SLEE Timer facility initiated with "
					+ value
					+ " for purge period.");
		}
		else {
			logger
			.warn("Setting timer facility purge period to "
					+ value
					+ ". If called with server running a stop and start is need to apply changes.");
		}
		this.purgePeriod = value;
	}
}


package javax.slee.facilities;

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

/**
 * This class defines an enumerated type for the alarm and trace levels supported by
 * the Alarm Facility and Trace Facility.  The class is based on the Java Logging API
 * included with J2SE 1.4.
 *

 * 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 levels in descending order are:
 *

 *
SEVERE (highest value)
 *
WARNING
 *
INFO
 *
CONFIG
 *
FINE
 *
FINER
 *
FINEST (lowest value)
 *

 * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
 */
public final class Level implements Serializable {
    /**
     * An integer representation of {@link #OFF}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_OFF = 0;

    /**
     * An integer representation of {@link #SEVERE}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_SEVERE = 1;

    /**
     * An integer representation of {@link #WARNING}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_WARNING = 2;

    /**
     * An integer representation of {@link #INFO}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_INFO = 3;

    /**
     * An integer representation of {@link #CONFIG}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_CONFIG = 4;

    /**
     * An integer representation of {@link #FINE}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_FINE = 5;

    /**
     * An integer representation of {@link #FINER}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_FINER = 6;

    /**
     * An integer representation of {@link #FINEST}.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final int LEVEL_FINEST = 7;

    /**
     * Level for maximum filtering.  Notifications cannot be generated using this level.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level OFF = new Level(LEVEL_OFF);

    /**
     * Level for messages indicating a severe failure.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level SEVERE = new Level(LEVEL_SEVERE);

    /**
     * Level for messages indicating warning conditions.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level WARNING = new Level(LEVEL_WARNING);

    /**
     * Level for information messages.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level INFO = new Level(LEVEL_INFO);

    /**
     * Level for configuration messages.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level CONFIG = new Level(LEVEL_CONFIG);

    /**
     * Level for basic debug messages.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level FINE = new Level(LEVEL_FINE);

    /**
     * Level for moderately detailed messages.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level FINER = new Level(LEVEL_FINER);

    /**
     * Level for highly detailed messages.  This is the lowest level.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static final Level FINEST = new Level(LEVEL_FINEST);

    /**
     * Get an Level object from an integer value.
     * @param level the level as an integer.
     * @return an Level object corresponding to level.
     * @throws IllegalArgumentException if level is not a valid
     *        level value.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public static Level fromInt(int level) throws IllegalArgumentException {
        switch (level) {
            case LEVEL_OFF: return OFF;
            case LEVEL_SEVERE: return SEVERE;
            case LEVEL_WARNING: return WARNING;
            case LEVEL_INFO: return INFO;
            case LEVEL_CONFIG: return CONFIG;
            case LEVEL_FINE: return FINE;
            case LEVEL_FINER: return FINER;
            case LEVEL_FINEST: return FINEST;
            default: throw new IllegalArgumentException("Invalid level: " + level);
        }
    }

    /**
     * Get an integer value representation for this Level object.
     * @return an integer value representation for this Level object.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public int toInt() {
        return level;
    }

    /**
     * Determine if this Level object represents the OFF level.
     *

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

     *     if (level.isOff()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.OFF) ...
     *

     * @return true if this object represents the OFF level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isOff() {
        return level == LEVEL_OFF;
    }

    /**
     * Determine if this Level object represents the SEVERE level.
     *

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

     *     if (level.isSevere()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.SEVERE) ...
     *

     * @return true if this object represents the SEVERE level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isSevere() {
        return level == LEVEL_SEVERE;
    }

    /**
     * Determine if this Level object represents the WARNING level.
     *

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

     *     if (level.isWarning()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.WARNING) ...
     *

     * @return true if this object represents the WARNING level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isWarning() {
        return level == LEVEL_WARNING;
    }

    /**
     * Determine if this Level object represents the INFO level.
     *

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

     *     if (level.isMinor()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.INFO) ...
     *

     * @return true if this object represents the INFO level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isMinor() {
        return level == LEVEL_INFO;
    }

    /**
     * Determine if this Level object represents the CONFIG level.
     *

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

     *     if (level.isConfig()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.CONFIG) ...
     *

     * @return true if this object represents the CONFIG level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isConfig() {
        return level == LEVEL_CONFIG;
    }

    /**
     * Determine if this Level object represents the FINE level.
     *

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

     *     if (level.isFine()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.FINE) ...
     *

     * @return true if this object represents the FINE level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isFine() {
        return level == LEVEL_FINE;
    }

    /**
     * Determine if this Level object represents the FINER level.
     *

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

     *     if (level.isFiner()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.FINER) ...
     *

     * @return true if this object represents the FINER level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isFiner() {
        return level == LEVEL_FINER;
    }

    /**
     * Determine if this Level object represents the FINEST level.
     *

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

     *     if (level.isFinest()) ...
     *

     * is interchangeable with the code:
     *

     *    if (level == Level.FINEST) ...
     *

     * @return true if this object represents the FINEST level,
     *       false otherwise.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isFinest() {
        return level == LEVEL_FINEST;
    }

    /**
     * Determine if this Level object represents a level that is higher or more severe
     * that some other Level object.  For the purposes of the comparison OFF
     * is considered a higher level than SEVERE.
     * @param other the Level object to compare this with.
     * @return true if the level represented by this Level
     *        object is a higher level than the level represented by the specified
     *        Level object, false otherwise.
     * @throws NullPointerException if other is null.
     * @deprecated Trace and alarm levels now defined in {@link TraceLevel} and {@link AlarmLevel}.
     */
    public boolean isHigherLevel(Level other) throws NullPointerException {
        if (other == null) throw new NullPointerException("other is null");

        return this.level < other.level;
    }

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

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

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

    /**
     * Get the textual representation of the Level object.
     * @return the textual representation of the Level object.
     */
    public String toString() {
        switch (level) {
            case LEVEL_OFF: return "Off";
            case LEVEL_SEVERE: return "Severe";
            case LEVEL_WARNING: return "Warning";
            case LEVEL_INFO: return "Info";
            case LEVEL_CONFIG: return "Config";
            case LEVEL_FINE: return "Fine";
            case LEVEL_FINER: return "Finer";
            case LEVEL_FINEST: return "Finest";
            default: return "Level in Unknown and Invalid State";
        }
    }

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

    /**
     * Resolve deserialisation references so that the singleton property of each
     * enumerated object is preserved.
     */
    private Object readResolve() throws StreamCorruptedException {
        if (level == LEVEL_OFF) return OFF;
        if (level == LEVEL_SEVERE) return SEVERE;
        if (level == LEVEL_WARNING) return WARNING;
        if (level == LEVEL_INFO) return INFO;
        if (level == LEVEL_CONFIG) return CONFIG;
        if (level == LEVEL_FINE) return FINE;
        if (level == LEVEL_FINER) return FINER;
        if (level == LEVEL_FINEST) return FINEST;
        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 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.slee.container.deployment.jboss;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.slee.resource.ConfigProperties;
import javax.slee.resource.ResourceAdaptorID;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.jboss.logging.Logger;
import org.mobicents.slee.container.deployment.jboss.action.ActivateResourceAdaptorEntityAction;
import org.mobicents.slee.container.deployment.jboss.action.BindLinkNameAction;
import org.mobicents.slee.container.deployment.jboss.action.CreateResourceAdaptorEntityAction;
import org.mobicents.slee.container.deployment.jboss.action.DeactivateResourceAdaptorEntityAction;
import org.mobicents.slee.container.deployment.jboss.action.ManagementAction;
import org.mobicents.slee.container.deployment.jboss.action.RemoveResourceAdaptorEntityAction;
import org.mobicents.slee.container.deployment.jboss.action.UnbindLinkNameAction;
import org.mobicents.slee.container.management.ResourceManagement;
import org.mobicents.slee.container.management.jmx.editors.ComponentIDPropertyEditor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * Parses a deploy-config.xml file, creating management actions from its
 * content.
 *
 * @author martins
 *
 */
public class DeployConfigParser {

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

	// The post-install actions needed to install/activate this DU components.
	private Map<String, Collection<ManagementAction>> postInstallActions;

	// The pre-uninstall actions needed to deactivate/uninstall this DU
	// components.
	private Map<String, Collection<ManagementAction>> preUninstallActions;

	public DeployConfigParser(InputStream deployConfigInputStream,
			ResourceManagement resourceManagement) throws SAXException,
			ParserConfigurationException, IOException {
		parseDeployConfig(deployConfigInputStream, resourceManagement);
	}

	/**
	 * Parser for the deployment config xml.
	 *
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws IOException
	 *
	 * @throws Exception
	 */
	private void parseDeployConfig(InputStream deployConfigInputStream,
			ResourceManagement resourceManagement) throws SAXException,
			ParserConfigurationException, IOException {

		if (deployConfigInputStream == null) {
			throw new NullPointerException("null deploy config input stream");
		}

		Document doc = null;
		try {

			// Read the file into a Document
			SchemaFactory schemaFactory = SchemaFactory
					.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			Schema schema = schemaFactory.newSchema(DeployConfigParser.class
					.getClassLoader().getResource("deploy-config.xsd"));

			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			// factory.setValidating(false);
			factory.setSchema(schema);
			DocumentBuilder builder = factory.newDocumentBuilder();

			builder.setErrorHandler(new ErrorHandler() {
				public void error(SAXParseException e) throws SAXException {
					logger.error("Error parsing deploy-config.xml", e);
					return;
				}

				public void fatalError(SAXParseException e) throws SAXException {
					logger.error("Fatal error parsing deploy-config.xml", e);
					return;
				}

				public void warning(SAXParseException e) throws SAXException {
					logger.warn("Warning parsing deploy-config.xml", e);
					return;
				}
			});

			doc = builder.parse(deployConfigInputStream);
		} finally {
			try {
				deployConfigInputStream.close();
			} catch (IOException e) {
				logger.error("failed to close deploy config input stream", e);
			}
		}

		Map<String, Collection<ManagementAction>> postInstallActions = new HashMap<String, Collection<ManagementAction>>();

		Map<String, Collection<ManagementAction>> preUninstallActions = new HashMap<String, Collection<ManagementAction>>();

		// By now we only care about <ra-entity> nodes
		NodeList raEntities = doc.getElementsByTagName("ra-entity");

		// The RA identifier
		String raId = null;

		// The collection of Post-Install Actions
		Collection<ManagementAction> cPostInstallActions = new ArrayList<ManagementAction>();

		// The collection of Pre-Uninstall Actions
		Collection<ManagementAction> cPreUninstallActions = new ArrayList<ManagementAction>();

		// Iterate through each ra-entity node
		for (int i = 0; i < raEntities.getLength(); i++) {
			Element raEntity = (Element) raEntities.item(i);

			// Get the component ID
			ComponentIDPropertyEditor cidpe = new ComponentIDPropertyEditor();
			cidpe.setAsText(raEntity.getAttribute("resource-adaptor-id"));

			raId = cidpe.getValue().toString();

			// The RA Entity Name
			String entityName = raEntity.getAttribute("entity-name");

			// Select the properties node
			NodeList propsNodeList = raEntity
					.getElementsByTagName("properties");

			if (propsNodeList.getLength() > 1) {
				logger.warn("Invalid ra-entity element, has more than one properties child. Reading only first.");
			}

			// The properties for this RA
			ConfigProperties props = new ConfigProperties();
			Element propsNode = (Element) propsNodeList.item(0);
			// Do we have any properties at all?
			if (propsNode != null) {
				// Select the property elements
				NodeList propsList = propsNode.getElementsByTagName("property");
				// For each element, add it to the Properties object
				for (int j = 0; j < propsList.getLength(); j++) {
					Element property = (Element) propsList.item(j);
					String propertyName = property.getAttribute("name");
					String propertyType = property.getAttribute("type");
					String propertyValue = property.getAttribute("value");
					props.addProperty(new ConfigProperties.Property(
							propertyName, propertyType,
							ConfigProperties.Property.toObject(propertyType,
									propertyValue)));
				}
			}

			// Create the Resource Adaptor ID
			cidpe.setAsText(raEntity.getAttribute("resource-adaptor-id"));

			ResourceAdaptorID componentID = (ResourceAdaptorID) cidpe
					.getValue();

			// Add the Create and Activate RA Entity actions to the Post-Install
			// Actions
			cPostInstallActions.add(new CreateResourceAdaptorEntityAction(
					componentID, entityName, props, resourceManagement));
			cPostInstallActions.add(new ActivateResourceAdaptorEntityAction(
					entityName, resourceManagement));

			// Each RA might have zero or more links.. get them
			NodeList links = raEntity.getElementsByTagName("ra-link");

			for (int j = 0; j < links.getLength(); j++) {
				String linkName = ((Element) links.item(j))
						.getAttribute("name");

				cPostInstallActions.add(new BindLinkNameAction(linkName,
						entityName, resourceManagement));

				cPreUninstallActions.add(new UnbindLinkNameAction(linkName,
						resourceManagement));
			}

			// Add the Deactivate and Remove RA Entity actions to the
			// Pre-Uninstall Actions
			cPreUninstallActions.add(new DeactivateResourceAdaptorEntityAction(
					entityName, resourceManagement));
			cPreUninstallActions.add(new RemoveResourceAdaptorEntityAction(
					entityName, resourceManagement));

			// Finally add the actions to the respective hashmap.
			if (raId != null) {
				// We need to check if we are updating or adding new ones.
				if (postInstallActions.containsKey(raId)) {
					postInstallActions.get(raId).addAll(cPostInstallActions);
				} else {
					postInstallActions.put(raId, cPostInstallActions);
				}

				// Same here...
				if (preUninstallActions.containsKey(raId)) {
					preUninstallActions.get(raId).addAll(cPreUninstallActions);
				} else {
					preUninstallActions.put(raId, cPreUninstallActions);
				}
			}

			// recreate the lists for the next round (might come a new RA ID)...
			cPostInstallActions = new ArrayList<ManagementAction>();
			cPreUninstallActions = new ArrayList<ManagementAction>();
			raId = null;

		}

		this.postInstallActions = Collections
				.unmodifiableMap(postInstallActions);
		this.preUninstallActions = Collections
				.unmodifiableMap(preUninstallActions);
	}

	public Map<String, Collection<ManagementAction>> getPostInstallActions() {
		return postInstallActions;
	}

	public Map<String, Collection<ManagementAction>> getPreUninstallActions() {
		return preUninstallActions;
	}
}


/*
 * 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.component.deployment.jaxb.descriptors;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.slee.SLEEException;
import javax.slee.management.DeploymentException;

import org.mobicents.slee.container.component.deployment.jaxb.descriptors.service.MService;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.service.MServiceXML;
import org.mobicents.slee.container.component.service.ServiceDescriptorFactory;

/**
 * Factory to build {@link ServiceDescriptorImpl} objects.
 * @author martins
 *
 */
public class ServiceDescriptorFactoryImpl extends AbstractDescriptorFactory implements ServiceDescriptorFactory {

	/**
	 * Builds a list of {@link ServiceDescriptorImpl} objects, from an {@link InputStream} containing the event jar xml.
	 * @param inputStream
	 * @return
	 * @throws DeploymentException
	 */
	public List<ServiceDescriptorImpl> parse(InputStream inputStream) throws DeploymentException {

		Object jaxbPojo = buildJAXBPojo(inputStream);

		List<ServiceDescriptorImpl> result = new ArrayList<ServiceDescriptorImpl>();

		MServiceXML serviceXML = null;
		if (jaxbPojo instanceof org.mobicents.slee.container.component.deployment.jaxb.slee.service.ServiceXml) {
			serviceXML = new MServiceXML((org.mobicents.slee.container.component.deployment.jaxb.slee.service.ServiceXml)jaxbPojo);
		}
		else if (jaxbPojo instanceof org.mobicents.slee.container.component.deployment.jaxb.slee11.service.ServiceXml) {
			serviceXML = new MServiceXML((org.mobicents.slee.container.component.deployment.jaxb.slee11.service.ServiceXml)jaxbPojo);
		}
		else {
			throw new SLEEException("unexpected class of jaxb pojo built: "+(jaxbPojo != null ? jaxbPojo.getClass() : null));
		}

		for (MService mService : serviceXML.getMServices()) {
			result.add(new ServiceDescriptorImpl(mService));
		}
		return result;
	}
}


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

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import javassist.ClassPath;
import javassist.CtClass;
import javassist.NotFoundException;

/**
 * Wrapper for javassist class pool. Tracks class made and allows the pool
 * cleanup.
 *
 * @author martins
 *
 */
public class ClassPool implements org.mobicents.slee.container.component.ClassPool {

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

	private final List<String> classesMade;
	private final List<ClassPath> classPaths;

	private final javassist.ClassPool classPool;

	public ClassPool() {
		classesMade = new ArrayList<String>();
		classPaths = new ArrayList<ClassPath>();
		/*
		 * classPool = new ClassPool(ClassPool.getDefault());
		 *
		 * try { File mobicentsSar = new File(SbbDeployer.getLibPath()); List
		 * filesJars =
		 * ConcreteClassGeneratorUtils.getJarsFileListing(mobicentsSar); for
		 * (int i = 0; i < filesJars.size(); i++) { File jar =
		 * (File)filesJars.get(i); appendClassPath(SbbDeployer.getLibPath() +
		 * jar.getName()); } appendClassPath(SbbDeployer.getLibPath());
		 *  } catch (Exception e) { e.printStackTrace(); throw new
		 * RuntimeException("Failed initializing javassist", e); }
		 */
		classPool = new javassist.ClassPool();
	}

	/**
	 * @see javassist.ClassPool#appendClassPath(ClassPath)
	 */
	public ClassPath appendClassPath(ClassPath cp) {
		ClassPath classPath = classPool.appendClassPath(cp);
		classPaths.add(classPath);
		return classPath;
	}

	/**
	 * @see javassist.ClassPool#get(String)
	 */
	public CtClass get(String classname) throws NotFoundException {
		return classPool.get(classname);
	}

	/**
	 * @see javassist.ClassPool#makeClass(String)
	 */
	public CtClass makeClass(String classname) throws RuntimeException {
		//classLoaderDomain.flushCaches();
		CtClass ctClass = classPool.makeClass(classname);
		classesMade.add(classname);
		return ctClass;
	}

	/**
	 * @see javassist.ClassPool#makeClass(InputStream)
	 * @param classfile
	 * @return
	 * @throws IOException
	 * @throws RuntimeException
	 */
	public CtClass makeClass(InputStream inputStream) throws IOException,
			RuntimeException {
		//classLoaderDomain.flushCaches();
		CtClass ctClass = classPool.makeClass(inputStream);
		classesMade.add(ctClass.getName());
		return ctClass;
	}

	/**
	 * @see javassist.ClassPool#makeInterface(String)
	 * @param interfacename
	 * @return
	 * @throws RuntimeException
	 */
	public CtClass makeInterface(String interfacename) throws RuntimeException {
		//classLoaderDomain.flushCaches();
		CtClass ctClass = classPool.makeInterface(interfacename);
		classesMade.add(interfacename);
		return ctClass;
	}

	/**
	 * cleans up the class pool cache
	 */
	public void clean() {
		for (ClassPath classPath : classPaths) {
			classPool.removeClassPath(classPath);
		}
		for (String classMade : classesMade) {
			try {
				classPool.get(classMade).detach();
			} catch (NotFoundException e) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to detach class " + classMade
						+ " from class pool", e);
				}
			}
		}
	}

  public javassist.ClassPool getClassPool()
  {
    return classPool;
  }

}


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

/*
 * ***************************************************
 *                                                 *
 *  Restcomm: The Open Source JSLEE Platform      *
 *                                                 *
 *  Distributable under LGPL license.              *
 *  See terms of license at gnu.org.               *
 *                                                 *
 ***************************************************
 *
 * Created on Dec 6, 2004 RemoteSleeEndpoint.java
 */
package org.mobicents.slee.connector.local;

import org.apache.log4j.Logger;
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.ActivityContextHandle;
import org.mobicents.slee.container.component.event.EventTypeComponent;
import org.mobicents.slee.container.facilities.nullactivity.NullActivityHandle;
import org.mobicents.slee.container.transaction.SleeTransactionManager;

import javax.resource.ResourceException;
import javax.slee.*;
import javax.slee.connection.ExternalActivityHandle;
import javax.slee.management.SleeState;
import java.util.Arrays;

/**
 * This interface duplicates methods from {@link javax.slee.connection.SleeConnection}.
 * @author baranowb
 *
 */
public class SleeConnectionServiceImpl extends AbstractSleeContainerModule implements SleeConnectionService {

	private static final Logger logger = Logger.getLogger(SleeConnectionServiceImpl.class);
	/* (non-Javadoc)
	 * @see javax.slee.connection.SleeConnection#createActivityHandle()
	 */
	public ExternalActivityHandle createActivityHandle() throws ResourceException {
		if(sleeContainer == null)
		{
			throw new ResourceException("Connection is in closed state");
		}
		if(sleeContainer.getSleeState()!=SleeState.RUNNING)
		{
			throw new ResourceException("Container is not in running state.");
		}

		return sleeContainer.getNullActivityFactory().createNullActivityHandle();
	}

	/* (non-Javadoc)
	 * @see javax.slee.connection.SleeConnection#fireEvent(java.lang.Object, javax.slee.EventTypeID, javax.slee.connection.ExternalActivityHandle, javax.slee.Address)
	 */
	public void fireEvent(Object event, EventTypeID eventType,
			ExternalActivityHandle activityHandle, Address address) throws NullPointerException,
			UnrecognizedActivityException, UnrecognizedEventException, ResourceException {
		if(sleeContainer == null)
		{
			throw new ResourceException("Connection is in closed state");
		}
		if(sleeContainer.getSleeState()!=SleeState.RUNNING)
		{
			throw new ResourceException("Container is not in running state.");
		}
		if (logger.isDebugEnabled()) {
			logger.debug("fireEvent(event=" + event + ",eventType=" + eventType
					+ ",activityHandle=" + activityHandle + ",address="
					+ address + ")");
		}

		if (event == null) {
			throw new NullPointerException("event is null");
		}
		if (eventType == null) {
			throw new NullPointerException("event type is null");
		}
		if (activityHandle == null) {
			throw new NullPointerException("activity handle is null");
		}

		EventTypeComponent eventTypeComponent = sleeContainer
				.getComponentRepository().getComponentByID(eventType);
		if (eventTypeComponent == null) {
			throw new UnrecognizedEventException("event type not installed");
		}

		if (!eventTypeComponent.getEventTypeClass().isAssignableFrom(event.getClass())) {
			logger.info(""+eventTypeComponent.getEventTypeClass().toString());
			logger.info(""+eventTypeComponent.getEventTypeClass().getCanonicalName());
			logger.info(""+Arrays.toString(eventTypeComponent.getEventTypeClass().getDeclaredMethods()));
			logger.info(""+eventTypeComponent.getEventTypeClass().getClass().toString());
			logger.info(""+eventTypeComponent.getEventTypeClass().getClass().getCanonicalName());
			logger.info(""+event.getClass().toString());
			logger.info(""+event.getClass().getCanonicalName());
			logger.info(""+Arrays.toString(event.getClass().getDeclaredMethods()));
			logger.info("*"+eventTypeComponent);
			logger.info("*"+eventTypeComponent.getEventTypeID());
			logger.info("*"+event);
			logger.info("&"+eventTypeComponent.getClassLoader());
			logger.info("&"+eventTypeComponent.getEventTypeClass().getClassLoader());
			logger.info("&"+event.getClass().getClassLoader());
			//throw new UnrecognizedEventException(
			//	"the class of the event object fired is not assignable to the event class of the event type.\n" +
			//	" EventClass: "+event.getClass()+", component class: "+eventTypeComponent.getEventTypeClass());

		}

		if (!(activityHandle instanceof NullActivityHandle)) {
			throw new UnrecognizedActivityException(activityHandle);
		}

		// check container state is running
		SleeContainer sleeContainer = SleeContainer.lookupFromJndi();
		if (sleeContainer.getSleeState() != SleeState.RUNNING) {
			throw new IllegalStateException("Container is not running");
		}

		SleeTransactionManager txMgr = sleeContainer.getTransactionManager();
		boolean newTx = txMgr.requireTransaction();
		boolean rollback = true;

		try {
			final NullActivityHandle nullActivityHandle = (NullActivityHandle) activityHandle;
			final ActivityContextHandle ach = nullActivityHandle.getActivityContextHandle();
			ActivityContext ac = sleeContainer.getActivityContextFactory()
					.getActivityContext(ach);
			if (ac == null) {
				sleeContainer.getNullActivityFactory().createNullActivity(
						nullActivityHandle, false);
				ac = sleeContainer.getActivityContextFactory()
						.getActivityContext(ach);
				if (ac == null) {
					throw new SLEEException(
							"unable to create null ac for external activity handle "
									+ activityHandle);
				}
			}
			ac.fireEvent(eventType, event, address, null, null,null,null);
			rollback = false;
		} catch (Throwable ex) {
			logger.error("Exception in fireEvent!", ex);
		} finally {
			if (newTx) {
				if (rollback) {
					try {
						txMgr.rollback();
					} catch (Throwable e) {
						logger.error("failed to rollback implicit tx", e);
					}
				} else {
					try {
						txMgr.commit();
					} catch (Throwable e) {
						logger.error("failed to commit implicit tx", e);
					}
				}
			}
			// else ignore, specs say there is no need to rollback a tx if event
			// queuing failed
		}
	}

	/* (non-Javadoc)
	 * @see javax.slee.connection.SleeConnection#getEventTypeID(java.lang.String, java.lang.String, java.lang.String)
	 */
	public EventTypeID getEventTypeID(String name, String vendor, String version) throws UnrecognizedEventException, ResourceException {
		if(sleeContainer == null)
		{
			throw new ResourceException("Connection is in closed state");
		}
		if(sleeContainer.getSleeState()!=SleeState.RUNNING)
		{
			throw new ResourceException("Container is not in running state.");
		}
		EventTypeID eventTypeID = new EventTypeID(name, vendor, version);
		EventTypeComponent eventTypeComponent = sleeContainer
				.getComponentRepository().getComponentByID(eventTypeID);
		if (eventTypeComponent == null) {
			throw new UnrecognizedEventException("event type not installed");
		} else {
			return eventTypeID;
		}
	}

}


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

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import javax.slee.ServiceID;

import org.mobicents.slee.container.sbbentity.SbbEntityID;

public class NonRootSbbEntityID implements SbbEntityID {

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

	private SbbEntityID parentSbbEntityID;
	private String parentChildRelation;
	private String childID;

	/**
	 * not to be used, needed due to externalizable
	 */
	public NonRootSbbEntityID() {

	}

	public NonRootSbbEntityID(SbbEntityID parentSbbEntityID, String parentChildRelation, String childID) {
		this.parentSbbEntityID = parentSbbEntityID;
		this.parentChildRelation = parentChildRelation;
		this.childID = childID;
	}

	public String getChildID() {
		return childID;
	}

	@Override
	public String getName() {
		return childID;
	}

	@Override
	public SbbEntityID getParentSBBEntityID() {
		return parentSbbEntityID;
	}

	@Override
	public String getParentChildRelation() {
		return parentChildRelation;
	}

	@Override
	public ServiceID getServiceID() {
		return parentSbbEntityID.getServiceID();
	}

	@Override
	public String getServiceConvergenceName() {
		return parentSbbEntityID.getServiceConvergenceName();
	}

	@Override
	public boolean isRootSbbEntity() {
		return false;
	}

	@Override
	public SbbEntityID getRootSBBEntityID() {
		return parentSbbEntityID.getRootSBBEntityID();
	}

	@Override
	public int hashCode() {
		int result = childID.hashCode();
		result = result*31 + parentChildRelation.hashCode();
		result = result*31 + parentSbbEntityID.hashCode();
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final NonRootSbbEntityID other = (NonRootSbbEntityID) obj;
		if (!parentSbbEntityID.equals(other.parentSbbEntityID))
			return false;
		if (!parentChildRelation.equals(other.parentChildRelation))
			return false;
		if (!childID.equals(other.childID))
			return false;
		return true;
	}

	private String toString = null;

	@Override
	public String toString() {
		if (toString == null) {
			toString = new StringBuilder(parentSbbEntityID.toString()).append('/').append(parentChildRelation).append('/').append(childID).toString();
		}
		return toString;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
	 */
	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		parentSbbEntityID = (SbbEntityID) in.readObject();
		parentChildRelation = in.readUTF();
		childID = in.readUTF();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
	 */
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(parentSbbEntityID);
		out.writeUTF(parentChildRelation);
		out.writeUTF(childID);
	}
}


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

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;

import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.Set;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.slee.Address;
import javax.slee.AddressPlan;

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

/**
 * Command performs operation on specific profile object.
 * @author baranowb
 *
 */
public class ProfileEditCommand extends AbstractSleeCommand {
	//TODO: add bussines method calls

	private ObjectName PROFILE_PROVISIONING_MBEAN;
	/**
	 */
	public ProfileEditCommand() {
		super("profile.edit", "This command performs operations on JSLEE Profile MBean like: javax.slee.profile:type=Profile,profileTableName=CallControl,profileName=");

		try {
			this.PROFILE_PROVISIONING_MBEAN = new ObjectName(Utils.SLEE_PROFILE_PROVISIONING); //damn annoying.
		} catch (MalformedObjectNameException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NullPointerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

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

		out.println(desc);
		out.println();
		//out.println("usage: " + name + " <-tprofileTableName> [-pprofileName] <operation> <arg>*");
		out.println("usage: " + name + " <profileTableName> <profileName> <-operation[[arg] | [--option[=arg]]]*>");
		//out.println("This command requires atleast option -t to specify table name. If -p is not used, it performs operations on defualt profiles.");
		out.println();
		//out.println("config:");
		//out.println("    -t, --table                    Specifies table name from which profile should be accessed. Requires table name as argument.");
		//out.println("    -p, --profile                  Specifies profile name of profile to be accessed. Requires profile name as argument.");
		out.println("operation:");
		out.println("    -l, --list                     Returns list of available attributes and information associated with each.");
        out.println("    -x, --extlist                  Returns extended list of available attributes, information and value associated with each.");
		out.println("    -d, --dirty                    Returns indication if profile is dirty. Does not require argument.");
		//isProfileDirty()
		out.println("    -w, --write                    Returns indication if profile is in write mode. Does not require argument.");
		//isProfileWriteable()
		out.println("    -e, --edit                     Marks profile as editable. Without this op any other will fail. Does not require argument.");
		//editProfile()
		out.println("    -c, --commit                   Commits changes done to profile. Can be invoked only after \"-e\". Does not require argument.");
		//commitProfile()
		out.println("    -r, --restore                  Revokes changes done to profile. Can be invoked only after \"-e\". Does not require argument.");
		//restoreProfile()
		out.println("    -o, --close                    De-registers MBean. Does not require argument.");
		//closeProfile()
		out.println("    -g, --get                      Returns value of profile attribute. Requires attribute name, ie. : \"voiceMailEnabled\".");
		out.println("    -s, --set                      Sets value of profile attribute. Supports mandatory options:.");
		out.println("         --name                    Specifies name of profile attribute. Requires attribute name as parameter.");
		out.println("         --separator               Specifies separator for parsing profile attribute value and creating arrays. Does not require separator as parameter if profile attribute value is not array.");
		out.println("         --value                   Specifies string representation of profile attribute value. Requires value as parameter.");
		out.println("                                   Command tries locally registered JMX Editor to parse value and optimize call, if editor is not found");
		out.println("                                   it dispatches call in hope that server side has better luck.");
		//out.println("    -b, --business                 Sets value of profile attribute. Supports mandatory options:.");
		//out.println("         --method                  Specifies business method name. Requires method name as parameter.");
		//out.println("         --name                    Specifies name of profile attribute. Requires attribute name as parameter.");
		//out.println("         --value                   Specifies string representation of profile attribute value. Requires value as parameter.");
		//out.println("                                   Command tries locally registered JMX Editor to parse value and optimize call, if editor is not found");
		//out.println("                                   it dispatches call in hope that server side has better luck.");
		out.println("Examples: ");
		out.println("");
		out.println("     1. Check if profile has been edited:");
		out.println("" + name + " CallControl mobile.user -w");
		out.println("");
		out.println("     2. Check if any changes were introduced to profile:");
		out.println("" + name + " CallControl mobile.user -d");
		out.println("");
		out.println("     3. Start editing:");
		out.println("" + name + " CallControl mobile.user -e");
		out.println("");
		out.println("     4. Set attribute:");
		out.println("" + name + " CallControl mobile.user -s --name=userPhone --value=sip:ala@ma.kota:5090");
		out.println("");
		out.println("     5. Set attribute with array:");
		out.println("" + name + " CallControl mobile.user -s --name=users --separator=\";\" --value=\"Bob;Alice;John\"");
		out.println("");
		out.println("     6. Commit changes:");
		out.println("" + name + " CallControl mobile.user -c");

		out.flush();
	}

	////////////////////////
	// Command essentials //
	////////////////////////

	private static final String OPERATION_DEFAULT_GET ="getDefaultProfile";
	private static final String OPERATION_GET ="getProfile";

	//object name for profile
	private ObjectName specificObjectName;
	//table name
	private String profileTableName;
	//profile name
	private String profileName;
	//info about bean. we will use it to get type of args, check editors etc.
	private MBeanInfo beanInfo;
	////////////////////////
	// Set     essentials //
	////////////////////////

	//list of fields, so we know what type they are?

	/* (non-Javadoc)
	 * @see org.mobicents.tools.twiddle.AbstractSleeCommand#getBeanOName()
	 */
	@Override
	public ObjectName getBeanOName() throws MalformedObjectNameException, NullPointerException {
		//tricky, its kind of dynamic :)
		//we get it from ProfileProvisioningMBean, we could create it by hand, but there is no need for that, since
		//user calls this, it indicates there is interest in editing

		return specificObjectName;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.tools.twiddle.AbstractSleeCommand#processArguments(java.lang.String[])
	 */
	@Override
	protected void processArguments(String[] args) throws CommandException {
		//first, we must get atleast -t, possibly -p, depending on those we call different method on ProfileProvisioningMBean
		//String sopts = ":t:p:ldwecrog:s";
		String sopts = "-:l:xdwecrog:s";

		LongOpt[] lopts = {
				//conf part
				//new LongOpt("table", LongOpt.REQUIRED_ARGUMENT, null, 't'),
				//new LongOpt("profile", LongOpt.REQUIRED_ARGUMENT, null, 'p'),
				 new LongOpt("noprefix", LongOpt.NO_ARGUMENT, null, 0x1000),
				//operration part
				new LongOpt("list", LongOpt.NO_ARGUMENT, null, 'l'),
                new LongOpt("extlist", LongOpt.NO_ARGUMENT, null, 'x'),
				new LongOpt("dirty", LongOpt.NO_ARGUMENT, null, 'd'),
				new LongOpt("write", LongOpt.NO_ARGUMENT, null, 'w'),
				new LongOpt("edit", LongOpt.NO_ARGUMENT, null, 'e'),
				new LongOpt("commit", LongOpt.NO_ARGUMENT, null, 'c'),
				new LongOpt("restore", LongOpt.NO_ARGUMENT, null, 'r'),
				new LongOpt("close", LongOpt.NO_ARGUMENT, null, 'o'),
				//get,set,bussines
				new LongOpt("get", LongOpt.REQUIRED_ARGUMENT, null, 'g'),
				new LongOpt("set", LongOpt.NO_ARGUMENT, null, 's'),
					//options for set
					new LongOpt("name", LongOpt.REQUIRED_ARGUMENT, null, SetAttributeOperation.name),
					new LongOpt("separator", LongOpt.OPTIONAL_ARGUMENT, null, SetAttributeOperation.separator),
					new LongOpt("value", LongOpt.OPTIONAL_ARGUMENT, null, SetAttributeOperation.value),
				//new LongOpt("bussines", LongOpt.NO_ARGUMENT, null, 'b'),
				};

		Getopt getopt = new Getopt(null, args, sopts, lopts);
		getopt.setOpterr(false);
		int nonOptArgIndex = 0;
		int code;
		while ((code = getopt.getopt()) != -1) {
			switch (code) {
			case ':':
				throw new CommandException("Option requires an argument: " + args[getopt.getOptind() - 1]);

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

				//CONF PART
//			case 't':
//				this.profileTableName = getopt.getOptarg();
//				break;
//			case 'p':
//				this.profileName = getopt.getOptarg();
//				break;
				// OPERATION PART
			case 0x1000:
				break;
			case 1:
				//non opt args, table and profile name(maybe)
				switch(nonOptArgIndex)
				{
				case 0:
					profileTableName  = getopt.getOptarg();
					nonOptArgIndex++;
					break;
				case 1:
					profileName  = getopt.getOptarg();
					nonOptArgIndex++;
					break;
				default:
					throw new CommandException("Command: \"" + getName() + "\" expects at most two non opt arguments!");
				}
				break;

			case 'l':
				//list
				super.operation = new ListOperation(super.context, super.log, this);
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
            case 'x':
                //extlist
                super.operation = new ExtListOperation(super.context, super.log, this);
                prepareCommand();
                super.operation.buildOperation(getopt, args);
                break;
			case 'd':
				//dirt
				super.operation = new SimpleInvokeOperation(super.context, super.log, this,"isProfileDirty");
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 'w':
				//write
				super.operation = new SimpleInvokeOperation(super.context, super.log, this,"isProfileWriteable");
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 'e':
				//edit
				super.operation = new SimpleInvokeOperation(super.context, super.log, this,"editProfile");
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 'c':
				//commit
				super.operation = new SimpleInvokeOperation(super.context, super.log, this,"commitProfile");
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 'r':
				//restore
				super.operation = new SimpleInvokeOperation(super.context, super.log, this,"restoreProfile");
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 'o':
				//close
				super.operation = new SimpleInvokeOperation(super.context, super.log, this,"closeProfile");
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 'g':
				//get
				super.operation = new GetAttributeOperation(super.context, super.log, this);
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
			case 's':
				//set
				super.operation = new SetAttributeOperation(super.context, super.log, this);
				prepareCommand();
				super.operation.buildOperation(getopt, args);
				break;
//			case 'b':
//				//bussines
//				super.operation = new BussinesMethodOperation(super.context, super.log, this);
//				super.operation.buildOperation(getopt, args);
//				break;

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

			}
		}
	}

	private void prepareCommand() throws CommandException
	{
		//get bean name and bean info.
		if(profileTableName == null)
		{
			throw new CommandException("Command: \"" + getName() + "\", expects atleast \"-t\" to specify table name!");
		}
		String getOperationName;
		String[] sig;
		Object[] parms;
		MBeanServerConnection server = super.context.getServer();
		if(profileName == null) {
			//default;
			getOperationName = OPERATION_DEFAULT_GET;
			parms = new Object[]{profileTableName};
			sig = new String[]{"java.lang.String"};
		}else
		{
			getOperationName = OPERATION_GET;
			parms = new Object[]{profileTableName, profileName};
			sig = new String[]{"java.lang.String", "java.lang.String"};
		}

		try {
			specificObjectName = (ObjectName) server.invoke(PROFILE_PROVISIONING_MBEAN, getOperationName, parms, sig);
            System.out.println("specificObjectName 1: "+specificObjectName);

		} catch (Exception e) {

			// if main operation is commitProfile and execute after createProfile
			// ProfileProvisioning MBean can not execute getProfile for new profile after createProfile execution
			if (this.operation.getOperationName().equals("commitProfile"))
			{
				try {
					String profileBeanName = "javax.slee.profile:profileName=" + this.profileName;
					profileBeanName += ",profileTableName=" + this.profileTableName;
					profileBeanName += ",type=Profile";
					specificObjectName = new ObjectName(profileBeanName);
                    System.out.println("specificObjectName 2: "+specificObjectName);
				}catch (Exception ce) {
					throw new CommandException ("Command: \"" + getName()+"\" failed to commit bean name for specified profile.", ce);
				}
			}
			else
			{
				throw new CommandException("Command: \"" + getName()+"\" failed to obtain bean name for specified table name and profile. Table or profile does not exist.", e);
			}
		}

		//ok, now lets get bean info;
		try{
			this.beanInfo = server.getMBeanInfo(specificObjectName);

		}catch (Exception e) {
			throw new CommandException("Command: \"" + getName()+"\" failed to obtain bean name for specified table name and profile.", e);
		}
	}

	private MBeanAttributeInfo findAttribute(String attr_name,
		      MBeanAttributeInfo[] attribute_info)
		   {
		      for (int i = 0; i < attribute_info.length; i++)
		      {
		         MBeanAttributeInfo mBeanAttributeInfo = attribute_info[i];
		         if (mBeanAttributeInfo.getName().equals(attr_name))
		            return mBeanAttributeInfo;
		      }
		      return null;
		   }

	private class ListOperation extends AbstractOperation {

		public ListOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			//not set, since its complicated :)
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			//nothing
		}
		@Override
		public void invoke() throws CommandException {
			try {

				//we dont need to invoke anything, we have everything already :)
				displayResult();
			} catch (Exception e) {
				//add handle error here?
				throw new CommandException("Failed to invoke \"" + this.operationName + "\" due to: ", e);
			}
		}

		/* (non-Javadoc)
		 * @see org.mobicents.tools.twiddle.op.AbstractOperation#displayResult()
		 */
		@Override
		public void displayResult() {

			if (!context.isQuiet()) {
				Set<String> notDisplayed = new HashSet<String>();
				notDisplayed.add("ProfileDirty");
				notDisplayed.add("ProfileWriteable");
				// Translate the result to text
				String resultText = null;
				PrintWriter out = context.getWriter();
				//dont look at operationResult, we want info

				if (beanInfo != null) {
					MBeanAttributeInfo[] infos = beanInfo.getAttributes();
					for(MBeanAttributeInfo info:infos)
					{
						if(notDisplayed.contains(info.getName()))
						{
							//skip
							continue;
						}
						out.println();
						//out.println("Class: "+info.getClass());
						out.println("Desc : "+info.getDescription());
						out.println("Name : "+info.getName());
						out.println("Type : "+info.getType());
						out.println("r/w  : "+info.isReadable()+"/"+info.isWritable());
						out.println("isIs : "+info.isIs()); // LOL :)

					}
				} else {
					resultText = "'success'";
					out.println(resultText);
				}
				// render results to out
				out.flush();
			}
		}

	}

    private class ExtListOperation extends AbstractOperation {

        //so we can access MBean info.
        private ProfileEditCommand editCommand;
        public ExtListOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
            super(context, log, sleeCommand);
            //not set, since its complicated :)
        }

        @Override
        public void buildOperation(Getopt opts, String[] args) throws CommandException {
            //nothing
        }
        @Override
        public void invoke() throws CommandException {
            try {
                //we dont need to invoke anything, we have everything already :)
                displayResult();
            } catch (Exception e) {
                //add handle error here?
                throw new CommandException("Failed to invoke \"" + this.operationName + "\" due to: ", e);
            }
        }

        /* (non-Javadoc)
         * @see org.mobicents.tools.twiddle.op.AbstractOperation#displayResult()
         */
        @Override
        public void displayResult() {
            if (!context.isQuiet()) {
                MBeanServerConnection conn = context.getServer();

                Set<String> notDisplayed = new HashSet<String>();
                notDisplayed.add("ProfileDirty");
                notDisplayed.add("ProfileWriteable");
                // Translate the result to text
                String resultText = null;
                PrintWriter out = context.getWriter();
                //dont look at operationResult, we want info

                try {
                    if (beanInfo != null) {
                        MBeanAttributeInfo[] infos = beanInfo.getAttributes();
                        for (MBeanAttributeInfo info : infos) {
                            if (notDisplayed.contains(info.getName())) {
                                //skip
                                continue;
                            }
                            out.println();
                            //out.println("Class: "+info.getClass());
                            out.println("Desc : " + info.getDescription());
                            out.println("Name : " + info.getName());
                            out.println("Type : " + info.getType());
                            out.println("r/w  : " + info.isReadable() + "/" + info.isWritable());
                            out.println("isIs : " + info.isIs()); // LOL :)

                            if (info.isReadable()) {
                                operationResult = conn.getAttribute(specificObjectName, info.getName());
                                if (operationResult == null) {
                                    resultText = "undefined";
                                } else {
                                    resultText = prepareResultText();
                                }
                                out.println("Value : " + resultText);
                            }

                        }
                    } else {
                        resultText = "'success'";
                        out.println(resultText);
                    }
                } catch(Exception e) {
                    e.printStackTrace();
                }
                // render results to out
                out.flush();
            }
        }

    }

	/**
	 * Simple op to invoke some non arg methods.
	 * @author baranowb
	 *
	 */
	private class SimpleInvokeOperation extends AbstractOperation {

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

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
		}
	}
	private class GetAttributeOperation extends AbstractOperation {

		//private String attributeName;
		//so we can access MBean info.
		private ProfileEditCommand editCommand;
		public GetAttributeOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			//name is derived from type of attribute, usually its "get", but in case MBean info will reveal bool, it will be "is"
			//command for bean info
			this.editCommand = (ProfileEditCommand) sleeCommand;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			String attributeName =  opts.getOptarg();

			//now we have to build name of command.
			MBeanAttributeInfo info = findAttribute(attributeName, editCommand.beanInfo.getAttributes());
			if(info == null)
			{
				throw new CommandException("Attribute: "+attributeName+", does not exist in bean!");
			}
			if(!info.isReadable())
			{
				throw new CommandException("Attribute: "+attributeName+", is not readable!");
			}

			if(info.isIs())
			{
				super.operationName = "is"+attributeName;
			}else
			{
				super.operationName = "get"+attributeName;
			}

		}
	}
	private class SetAttributeOperation extends AbstractOperation {
		//TODO: allow no value to indicate set(null);
		public static final char name = 'n';
		public static final char separator = 'p';
		public static final char value = 'v';

		private String attributeName;
		private String valueSeparator;
		private String stringAttributeValue;
		//so we can access MBean info.
		private ProfileEditCommand editCommand;
		public SetAttributeOperation(CommandContext context, Logger log, AbstractSleeCommand sleeCommand) {
			super(context, log, sleeCommand);
			//name is derived from type of attribute, usually its "get", but in case MBean info will reveal bool, it will be "is"
			//command for bean info
			this.editCommand = (ProfileEditCommand) sleeCommand;
		}

		@Override
		public void buildOperation(Getopt opts, String[] args) throws CommandException {
			int code;
			while ((code = opts.getopt()) != -1) {
				switch (code) {
				case ':':
					throw new CommandException("Option requires an argument: " + args[opts.getOptind() - 1]);

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

				case name:
					attributeName = opts.getOptarg();
					break;
				case separator:
					//this can be empty.
					valueSeparator = opts.getOptarg();
					break;
				case value:
					//this can be empty.
					stringAttributeValue = opts.getOptarg();
					break;
				default:
					throw new CommandException("Operation \"" + this.operationName + "\" for command: \"" + sleeCommand.getName()
							+ "\", found unexpected opt: " + args[opts.getOptind() - 1]);

				}
			}

			if(attributeName == null)
			{
				throw new CommandException("Operation \"" + this.operationName + "\" for command: \"" + sleeCommand.getName()
						+ "\", requiers \"--name\".");
			}
//			if(stringAttributeValue == null)
//			{
//				throw new CommandException("Operation \"" + this.operationName + "\" for command: \"" + sleeCommand.getName()
//						+ "\", requiers \"--value\".");
//			}

			MBeanAttributeInfo info = findAttribute(attributeName, editCommand.beanInfo.getAttributes());

			//hmm
			super.operationName = "set" + info.getName();

			if (valueSeparator != null) {
				Class arrayClazz;
				Class valueClazz;
				try {
					arrayClazz = Class.forName(info.getType());
					if (arrayClazz.isArray()) {
						valueClazz = arrayClazz.getComponentType();
					} else {
						throw new CommandException("Attribute type is not array and separator is not used.");
					}
				} catch (ClassNotFoundException e) {
					throw new CommandException("Class is not found for attribute type.");
				}

				if (valueSeparator.length() > 1) {
					throw new CommandException("Separator must contains only single character.");
				}

				String delims = "["+valueSeparator+"]";
				String[] tokens = stringAttributeValue.split(delims);

				Object array = Array.newInstance(valueClazz, tokens.length);

				for (int i=0; i<tokens.length; i++) {

					try {
						if (valueClazz.isPrimitive()) {
							// Fixing issue #47
                            String token = tokens[i];
							switch (array.getClass().getComponentType().getCanonicalName()) {
								case "byte": {
									Array.set(array, i, Byte.parseByte(token));
									break; }
								case "short": {
									Array.set(array, i, Short.parseShort(token));
									break; }
								case "int": {
									Array.set(array, i, Integer.parseInt(token));
									break; }
								case "long": {
									Array.set(array, i, Long.parseLong(token));
									break; }
								case "float": {
									Array.set(array, i, Float.parseFloat(token));
									break; }
								case "double": {
									Array.set(array, i, Double.parseDouble(token));
									break; }
								case "boolean": {
									Array.set(array, i, Boolean.parseBoolean(token));
									break; }
								case "char": {
									Array.set(array, i, token.charAt(0));
									break; }
								default: {
									System.out.println("Did not find cast method for type: " + valueClazz.getCanonicalName());
									break; }
							}

						}

						if (!valueClazz.isPrimitive()) {
							Object value = null;
							if (valueClazz.getName().equals("javax.slee.Address")) {
								try {
									int delimerSize = 2;
									int delimiter = tokens[i].indexOf(": ");
									if (delimiter == -1) {
										delimerSize = 1;
										delimiter = tokens[i].indexOf(":");
									}
									if (delimiter == -1) {
										throw new IllegalArgumentException("Address arg should be \"address plan as string\" + \": \" + \"address as string\"");
									}
									String addressPlan = tokens[i].substring(0, delimiter);
									String address = tokens[i].substring(delimiter + delimerSize);
									value = new Address(AddressPlan.fromString(addressPlan), address);
								} catch (Exception ex) {
									throw new IllegalArgumentException(ex.getMessage(), ex);
								}
							} else {
								Constructor con = valueClazz.getConstructor(String.class);
								if (con != null) {
									value = con.newInstance((String) tokens[i]);
								}
							}
							Array.set(array, i, value);
						}

					} catch (Exception ex) {
						throw new CommandException("Value getting has problem: " + ex.getMessage());
					}
				}

				try {
					super.addArg(array, info.getType(), false);
				} catch (CommandException ce) {
					//lets add, hope server knows how to handle
					//super.addArg(array, info.getType(), false);
				}
			} else {
				try {
					super.addArg(stringAttributeValue, info.getType(), true);
				} catch (CommandException ce) {
					//lets add, hope server knows how to handle
					super.addArg(stringAttributeValue, info.getType(), false);
				}
			}
		}
	}

}


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

import java.util.LinkedList;

import javax.slee.SLEEException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.profile.UnrecognizedProfileTableNameException;
import javax.transaction.SystemException;

import org.apache.log4j.Logger;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.facilities.NotificationSourceWrapper;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.container.transaction.TransactionContext;

/**
 *
 * Start time:17:47:16 2009-03-25

 * Project: restcomm-jainslee-server-core

 * This is class that carefully records calls to profile classes. On calls
 * within transaction this records profile name that has been called. In case of
 * reentrant profile we look for:
 *

 *
loop call - if currently called profile is reentrant && call names
 * contain this profile && last call name is not this profile

 *
calls from different thread
 --- how should we do that ? lol
 *

 *
 * @author  Bartosz Baranowski
 * @author  Alexandre Mendonca
 *
 */
public class ProfileCallRecorderTransactionData {

	// FIXME: Shoudl we also look up threads ?
	private final static SleeContainer sleeContainer = SleeContainer.lookupFromJndi();
	private static Logger logger = Logger.getLogger(ProfileCallRecorderTransactionData.class);
	private static final String TRANSACTION_CONTEXT_KEY = "pctd";

	/**
	 * a linked list with the which contains string values representing profile
	 * table - profile pairs that has been called within transaction.
	 */
	private final LinkedList<String> invokedProfiles = new LinkedList<String>();

	/**
	 * Stores profile table name. This is required for alarm factility, as
	 * source changes with call to different profile table+profile pair.
	 */
	private final LinkedList<String> invokedProfileTablesNames = new LinkedList<String>();

	/**
	 * Adds call to this profile.
	 *
	 * @param po
	 * @throws SLEEException
	 */
	@SuppressWarnings("unchecked")
	public static void addProfileCall(ProfileObjectImpl po) throws SLEEException
	{
    SleeTransactionManager sleeTransactionManager = sleeContainer.getTransactionManager();

    try {
      if(sleeTransactionManager.getTransaction() == null) {
        return;
      }
    }
    catch ( SystemException se ) {
      throw new SLEEException("Unable to verify SLEE Transaction.", se);
    }

	  String key = makeKey(po);

		if (logger.isTraceEnabled()) {
			logger.trace("Recording call to profile. Key[" + key + "]");
		}

		final TransactionContext txContext = sleeTransactionManager.getTransactionContext();
		ProfileCallRecorderTransactionData data = (ProfileCallRecorderTransactionData) txContext.getData().get(TRANSACTION_CONTEXT_KEY);

		// If data does not exist, create it
		if (data == null) {
			data = new ProfileCallRecorderTransactionData();
			txContext.getData().put(TRANSACTION_CONTEXT_KEY, data);
		}

		if (!po.isProfileReentrant())
		{
			// we need to check
			if (data.invokedProfiles.contains(key) && data.invokedProfiles.getLast().compareTo(key) != 0) {
				throw new SLEEException("Detected loopback call. Call sequence: " + data.invokedProfiles);
			}
			data.invokedProfiles.add(key);
			data.invokedProfileTablesNames.add(po.getProfileTable().getProfileTableName());
		}

	}

	public static void removeProfileCall(ProfileObjectImpl po) throws SLEEException
	{
		SleeTransactionManager sleeTransactionManager = sleeContainer.getTransactionManager();

		try {
			if(sleeTransactionManager.getTransaction() == null) {
				return;
			}
		}
		catch ( SystemException se ) {
			throw new SLEEException("Unable to verify SLEE Transaction.", se);
		}

		String key = makeKey(po);

		if (logger.isTraceEnabled()) {
			logger.trace("Removing call to profile. Key[" + key + "]");
		}

		final TransactionContext txContext = sleeTransactionManager.getTransactionContext();
		ProfileCallRecorderTransactionData data = (ProfileCallRecorderTransactionData) txContext.getData().get(TRANSACTION_CONTEXT_KEY);

		if (data == null) {
			throw new SLEEException("No Profile call recorder in memory, this is a bug.");
		}

		if (!po.isProfileReentrant())
		{
			// we need to check
			String lastKey = data.invokedProfiles.getLast();
			if (lastKey.compareTo(key) != 0)
			{
				// logger.error("Last called profile does not match current: " + key + ", last call: " + lastKey + ". Please report this, it is a bug.");
				throw new SLEEException("Last called profile does not match current: " + key + ", last call: " + lastKey);
			}
			else
			{
				data.invokedProfiles.removeLast();
				data.invokedProfileTablesNames.removeLast();
				if (data.invokedProfiles.isEmpty())
				{
					txContext.getData().remove(TRANSACTION_CONTEXT_KEY);
				}
			}
		}

	}

	public static NotificationSourceWrapper getCurrentNotificationSource() throws TransactionRequiredLocalException, SLEEException
	{
		if(logger.isTraceEnabled()) {
			logger.trace("Trying to get Notification source for profile table.");
		}

		final TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
		ProfileCallRecorderTransactionData data = (ProfileCallRecorderTransactionData) txContext.getData().get(TRANSACTION_CONTEXT_KEY);
		if (data == null) {
			throw new SLEEException("No Profile call recorder in memory, this is a bug.");
		}

		//IF data is present, there is something in it.
		String tableName = data.invokedProfileTablesNames.getLast();
		try {
			return sleeContainer.getSleeProfileTableManager().getProfileTable(tableName).getProfileTableNotification();
		}
		catch (UnrecognizedProfileTableNameException e) {
			throw new SLEEException("Failed to fetch notification source due to some system level error.", e);
		}
	}

	private static String makeKey(ProfileObjectImpl pc)
	{
		return pc.getProfileTable().getProfileTableName() + "-" + (pc.getProfileEntity() == null ? "NO_PROFILE_ENTITY" : pc.getProfileEntity().getProfileName());
	}
}


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

import java.util.Map;

import javax.slee.CreateException;
import javax.slee.SLEEException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.profile.UnrecognizedProfileNameException;

import org.mobicents.slee.container.profile.entity.ProfileEntity;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;

/**
 * The transaction view of a profile table.
 *
 * @author martins
 *
 */
public class ProfileTableTransactionView {

	/**
	 * the profile table
	 */
	private final ProfileTableImpl profileTable;

	/**
	 *
	 * @param profileTable
	 */
	public ProfileTableTransactionView(ProfileTableImpl profileTable) {
		this.profileTable = profileTable;
	}

	/**
	 *
	 * @return
	 * @throws SLEEException
	 */
	private Map getTxData() throws SLEEException {
		final SleeTransactionManager txManager = profileTable
				.getSleeContainer().getTransactionManager();
		txManager.mandateTransaction();
		return txManager.getTransactionContext().getData();
	}

	/**
	 * Retrieves a profile object for the table and specified profile name,
	 * there is only one profile object per profile entity per transaction
	 *
	 * @param profileName
	 * @return
	 * @throws TransactionRequiredLocalException
	 * @throws SLEEException
	 */
	public ProfileObjectImpl getProfile(String profileName)
			throws TransactionRequiredLocalException, SLEEException {

		Map txData = getTxData();
		ProfileTransactionID key = new ProfileTransactionID(profileName,
				profileTable.getProfileTableName());
		ProfileObjectImpl value = (ProfileObjectImpl) txData.get(key);
		if (value == null) {
			ProfileObjectPool pool = profileTable.getProfileManagement()
				.getObjectPoolManagement().getObjectPool(
							profileTable.getProfileTableName());
			value = pool.borrowObject();
			passivateProfileObjectOnTxEnd(profileTable.getSleeContainer()
					.getTransactionManager(), value, pool);
			try {
				value.profileActivate(profileName);
			} catch (UnrecognizedProfileNameException e) {
				value.invalidateObject();
				pool.invalidateObject(value);
				return null;
			}
			txData.put(key, value);
		}
		return value;
	}

	/**
	 *
	 * Retrieves a profile object for the table and specified profile entity,
	 * there is only one profile object per profile entity per transaction
	 *
	 * @param profileEntity
	 * @return
	 * @throws TransactionRequiredLocalException
	 * @throws SLEEException
	 */
	public ProfileObjectImpl getProfile(ProfileEntity profileEntity)
			throws TransactionRequiredLocalException, SLEEException {

		Map txData = getTxData();
		ProfileTransactionID key = new ProfileTransactionID(profileEntity
				.getProfileName(), profileTable.getProfileTableName());
		ProfileObjectImpl value = (ProfileObjectImpl) txData.get(key);
		if (value == null) {
			ProfileObjectPool pool = profileTable.getProfileManagement()
			.getObjectPoolManagement().getObjectPool(
							profileTable.getProfileTableName());
			value = pool.borrowObject();
			passivateProfileObjectOnTxEnd(profileTable.getSleeContainer()
					.getTransactionManager(), value, pool);
			value.profileActivate(profileEntity);
			txData.put(key, value);
		}
		return value;
	}

	/**
	 *
	 * @param profileName
	 * @return
	 * @throws TransactionRequiredLocalException
	 * @throws SLEEException
	 * @throws CreateException
	 */
	public ProfileObjectImpl createProfile(String profileName)
			throws TransactionRequiredLocalException, SLEEException,
			CreateException {

		Map txData = getTxData();
		ProfileTransactionID key = new ProfileTransactionID(profileName,
				profileTable.getProfileTableName());
		ProfileObjectImpl value = (ProfileObjectImpl) txData.get(key);
		if (value == null) {
			ProfileObjectPool pool = profileTable.getProfileManagement()
			.getObjectPoolManagement().getObjectPool(
							profileTable.getProfileTableName());
			value = pool.borrowObject();
			passivateProfileObjectOnTxEnd(profileTable.getSleeContainer()
					.getTransactionManager(), value, pool);
			value.profileCreate(profileName);
			txData.put(key, value);
		}
		return value;
	}

	/**
	 * Adds transactional actions to the active transaction to passivate a
	 * profile object.
	 *
	 * @param txManager
	 * @param profileObject
	 * @param pool
	 */
	public static void passivateProfileObjectOnTxEnd(
			SleeTransactionManager txManager,
			final ProfileObjectImpl profileObject, final ProfileObjectPool pool) {
		TransactionalAction afterRollbackAction = new TransactionalAction() {
			public void execute() {
				profileObject.invalidateObject();
				pool.returnObject(profileObject);
			}
		};
		TransactionalAction beforeCommitAction = new TransactionalAction() {
			public void execute() {
				if (profileObject.getState() == ProfileObjectState.READY) {
					if (!profileObject.getProfileEntity().isRemove()) {
						profileObject.fireAddOrUpdatedEventIfNeeded();
						profileObject.profilePassivate();
					} else {
						profileObject.profileRemove(true, false);
					}
					pool.returnObject(profileObject);
				}
			}
		};
		final TransactionContext txContext = txManager.getTransactionContext();
		txContext.getAfterRollbackActions().add(afterRollbackAction);
		txContext.getBeforeCommitActions().add(beforeCommitAction);
	}

	/**
	 *
	 * @author martins
	 *
	 */
	private static class ProfileTransactionID {

		private final String profileName;
		private final String profileTableName;

		public ProfileTransactionID(String profileName, String profileTableName) {
			this.profileName = profileName;
			this.profileTableName = profileTableName;
		}

		@Override
		public int hashCode() {
			return profileTableName.hashCode() * 31
					+ ((profileName == null) ? 0 : profileName.hashCode());
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ProfileTransactionID other = (ProfileTransactionID) obj;
			if (profileName == null) {
				if (other.profileName != null)
					return false;
			} else if (!profileName.equals(other.profileName))
				return false;
			return profileTableName.equals(other.profileTableName);
		}
	}
}