package javax.slee.facilities;

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

/**
 * This class defines an enumerated type for the trace levels supported by the 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 trace levels in descending order are:
 *

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

 *

 * The trace level OFF is also defined for the purposes of disabling trace message
 * generation at the management level.
 * @since SLEE 1.1
 */
public final class TraceLevel implements Serializable {
    /**
     * An integer representation of {@link #OFF}.
     */
    public static final int LEVEL_OFF = 0;

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

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

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

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

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

    /**
     * An integer representation of {@link #FINER}.
     */
    public static final int LEVEL_FINER = 6;

    /**
     * An integer representation of {@link #FINEST}.
     */
    public static final int LEVEL_FINEST = 7;

    /**
     * A string representation of {@link #OFF}.
     */
    public static final String OFF_STRING = "Off";

    /**
     * A string representation of {@link #SEVERE}.
     */
    public static final String SEVERE_STRING = "Severe";

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

    /**
     * A string representation of {@link #INFO}.
     */
    public static final String INFO_STRING = "Info";

    /**
     * A string representation of {@link #CONFIG}.
     */
    public static final String CONFIG_STRING = "Config";

    /**
     * A string representation of {@link #FINE}.
     */
    public static final String FINE_STRING = "Fine";

    /**
     * A string representation of {@link #FINER}.
     */
    public static final String FINER_STRING = "Finer";

    /**
     * A string representation of {@link #FINEST}.
     */
    public static final String FINEST_STRING = "Finest";

    /**
     * Trace level for maximum filtering.  Notifications are not be generated using this level.
     */
    public static final TraceLevel OFF = new TraceLevel(LEVEL_OFF);

    /**
     * Trace level for messages that indicate a severe error has occurred.
     */
    public static final TraceLevel SEVERE = new TraceLevel(LEVEL_SEVERE);

    /**
     * Trace level for messages that indicate warnings.
     */
    public static final TraceLevel WARNING = new TraceLevel(LEVEL_WARNING);

    /**
     * Trace level for general information messages.
     */
    public static final TraceLevel INFO = new TraceLevel(LEVEL_INFO);

    /**
     * Trace level for messages relating to configuration.
     */
    public static final TraceLevel CONFIG = new TraceLevel(LEVEL_CONFIG);

    /**
     * Trace level for general coarse-grained debugging messages.
     */
    public static final TraceLevel FINE = new TraceLevel(LEVEL_FINE);

    /**
     * Trace level for debug messages containing moderate detail.
     */
    public static final TraceLevel FINER = new TraceLevel(LEVEL_FINER);

    /**
     * Trace level for highly detailed or fine-grained messages.  This is the lowest trace level.
     */
    public static final TraceLevel FINEST = new TraceLevel(LEVEL_FINEST);

    /**
     * Get a TraceLevel object from an integer value.
     * @param level the level as an integer.
     * @return a TraceLevel object corresponding to level.
     * @throws IllegalArgumentException if level is not a valid trace
     *        level value.
     */
    public static TraceLevel 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 a TraceLevel object from a string value.
     * @param level the level as a string, for example as returned by the {@link #toString()}
     *        method (case insensitive).
     * @return a TraceLevel object corresponding to level.
     * @throws NullPointerException if level is null.
     * @throws IllegalArgumentException if level is not a valid trace
     *        level string.
     */
    public static TraceLevel fromString(String level) throws NullPointerException, IllegalArgumentException {
        if (level == null) throw new NullPointerException("level is null");
        if (level.equalsIgnoreCase(OFF_STRING)) return OFF;
        if (level.equalsIgnoreCase(SEVERE_STRING)) return SEVERE;
        if (level.equalsIgnoreCase(WARNING_STRING)) return WARNING;
        if (level.equalsIgnoreCase(INFO_STRING)) return INFO;
        if (level.equalsIgnoreCase(CONFIG_STRING)) return CONFIG;
        if (level.equalsIgnoreCase(FINE_STRING)) return FINE;
        if (level.equalsIgnoreCase(FINER_STRING)) return FINER;
        if (level.equalsIgnoreCase(FINEST_STRING)) return FINEST;
        throw new IllegalArgumentException("Invalid level: " + level);
    }

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

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

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

     *     if (traceLevel.isOff()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.OFF) ...
     *

     * @return true if this object represents the OFF level,
     *       false otherwise.
     */
    public boolean isOff() {
        return level == LEVEL_OFF;
    }

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

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

     *     if (traceLevel.isSevere()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.SEVERE) ...
     *

     * @return true if this object represents the SEVERE level,
     *       false otherwise.
     */
    public boolean isSevere() {
        return level == LEVEL_SEVERE;
    }

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

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

     *     if (traceLevel.isWarning()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.WARNING) ...
     *

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

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

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

     *     if (traceLevel.isMinor()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.INFO) ...
     *

     * @return true if this object represents the INFO level,
     *       false otherwise.
     */
    public boolean isInfo() {
        return level == LEVEL_INFO;
    }

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

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

     *     if (traceLevel.isConfig()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.CONFIG) ...
     *

     * @return true if this object represents the CONFIG level,
     *       false otherwise.
     */
    public boolean isConfig() {
        return level == LEVEL_CONFIG;
    }

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

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

     *     if (traceLevel.isFine()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.FINE) ...
     *

     * @return true if this object represents the FINE level,
     *       false otherwise.
     */
    public boolean isFine() {
        return level == LEVEL_FINE;
    }

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

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

     *     if (traceLevel.isFiner()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.FINER) ...
     *

     * @return true if this object represents the FINER level,
     *       false otherwise.
     */
    public boolean isFiner() {
        return level == LEVEL_FINER;
    }

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

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

     *     if (traceLevel.isFinest()) ...
     *

     * is interchangeable with the code:
     *

     *    if (traceLevel == TraceLevel.FINEST) ...
     *

     * @return true if this object represents the FINEST level,
     *       false otherwise.
     */
    public boolean isFinest() {
        return level == LEVEL_FINEST;
    }

    /**
     * Determine if this TraceLevel object represents a level that is higher than some other
     * TraceLevel object.  For the purposes of the comparison OFF is considered a higher level
     * than SEVERE, and FINEST is the lowest level.
     * @param other the TraceLevel object to compare this with.
     * @return true if the level represented by this TraceLevel
     *        object is a higher level than the level represented by the specified
     *        TraceLevel object, false otherwise.
     * @throws NullPointerException if other is null.
     */
    public boolean isHigherLevel(TraceLevel other) throws NullPointerException {
        if (other == null) throw new NullPointerException("other is null");

        return this.level < other.level;
    }

    /**
     * Compare this trace 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 TraceLevel) && ((TraceLevel)obj).level == level;
    }

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

    /**
     * Get the textual representation of the TraceLevel object.
     * @return the textual representation of the TraceLevel object.
     */
    public String toString() {
        switch (level) {
            case LEVEL_OFF: return OFF_STRING;
            case LEVEL_SEVERE: return SEVERE_STRING;
            case LEVEL_WARNING: return WARNING_STRING;
            case LEVEL_INFO: return INFO_STRING;
            case LEVEL_CONFIG: return CONFIG_STRING;
            case LEVEL_FINE: return FINE_STRING;
            case LEVEL_FINER: return FINER_STRING;
            case LEVEL_FINEST: return FINEST_STRING;
            default: return "TraceLevel in Unknown and Invalid State";
        }
    }

    /**
     * Private constructor to prevent unauthorized object creation.
     */
    private TraceLevel(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 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.slee.container.management;

import org.apache.log4j.Logger;
import org.jboss.as.naming.context.NamespaceContextSelector;
import org.mobicents.slee.container.AbstractSleeContainerModule;
import org.mobicents.slee.container.activity.ActivityContext;
import org.mobicents.slee.container.activity.ActivityContextHandle;
import org.mobicents.slee.container.activity.ActivityType;
import org.mobicents.slee.container.component.common.EnvEntryDescriptor;
import org.mobicents.slee.container.component.profile.ProfileSpecificationComponent;
import org.mobicents.slee.container.deployment.profile.SleeProfileClassCodeGenerator;
import org.mobicents.slee.container.deployment.profile.jpa.Configuration;
import org.mobicents.slee.container.deployment.profile.jpa.JPAProfileEntityFramework;
import org.mobicents.slee.container.deployment.profile.jpa.JPAProfileTableFramework;
import org.mobicents.slee.container.jndi.JndiManagement;
import org.mobicents.slee.container.profile.ProfileObjectPoolManagement;
import org.mobicents.slee.container.profile.ProfileTableImpl;
import org.mobicents.slee.container.profile.entity.ProfileEntityFramework;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.mobicents.slee.runtime.facilities.ProfileAlarmFacilityImpl;
import org.mobicents.slee.runtime.facilities.profile.ProfileFacilityImpl;
import org.mobicents.slee.runtime.facilities.profile.ProfileTableActivityContextInterfaceFactoryImpl;

import javax.naming.Context;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NamingException;
import javax.slee.SLEEException;
import javax.slee.management.DeploymentException;
import javax.slee.profile.*;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * Start time:16:56:21 2009-03-13

 * Project: restcomm-jainslee-server-core

 * Class that manages ProfileSpecification, profile tables, ProfileObjects. It
 * is responsible for setting up profile specification env.
 *
 * @author  Bartosz Baranowski
 * @author  Alexandre Mendonca
 * @author martins
 */
public class ProfileManagementImpl extends AbstractSleeContainerModule implements ProfileManagement {

	private static final Logger logger = Logger.getLogger(ProfileManagementImpl.class);
	private final static SleeProfileClassCodeGenerator sleeProfileClassCodeGenerator = new SleeProfileClassCodeGenerator();

	/**
	 * This map contains mapping - profieltable name ---> profile table concrete
	 * object. see 10.2.4 section of JSLEE 1.1 specs - there can be only single
	 * profile profile table in SLEE container
	 *
	 */
	//private ConcurrentHashMap nameToProfileTableMap = new ConcurrentHashMap();
	private ConcurrentHashMap<String, ProfileTableImpl> profileTablesLocalObjects;

	private final Configuration configuration;
	private JPAProfileTableFramework profileTableFramework;

	private ProfileObjectPoolManagement objectPoolManagement;
	private ProfileFacility profileFacility;
	private ProfileTableActivityContextInterfaceFactory profileTableActivityContextInterfaceFactory;

	public ProfileManagementImpl(Configuration configuration) {
		this.profileTablesLocalObjects = new ConcurrentHashMap<String, ProfileTableImpl>();
		this.configuration = configuration;
	}

	@Override
	public void sleeInitialization() {
		this.objectPoolManagement = new ProfileObjectPoolManagement(sleeContainer);
		this.objectPoolManagement.register();
		this.profileTableActivityContextInterfaceFactory = new ProfileTableActivityContextInterfaceFactoryImpl(sleeContainer,this);
		//JndiRegistrationManager.registerWithJndi("slee/facilities",
		//		ProfileTableActivityContextInterfaceFactoryImpl.JNDI_NAME,
		//		profileTableActivityContextInterfaceFactory);
		this.profileFacility = new ProfileFacilityImpl(this);
		//JndiRegistrationManager.registerWithJndi("slee/facilities", ProfileFacilityImpl.JNDI_NAME,
		//		profileFacility);
		// FIXME if it is a framework then it should be passed as arg in the beans xml, and if possible be independent of slee
		this.profileTableFramework = new JPAProfileTableFramework(this, sleeContainer.getTransactionManager(), configuration);
	}

	/**
	 * @return the objectPoolManagement
	 */
	public ProfileObjectPoolManagement getObjectPoolManagement() {
		return objectPoolManagement;
	}

	/**
	 * @return the profileFacility
	 */
	public ProfileFacility getProfileFacility() {
		return profileFacility;
	}

	/**
	 * @return the profileTableActivityContextInterfaceFactory
	 */
	public ProfileTableActivityContextInterfaceFactory getProfileTableActivityContextInterfaceFactory() {
		return profileTableActivityContextInterfaceFactory;
	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.management.ProfileManagement#installProfileSpecification(org.mobicents.slee.core.component.profile.ProfileSpecificationComponent)
	 */
	public void installProfileSpecification(ProfileSpecificationComponent component) throws DeploymentException {

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

		try {
			JndiManagement jndiManagement = sleeContainer.getJndiManagement();
			logger.debug("JndiManagement: "+jndiManagement+" for ProfileSpecificationComponent: "+component);
			jndiManagement.componentInstall(component);
			jndiManagement.pushJndiContext(component);
			try {
				this.createJndiSpace(component);
			} finally {
				jndiManagement.popJndiContext();
			}

			// FIXME: we wont use trace and alarm in 1.0 way wont we?
			ProfileEntityFramework profileEntityFramework = new JPAProfileEntityFramework(component,configuration,sleeContainer.getTransactionManager());
			profileEntityFramework.install();
			sleeProfileClassCodeGenerator.process(component);
			profileTableFramework.loadProfileTables(component);
		} catch (DeploymentException de) {
			throw de;
		} catch (Throwable t) {
		  t.printStackTrace();
			throw new SLEEException(t.getMessage(),t);
		}

	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.management.ProfileManagement#uninstallProfileSpecification(org.mobicents.slee.core.component.profile.ProfileSpecificationComponent)
	 */
	public void uninstallProfileSpecification(ProfileSpecificationComponent component) throws UnrecognizedProfileSpecificationException {

		Collection<String> profileTableNames = getDeclaredProfileTableNames(component.getProfileSpecificationID());

		for(String profileTableName:profileTableNames) {
			try {
				this.removeProfileTable(profileTableName, true);
			} catch (Throwable e) {
				throw new SLEEException(e.getMessage(),e);
			}
		}

		// FIXME: For JBoss 7.2.0.Final:
		// we have a problem with org.hibernate.service.UnknownServiceException:
		// Unknown service requested [org.hibernate.event.service.spi.EventListenerRegistry]
		// see https://hibernate.atlassian.net/browse/HHH-8586
		//component.getProfileEntityFramework().uninstall();
	}

	public void closeEntityManagerFactory(ProfileSpecificationComponent component) {
		component.getProfileEntityFramework().closeEntityManagerFactory();
	}

	/**
	 *
	 * @param component
	 * @throws Exception
	 */
	private void createJndiSpace(ProfileSpecificationComponent component) throws Exception {
		Context ctx = NamespaceContextSelector.getCurrentSelector().getContext("comp");

		if (logger.isTraceEnabled()) {
			logger.trace("Setting up Profile Spec env. Initial context is " + ctx);
		}

		Context envCtx = null;
		try {
			envCtx = ctx.createSubcontext("env");
		} catch (NameAlreadyBoundException ex) {
			envCtx = (Context) ctx.lookup("env");
		}

		Context sleeCtx = null;

		try {
			sleeCtx = envCtx.createSubcontext("slee");
		} catch (NameAlreadyBoundException ex) {
			sleeCtx = (Context) envCtx.lookup("slee");
		}

		Context facilitiesCtx = null;

		try {
			facilitiesCtx = sleeCtx.createSubcontext("facilities");
		} catch (NameAlreadyBoundException ex) {
			facilitiesCtx = (Context) sleeCtx.lookup("facilities");
		}

		try
		{
			facilitiesCtx.bind(ProfileTableActivityContextInterfaceFactoryImpl.JNDI_NAME,
					this.profileTableActivityContextInterfaceFactory);
		}
		catch (NamingException e) {
			// ignore.
		}

		try
		{
			facilitiesCtx.bind(ProfileFacilityImpl.JNDI_NAME,
					this.profileFacility);
		}
		catch (NamingException e) {
			// ignore.
		}

		ProfileAlarmFacilityImpl alarmFacility = new ProfileAlarmFacilityImpl(this.sleeContainer.getAlarmManagement());
		// FIXME: Alexandre: This should be AlarmFacility.JNDI_NAME. Any problem if already bound?
		try
		{
		  facilitiesCtx.bind("alarm", alarmFacility);
		  component.setAlarmFacility(alarmFacility);
		}
		catch (NamingException e) {
		  // ignore.
		}

		for (EnvEntryDescriptor mEnvEntry : component.getDescriptor().getEnvEntries()) {
			Class<?> type = null;

			if (logger.isTraceEnabled()) {
				logger.trace("Got an environment entry:" + mEnvEntry);
			}

			try {
				type = Thread.currentThread().getContextClassLoader().loadClass(mEnvEntry.getEnvEntryType());
			} catch (Exception e) {
				throw new DeploymentException(mEnvEntry.getEnvEntryType() + " is not a valid type for an environment entry");
			}
			Object entry = null;
			String s = mEnvEntry.getEnvEntryValue();

			try {
				if (type == String.class) {
					entry = new String(s);
				} else if (type == Character.class) {
					if (s.length() != 1) {
						throw new DeploymentException(s + " is not a valid value for an environment entry of type Character");
					}
					entry = new Character(s.charAt(0));
				} else if (type == Integer.class) {
					entry = new Integer(s);
				} else if (type == Boolean.class) {
					entry = new Boolean(s);
				} else if (type == Double.class) {
					entry = new Double(s);
				} else if (type == Byte.class) {
					entry = new Byte(s);
				} else if (type == Short.class) {
					entry = new Short(s);
				} else if (type == Long.class) {
					entry = new Long(s);
				} else if (type == Float.class) {
					entry = new Float(s);
				}
			} catch (NumberFormatException e) {
				throw new DeploymentException("Environment entry value " + s + " is not a valid value for type " + type);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Binding environment entry with name:" + mEnvEntry.getEnvEntryName() + " type  " + entry.getClass() + " with value:" + entry + ". Current classloader = "
						+ Thread.currentThread().getContextClassLoader());
			}
			try {
				envCtx.bind(mEnvEntry.getEnvEntryName(), entry);
			} catch (NameAlreadyBoundException ex) {
				logger.error("Name already bound ! ", ex);
			}
		}

	}

	/*
	 * (non-Javadoc)
	 * @see org.mobicents.slee.container.management.ProfileManagement#getProfileTable(java.lang.String)
	 */
	public ProfileTableImpl getProfileTable(String profileTableName) throws NullPointerException, UnrecognizedProfileTableNameException {

		if (profileTableName == null) throw new NullPointerException("profile table name is null");

		ProfileTableImpl profileTable = null;
		if (sleeContainer.getCluster().getMobicentsCache().isLocalMode()) {
			// no replication, table may only exist in local resources
			profileTable = profileTablesLocalObjects.get(profileTableName);
			if (profileTable == null) {
				throw new UnrecognizedProfileTableNameException(profileTableName);
			}
		}
		else {
			if (!profileTableFramework.getConfiguration().isClusteredProfiles()) {
				// profiles are not clustered, table may only exist in local resources
				profileTable = profileTablesLocalObjects.get(profileTableName);
				if (profileTable == null) {
					throw new UnrecognizedProfileTableNameException();
				}
			}
			else {
				// profiles are clustered, table may exist "remotely" and not in local resources, due to runtime creation in another cluster node, we need to go to database first
				final ProfileSpecificationID profileSpecificationID = profileTableFramework.getProfileSpecificationID(profileTableName);
				if (profileSpecificationID != null) {
					// exists in database
					profileTable = profileTablesLocalObjects.get(profileTableName);
					if (profileTable == null) {
						// local resource does not exists, create it
						ProfileSpecificationComponent component = sleeContainer.getComponentRepository().getComponentByID(profileSpecificationID);
						if (component != null) {
							profileTable = addProfileTableLocally(createProfileTableInstance(profileTableName, component),false, false);
						}
					}
				}
				else {
					// does not exists in database, ensure it is not in local objects
					profileTablesLocalObjects.remove(profileTableName);
				}
			}
		}

		if (profileTable == null)
			throw new UnrecognizedProfileTableNameException();
		else
			return profileTable;

	}

	private ProfileTableImpl createProfileTableInstance(String profileTableName, ProfileSpecificationComponent component) {
		if (component.getProfileTableConcreteClass() == null) {
			return new ProfileTableImpl(profileTableName, component, this);
		}
		else {
			try {
				return (ProfileTableImpl)component.getProfileTableConcreteClass().getConstructor(String.class, ProfileSpecificationComponent.class, ProfileManagementImpl.class).newInstance(profileTableName, component, this);
			} catch (Throwable e) {
				throw new SLEEException(e.getMessage(),e);
			}
		}
	}

	private ProfileTableImpl addProfileTableLocally(final ProfileTableImpl newProfileTable, boolean startActivity, boolean storeInFramework) throws SLEEException {
		ProfileTableImpl profileTable = profileTablesLocalObjects.putIfAbsent(newProfileTable.getProfileTableName(), newProfileTable);
		if (profileTable == null) {
			// new profile table object was inserted
			profileTable = newProfileTable;
			newProfileTable.registerTracer();
			final TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
			// add tx action to remove local object if tx rollbacks
			TransactionalAction action1 = new TransactionalAction() {
				public void execute() {
					profileTablesLocalObjects.remove(newProfileTable.getProfileTableName());
				}
			};
			txContext.getAfterRollbackActions().add(action1);

			// register usage mbean
			newProfileTable.registerUsageMBean();
			TransactionalAction action3 = new TransactionalAction() {
				public void execute() {
					newProfileTable.unregisterUsageMBean();
				}
			};
			txContext.getAfterRollbackActions().add(action3);

			// create object pool
			objectPoolManagement.createObjectPool(newProfileTable, sleeContainer.getTransactionManager());

			// the next 2 operations needs to be done before creating default profile, since
			// that operation may set tx as rollback, and if that happens
			// cache data (actually this limitation comes from jboss cache) or jpa
			// can't be initiated

			// start activity
			if (startActivity) {
				newProfileTable.startActivity();
			}
			if(storeInFramework) {
				// store it in jpa
				profileTableFramework.storeProfileTable( profileTable );
			}

			// create default profile
			try {
				newProfileTable.createDefaultProfile();
			} catch (Throwable e) {
				throw new SLEEException(e.getMessage(),e);
			}
		}
		return profileTable;
	}

	/**
	 *
	 * @return
	 */
	public Collection<String> getDeclaredProfileTableNames() {
		return profileTableFramework.getProfileTableNames();
	}

	/**
	 *
	 * @param id
	 * @return
	 * @throws UnrecognizedProfileSpecificationException
	 */
	public Collection<String> getDeclaredProfileTableNames(ProfileSpecificationID id) throws UnrecognizedProfileSpecificationException {

		if (this.sleeContainer.getComponentRepository().getComponentByID(id) == null) {
			throw new UnrecognizedProfileSpecificationException("No such profile specification: " + id);
		}

		return profileTableFramework.getProfileTableNames(id);
	}

	@Override
	public void sleeRunning() {
		if (sleeContainer.getCluster().isHeadMember()) {
			startAllProfileTableActivities();
		}
	}

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

	private void stopAllProfileTableActivities() {

		logger.info("Ending all profile table activities...");

		try {

			for (ActivityContextHandle handle : sleeContainer
					.getActivityContextFactory()
					.getAllActivityContextsHandles()) {
				if (handle.getActivityType() == ActivityType.PTABLE) {
					try {
						if (logger.isDebugEnabled()) {
							logger.debug("Ending profile table activity " + handle);
						}
						ActivityContext ac = sleeContainer
						.getActivityContextFactory()
						.getActivityContext(handle);
						if (ac != null) {
							ac.endActivity();
						}
					} catch (Exception e) {
						if (logger.isDebugEnabled()) {
							logger.debug("Failed to end profile table activity "
									+ handle, e);
						}
					}
				}
			}

		} catch (Exception e) {
			logger
			.error(
					"Exception while ending all profile table activities",
					e);

		}

		// wait all activities end

		boolean loop;
		do {
			loop = false;

			try {
				for (ActivityContextHandle handle : sleeContainer
						.getActivityContextFactory()
						.getAllActivityContextsHandles()) {
					if (handle.getActivityType() == ActivityType.PTABLE) {
						logger.info("Waiting for profile table activity "+handle+" to end...");
						loop = true;
						break;
					}
				}
			} catch (Exception e) {
				if (logger.isDebugEnabled()) {
					logger.debug(e.getMessage(), e);
				}
			}
			if (loop) {
				try {
					// wait a sec
					Thread.sleep(1000);

					logger.warn("WORKAROUND USAGE: ENDING ALL PROFILE TABLE ACTIVITIES");
					sleeContainer.getActivityContextFactory().WAremove("ACH=PTABLE");

				} catch (InterruptedException e) {
					logger.error(e.getMessage(), e);
				}
			}
		} while (loop);

	}

	/**
	 *
	 */
	private void startAllProfileTableActivities() {
		if (logger.isDebugEnabled()) {
			logger.debug("starting all profile table activities");
		}
		for (String profileTableName : this.getDeclaredProfileTableNames()) {
			try {
				ProfileTableImpl pt = getProfileTable(profileTableName);
				ActivityContext ac = pt.getActivityContext();
				if (ac == null) {
					pt.startActivity();
				}
			}
			catch (Throwable e) {
				if (logger.isDebugEnabled()){
					logger.debug("Not starting activity for profile table named "+profileTableName+". The profile spec component is not deployed.");
				}
			}
		}
	}

	/**
	 *
	 * @param profileTableName
	 * @param component
	 * @return
	 * @throws ProfileTableAlreadyExistsException
	 * @throws SLEEException
	 */
	public ProfileTableImpl addProfileTable(final String profileTableName, ProfileSpecificationComponent component) throws ProfileTableAlreadyExistsException, SLEEException {

		try {
			getProfileTable(profileTableName);
			throw new ProfileTableAlreadyExistsException("there is already a profile table named "+profileTableName);
		}
		catch (UnrecognizedProfileTableNameException e) {
			// expected
		}

		return addProfileTableLocally(createProfileTableInstance(profileTableName, component),true,true);

	}

	/**
	 *
	 * @param profileTableName
	 * @throws NullPointerException
	 * @throws UnrecognizedProfileTableNameException
	 */
	public void removeProfileTable(final String profileTableName) throws NullPointerException, UnrecognizedProfileTableNameException {
	  removeProfileTable(profileTableName, false);
	}

    private void removeProfileTable(final String profileTableName, boolean isUninstall) throws NullPointerException, UnrecognizedProfileTableNameException {
      ProfileTableImpl profileTable = getProfileTable(profileTableName);
      TransactionalAction action = new TransactionalAction() {
        public void execute() {
          profileTablesLocalObjects.remove(profileTableName);
        }
      };
      try {
        sleeContainer.getTransactionManager().getTransactionContext().getAfterCommitActions().add(action);
      } catch (IllegalStateException e) {
        throw new SLEEException(e.getMessage(),e);
      }
      profileTable.remove(isUninstall);
      if(!isUninstall) {
        profileTableFramework.removeProfileTable(profileTable.getProfileTableName());
      }
    }

    /**
	 *
	 * @param oldProfileTableName
	 * @param newProfileTableName
	 * @throws ProfileTableAlreadyExistsException
	 * @throws NullPointerException
	 * @throws UnrecognizedProfileTableNameException
	 */
	public void renameProfileTable(String oldProfileTableName, String newProfileTableName) throws ProfileTableAlreadyExistsException, NullPointerException, UnrecognizedProfileTableNameException {
	  // Get the old table
	  ProfileTableImpl oldProfileTable = getProfileTable(oldProfileTableName);
	  // Create the new one
	  addProfileTable(newProfileTableName, oldProfileTable.getProfileSpecificationComponent());
	  // Move contents
	  oldProfileTable.rename(newProfileTableName);
	  // Remove the old one - this wont remove profiles/mbeans, since they are already part of new table, this has to be done
	  // above.
	  removeProfileTable(oldProfileTableName);
	}

	@Override
	public String toString() {
		return "Profile Table Manager: "
			+ "\n+-- Profile Tables: " + getDeclaredProfileTableNames();
	}

	/**
	 * @param profileTableName
	 * @param component
	 */
	public void loadProfileTableLocally(String profileTableName,
			ProfileSpecificationComponent component) throws IllegalArgumentException {
		if(profileTableFramework.getProfileTableNames(component.getProfileSpecificationID()).contains(profileTableName)) {
			boolean createAC = sleeContainer.getSleeState() != null && sleeContainer.getSleeState().isRunning();
			addProfileTableLocally(createProfileTableInstance(profileTableName, component), createAC, false);
		}
		else {
			throw new IllegalArgumentException("Either profile table named "+profileTableName+" does not exists or its component is not the specified one");
		}
	}

	/**
	 * @return
	 */
	public Configuration getJPAConfiguration() {
		return profileTableFramework.getConfiguration();
	}
}


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

/**
 * Abstract event context class, that only puts itself on the datasource if the handle is requested, i.e., is stored in a cmp
 * @author martins
 *
 */
public abstract class LazyStoredEventContext implements EventContext {

	protected final EventContextFactoryImpl factory;

	private EventContextHandle handle;

	/**
	 *
	 */
	public LazyStoredEventContext(EventContextFactoryImpl factory) {
		this.factory = factory;
	}

	/* (non-Javadoc)
	 * @see org.mobicents.slee.container.event.EventContext#getEventContextHandle()
	 */
	public EventContextHandle getEventContextHandle() {
		if (handle == null) {
			handle = new EventContextHandleImpl(factory.getSleeContainer().getUuidGenerator().createUUID());
			factory.getDataSource().addEventContext(handle, this);
		}
		return handle;
	}

	/**
	 *
	 */
	public void remove() {
		if (handle != null) {
			factory.getDataSource().removeEventContext(handle);
			handle = null;
		}
	}
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

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

import org.apache.log4j.Logger;
import org.mobicents.slee.container.component.profile.ProfileSpecificationComponent;
import org.mobicents.slee.container.deployment.SleeComponentWithUsageParametersClassCodeGenerator;

/**
 *
 * Start time:17:25:37 2009-03-12

 * Project: restcomm-jainslee-server-core

 *
 * Base class for calling profile code generation methdos.
 *
 * @author  Bartosz Baranowski
 * @author  Alexandre Mendonca
 */
public class SleeProfileClassCodeGenerator {

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

  public void process(ProfileSpecificationComponent component) throws Exception
  {
    if (logger.isDebugEnabled()) {
      logger.debug("Generating class code for " + component+" at "+component.getDeploymentDir().getAbsolutePath());
    }

    // generate profile cmp slee 1.0 wrapper
	new ProfileCmpSlee10WrapperClassGenerator(component).generateClass();
	if (logger.isDebugEnabled()) {
		logger.debug("Generated Profile CMP Slee 1.0 Wrapper for " + component);
	}

    // Generate Profile MBean Interface
    ConcreteProfileMBeanGenerator mbeanGenerator = new ConcreteProfileMBeanGenerator(component);
    mbeanGenerator.generateProfileMBeanInterface();
    if (logger.isDebugEnabled()) {
      logger.debug("Generated MBean interface for " + component);
    }

	// Generate Profile MBean Impl
    mbeanGenerator.generateProfileMBean();
    if (logger.isDebugEnabled()) {
      logger.debug("Generated MBean impl for " + component);
    }

   	ConcreteProfileLocalObjectGenerator concreteProfileLocalObjectGenerator = new ConcreteProfileLocalObjectGenerator(component);
    concreteProfileLocalObjectGenerator.generateProfileLocalConcreteClass();
    if (logger.isDebugEnabled()) {
    	logger.debug("Generated Profile Local Object impl for " + component);
    }

    if (component.getProfileTableInterfaceClass() != null) {
    	ConcreteProfileTableGenerator concreteProfileTableGenerator = new ConcreteProfileTableGenerator(component);
    	concreteProfileTableGenerator.generateProfileTable();
    	if (logger.isDebugEnabled()) {
    		logger.debug("Generated Profile Table Interface impl for " + component);
    	}
    }

    if (component.getUsageParametersInterface() != null) {
    	new SleeComponentWithUsageParametersClassCodeGenerator().process(component);
    	if (logger.isDebugEnabled()) {
    		logger.debug("Generated Profile Table Usage Interface impl for " + component);
    		logger.debug("Generated Profile Table Usage MBean impl for " + component);
    	}
    }

  }

  /**
   * Check which combination (see JSLEE 1.0 spec section 10.5.2) matches the
   * Sbb Developer's Profile Specification
   *
   * @param profileCMPInterfaceName
   *            name of the Profile CMP interface
   * @param profileManagementInterfaceName
   *            name of the Profile Management interface
   * @param profileManagementAbstractClassName
   *            name of the Profile Management Abstract class
   * @return the number of the combination (see JSLEE 1.0 spec section 10.5.2
   *         or 10.5.1.2 in JSLEE 1.1 spec), -1 if it doesn't match no
   *         combination

   *

   *
1 - all cmp fields are exposed to management client and SLEE components(for SLEE as read only), no managemetn methods

   *
2 - ony double defined CMP accessors and management methods are visible

   *
3

   *
4

   *
-1 - when error occurs, no definitions

   *

   */
  public static int checkCombination(ProfileSpecificationComponent component)
  {
    Object profileCmpInterface = component.getDescriptor().getProfileCMPInterface();
    Object profileManagementInterface = component.getDescriptor().getProfileManagementInterface();
    Object profileManagementAbstractClass = component.getDescriptor().getProfileAbstractClass();
    //Object profileManagementLocalObjectInterface = compoenent.getDescriptor().getProfileLocalInterface();
    // if the Profile Specification has no Profile CMP interface, it is incorrect
    if (profileCmpInterface == null)
      return -1;

//    if (compoenent.isSlee11()) {
//      if (profileCmpInterface != null && profileManagementLocalObjectInterface != null && profileManagementAbstractClass != null)
//        return 4;
//      if (profileCmpInterface != null && profileManagementAbstractClass != null)
//        return 3;
//      if (profileCmpInterface != null && profileManagementLocalObjectInterface != null)
//        return 2;
//
//      return 1;
//    }
//    else
//    {
      if (profileCmpInterface != null && profileManagementInterface != null && profileManagementAbstractClass != null)
        return 4;
      if (profileCmpInterface != null && profileManagementAbstractClass != null)
        return 3;
      if (profileCmpInterface != null && profileManagementInterface != null)
        return 2;

      return 1;
//    }
  }

}


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

import java.security.AccessController;
import java.security.PrivilegedAction;

import javax.slee.ActivityContextInterface;
import javax.slee.NoSuchObjectLocalException;
import javax.slee.SLEEException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.TransactionRolledbackLocalException;
import javax.transaction.SystemException;

import org.apache.log4j.Logger;
import org.mobicents.slee.SbbLocalObjectExt;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.deployment.interceptors.SbbLocalObjectInterceptor;
import org.mobicents.slee.container.eventrouter.EventRoutingTransactionData;
import org.mobicents.slee.container.sbb.SbbLocalObject;
import org.mobicents.slee.container.sbbentity.SbbEntity;
import org.mobicents.slee.container.sbbentity.SbbEntityID;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.mobicents.slee.runtime.sbbentity.SbbEntityImpl;

/**
 * This is a SLEE provided interface to the Sbb object. The SLEE uses this
 * interface to allow sbbs to access local methods.
 *
 * The following is excerpted from Slee Spec
 *

 * An SBB may define SBB specific local interface methods in an SBB specific
 * local interface. The SBB specific local interface must be public and must
 * extend, either directly or indirectly, the SbbLocalObject interface. All SBBs
 * have an SBB local interface. If the SBB Developer does not provide an SBB
 * local interface for an SBB, then the SBB local interface of the SBB is the
 * generic SbbLocalObject interface. The names of the SBB specific local
 * interface methods must not begin with sbb or ejb. The SLEE provides the
 * implementation of the methods defined in the SBB local interface. More
 * precisely, the SLEE provides a concrete class that implements each SBB local
 * interface. An SBB local object is an instance of this class. The SLEE
 * provided implementations of these methods delegate invocations on an SBB
 * local object that represents an SBB entity to an SBB object that represents
 * the SBB entity (if the SBB entity has not been removed). The SBB Developer
 * provides the implementation of SBB Developer defined local interface methods
 * declared in the SBB specific extension of the SbbLocalObject interface in the
 * SBB abstract class. For each method defined by the SBB Developer, there must
 * be a matching method in the SBB abstract class. The matching method must
 * have:
 *

 *
The same name.
 *
The same number of arguments, and same argument and return types.
 *
The same set of exceptions in the throws clause.
 *

 *
 *

 * All SBB local objects that represent an SBB entity that does not exist are
 * invalid. An attempted invocation on an invalid SBB local object marks the
 * current transaction for rollback and throws a
 * javax.slee.TransactionRolledbackLocalException (a subclass of javax.slee.
 * SLEEException). An SBB Developer defined local interface method is a
 * mandatory transactional method (see Section 9.6.1). The SLEE throws a
 * javax.slee.TransactionRequiredLocalException if an SBB Developer defined
 * local interface method of an SBB local object is invoked without a valid
 * transaction context.
 *
 *

 * This method may also throw a javax.slee.SLEEException if the method failed
 * due to a SLEE level or system level failure. If the caller of this method
 * receives this exception, the caller does not know, in general, whether the
 * corresponding method implementation in the SBB abstract class was invoked.
 * The caller also does not know if the transaction has been marked for
 * rollback. However, the caller may determine the transaction status by using
 * the getRollbackOnly method (see Section 6.10.3).
 *

 * The SBB Developer defined SBB local interface methods must not throw
 * java.rmi.Remote- Exception, any subclass of RemoteException, or any
 * RuntimeException. For more information on exception handling for SBB local
 * object invocations, see Section 6.9.
 *

 * Parameters to local interface methods are passed by reference.
 *

 * Note that the SbbLocalObject does not expose the methods of the
 * javax.slee.Sbb interface, event handler methods, or the various callback
 * methods. These methods are used by the SLEE to manage SBB object instances,
 * deliver events, and handle callbacks.
 *
 * @author M. Ranganathan
 * @author F. Moggia
 * @author eduardomartins
 */
public class SbbLocalObjectImpl implements SbbLocalObject,
        SbbLocalObjectConcrete {

    /**
     * Container where this resides -- for now this is the same location where
     * the sbb resides.
     */
    private static final SleeContainer sleeContainer = SleeContainer.lookupFromJndi();

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

    private boolean rollbackOnly;

    /*
     * This flag is set to true if the sbb local object is removed (no longer
     * valid)
     */
    private boolean isRemoved;

    private final SbbEntityImpl sbbEntity;

    private ClassLoader contextClassLoader;

    private final boolean trace;

    /**
     * used by class extensions to invoke custom sbb local object methods
     */
    protected SbbLocalObjectInterceptor sbbLocalObjectInterceptor = new SbbLocalObjectInterceptor();

    /**
     * Constructor -- assume that the Sbb entity is co-located with the Sbb
     * local object.
     *
     * @param sbbEntity --
     *            sbb entity for which this is a local object.
     */
    public SbbLocalObjectImpl(final SbbEntityImpl sbbEntity) {
        this.sbbEntity = sbbEntity;
        if (sbbEntity.getSbbObject() == null){
        	if (System.getSecurityManager()!=null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                    	assignSbbObject();
                        return null;
                    }
                });
        	}
            else {
            	assignSbbObject();
            }
        }
        trace = logger.isTraceEnabled();
        if(trace)
            logger.trace("SbbLocalObjectImpl(sbbEntity = "+sbbEntity.getSbbEntityId()+" )");
    }

    private void assignSbbObject() {
    	final Thread t = Thread.currentThread();
    	final ClassLoader cl = t.getContextClassLoader();
    	t.setContextClassLoader(sbbEntity.getSbbComponent().getClassLoader());
    	try {
            sbbEntity.assignSbbObject();
        } catch (Exception e) {
           logger.error(e.getMessage(),e);
        }
        finally {
        	t.setContextClassLoader(cl);
        }
    }

	/* (non-Javadoc)
	 * @see org.mobicents.slee.runtime.sbb.SbbLocalObjectConcrete#getContextClassLoader()
	 */
	public ClassLoader getContextClassLoader() {
		if (contextClassLoader == null) {
			contextClassLoader = sbbEntity.getSbbComponent().getClassLoader();
		}
		return contextClassLoader;
	}

	/**
	 * @return the sbbEntity
	 */
	public SbbEntityImpl getSbbEntity() {
		return sbbEntity;
	}

	/**
	 * Validates an invocation of the {@link SbbLocalObject}.
	 *
	 * @throws TransactionRolledbackLocalException
	 * @throws NoSuchObjectLocalException
	 * @throws SLEEException
	 */
	private void validateInvocation() throws TransactionRolledbackLocalException, NoSuchObjectLocalException, SLEEException {
		// validate tx
		sleeContainer.getTransactionManager().mandateTransaction();
		// validate object
		if (this.rollbackOnly) {
			try {
				sleeContainer.getTransactionManager().setRollbackOnly();
			} catch (SystemException ex) {
				throw new SLEEException("unable to set rollbackOnly in transaction manager", ex);
			}
			throw new TransactionRolledbackLocalException(
			"Unable to proceed, transaction is set to rollback");
		}
		if (this.isRemoved)
			throw new NoSuchObjectLocalException("sbb local object is removed");
	}

	/*
	 * (non-Javadoc)
	 * @see javax.slee.SbbLocalObject#getSbbPriority()
	 */
    public byte getSbbPriority() throws TransactionRequiredLocalException,
            NoSuchObjectLocalException, SLEEException {
    	validateInvocation();
    	return sbbEntity.getPriority();
    }

    /*
     * (non-Javadoc)
     * @see javax.slee.SbbLocalObject#isIdentical(javax.slee.SbbLocalObject)
     */
    public boolean isIdentical(javax.slee.SbbLocalObject obj)
            throws TransactionRequiredLocalException, SLEEException {
    	validateInvocation();
        return this.equals(obj);
    }

    /*
     * (non-Javadoc)
     * @see javax.slee.SbbLocalObject#remove()
     */
    public void remove() throws TransactionRequiredLocalException,
            NoSuchObjectLocalException, SLEEException {
    	if(trace)
    		logger.trace("remove()");

       validateInvocation();

       if (!sbbEntity.isReentrant()
				&& sleeContainer.getTransactionManager().getTransactionContext()
						.getInvokedNonReentrantSbbEntities().contains(sbbEntity.getSbbEntityId()))
			throw new SLEEException(" re-entrancy not allowed ");

		if (logger.isDebugEnabled()) {
            logger.debug("nonSleeInitiatedCascadingRemoval : " + sbbEntity.getSbbId()
                    + " entityID = " + sbbEntity.getSbbEntityId());
        }

        try {
        	sleeContainer.getSbbEntityFactory().removeSbbEntity(sbbEntity,false);
        } catch (Throwable e) {
            throw new SLEEException("Removal of the sbb entity failed",e);
        }

        try {
            if (sleeContainer.getTransactionManager().getRollbackOnly()) {
            	final TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
            	EventRoutingTransactionData ertd = txContext.getEventRoutingTransactionData();
				txContext.getAfterRollbackActions()
						.add(
								new RolledBackAction(sbbEntity.getSbbEntityId(),ertd.getEventBeingDelivered().getEvent(),ertd.getAciReceivingEvent(), true));
			}
		} catch (Exception e) {
            throw new SLEEException("Failed to check and possibly set rollback context of entity "+sbbEntity.getSbbEntityId(),e);
        }

        // I Think this should set isRemoved only to true but then test 323
        // will fail.
        // :-(
        // Ralf: see above
        this.rollbackOnly = true;
        this.isRemoved = true;

    }

    /*
     * (non-Javadoc)
     * @see javax.slee.SbbLocalObject#setSbbPriority(byte)
     */
    public void setSbbPriority(byte priority)
            throws TransactionRequiredLocalException,
            NoSuchObjectLocalException, SLEEException {
    	if(trace)
    		logger.trace("setSbbPriority( priority = "+priority+" )");
    	validateInvocation();
    	sbbEntity.setPriority(priority);
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
    	if (obj != null && obj.getClass() == this.getClass()) {
    		final SbbLocalObjectImpl other = (SbbLocalObjectImpl) obj;
    		return this.sbbEntity.getSbbEntityId().equals(
    				other.sbbEntity.getSbbEntityId()) && this.isRemoved == other.isRemoved;
    	}
    	else {
    		return false;
    	}
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
    	return sbbEntity.getSbbEntityId().hashCode();
    }

    /*
     * (non-Javadoc)
     * @see org.mobicents.slee.runtime.sbb.SbbLocalObjectConcrete#getSbbEntityId()
     */
    public SbbEntityID getSbbEntityId() {
        return sbbEntity.getSbbEntityId();
    }

    private static class RolledBackAction implements TransactionalAction {

    	final SbbEntityID sbbeId;

    	private Object event;

    	private ActivityContextInterface activityContextInterface;

    	private boolean removeRollback;

        public RolledBackAction(SbbEntityID sbbeId,Object event,
                ActivityContextInterface activityContextInterface,
                boolean removeRollback) {
            this.event = event;
            this.activityContextInterface = activityContextInterface;
            this.removeRollback = removeRollback;
            this.sbbeId = sbbeId;
        }

        /*
         * (non-Javadoc)
         * @see org.mobicents.slee.runtime.transaction.TransactionalAction#execute()
         */
        public void execute() {
            SbbEntity sbbe = null;
            final SleeTransactionManager txManager = sleeContainer.getTransactionManager();
            try {
            	txManager.begin();
                sbbe = sleeContainer.getSbbEntityFactory().getSbbEntity(sbbeId,true);
                if (sbbe != null) {
                	sbbe.sbbRolledBack(event,activityContextInterface,removeRollback);
                }
                txManager.commit();
            } catch (RuntimeException e) {
            	if (sbbe != null) {
            		sbbe.trashObject();
            		logger.error("Exception while executing RolledBackAction",e);
            		try {
            			if (txManager.getTransaction() != null)
            				txManager.rollback();
            		} catch (SystemException e1) {
                    logger.error(e1.getMessage(),e1);
                }
            	}
            } catch (Exception e) {
            	if (sbbe != null) {
            		sbbe.trashObject();
            		logger.error("Exception while executing RolledBackAction",e);
            		try {
            			if (txManager.getTransaction() != null)
            				txManager.rollback();
            		} catch (SystemException e1) {
            			logger.error(e1.getMessage(),e1);
            		}
            	}
            }
        }
    }

    @Override
    public String toString() {
    	return new StringBuilder("SbbLocalObjectImpl[").append(sbbEntity != null ? sbbEntity.getSbbEntityId() : "null").append("]").toString();
    }

    // extension methods

    @Override
    public String getChildRelation() throws TransactionRequiredLocalException,
    		SLEEException {

    	validateInvocation();

    	return getSbbEntityId().getParentChildRelation();
    }

    @Override
    public String getName() throws NoSuchObjectLocalException,
    		TransactionRequiredLocalException, SLEEException {

    	validateInvocation();

    	return getSbbEntityId().getName();
    }

    @Override
    public SbbLocalObjectExt getParent() throws NoSuchObjectLocalException,
    		TransactionRequiredLocalException, SLEEException {

    	validateInvocation();

    	SbbLocalObjectExt parent = null;
    	final SbbEntityID parentSbbEntityID = getSbbEntityId().getParentSBBEntityID();
    	if (parentSbbEntityID != null) {
    		SbbEntity parentSbbEntity = sleeContainer.getSbbEntityFactory().getSbbEntity(parentSbbEntityID, false);
    		if (parentSbbEntity != null) {
    			parent = parentSbbEntity.getSbbLocalObject();
    		}
    	}
    	return parent;
    }

}


/*
 * 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.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.zip.ZipEntry;

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

import org.apache.log4j.Logger;
import org.mobicents.slee.container.component.AbstractSleeComponent;
import org.mobicents.slee.container.component.ComponentManagementImpl;
import org.mobicents.slee.container.component.EventTypeComponentImpl;
import org.mobicents.slee.container.component.LibraryComponentImpl;
import org.mobicents.slee.container.component.ProfileSpecificationComponentImpl;
import org.mobicents.slee.container.component.ResourceAdaptorComponentImpl;
import org.mobicents.slee.container.component.ResourceAdaptorTypeComponentImpl;
import org.mobicents.slee.container.component.SbbComponentImpl;
import org.mobicents.slee.container.component.deployment.classloading.URLClassLoaderDomainImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.EventTypeDescriptorFactoryImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.EventTypeDescriptorImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.LibraryDescriptorFactoryImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.LibraryDescriptorImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ProfileSpecificationDescriptorFactoryImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ProfileSpecificationDescriptorImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ResourceAdaptorDescriptorFactoryImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ResourceAdaptorDescriptorImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ResourceAdaptorTypeDescriptorFactoryImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.ResourceAdaptorTypeDescriptorImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.SbbDescriptorFactoryImpl;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.SbbDescriptorImpl;
import org.mobicents.slee.container.component.library.JarDescriptor;

/**
 * DU Component jar builder
 *
 * @author martins
 *
 */
public class DeployableUnitJarComponentBuilder {

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

	private final ComponentManagementImpl componentManagement;

	/**
	 *
	 */
	public DeployableUnitJarComponentBuilder(
			ComponentManagementImpl componentManagement) {
		this.componentManagement = componentManagement;
	}

	/**
	 * Builds the DU component from a jar with the specified file name,
	 * contained in the specified DU jar file. The component is built in the
	 * specified deployment dir.
	 *
	 * @param componentJarFileName
	 * @param deployableUnitJar
	 * @param deploymentDir
	 * @param documentBuilder
	 * @return
	 * @throws DeploymentException
	 */
	@SuppressWarnings("deprecation")
	public List<AbstractSleeComponent> buildComponents(
			String componentJarFileName, JarFile deployableUnitJar,
			File deploymentDir)
			throws DeploymentException {

		// extract the component jar from the DU jar, to the temp du dir
		File extractedFile = extractFile(componentJarFileName,
				deployableUnitJar, deploymentDir);
		JarFile componentJarFile = null;
		try {
			componentJarFile = new JarFile(extractedFile);
		} catch (IOException e) {
			throw new DeploymentException(
					"failed to create jar file for extracted file "
							+ extractedFile);
		}

		InputStream componentDescriptorInputStream = null;
		List<AbstractSleeComponent> components = new ArrayList<AbstractSleeComponent>();

		try {
			// now extract the jar file to a new dir
			File componentJarDeploymentDir = new File(deploymentDir,
					componentJarFileName + "-contents");
			if (!componentJarDeploymentDir.exists()) {
				// the jar may not be on root of DU, create additional dirs if needed
				LinkedList<File> dirsToCreate = new LinkedList<File>();
				File dir = componentJarDeploymentDir.getParentFile();
				while(!dir.equals(deploymentDir)) {
					dirsToCreate.addFirst(dir);
					dir = dir.getParentFile();
				}
				for (File f : dirsToCreate) {
					f.mkdir();
				}
				// now create the dir for the component jar
				if (!componentJarDeploymentDir.mkdir()) {
						throw new SLEEException("dir for jar "
								+ componentJarFileName + " not created in "
								+ deploymentDir);
				}
			} else {
				throw new SLEEException("dir for jar " + componentJarFileName
						+ " already exists in " + deploymentDir);
			}
			extractJar(componentJarFile, componentJarDeploymentDir);
			// create components from descriptor
			JarEntry componentDescriptor = null;
			if ((componentDescriptor = componentJarFile
					.getJarEntry("META-INF/sbb-jar.xml")) != null) {
				// create class loader domain shared by all components
				URLClassLoaderDomainImpl classLoaderDomain = componentManagement
				.getClassLoaderFactory()
				.newClassLoaderDomain(
						new URL[] { componentJarDeploymentDir.toURL() },
						Thread.currentThread().getContextClassLoader());
				// parse descriptor
				componentDescriptorInputStream = componentJarFile
						.getInputStream(componentDescriptor);
				SbbDescriptorFactoryImpl descriptorFactory = componentManagement
						.getComponentDescriptorFactory()
						.getSbbDescriptorFactory();
				List<SbbDescriptorImpl> descriptors = descriptorFactory
						.parse(componentDescriptorInputStream);
				// create components
				for (SbbDescriptorImpl descriptor : descriptors) {
                                        PreferredPackagesBuilder.buildPreferredPackages(descriptor, classLoaderDomain);
					SbbComponentImpl component = new SbbComponentImpl(
							descriptor);
					component.setDeploymentDir(componentJarDeploymentDir);
					component.setClassLoaderDomain(classLoaderDomain);
					components.add(component);
				}
			} else if ((componentDescriptor = componentJarFile
					.getJarEntry("META-INF/profile-spec-jar.xml")) != null) {
				// create class loader domain shared by all components
				URLClassLoaderDomainImpl classLoaderDomain = componentManagement
				.getClassLoaderFactory()
				.newClassLoaderDomain(
						new URL[] { componentJarDeploymentDir.toURL() },
						Thread.currentThread().getContextClassLoader());
				// parse descriptor
				componentDescriptorInputStream = componentJarFile
						.getInputStream(componentDescriptor);
				ProfileSpecificationDescriptorFactoryImpl descriptorFactory = componentManagement
						.getComponentDescriptorFactory()
						.getProfileSpecificationDescriptorFactory();
				List<ProfileSpecificationDescriptorImpl> descriptors = descriptorFactory
						.parse(componentDescriptorInputStream);
				// create components
				for (ProfileSpecificationDescriptorImpl descriptor : descriptors) {
					ProfileSpecificationComponentImpl component = new ProfileSpecificationComponentImpl(
							descriptor);
					component.setDeploymentDir(componentJarDeploymentDir);
					component.setClassLoaderDomain(classLoaderDomain);
					components.add(component);
				}
			} else if ((componentDescriptor = componentJarFile
					.getJarEntry("META-INF/library-jar.xml")) != null) {
				Set<LibraryComponentImpl> libraryComponents = new HashSet<LibraryComponentImpl>();
				// we need to gather all URLs for the shared class loader domain
				// to watch
				Set<URL> classLoaderDomainURLs = new HashSet<URL>();
				classLoaderDomainURLs.add(componentJarDeploymentDir.toURL());
				// parse the descriptor
				componentDescriptorInputStream = componentJarFile
						.getInputStream(componentDescriptor);
				LibraryDescriptorFactoryImpl descriptorFactory = componentManagement
						.getComponentDescriptorFactory()
						.getLibraryDescriptorFactory();
				List<LibraryDescriptorImpl> descriptors = descriptorFactory
						.parse(componentDescriptorInputStream);
				// create components
				for (LibraryDescriptorImpl descriptor : descriptors) {
					LibraryComponentImpl component = new LibraryComponentImpl(
							descriptor);
					for (JarDescriptor mJar : descriptor.getJars()) {
						classLoaderDomainURLs.add(new File(
								componentJarDeploymentDir, mJar.getJarName())
								.toURL());
					}
					// set deploy dir and cl domain
					component.setDeploymentDir(componentJarDeploymentDir);
					components.add(component);
					libraryComponents.add(component);
				}
				// create shared url domain
				URLClassLoaderDomainImpl classLoaderDomain = componentManagement
				.getClassLoaderFactory()
				.newClassLoaderDomain(
						classLoaderDomainURLs
						.toArray(new URL[classLoaderDomainURLs.size()]),
						Thread.currentThread().getContextClassLoader());
				// add it to each component
				for (LibraryComponentImpl component : libraryComponents) {
					component.setClassLoaderDomain(classLoaderDomain);
				}
			} else if ((componentDescriptor = componentJarFile
					.getJarEntry("META-INF/event-jar.xml")) != null) {
				// create class loader domain shared by all components
				URLClassLoaderDomainImpl classLoaderDomain = componentManagement
				.getClassLoaderFactory()
				.newClassLoaderDomain(
						new URL[] { componentJarDeploymentDir.toURL() },
						Thread.currentThread().getContextClassLoader());
				// parse descriptor
				componentDescriptorInputStream = componentJarFile
						.getInputStream(componentDescriptor);
				EventTypeDescriptorFactoryImpl descriptorFactory = componentManagement
						.getComponentDescriptorFactory()
						.getEventTypeDescriptorFactory();
				List<EventTypeDescriptorImpl> descriptors = descriptorFactory
						.parse(componentDescriptorInputStream);
				// create components
				for (EventTypeDescriptorImpl descriptor : descriptors) {
					EventTypeComponentImpl component = new EventTypeComponentImpl(
							descriptor);
					component.setDeploymentDir(componentJarDeploymentDir);
					component.setClassLoaderDomain(classLoaderDomain);
					components.add(component);
				}
			} else if ((componentDescriptor = componentJarFile
					.getJarEntry("META-INF/resource-adaptor-type-jar.xml")) != null) {
				// create class loader domain shared by all components
				URLClassLoaderDomainImpl classLoaderDomain = componentManagement.getClassLoaderFactory().newClassLoaderDomain(new URL[] { componentJarDeploymentDir.toURL() }, Thread
						.currentThread().getContextClassLoader());
				// parse descriptor
				componentDescriptorInputStream = componentJarFile
						.getInputStream(componentDescriptor);
				ResourceAdaptorTypeDescriptorFactoryImpl descriptorFactory = componentManagement
						.getComponentDescriptorFactory()
						.getResourceAdaptorTypeDescriptorFactory();
				List<ResourceAdaptorTypeDescriptorImpl> descriptors = descriptorFactory
						.parse(componentDescriptorInputStream);
				// create components
				for (ResourceAdaptorTypeDescriptorImpl descriptor : descriptors) {
					ResourceAdaptorTypeComponentImpl component = new ResourceAdaptorTypeComponentImpl(
							descriptor);
					component.setDeploymentDir(componentJarDeploymentDir);
					component.setClassLoaderDomain(classLoaderDomain);
					components.add(component);
				}
			} else if ((componentDescriptor = componentJarFile
					.getJarEntry("META-INF/resource-adaptor-jar.xml")) != null) {
				// create class loader domain shared by all components
				URLClassLoaderDomainImpl classLoaderDomain = componentManagement
						.getClassLoaderFactory()
						.newClassLoaderDomain(
								new URL[] { componentJarDeploymentDir.toURL() },
								Thread.currentThread().getContextClassLoader());
				// parse descriptor
				componentDescriptorInputStream = componentJarFile
						.getInputStream(componentDescriptor);
				ResourceAdaptorDescriptorFactoryImpl descriptorFactory = componentManagement
						.getComponentDescriptorFactory()
						.getResourceAdaptorDescriptorFactory();
				List<ResourceAdaptorDescriptorImpl> descriptors = descriptorFactory
						.parse(componentDescriptorInputStream);
				// create components
				for (ResourceAdaptorDescriptorImpl descriptor : descriptors) {
					ResourceAdaptorComponentImpl component = new ResourceAdaptorComponentImpl(
							descriptor);
					component.setDeploymentDir(componentJarDeploymentDir);
					component.setClassLoaderDomain(classLoaderDomain);
					components.add(component);
				}
			} else {
				throw new DeploymentException(
						"No Deployment Descriptor found in the "
								+ componentJarFile.getName()
								+ " entry of a deployable unit.");
			}
		} catch (IOException e) {
			throw new DeploymentException(
					"failed to parse jar descriptor from "
							+ componentJarFile.getName(), e);
		} finally {
			if (componentDescriptorInputStream != null) {
				try {
					componentDescriptorInputStream.close();
				} catch (IOException e) {
					logger
							.error("failed to close inputstream of descriptor for jar "
									+ componentJarFile);
				}
			}
		}

		// close component jar file
		try {
			componentJarFile.close();
		} catch (IOException e) {
			logger.error("failed to close component jar file", e);
		}
		// and delete the extracted jar file, we don't need it anymore
		if (!extractedFile.delete()) {
			logger.warn("failed to delete " + extractedFile);
		}

		return components;
	}

	/**
	 * Extracts the file with name fileName out of the
	 * containingJar archive and stores it in dstDir.
	 *
	 * @param fileName
	 *            the name of the file to extract.
	 * @param containingJar
	 *            the archive where to extract it from.
	 * @param dstDir
	 *            the location where the extracted file should be stored.
	 * @return a java.io.File reference to the extracted file.
	 * @throws DeploymentException
	 */
	private File extractFile(String fileName, JarFile containingJar, File dstDir)
			throws DeploymentException {

		ZipEntry zipFileEntry = containingJar.getEntry(fileName);
		logger.trace("Extracting file " + fileName + " from "
				+ containingJar.getName());
		if (zipFileEntry == null) {
			throw new DeploymentException("Error extracting jar file  "
					+ fileName + " from " + containingJar.getName());
		}
		File extractedFile = new File(dstDir, new File(zipFileEntry.getName())
				.getName());
		try {
			pipeStream(containingJar.getInputStream(zipFileEntry),
					new FileOutputStream(extractedFile));
		} catch (FileNotFoundException e) {
			throw new DeploymentException("file " + fileName + " not found in "
					+ containingJar.getName(), e);
		} catch (IOException e) {
			throw new DeploymentException("erro extracting file " + fileName
					+ " from " + containingJar.getName(), e);
		}
		logger.debug("Extracted file " + extractedFile.getName());
		return extractedFile;
	}

	/**
	 * This method will extract all the files in the jar file
	 *
	 * @param jarFile
	 *            the jar file
	 * @param dstDir
	 *            the destination where files in the jar file be extracted
	 * @param deployableUnitID
	 * @return
	 * @throws DeploymentException
	 *             failed to extract files
	 */
	private void extractJar(JarFile jarFile, File dstDir)
			throws DeploymentException {

		// Extract jar contents to a classpath location
		JarInputStream jarIs = null;
		try {
			jarIs = new JarInputStream(new BufferedInputStream(
					new FileInputStream(jarFile.getName())));

			for (JarEntry entry = jarIs.getNextJarEntry(); jarIs.available() > 0
					&& entry != null; entry = jarIs.getNextJarEntry()) {
				logger.trace("jar entry = " + entry.getName());

				if (entry.isDirectory()) {
					// Create jar directories.
					File dir = new File(dstDir, entry.getName());
					if (!dir.exists()) {
						if (!dir.mkdirs()) {
							logger.debug("Failed to create directory "
									+ dir.getAbsolutePath());
							throw new IOException("Failed to create directory "
									+ dir.getAbsolutePath());
						}
					} else
						logger.trace("Created directory"
								+ dir.getAbsolutePath());
				} else // unzip files
				{
					File file = new File(dstDir, entry.getName());
					File dir = file.getParentFile();
					if (!dir.exists()) {
						if (!dir.mkdirs()) {
							logger.debug("Failed to create directory "
									+ dir.getAbsolutePath());
							throw new IOException("Failed to create directory "
									+ dir.getAbsolutePath());
						} else
							logger.trace("Created directory"
									+ dir.getAbsolutePath());
					}
					pipeStream(jarFile.getInputStream(entry),
							new FileOutputStream(file));

				}
			}
		} catch (Exception e) {
			throw new DeploymentException("failed to extract jar file "
					+ jarFile.getName());
		} finally {
			if (jarIs != null) {
				try {
					jarIs.close();
				} catch (IOException e) {
					logger.error("failed to close jar input stream", e);
				}
			}
		}

	}

	private static byte buffer[] = new byte[8192];

	/**
	 * Pipes data from the input stream into the output stream.
	 *
	 * @param is
	 *            The InputStream where the data is coming from.
	 * @param os
	 *            The OutputStream where the data is going to.
	 * @throws IOException
	 *             if reading or writing the data fails.
	 */
	private void pipeStream(InputStream is, OutputStream os) throws IOException {
		synchronized (buffer) {
			try {
				for (int bytesRead = is.read(buffer); bytesRead != -1; bytesRead = is
						.read(buffer))
					os.write(buffer, 0, bytesRead);
				is.close();
				os.close();
			} catch (IOException ioe) {
				try {
					is.close();
				} catch (Exception ioexc) {/* do sth? */
				}
				try {
					os.close();
				} catch (Exception ioexc) {/* do sth? */
				}
				throw ioe;
			}
		}
	}

}


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

import org.infinispan.tree.Fqn;
import org.infinispan.tree.Node;
import org.mobicents.slee.container.activity.ActivityContextHandle;
import org.mobicents.slee.container.sbbentity.SbbEntityID;
import org.restcomm.cache.CacheData;
import org.restcomm.cache.FqnWrapper;
import org.restcomm.cluster.MobicentsCluster;

import javax.slee.facilities.TimerID;
import java.util.*;

/**
 *
 * Proxy object for activity context factory data management through JBoss Cache
 *
 * @author martins
 *
 */
@SuppressWarnings("rawtypes")
public class ActivityContextCacheData extends CacheData {

	/**
	 * the fqn of the node that holds all activity context cache child nodes
	 */
	public final static String parentNodeFqn = "ac";

	// --- child cache nodes naming

	private static final String ATTACHED_SBBs_NODE_NAME = "attached-sbbs";

	private static final String ATTACHED_TIMERS_NODE_NAME = "attached-timers";

	private static final String NAMES_BOUND_NODE_NAME = "names-bound";

	private static final String CMP_ATTRIBUTES_NODE_NAME = "cmp-attributes";

	private static final String IS_ENDING_NODE_NAME = "is-ending";

	private static final Fqn ATTACHED_SBBs_FQN = Fqn
			.fromElements(ATTACHED_SBBs_NODE_NAME);

	private static final Fqn ATTACHED_TIMERS_FQN = Fqn
			.fromElements(ATTACHED_TIMERS_NODE_NAME);

	private static final Fqn NAMES_BOUND_FQN = Fqn.fromElements(NAMES_BOUND_NODE_NAME);

	private static final Fqn CMP_ATTRIBUTES_FQN = Fqn
			.fromElements(CMP_ATTRIBUTES_NODE_NAME);

	private static final Fqn IS_ENDING_FQN = Fqn
	.fromElements(IS_ENDING_NODE_NAME);

	private static final Boolean CMP_ATTRIBUTES_NODE_MAP_KEY = Boolean.TRUE;

	private static final String STRING_ID_ATTRIBUTE_MAP_KEY = "sid";

	private Node _attachedSbbsNode;

	private Node getAttachedSbbsNode(boolean createIfNotExists) {
		if (_attachedSbbsNode == null) {
			final Node node = getNode();
			_attachedSbbsNode = node.getChild(ATTACHED_SBBs_NODE_NAME);
			if (_attachedSbbsNode == null && createIfNotExists) {
				_attachedSbbsNode = node.addChild(ATTACHED_SBBs_FQN);
			}
		}
		return _attachedSbbsNode;
	}

	private Node _attachedTimersNode;

	private Node getAttachedTimersNode(boolean createIfNotExists) {
		if (_attachedTimersNode == null) {
			final Node node = getNode();
			_attachedTimersNode = node.getChild(ATTACHED_TIMERS_NODE_NAME);
			if (_attachedTimersNode == null && createIfNotExists) {
				_attachedTimersNode = node.addChild(ATTACHED_TIMERS_FQN);
			}
		}
		return _attachedTimersNode;
	}

	private Node _namesBoundNode;

	private Node getNamesBoundNode(boolean createIfNotExists) {
		if (_namesBoundNode == null) {
			final Node node = getNode();
			_namesBoundNode = node.getChild(NAMES_BOUND_NODE_NAME);
			if (_namesBoundNode == null && createIfNotExists) {
				_namesBoundNode = node.addChild(NAMES_BOUND_FQN);
			}
		}
		return _namesBoundNode;
	}

	private Node _cmpAttributesNode;

	private Node getCmpAttributesNode(boolean createIfNotExists) {
		if (_cmpAttributesNode == null) {
			final Node node = getNode();
			_cmpAttributesNode = node.getChild(CMP_ATTRIBUTES_NODE_NAME);
			if (_cmpAttributesNode == null && createIfNotExists) {
				_cmpAttributesNode = node.addChild(CMP_ATTRIBUTES_FQN);
			}
		}
		return _cmpAttributesNode;
	}

	/**
	 *
	 * @param activityContextHandle
	 */
	public ActivityContextCacheData(ActivityContextHandle activityContextHandle,
			MobicentsCluster cluster) {
		super(FqnWrapper.fromElementsWrapper(parentNodeFqn, activityContextHandle),
				cluster.getMobicentsCache());
	}

	/**
	 * Puts an object in cache data
	 *
	 * @param key
	 * @param value
	 * @return the old object for the specified key, null if key was not mapped
	 */
	@SuppressWarnings("unchecked")
	public Object putObject(Object key, Object value) {
		return getNode().put(key, value);
	}

	/**
	 * Retrieves an object in cache data mapped to the specified key
	 *
	 * @param key
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Object getObject(Object key) {
		return getNode().get(key);
	}

	/**
	 * Removes an object in cache data mapped to the specified key
	 *
	 * @param key
	 * @return the object removed, null if the key was not mapped
	 */
	@SuppressWarnings("unchecked")
	public Object removeObject(Object key) {
		return getNode().remove(key);
	}

	public boolean isEnding() {
		return getNode().hasChild(IS_ENDING_NODE_NAME);
	}

	public boolean setEnding(boolean value) {
		if (value) {
			if (!isEnding()) {
				getNode().addChild(IS_ENDING_FQN);
				return true;
			}
			else {
				return false;
			}
		}
		else {
			if (isEnding()) {
				getNode().removeChild(IS_ENDING_NODE_NAME);
				return true;
			}
			else {
				return false;
			}
		}
	}

	/**
	 * Tries to attaches an sbb entity
	 *
	 * @param sbbEntityId
	 * @return true if it was attached, false if already was attached
	 */
	public boolean attachSbbEntity(SbbEntityID sbbEntityId) {
		final Node node = getAttachedSbbsNode(true);
		if (!node.hasChild(sbbEntityId)) {
			node.addChild(Fqn.fromElements(sbbEntityId));
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Detaches an sbb entity
	 *
	 * @param sbbEntityId
	 */
	public boolean detachSbbEntity(SbbEntityID sbbEntityId) {
		final Node node  = getAttachedSbbsNode(false);
		return node != null ? node.removeChild(sbbEntityId) : false;
	}

	/**
	 * Verifies if there at least one sbb entity attached
	 *
	 * @return false is there are no sbb entities attached, true otherwise
	 */
	public boolean noSbbEntitiesAttached() {
		final Node node  = getAttachedSbbsNode(false);
		return node != null ? node.getChildrenNames().isEmpty() : true;
	}

	/**
	 * Return a set with all sbb entities attached.
	 *
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Set<SbbEntityID> getSbbEntitiesAttached() {
		final Node node  = getAttachedSbbsNode(false);
		return node != null ? node.getChildrenNames() : Collections.emptySet();
	}

	/**
	 * Attaches a timer
	 *
	 * @param timerID
	 */
	public boolean attachTimer(TimerID timerID) {
		final Node node = getAttachedTimersNode(true);
		if (!node.hasChild(timerID)) {
			node.addChild(Fqn.fromElements(timerID));
			return true;
		}
		else {
			return false;
		}
	}

	/**
	 * Detaches a timer
	 *
	 * @param timerID
	 */
	public boolean detachTimer(TimerID timerID) {
		final Node node = getAttachedTimersNode(false);
		return node != null ? node.removeChild(timerID) : false;
	}

	/**
	 * Verifies if there at least one timer attached
	 *
	 * @return false is there are no timers attached, true otherwise
	 */
	public boolean noTimersAttached() {
		final Node node = getAttachedTimersNode(false);
		return node != null ? node.getChildrenNames().isEmpty() : true;
	}

	/**
	 * Returns the set of timers attached to the ac
	 *
	 * @return
	 */
	public Set getAttachedTimers() {
		final Node node = getAttachedTimersNode(false);
		return node != null ? node.getChildrenNames() : Collections.emptySet();
	}

	/**
	 * Adds the specified name to the set of names bound to the ac
	 *
	 * @param name
	 */
	public void nameBound(String name) {
		final Node node = getNamesBoundNode(true);
		if (!node.hasChild(name)) {
			node.addChild(Fqn.fromElements(name));
		}
	}

	/**
	 * Removes the specified name from the set of names bound to the ac
	 *
	 * @param name
	 */
	public boolean nameUnbound(String name) {
		final Node node = getNamesBoundNode(false);
		return node != null ? node.removeChild(name) : false;
	}

	/**
	 * Verifies if there at least one name bound to the ac
	 *
	 * @return false is there are no names bound, true otherwise
	 */
	public boolean noNamesBound() {
		final Node node = getNamesBoundNode(false);
		return node != null ? node.getChildrenNames().isEmpty() : true;
	}

	/**
	 * Returns the set of names bound to the ac
	 *
	 * @return
	 */
	public Set getNamesBoundCopy() {
		final Node node = getNamesBoundNode(false);
		return node != null ? node.getChildrenNames() : Collections.emptySet();
	}

	/**
	 * Sets the aci cmp attribute
	 *
	 * @param attrName
	 * @param attrValue
	 */
	@SuppressWarnings("unchecked")
	public void setCmpAttribute(String attrName, Object attrValue) {
		final Node node = getCmpAttributesNode(true);
		Node cmpNode = node.getChild(attrName);
		if (cmpNode == null) {
			cmpNode = node.addChild(Fqn.fromElements(attrName));
		}
		cmpNode.put(CMP_ATTRIBUTES_NODE_MAP_KEY, attrValue);
	}

	/**
	 * Retrieves the aci cmp attribute
	 *
	 * @param attrName
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Object getCmpAttribute(String attrName) {
		final Node node = getCmpAttributesNode(false);
		if(node == null) {
			return null;
		}
		else {
			final Node cmpNode = node.getChild(attrName);
			if (cmpNode != null) {
				return cmpNode.get(CMP_ATTRIBUTES_NODE_MAP_KEY);
			}
			else {
				return null;
			}
		}
	}

	/**
	 * Retrieves a map copy of the aci attributes set
	 *
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Map getCmpAttributesCopy() {
		final Node node = getCmpAttributesNode(false);
		if(node == null) {
			return Collections.emptyMap();
		}
		else {
			Map result = new HashMap();
			Node cmpNode = null;
			for (Object obj : node.getChildren()) {
				cmpNode = (Node) obj;
				result.put(cmpNode.getFqn().getLastElement(), cmpNode
						.get(CMP_ATTRIBUTES_NODE_MAP_KEY));
			}
			return result;
		}
	}

	@SuppressWarnings("unchecked")
	public String getStringID() {
		return (String) getNode().get(STRING_ID_ATTRIBUTE_MAP_KEY);
	}

	@SuppressWarnings("unchecked")
	public void setStringID(String sid) {
		getNode().put(STRING_ID_ATTRIBUTE_MAP_KEY, sid);
	}

}


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

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javassist.Modifier;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.component.ComponentRepository;
import org.mobicents.slee.container.component.ResourceAdaptorComponentImpl;
import org.mobicents.slee.container.component.ra.ConfigPropertyDescriptor;

public class ResourceAdaptorValidator implements Validator {

	private ResourceAdaptorComponentImpl component = null;
	private Logger logger = Logger.getLogger(ResourceAdaptorValidator.class);

	private static final Set<String> _VALID_CONF_PROPERTIES;

	static {

		Set<String> tmp = new HashSet<String>();

		// java.lang.Integer, java.lang.Long,
		// java.lang.Double, java.lang.Float, java.lang.Short,
		// java.lang.Byte, java.lang.Character, java.lang.Boolean, and
		// java.lang.String.

		tmp.add(java.lang.Integer.class.getName());
		tmp.add(java.lang.Long.class.getName());
		tmp.add(java.lang.Double.class.getName());
		tmp.add(java.lang.Float.class.getName());
		tmp.add(java.lang.Short.class.getName());
		tmp.add(java.lang.Byte.class.getName());
		tmp.add(java.lang.Character.class.getName());
		tmp.add(java.lang.Boolean.class.getName());
		tmp.add(java.lang.String.class.getName());

		_VALID_CONF_PROPERTIES = Collections.unmodifiableSet(tmp);
	}

	public void setComponentRepository(ComponentRepository repository) {

	}

	public void setComponent(ResourceAdaptorComponentImpl component) {
		this.component = component;
	}

	public boolean validate() {

		boolean valid = true;
		// Uf here we go
		try {
			if (!validateDescriptor()) {
				valid = false;
				return valid;
			}

			if (!validateResourceAdaptorClass()) {
				valid = false;
			}

			if (!validateUsageInterface()) {
				valid = false;
			}
		} catch (Exception e) {
			e.printStackTrace();
			valid = false;

		}

		return valid;
	}

	boolean validateUsageInterface() {
		boolean passed = true;
		// String errorBuffer = new String("");

		if (!this.component.isSlee11()) {
			return passed;
		}

		try {

			if (this.component.getDescriptor()
					.getResourceAdaptorUsageParametersInterface() != null) {

				return UsageInterfaceValidator
						.validateResourceAdaptorUsageParameterInterface(this.component);

			}

		} finally {
			// if (!passed) {
			// System.err.println(errorBuffer);
			// logger.error(errorBuffer);
			// }

		}

		return passed;

	}

	boolean validateDescriptor() {

		boolean passed = true;
		//String errorBuffer = new String("");

		try {

			if(!validateConfigProperties())
			{
				passed = false;
			}

		} finally {
			// if (!passed) {
			// System.err.println(errorBuffer);
			// logger.error(errorBuffer);
			// }

		}

		return passed;

	}

	boolean validateConfigProperties() {
		boolean passed = true;
		String errorBuffer = new String("");

		if (!this.component.isSlee11())
			return passed;

		try {

			Set<String> declaredConfigProperty = new HashSet<String>();
			// Map<String, String> nameToType = new HashMap<String, String>();

			for (ConfigPropertyDescriptor prop : this.component.getDescriptor()
					.getConfigProperties()) {

				if (prop.getConfigPropertyName() == null
						|| prop.getConfigPropertyName().compareTo("") == 0) {
					passed = false;
					errorBuffer = appendToBuffer(
							"Resource adaptor descriptor declares config property with empty name.",
							"15.4.1", errorBuffer);
					continue;

				} else if (prop.getConfigPropertyType() == null
						|| prop.getConfigPropertyType().compareTo("") == 0) {
					passed = false;
					errorBuffer = appendToBuffer(
							"Resource adaptor descriptor declares config property with empty type.",
							"15.4.1", errorBuffer);
					continue;

				} else if (declaredConfigProperty.contains(prop
						.getConfigPropertyName())) {
					passed = false;
					errorBuffer = appendToBuffer(
							"Resource adaptor descriptor declares config property more than once, name: "
									+ prop.getConfigPropertyName(), "15.4.1",
							errorBuffer);
				} else {
					declaredConfigProperty.add(prop.getConfigPropertyName());

					if(!_VALID_CONF_PROPERTIES.contains(prop.getConfigPropertyType()))
					{
						passed = false;
						errorBuffer = appendToBuffer(
								"Resource adaptor descriptor declares config property of wrong type, type: "+prop.getConfigPropertyType(), "15.4.1",errorBuffer);
					}
				}

				// confirm config properties can be built
				try {
					component.getDefaultConfigPropertiesInstance();
				}
				catch (Throwable e) {
					passed = false;
					errorBuffer = appendToBuffer(
							"Resource adaptor descriptor config property validation passed but creation of ConfigProperties object failed:\n"+e.getStackTrace()+"\n", "15.4.1",errorBuffer);
				}
			}

		} finally {
			if (!passed) {
				if(logger.isEnabledFor(Level.ERROR))
					logger.error(errorBuffer);
			}

		}

		return passed;

	}

	boolean validateResourceAdaptorClass() {

		boolean passed = true;
		String errorBuffer = new String("");

		try {

			Class<?> resourceAdaptorClass = this.component
					.getResourceAdaptorClass();

			if(this.component.isSlee11() && resourceAdaptorClass.getPackage()==null)
			{
				passed = false;
				errorBuffer = appendToBuffer(
						"Resouce adaptor class must defined in package.",
						"15.6", errorBuffer);
			}

			// must implement javax.slee.resource.ResourceAdaptor
			if (ClassUtils.checkInterfaces(resourceAdaptorClass,
					"javax.slee.resource.ResourceAdaptor") == null) {
				passed = false;
				errorBuffer = appendToBuffer(
						"Resouce adaptor class must implement javax.slee.ResourceAdaptor.",
						"15.6", errorBuffer);

			}

			int modifiers = resourceAdaptorClass.getModifiers();

			if (!Modifier.isPublic(modifiers)) {
				passed = false;
				errorBuffer = appendToBuffer(
						"Resouce adaptor class must be public.", "15.6",
						errorBuffer);

			}

			if (Modifier.isAbstract(modifiers)) {
				passed = false;
				errorBuffer = appendToBuffer(
						"Resouce adaptor class must not be abstract.", "15.6",
						errorBuffer);

			}

			if (Modifier.isFinal(modifiers)) {
				passed = false;
				errorBuffer = appendToBuffer(
						"Resouce adaptor class must not be final.", "15.6",
						errorBuffer);

			}

			try {
				resourceAdaptorClass.getConstructor();
			} catch (Exception e) {

				// e.printStackTrace();
				passed = false;
				errorBuffer = appendToBuffer(
						"Resouce adaptor class must decalre public no arg constructor.",
						"15.6", errorBuffer);
			}

		} finally {
			if (!passed) {
				if(logger.isEnabledFor(Level.ERROR))
					logger.error(errorBuffer);
			}

		}

		return passed;

	}

	// FIXME: add check for access?

	protected String appendToBuffer(String message, String section,
			String buffer) {
		buffer += (this.component.getDescriptor().getResourceAdaptorID()
				+ " : violates section " + section
				+ " of jSLEE 1.1 specification : " + message + "\n");
		return buffer;
	}
}