/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: JOnASEJBService.java 21888 2011-11-06 17:48:21Z cazauxj $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ejb2.internal;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.Timer;
import javax.management.Attribute;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.resource.spi.work.WorkManager;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.Version;
import org.ow2.jonas.cmi.CmiService;
import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.ejb.BeanDesc;
import org.ow2.jonas.deployment.ejb.DeploymentDesc;
import org.ow2.jonas.deployment.ejb.EntityBmpDesc;
import org.ow2.jonas.deployment.ejb.EntityCmpDesc;
import org.ow2.jonas.deployment.ejb.MessageDrivenDesc;
import org.ow2.jonas.deployment.ejb.SessionDesc;
import org.ow2.jonas.deployment.ejb.SessionStatefulDesc;
import org.ow2.jonas.deployment.ejb.SessionStatelessDesc;
import org.ow2.jonas.deployment.ejb.lib.EjbDeploymentDescManager;
import org.ow2.jonas.ejb2.EJBService;
import org.ow2.jonas.ejb2.JTimerHandleInfo;
import org.ow2.jonas.ejb2.internal.mbean.EJBModule;
import org.ow2.jonas.ejb2.internal.mbean.EntityBean;
import org.ow2.jonas.ejb2.internal.mbean.MessageDrivenBean;
import org.ow2.jonas.ejb2.internal.mbean.StatefulSessionBean;
import org.ow2.jonas.ejb2.internal.mbean.StatelessSessionBean;
import org.ow2.jonas.generators.genic.wrapper.GenicServiceWrapper;
import org.ow2.jonas.ha.HaService;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.jndi.checker.api.IResourceCheckerManager;
import org.ow2.jonas.lib.bootstrap.LoaderManager;
import org.ow2.jonas.lib.ejb21.BeanFactory;
import org.ow2.jonas.lib.ejb21.Container;
import org.ow2.jonas.lib.ejb21.JContainer;
import org.ow2.jonas.lib.ejb21.JEntityFactory;
import org.ow2.jonas.lib.ejb21.JEntitySwitch;
import org.ow2.jonas.lib.ejb21.JFactory;
import org.ow2.jonas.lib.ejb21.JSessionFactory;
import org.ow2.jonas.lib.ejb21.JStatelessFactory;
import org.ow2.jonas.lib.ejb21.JTimerService;
import org.ow2.jonas.lib.ejb21.PermissionManager;
import org.ow2.jonas.lib.ejb21.Protocols;
import org.ow2.jonas.lib.ejb21.TraceEjb;
import org.ow2.jonas.lib.loader.EjbJarClassLoader;
import org.ow2.jonas.lib.loader.FilteringClassLoader;
import org.ow2.jonas.lib.management.javaee.J2eeObjectName;
import org.ow2.jonas.lib.naming.ComponentContext;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.timer.TraceTimer;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.lib.util.ModuleNamingUtils;
import org.ow2.jonas.lib.work.DeployerLog;
import org.ow2.jonas.workcleaner.DeployerLogException;
import org.ow2.jonas.naming.JComponentContextFactory;
import org.ow2.jonas.naming.JNamingManager;
import org.ow2.jonas.registry.RegistryService;
import org.ow2.jonas.resource.ResourceService;
import org.ow2.jonas.security.SecurityService;
import org.ow2.jonas.service.ServiceException;
import org.ow2.jonas.tm.TransactionManager;
import org.ow2.jonas.tm.TransactionService;
import org.ow2.jonas.versioning.VersioningService;
import org.ow2.jonas.workcleaner.CleanTask;
import org.ow2.jonas.workcleaner.WorkCleanerService;
import org.ow2.jonas.workmanager.WorkManagerService;
import org.ow2.jonas.ws.jaxrpc.IJAXRPCService;
import org.ow2.util.archive.api.ArchiveException;
import org.ow2.util.archive.api.IArchive;
import org.ow2.util.archive.impl.ArchiveManager;
import org.ow2.util.ee.deploy.api.deployable.IDeployable;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.ee.deploy.impl.helper.DeployableHelper;
import org.ow2.util.event.api.IEventDispatcher;
import org.ow2.util.event.api.IEventService;
import org.ow2.util.event.impl.EventDispatcher;
import org.ow2.util.file.FileUtils;
import org.ow2.util.url.URLUtils;

/**
 * Implementation of the EJB Container Service for jonas. This class works only with jonas_ejb classes.
 * @author Philippe Coq
 * @author Jeff Mesnil (Security)
 * @author Markus Karg (Novell port)
 * @author Christophe Ney (for making easier Enhydra integration)
 * @author Adriana Danes (complete management methods)
 * @author Florent Benoit & Ludovic Bert (Ear service, deployJars, undeployJars)
 * @author Benjamin Bonnet (max thread pool size)
 * @author Michel-Ange Anton (JSR77 MBean : EJBModule)
 * @author Adriana Danes (JSR77)
 * @author S. Ali Tokmen (versioning)
 * @author S. Ali Tokmen, Malek Chahine: EJB statistics
 */
public class JOnASEJBService extends AbsServiceImpl implements EJBService, JOnASEJBServiceMBean {

    /**
     * server logger.
     */
    private static Logger logger = Log.getLogger(Log.JONAS_EJB_PREFIX);

    /**
     * loader logger.
     */
    private static Logger loaderlog = Log.getLogger(Log.JONAS_LOADER_PREFIX);

    /**
     * Minimum timer duration.
     */
    private static final int MINIMUM_TIMER_DURATION = 100;

    /**
     * Transaction manager reference.
     */
    private TransactionManager tm = null;

    /**
     * JMX Service.
     */
    private JmxService jmxService = null;

    /**
     * Security service reference.
     */
    private SecurityService securityService = null;

    /**
     * JAX-RPC service reference.
     */
    private IJAXRPCService jaxrpcService = null;

    /**
     * Registry service used to discover active protocols (auto GenIC).
     */
    private RegistryService registryService = null;

    /**
     * List of the ejb names to load when starting the EJB Container Service.
     */
    private List<String> ejbNames = new Vector<String>();

    /**
     * We have a Container by ejb-jar file.
     */
    private Vector<JContainer> containers = new Vector<JContainer>();

    /**
     * The WorkManager instance.
     */
    private WorkManager workManager;

    /**
     * Will EJB monitoring enabled for EJBs deployed in the future?
     */
    private boolean monitoringEnabled = false;

    /**
     * Number of milliseconds after which methods of EJBs deployed in the future will start warning.
     */
    private int warningThreshold = 0;

    /**
     * BeanManaged Management properties.
     */
    public static final String BMP = "Bean-Managed";

    /**
     * ContainerManaged Management properties.
     */
    public static final String CMP = "Container-Managed";

    /**
     * External classloader.
     */
    private ClassLoader extClassLoader = null;

    /**
     * Reference to the {@link DeployerLog} which is the class that manage the accesses to the log file (to remove the jar).
     */
    private DeployerLog deployerLog = null;

    /**
     * Auto-GenIC has been enabled or not ?
     */
    private boolean autoGenIC = true;

    /**
     * List of arguments for the autoGenIC.
     */
    private List<String> autoGenICArgsList = new ArrayList<String>();

    /**
     * Singleton object.
     */
    private JComponentContextFactory componentContextFactory = null;

    /**
     * TransactionService.
     */
    private TransactionService transactionService;

    /**
     * WorkManager Service.
     */
    private WorkManagerService workManagerService;

    /**
     * NamingManager for EJB binding.
     */
    private JNamingManager naming;

    /**
     * Link to the EJB 2.1 Deployer.
     */
    private EJB21Deployer ejb21Deployer = null;

    /**
     * CMI service reference (can be null).
     */
    private CmiService cmiService = null;

    /**
     * HA service reference (can be null).
     */
    private HaService haService = null;

    /**
     * Resource service reference (can be null).
     */
    private ResourceService resService = null;

    /**
     * DeployerManager service.
     */
    private IDeployerManager deployerManager;

    /**
     * Working directory for EJB2.
     */
    private File workEjbjarsFile;

    /**
     * Versioning service.
     */
    private VersioningService versioningService;

    /**
     * Event service.
     */
    private IEventService eventService;

    /**
     * Dispatcher name for EJB2 monitoring events.
     */
    private static final String MONITORING_DISPATCHER_NAME = "/jonas/service/ejb2/monitoring";

    /**
     * Dispatcher name for EJB2 monitoring events.
     */
    private static final String CONTAINER_LIFECYCLE_DISPATCHER_NAME = "/beans/lifecycle/events";

    /**
     * Monitoring dispatcher.
     */
    private IEventDispatcher monitoringDispatcher;

    /**
     * Monitoring dispatcher.
     */
    private IEventDispatcher containerLifecycleDispatcher;

    /**
     * Link to the resource checker manager.
     */
    private IResourceCheckerManager resourceCheckerManager = null;


    /**
     * Statistics for beans, updated at each undeploy.
     */
    private class BeanMonitoringStatistics {
        /**
         * Number of calls.
         */
        private long numberOfCalls;

        /**
         * Total processing time.
         */
        private long totalProcessingTime;

        /**
         * Total business processing time.
         */
        private long totalBusinessProcessingTime;

        /**
         * Constructor.
         * @param numberOfCalls number of calls.
         * @param totalProcessingTime total processing time.
         * @param totalBusinessProcessingTime total business processing time.
         */
        public BeanMonitoringStatistics(final long numberOfCalls, final long totalProcessingTime,
                final long totalBusinessProcessingTime) {
            this.numberOfCalls = numberOfCalls;
            this.totalProcessingTime = totalProcessingTime;
            this.totalBusinessProcessingTime = totalBusinessProcessingTime;
        }

        /**
         * @return number of calls.
         */
        public long getNumberOfCalls() {
            return numberOfCalls;
        }

        /**
         * @return total processing time.
         */
        public long getTotalProcessingTime() {
            return totalProcessingTime;
        }

        /**
         * @return total business processing time.
         */
        public long getTotalBusinessProcessingTime() {
            return totalBusinessProcessingTime;
        }
    }

    /**
     * Undeployed beans' monitoring statistics.
     */
    private Map<String, BeanMonitoringStatistics> beanMonitoringStatistics = new HashMap<String, BeanMonitoringStatistics>();

    /**
     * Default constructor.
     */
    public JOnASEJBService() {
        this.ejb21Deployer = new EJB21Deployer();
    }

    /**
     * @param autoGenIC Engage Auto GenIC ?
     */
    public void setAutoGenic(final boolean autoGenIC) {
        this.autoGenIC = autoGenIC;
        if (!autoGenIC) {
            logger.log(BasicLevel.INFO, "Auto GenIC has been disabled");
        }
    }

    /**
     * @param validate Use a validating parser ?
     */
    public void setParsingwithvalidation(final boolean validate) {
        EjbDeploymentDescManager.setParsingWithValidation(validate);
        if (!validate) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "XML parsing without validation");
            }
        }
    }

    /**
     * @param args List of arguments to the Auto-GenIC process.
     */
    public void setAutoGenicArgs(final String args) {
        autoGenICArgsList = convertToList(args);
        if (!autoGenICArgsList.isEmpty()) {
            logger.log(BasicLevel.DEBUG, "Auto GenIC args are set to '" + autoGenICArgsList + "'.");
        }
    }

    /**
     * Start the Service Initialization of the service is already done. {@inheritDoc}
     */
    @Override
    public void doStart() throws ServiceException {
        initWorkingDirectory();

        // Get some objects from services
        tm = transactionService.getTransactionManager();
        workManager = workManagerService.getWorkManager();

        // Init the loggers: Timer & Ejb
        TraceTimer.configure(Log.getLoggerFactory());
        TraceEjb.configure(Log.getLoggerFactory());

        // Init the deployer
        ejb21Deployer.setEjb21Service(this);

        // Register the deployer
        deployerManager.register(ejb21Deployer);

        // Create dispatcher
        try {
            monitoringDispatcher = new EventDispatcher();
            monitoringDispatcher.start();
            eventService.registerDispatcher(MONITORING_DISPATCHER_NAME, monitoringDispatcher);
        } catch (Exception e) {
            throw new ServiceException("Cannot create the EJB monitoring dispatcher", e);
        }

        /**
         * d Create life cycle dispatcher.
         */
        if (containerLifecycleDispatcher == null) {
            try {
                containerLifecycleDispatcher = new EventDispatcher();
                containerLifecycleDispatcher.start();
                eventService.registerDispatcher(CONTAINER_LIFECYCLE_DISPATCHER_NAME, containerLifecycleDispatcher);
            } catch (Exception e) {
                throw new ServiceException("Cannot create the EJB Container lifecycle dispatcher", e);
            }
        }
        try {
            LoaderManager lm = LoaderManager.getInstance();
            extClassLoader = lm.getExternalLoader();
        } catch (Throwable e) {
            logger.log(BasicLevel.ERROR, "Cannot get the Applications ClassLoader from EJB Container Service");
            throw new ServiceException("Cannot get the Applications ClassLoader from EJB Container Service", e);
        }

        // Load the Model MBeans
        // TODO, When ejb service will become a bundle, we could use
        // the meta-data located in META-INF/mbeans-descriptors.xml
        jmxService.loadDescriptors(getClass().getPackage().getName(), getClass().getClassLoader());

        // Creates all predefined containers
        // 1 container per ejbjar file.
        String fileName = null;
        Context contctx = null;
        for (int i = 0; i < ejbNames.size(); i++) {
            fileName = ejbNames.get(i);
            try {
                contctx = new ComponentContext(fileName);
                contctx.rebind("filename", fileName);
            } catch (NamingException e) {
                logger.log(BasicLevel.WARN, "Cannot bind filename '" + fileName + "' in component context", e);
            }

            try {
                createContainer(contctx);
            } catch (Throwable e) {
                logger.log(BasicLevel.WARN, "Cannot create container for " + fileName, e);
                // Close naming context
                try {
                    contctx.close();
                } catch (NamingException nne) {
                    if (logger.isLoggable(BasicLevel.DEBUG)) {
                        logger.log(BasicLevel.DEBUG, "Cannot close deploy context for " + fileName, nne);
                    }
                }
            }
        }

        // Register EJBService MBean
        registerEjbServiceMBean(this, getDomainName());

        logger.log(BasicLevel.INFO, "EJB 2.1 Service started");
    }

    /**
     * Stop the service.
     * <ul>
     * <li>Remove all JOnAS EJB Containers.</li>
     * <li>Unbinds all the EJB(Local)Home names from JNDI.</li>
     * </ul>
     */
    @Override
    public void doStop() {
        // Undeploy EJBs 2.1
        ejb21Deployer.stop();

        if (deployerManager != null) {
            // Unregister the deployer
            deployerManager.unregister(ejb21Deployer);
        }

        // Destroy dispatcher
        try {
            eventService.unregisterDispatcher(MONITORING_DISPATCHER_NAME);
            monitoringDispatcher.stop();
            monitoringDispatcher = null;
        } catch (Exception e) {
            throw new ServiceException("Cannot unregister the EJB monitoring dispatcher", e);
        }

        // Destroy dispatcher
        if (cmiService != null) {
            try {
                eventService.unregisterDispatcher(CONTAINER_LIFECYCLE_DISPATCHER_NAME);
                containerLifecycleDispatcher.stop();
                containerLifecycleDispatcher = null;
            } catch (Exception e) {
                throw new ServiceException("Cannot unregister the EJB container lifecycle dispatcher", e);
            }
        }

        // Unregister EJBService MBean
        unregisterEjbServiceMBean(getDomainName());

        logger.log(BasicLevel.INFO, "EJB 2.1 Service stopped");
    }

    /**
     * Create working directory for EJB2.
     */
    protected void initWorkingDirectory() {
        if (workEjbjarsFile == null) {
            // Create $JONAS_BASE/work/ejbjars directory file
            workEjbjarsFile = new File(getWorkDirectory() + File.separator + getServerProperties().getServerName());
            workEjbjarsFile.mkdirs();
        }
    }

    /**
     * Method called when the workCleanerService is bound to the component.
     * @param workCleanerService the workCleanerService reference
     */
    protected void setWorkCleanerService(final WorkCleanerService workCleanerService) {
        initWorkingDirectory();
        File fileLog = new File(workEjbjarsFile.getPath() + File.separator + getServerProperties().getServerName() + ".log");

        if (!fileLog.exists()) {
            try {
                // Create log file
                fileLog.createNewFile();
            } catch (IOException e) {
                logger.log(BasicLevel.ERROR, "Cannot create the log file " + fileLog, e);
            }
        }

        try {
            // Create the logger
            deployerLog = new DeployerLog(fileLog);
            CleanTask cleanTask = new JarCleanTask(this, deployerLog);

            workCleanerService.registerTask(cleanTask);
            workCleanerService.executeTasks();
        } catch (DeployerLogException e) {
            logger.log(BasicLevel.ERROR, "Cannot register the clean task", e);
        }

    }

    // -------------------------------------------------------------------
    // EJBService Implementation
    // -------------------------------------------------------------------

    /**
     * Create a JOnAS Container for all the beans that are described in a .xml file, or belong to .jar file.
     * @param ctx Deployment context holding all needed values for an EjbJar deployment.
     * @return The ObjectName of the MBean associated to the container (i.e. to the deployed module)
     * @throws Exception if an error occur during the creation of the container.
     */
    public String createContainer(final Context ctx) throws Exception {
        // Get the file name
        String originalFileName = (String) ctx.lookup("filename");
        File originalFile = new File(originalFileName);

        boolean isEjbJar = originalFileName.toLowerCase().endsWith(".jar");
        boolean isEjbJarXml = originalFileName.toLowerCase().endsWith(".xml") && originalFile.isFile();

        // Force fileName with slash to avoid conflict under Windows (slash and back-slash)
        try {
            originalFileName = originalFile.toURL().getPath();
        } catch (MalformedURLException e) {
            logger.log(BasicLevel.ERROR, "Invalid ejb-jar file name '" + originalFileName + "'", e);
        }

        if (originalFile.isFile()) {
            if (!isEjbJar && !isEjbJarXml) {
                throw new ServiceException("The ejbjar to deploy is not a jar file nor an xml file");
            }
        }

        // Check if container already exists
        if (getContainer(originalFileName) != null) {
            logger.log(BasicLevel.ERROR, "createContainer: " + originalFileName + " already exists");
            throw new Exception("Container already exists");
        }

        // In order to avoid lock problem after undeploy
        // the jar file is copied under the working directory before its loading
        // ! only if the ejbjars isn't included in an ear
        boolean isInEar = true;
        try {
            ctx.lookup("earClassLoader");
        } catch (NamingException ne) {
            isInEar = false;
        }

        String workFileName = originalFileName;

        // if need, call Genic. Only in single ejb-jar mode.
        // In ear case, this has been checked by ear service.
        if (!isInEar && isEjbJar) {
            // Build working file name containing the last modification date
            workFileName = FileUtils.lastModifiedFileName(originalFile);

            // Concat working directory path to the working file name
            workFileName = getWorkDirectory() + File.separator + getServerProperties().getServerName() + File.separator
                    + workFileName;
            File workDirectory = new File(workFileName);
            workDirectory.mkdirs();
            if (!workDirectory.isDirectory()) {
                throw new FileNotFoundException("Cannot create work folder : " + workFileName);
            }
            workFileName += File.separator + originalFile.getName();

            if (!new File(workFileName).exists()) {
                // Create working file
                FileUtils.copyFile(originalFileName, workFileName);
            }

            checkGenIC(workFileName, null);

            String resultFilename = applyWSGenIfNeeded(workFileName);

            // Check if a new archive has been created
            if (resultFilename.endsWith(".ear")) {
                // Remove generated file
                FileUtils.delete(workDirectory);

                // Case not supported
                throw new DeployerException(
                        "WSGen has changed archive type from EJB-JAR to EAR. WSGen tool needs to be launched on your EJB-JAR '"
                                + workFileName + "' before deploying it on JOnAS.");
            }

            // add an entry in the log file
            try {
                if (deployerLog != null) {
                    deployerLog.addEntry(originalFile, workDirectory);
                }
            } catch (DeployerLogException e) {
                String err = "Error while adding the " + originalFileName + " entry in the log file";
                logger.log(BasicLevel.ERROR, err + " : " + e.getMessage());
                throw new Exception(err, e);
            }
        }

        // Classloader
        URL[] url = new URL[1];
        url[0] = URLUtils.fileToURL(new File(workFileName));

        // Get the classloaders
        ClassLoader ejbClassLoader = null;
        URLClassLoader earClassLoader = null;
        try {
            earClassLoader = (URLClassLoader) ctx.lookup("earClassLoader");
            ejbClassLoader = (URLClassLoader) ctx.lookup("ejbClassLoader");
            if (loaderlog.isLoggable(BasicLevel.DEBUG)) {
                loaderlog.log(BasicLevel.DEBUG, "earClassLoader=" + earClassLoader);
            }
            // Filtering the access to the parent classloader
            // TODO If this is always transparent, maybe we could simply remove it ?
            FilteringClassLoader filtering = new FilteringClassLoader(ejbClassLoader);
            filtering.setTransparent(true);
            filtering.start();
            ejbClassLoader = filtering;

        } catch (NamingException ne) {
            // no ear classloader => ejb application case (not ear case).
            if (isEjbJar) {

                // Create the filtering ClassLoader at the EjbJar level
                FilteringClassLoader filteringClassLoader = createFilteringClassLoader(originalFile);

                ejbClassLoader = new EjbJarClassLoader(url, filteringClassLoader);
            } else {
                ejbClassLoader = extClassLoader;
            }
            if (loaderlog.isLoggable(BasicLevel.DEBUG)) {
                loaderlog.log(BasicLevel.DEBUG, "parent Loader=" + extClassLoader);
            }
        }
        if (loaderlog.isLoggable(BasicLevel.DEBUG)) {
            loaderlog.log(BasicLevel.DEBUG, "ejbClassLoader=" + ejbClassLoader);
        }

        // Get the deployment descriptor from file
        DeploymentDesc dd = null;
        try {
            // ejb-jar file
            EjbDeploymentDescManager mgr = EjbDeploymentDescManager.getInstance();
            dd = mgr.getDeploymentDesc(url[0], ejbClassLoader, earClassLoader);
        } catch (DeploymentDescException e) {
            String err = "Cannot read the deployment descriptors '" + originalFileName + "'";
            logger.log(BasicLevel.ERROR, err);
            logger.log(BasicLevel.ERROR, "DeploymentDescException:" + e);
            throw new ServiceException(err, e);
        }

        // container name
        String cname = ModuleNamingUtils.fromFileName(originalFileName);
        if (cname == null) {
            // use displayName
            cname = "EJB container ";
            if (dd.getDisplayName() != null) {
                cname += dd.getDisplayName();
            }
        }

        // Create the Container Object
        JContainer cont = new JContainer(cname, originalFileName, workFileName, ejbClassLoader, dd, cmiService, haService,
                jaxrpcService, jmxService.getJmxServer(), resService, this.getServerProperties().getWorkDirectory());
        cont.setContainerNaming(naming);
        if (componentContextFactory == null) {
            logger.log(BasicLevel.ERROR, "componentContextFactory has not been set");
        }
        cont.setComponentContextFactory(componentContextFactory);
        cont.setTransactionManager(tm);
        cont.setPrincipalFactory(new PrincipalFactoryImpl());

        // Set the name of the ear application containing this container
        // in the case of an ear application.
        URL earUrl;
        String earRoot;
        String earFileName;
        File earFile;
        try {
            earUrl = (URL) ctx.lookup("earURL");
            earRoot = (String) ctx.lookup("earRoot");
            earFileName = URLUtils.urlToFile(earUrl).getPath();
            cont.setEarFileName(earFileName);
            earFile = new File(earRoot);
        } catch (NamingException ne) {
            earUrl = null;
            earRoot = null;
            earFile = null;
            earFileName = null;
        }

        IDeployable<?> originalDeployable;
        if (earFile != null) {
            IArchive archive = ArchiveManager.getInstance().getArchive(earFile);
            originalDeployable = DeployableHelper.getDeployable(archive);
        } else {
            IArchive archive = ArchiveManager.getInstance().getArchive(originalFile);
            originalDeployable = DeployableHelper.getDeployable(archive);
        }

        String prefix = null;
        if (isVersioningEnabled()) {
            prefix = versioningService.getPrefix(originalDeployable);
        }

        String javaEEApplicationName = null;
        try {
            javaEEApplicationName = (String) ctx.lookup("j2eeApplicationName");
        } catch (NamingException ne) {
            javaEEApplicationName = null;
        }
        cont.setJavaEEApplicationName(javaEEApplicationName);

        // unset the security if no security service
        if (securityService == null) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "EJB Container Service: working without Security Service");
            }
            cont.setSecurity(false);
        } else {
            PermissionManager permissionManager = null;
            try {
                permissionManager = new PermissionManager(dd, cont.getContextId());
                permissionManager.translateEjbDeploymentDescriptor();
                // if not in ear case, commit the policy configuration, else it
                // is done
                // by EAR service after linking all policy configuration objects
                if (!isInEar) {
                    permissionManager.commit();
                }
            } catch (Exception e) {
                logger.log(BasicLevel.ERROR, "Can't build permission manager object for the ejbjar '" + originalFileName + "'",
                        e);
            }
            cont.setPermissionManager(permissionManager);
        }

        // Give access to the WorkManager
        cont.setWorkManager(workManager);

        // Process each bean from the DD
        BeanDesc[] beans = dd.getBeanDesc();
        ArrayList<BeanFactory> bflist = new ArrayList<BeanFactory>();
        // Keep factories in order to do admin stuff later
        Map<String, BeanFactory> factoryMap = new HashMap<String, BeanFactory>();
        // Keep bean descriptors in order to do admin stuff later
        Map<String, BeanDesc> beanDescMap = new HashMap<String, BeanDesc>();
        for (BeanDesc beanDesc : beans) {
            String beanName = null;
            try {
                beanName = beanDesc.getEjbName();
                if (prefix != null) {
                    beanName = prefix + beanName;
                    beanDesc.setJndiName(prefix + beanDesc.getJndiName());
                    beanDesc.setJndiLocalName(prefix + beanDesc.getJndiLocalName());
                }
                /**
                 * Set life cycle dispatcher for this bean.
                 */
                cont.setLifeCycleDispatcher(containerLifecycleDispatcher);
                /**
                 * Add the bean to the container.
                 */
                BeanFactory bf = cont.addBean(beanDesc);

                // Sets the resource checker manager
                bf.setResourceCheckerManager(resourceCheckerManager);

                if (beanDesc instanceof SessionDesc) {
                    JSessionFactory jsf = (JSessionFactory) bf;
                    jsf.setDispatcher(monitoringDispatcher);
                    eventService.registerListener(jsf, JOnASEJBService.MONITORING_DISPATCHER_NAME);

                    // if Monitoring Settings are not defined in DD, it will
                    // take the default value
                    if (!jsf.getMonitoringSettingsDefinedInDD()) {
                        jsf.setMonitoringEnabled(monitoringEnabled);
                        jsf.setWarningThreshold(warningThreshold);
                    }
                    BeanMonitoringStatistics bms = beanMonitoringStatistics.get(jsf.getEJBName());
                    if (bms != null) {
                        jsf.setNumberOfCalls(bms.getNumberOfCalls());
                        jsf.setTotalProcessingTime(bms.getTotalProcessingTime());
                        jsf.setTotalBusinessProcessingTime(bms.getTotalBusinessProcessingTime());
                    }
                }

                bflist.add(bf);
                factoryMap.put(beanName, bf);
                beanDescMap.put(beanName, beanDesc);
            } catch (Exception e) {
                logger.log(BasicLevel.WARN, "Can't deploy bean '" + beanName + "'", e);
            }

        }
        // Init now pools of instances, in case min-pool-size have been defined.
        // This must be done once all beans have been deployed.
        for (BeanFactory bf : bflist) {
            bf.initInstancePool();
        }

        // Deployment was successful, add the bean as deployed
        containers.addElement(cont);

        // Restart Timers. Must be done when containers has been updated.
        for (BeanFactory bf : bflist) {
            bf.restartTimers();
        }

        logger.log(BasicLevel.DEBUG, "created container for " + originalFileName);

        // Do administrative stuff
        String domainName = getDomainName();
        String serverName = getJonasServerName();
        String ejbModuleON = registerCompMBeans(domainName, serverName, javaEEApplicationName, cname, cont, originalFileName,
                earUrl, dd, factoryMap, beanDescMap);
        if (prefix != null) {
            if (earRoot != null) {
                versioningService.createJNDIBindingMBeans(originalDeployable);
            } else {
                versioningService.createJNDIBindingMBeans(originalDeployable);
            }
        }

        // return Model MBean OBJECT_NAME
        return ejbModuleON;
    }

    /**
     * Creates a {@code FilteringClassLoader} for this EjbJar.
     * @param ejbjarFile the EjbJar File
     * @return a Filtering ClassLoader configured from {@code META-INF/classloader-filtering.xml} (if any).
     */
    private FilteringClassLoader createFilteringClassLoader(final File ejbjarFile) {

        // This one is sitting between the common applications loader and this ejbjar loader
        FilteringClassLoader filteringClassLoader = new FilteringClassLoader(extClassLoader);
        // Configure the loader with the provided filter definition file (if there is one)
        IArchive ejbjar = ArchiveManager.getInstance().getArchive(ejbjarFile);
        try {
            String name = "META-INF/" + FilteringClassLoader.CLASSLOADER_FILTERING_FILE;
            URL filteringDefinitionUrl = ejbjar.getResource(name);
            if (filteringDefinitionUrl != null) {
                filteringClassLoader.setDefinitionUrl(filteringDefinitionUrl);
            }
        } catch (ArchiveException ae) {
            // Ignored
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                String message = "Cannot get classloader-filtering.xml file from the archive " + ejbjar;
                logger.log(BasicLevel.DEBUG, message, ae);
            }
        } finally {
            ejbjar.close();
        }
        // Finally start the loader
        filteringClassLoader.start();
        return filteringClassLoader;
    }

    /**
     * Apply WSGen on the given file if needed.
     * @param path the path to use
     * @return the modified file or the original file if WSGen has not been launched.
     * @throws DeployerException if WSGen cannot be applied.
     */
    private String applyWSGenIfNeeded(final String path) throws DeployerException {
        // JAX-RPC service started ?
        if (jaxrpcService == null) {
            logger.log(BasicLevel.DEBUG, "The JAX-RPC service is not present, no need to call WSGen");
            return path;
        }

        // Auto WsGen enabled ?
        if (!jaxrpcService.isAutoWsGenEngaged()) {
            logger.log(BasicLevel.DEBUG, "Automatic WsGen is not enabled, no need to call WSGen");
            return path;
        }

        // Check version in manifest
        String jonasVersionWsGen = getAttributeInManifest(path, "WsGen-JOnAS-Version");
        if (Version.getNumber().equals(jonasVersionWsGen)) {
            // no changes, just continue the normal deployment process
            logger.log(BasicLevel.DEBUG, "No change: no need to call WSGen");
            return path;
        }

        try {
            IArchive archive = ArchiveManager.getInstance().getArchive(new File(path));
            IDeployable<?> deployable = DeployableHelper.getDeployable(archive);
            return jaxrpcService.applyWSGen(deployable);
        } catch (Exception e) {
            throw new DeployerException(e);
        }
    }

    /**
     * Get the Container by its file name (.xml or .jar).
     * @param fileName given file name on which
     * @return Container
     */
    public Container getContainer(final String fileName) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, fileName);
        }

        File f = null;
        String pathFile = "";
        try {
            f = new File(fileName);
            // Force pathFile with slash to avoid conflict under Windows (slash
            // and back-slash)
            pathFile = f.toURL().getPath();
        } catch (Exception e) {
            String err = "Error while trying to get canonical file '" + fileName + "'";
            logger.log(BasicLevel.ERROR, err);
            return null;
        }

        // for each container loaded
        for (Container cont : containers) {
            String contName = cont.getExternalFileName();

            // first, search abs path
            if (contName.equals(pathFile)) {
                return cont;
            }
        }

        // not found
        return null;
    }

    /**
     * Remove a JOnAS container.
     * @param cont EJB 2.1 Container to be removed
     */
    public void removeContainer(final Container cont) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, cont.getName());
        }
        // Admin code
        String domainName = getDomainName();
        String serverName = getJonasServerName();
        String javaEEApplicationName = cont.getJavaEEApplicationName();
        String moduleName = cont.getName();

        String[] beans = cont.listBeanNames();
        for (int i = 0; i < beans.length; i++) {
            String bean = beans[i];
            BeanFactory bf = cont.getBeanFactory(bean);
            if (bf instanceof JSessionFactory) {
                JSessionFactory jsf = (JSessionFactory) bf;
                BeanMonitoringStatistics bms = new BeanMonitoringStatistics(jsf.getNumberOfCalls(), jsf
                        .getTotalProcessingTime(), jsf.getTotalBusinessProcessingTime());
                beanMonitoringStatistics.put(jsf.getEJBName(), bms);
                monitoringDispatcher.removeListener(jsf);
            }
        }

        unregisterCompMBeans(domainName, serverName, javaEEApplicationName, moduleName);
        // Remove container
        cont.remove();
        containers.removeElement(cont);
        if (versioningService != null && versioningService.isVersioningEnabled()) {
            versioningService.garbageCollectJNDIBindingMBeans();
        }

        // Run the garbage collector
        Runtime.getRuntime().gc();
    }

    /**
     * @return JOnAS containers created by the EJB Service.
     */
    public Container[] listContainers() {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "size= " + containers.size());
        }

        Container[] ret = new Container[containers.size()];
        containers.copyInto(ret);
        return ret;
    }

    /**
     * Synchronized all entity bean containers.
     * @param passivate passivate instances after synchronization.
     */
    public void syncAllEntities(final boolean passivate) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }

        for (Container cont : containers) {
            cont.syncAll(true, passivate);
        }
    }

    /** ******* MBean methods ******** */

    /**
     * MBean method.
     * @return Integer Total Number of Container currently in JOnAS
     */
    public Integer getCurrentNumberOfContainer() {
        return new Integer(containers.size());
    }

    /**
     * MBean method.
     * @return Integer Total Number of Bean Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfBeanType() {
        int count = 0;
        for (JContainer cont : containers) {
            count += cont.getBeanNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method.
     * @return Integer Total Number of Bmp Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfBMPType() {
        int count = 0;
        for (JContainer cont : containers) {
            count += cont.getEntityBMPNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method.
     * @return Integer Total Number of Cmp Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfCMPType() {
        int count = 0;
        for (JContainer cont : containers) {
            count += cont.getEntityCMPNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method.
     * @return Integer Total Number of Sbf Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfSBFType() {
        int count = 0;
        for (JContainer cont : containers) {
            count += cont.getStatefulSessionNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method.
     * @return Integer Total Number of Sbl Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfSBLType() {
        int count = 0;
        for (JContainer cont : containers) {
            count += cont.getStatelessSessionNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method.
     * @return Integer Total Number of Mdb Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfMDBType() {
        int count = 0;
        for (JContainer cont : containers) {
            count += cont.getMessageDrivenNb();
        }
        return new Integer(count);
    }

    /**
     * Return the list of all loaded EJB container.
     * @return The list of deployed EJB container
     */
    public List<String> getDeployedJars() {
        List<String> al = new ArrayList<String>();
        for (Container oContainer : containers) {
            try {
                al.add((new File(oContainer.getExternalFileName())).toURL().getPath());
            } catch (Exception e) {
                // none
            }
        }
        return al;
    }

    /**
     * Remove EJB container corresponding to a .JAR file. This method is called by the J2EEServer MBean's undeployJar(fileName)
     * @param fileName name of the file containing the JAR module
     * @throws Exception when there is no such container to undeploy or when the service was unable to undeploy the container.
     */
    public void removeContainer(final String fileName) throws Exception {
        Container cont = null;
        try {
            cont = getContainer(fileName);
        } catch (Exception e) {
            String err = "Error while trying to find file '" + fileName + "'";
            logger.log(BasicLevel.ERROR, err);
            throw new Exception(err, e);
        }

        if (cont != null) {
            removeContainer(cont, false);
        } else {
            String err = "Cannot remove the non-existant container '" + fileName + "'";
            logger.log(BasicLevel.ERROR, err);
            throw new Exception(err);
        }
    }

    /**
     * Create an ejb container for the EJBs contained in a .jar (or .xml) file.
     * @param fileName Name of the file to be deployed
     * @return The ObjectName of the MBean associated to the container (to the deployed module)
     */
    public String createContainer(final String fileName) throws Exception {
        String ejbModuleObjectName = null;
        try {
            Context contctx = new ComponentContext(fileName);
            contctx.rebind("filename", fileName);
            ejbModuleObjectName = createContainer(contctx);
        } catch (Exception e) {
            throw new Exception("Cannot create Container", e);
        }
        return ejbModuleObjectName;
    }

    /**
     * Test if the specified file is already deployed (if a container is created for this jar).
     * @param fileName the name of the jar file
     * @return true if the jar was deployed, false otherwise
     */
    public Boolean isJarDeployed(final String fileName) {
        return new Boolean(isJarLoaded(fileName));
    }

    /**
     * Test if the specified jar identified with its work name is already deployed (if a container is created for this jar).
     * @param workFileName the internal name of the jar file (working copy)
     * @return true if the jar was deployed, false otherwise
     */
    public boolean isJarDeployedByWorkName(final String workFileName) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, workFileName);
        }

        // for each container loaded
        for (Container cont : containers) {
            // get name of the container (basename)
            // cont.getFileName return the internal name (working copy)
            String contName = new File(cont.getFileName()).getName();
            if (contName.equals(workFileName)) {
                return true;
            }
        }

        // not found
        return false;
    }

    // Kind of resources:
    /** DataSource Resource Type. */
    static final int DATASOURCE = 1;

    /** JMS Destination Resource. Type */
    static final int JMS_DESTINATION = 2;

    /** JMS Factory Resource Type. */
    static final int JMS_FACTORY = 3;

    /** Mail Factory Resource Type. */
    static final int MAIL_FACTORY = 4;

    /**
     * This method is used by the getXXXDependence MBean methods. It gathers the ObjectNames of the EJBs using a given resource.
     * @param name the resource name
     * @param type the resource type
     * @return the ObjectNames of all the ejb using this resource
     */
    Set<ObjectName> getDependence(final String name, final int type) {
        Set<ObjectName> resultObjectName = new HashSet<ObjectName>(); // to be
        // returned
        // by
        // this
        // method
        Set<Properties> resultProperties = new HashSet<Properties>(); // constructed
        // by
        // the
        // JContainer
        try {
            Context ctx = new InitialContext();
            ctx.lookup(name);
            ctx.close();
            // this resource is rebind
            for (JContainer cont : containers) {
                Set<Properties> depProps = null; // Dependences described as
                // a set of
                // Properties objects
                switch (type) {
                case DATASOURCE:
                    depProps = cont.getDataSourceDependence(name);
                    break;

                case JMS_DESTINATION:
                    depProps = cont.getJmsDestinationDependence(name);
                    break;

                case JMS_FACTORY:
                    depProps = cont.getJmsConnectionFactoryDependence(name);
                    break;

                case MAIL_FACTORY:
                    depProps = cont.getMailFactoryDependence(name);
                    break;

                default:
                    throw new IllegalArgumentException("Unknown type : " + type);
                }
                resultProperties.addAll(depProps);
                // change string to ObjectName
                try {
                    resultObjectName = convertToObjectNames(resultProperties);
                } catch (Exception e) {
                    logger.log(BasicLevel.ERROR, "EjbServiceImpl: Object Name Error", e);
                }
            }
        } catch (NamingException ne) {
            // unexisting ds
            resultObjectName = new HashSet<ObjectName>();
        }
        return resultObjectName;
    }

    /**
     * Converts a Set of properties to ObjectNames.
     * @param resultProperties properties to be converted
     * @return Returns a Set of ObjectNames
     */
    private Set<ObjectName> convertToObjectNames(final Set<Properties> resultProperties) {
        Set<ObjectName> resultObjectName = new HashSet<ObjectName>();
        String domainName = getDomainName();
        String serverName = getJonasServerName();
        String ejbType = null;
        String ejbName = null;
        String earFileName = null;
        String moduleName = null;
        String j2eeAppName = null;
        for (Properties item : resultProperties) {
            ObjectName ejbObjectName = null;
            // See JContainer.beansDependence() method for info about the
            // Properties object containt
            ejbType = item.getProperty("type");
            ejbName = item.getProperty("name");
            earFileName = item.getProperty("earFileName");
            // moduleName = buildEJBModuleName(fileName);
            moduleName = item.getProperty("cname");
            if (earFileName != null) {
                j2eeAppName = ModuleNamingUtils.fromFileName(earFileName);
            }

            if (ejbType.equals("ejbbmp") || ejbType.equals("ejbcmp")) {
                ejbObjectName = J2eeObjectName.getEntityBean(domainName, moduleName, serverName, j2eeAppName, ejbName);
            } else if (ejbType.equals("ejbsbf")) {
                ejbObjectName = J2eeObjectName.getStatefulSessionBean(domainName, moduleName, serverName, j2eeAppName, ejbName);
            } else if (ejbType.equals("ejbsbl")) {
                ejbObjectName = J2eeObjectName
                        .getStatelessSessionBean(domainName, moduleName, serverName, j2eeAppName, ejbName);
            } else if (ejbType.equals("ejbmdb")) {
                ejbObjectName = J2eeObjectName.getMessageDrivenBean(domainName, moduleName, serverName, j2eeAppName, ejbName);
            }
            if (ejbObjectName != null) {
                resultObjectName.add(ejbObjectName);
            }
        }
        return resultObjectName;
    }

    /**
     * MBean method.
     * @return the ObjectName of all the ejbs using this datasource.
     */
    public Set<ObjectName> getDataSourceDependence(final String dsName) {
        return getDependence(dsName, DATASOURCE);
    }

    /**
     * MBean method.
     * @return the ObjectName of all the ejb using this destination.
     */
    public Set<ObjectName> getJmsDestinationDependence(final String destName) {
        return getDependence(destName, JMS_DESTINATION);
    }

    /**
     * MBean method.
     * @return the ObjectName of all the ejb using this Connection Factory.
     */
    public Set<ObjectName> getJmsConnectionFactoryDependence(final String cfName) {
        return getDependence(cfName, JMS_FACTORY);
    }

    /**
     * MBean method.
     * @return the ObjectName of all the ejb using a given Mail Factory.
     */
    public Set<ObjectName> getMailFactoryDependence(final String mfName) {
        return getDependence(mfName, MAIL_FACTORY);
    }

    /**
     * Deploy the given ejb-jars of an ear file with the specified parent classloader (ear classloader). (This method is only
     * used for the ear applications, not for the ejb-jar applications).
     * @param ctx the context containing the configuration to deploy the ejbjars. <BR>
     *        This context contains the following parameters : <BR>
     *        - earRootUrl the root of the ear application. <BR>
     *        - earURL filename of the EAR. <BR>
     *        - earClassLoader the ear classLoader of the ear application. <BR>
     *        - ejbClassLoader the ejb classLoader for the ejbjars. <BR>
     *        - jarURLs the list of the urls of the ejb-jars to deploy. <BR>
     *        - roleNames the role names of the security-role. <BR>
     * @throws ServiceException if an error occurs during the deployment.
     */
    public void deployJars(final Context ctx) throws ServiceException {

        // Get the 6 parameters from the context
        // - earRootUrl the root of the ear application.
        // - earClassLoader the ear classLoader of the ear application.
        // - ejbClassLoader the ejb classLoader for the ejbjars.
        // - jarURLs the list of the urls of the ejb-jars to deploy.
        // - roleNames the role names of the security-role
        // - j2eeApplicationName the J2EE Application Name

        URL earRootUrl = null;
        URL earUrl = null;
        ClassLoader earClassLoader = null;
        ClassLoader ejbClassLoader = null;
        URL[] jarURLs = null;
        String[] roleNames = null;
        String j2eeApplicationName = null;
        try {
            earRootUrl = (URL) ctx.lookup("earRootUrl");
            earUrl = (URL) ctx.lookup("earUrl");
            earClassLoader = (ClassLoader) ctx.lookup("earClassLoader");
            ejbClassLoader = (ClassLoader) ctx.lookup("ejbClassLoader");
            jarURLs = (URL[]) ctx.lookup("jarURLs");
            j2eeApplicationName = (String) ctx.lookup("j2eeApplicationName");
            roleNames = (String[]) ctx.lookup("roleNames");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param :" + e.getMessage();
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err, e);
        }

        // Deploy all the ejb-jars of the ear application.
        for (int i = 0; i < jarURLs.length; i++) {

            // Get the name of an ejb-jar to deploy.
            String fileName = URLUtils.urlToFile(jarURLs[i]).getPath();
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Deploy '" + fileName + "' for the ear service.");
            }

            // The context to give for the creation of the container
            // associated to the ejb-jar.
            Context contctx = null;
            try {
                contctx = new ComponentContext(fileName);
                contctx.rebind("filename", fileName);
                contctx.rebind("earClassLoader", earClassLoader);
                contctx.rebind("ejbClassLoader", ejbClassLoader);
                contctx.rebind("earRoot", URLUtils.urlToFile(earRootUrl).getPath());
                // contctx.rebind("earFileName",
                // URLUtils.urlToFile(earUrl).getPath());
                contctx.rebind("earURL", earUrl);
                contctx.rebind("j2eeApplicationName", j2eeApplicationName);
                contctx.rebind("roleNames", roleNames);
                createContainer(contctx);
            } catch (Exception e) {
                // An ejb-jar is corrupted so undeploy all the deployed jar
                // of the ear application.
                logger.log(BasicLevel.ERROR, "Error when deploying '" + fileName + "'", e);
                logger.log(BasicLevel.ERROR, "Undeploy ejb-jar of the ear application");

                for (int j = 0; j <= i; j++) {
                    Container cont = getContainer(URLUtils.urlToFile(jarURLs[j]).getPath());
                    if (cont != null) {
                        removeContainer(cont, true);
                    } else {
                        logger.log(BasicLevel.ERROR, "Cannot remove the non-existant container '" + fileName + "'");
                    }
                }
                throw new ServiceException("Error during the deployment", e);
            }
        }
        // return ejbClassLoader;
    }

    /**
     * Undeploy the given ejb-jars of an ear file. (This method is only used for the ear applications, not for the ejb-jar
     * applications).
     * @param urls the list of the urls of the ejb-jars to undeploy.
     */
    public void unDeployJars(final URL[] urls) {
        for (int i = 0; i < urls.length; i++) {
            String fileName = URLUtils.urlToFile(urls[i]).getPath();
            Container cont = getContainer(fileName);
            if (cont != null) {
                removeContainer(cont, true);
            } else {
                logger.log(BasicLevel.ERROR, "Cannot remove the non-existant container '" + fileName + "'");
            }
        }
    }

    /**
     * Remove the specified container.
     * @param cont the container to remove.
     * @param isEarCase true if only if the removeContainer method is called in the ear case, false otherwise.
     */
    public void removeContainer(final Container cont, final boolean isEarCase) {
        if (isEarCase == (cont.getEarFileName() != null)) {
            removeContainer(cont);
        } else {
            String err = "Cannot remove container '" + cont.getName()
                    + "' it is in an ear application. You must undeploy the ear associated.";
            logger.log(BasicLevel.ERROR, err);
        }
    }

    /**
     * Make a cleanup of the cache of deployment descriptor. This method must be invoked after the ear deployment by the EAR
     * service. the deployment of an ear by .
     * @param earClassLoader the ClassLoader of the ear application to remove from the cache.
     */
    public void removeCache(final ClassLoader earClassLoader) {
        EjbDeploymentDescManager mgr = EjbDeploymentDescManager.getInstance();
        mgr.removeCache(earClassLoader);
    }

    /**
     * Test if the specified filename is already deployed or not.
     * @param fileName the name of the jar file.
     * @return true if the jar is deployed, else false.
     */
    public boolean isJarLoaded(final String fileName) {
        return (getContainer(fileName) != null);
    }

    /**
     * Check if current carol protocol choose is include into those choose during generation.
     * @param listProtocolGenerated (string with comma separator)
     * @param listCurrentProtocol (string with comma separator)
     * @return true if we have find at least one current protocol not in those generated
     */
    private static boolean checkCurrentProtocolIncludeIntoGenerated(final String listProtocolGenerated,
            final String listCurrentProtocol) {
        Protocols generated = new Protocols(listProtocolGenerated);
        Protocols current = new Protocols(listCurrentProtocol);
        return generated.isSupported(current);
    }

    /**
     * Get the attibut value in manifest file.
     * @param fileName : the file/directory containing the manifest
     * @param attributName : the attribut name
     * @return the value corresponding to the attributName if it's exist null otherwise
     */
    private static String getAttributeInManifest(final String fileName, final String attributName) {
        try {
            IArchive archive = ArchiveManager.getInstance().getArchive(new File(fileName));
            return archive.getMetadata().get(attributName);
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Cannot read Manifest: " + e);
            return null;
        }
    }

    /**
     * Check that GenIC have been applied on the given ejb-jar file If it was not done, it run GenIC against the file.
     * @param fileName given EJB-JAR file.
     * @param urls Array of URLs used as CLASSPATH during EJB compilation
     */
    public void checkGenIC(final String fileName, final URL[] urls) {
        // Detect if auto genIC is disabled or not in jonas.properties file.
        if (!autoGenIC) {
            return;
        }

        // get Carol configuration to intialize Genic Arguments
        List<String> protocols = registryService.getActiveProtocolNames();

        // Extra args are stored in autoGenICArgsList

        String[] genicArgsString = null;

        int argIndex = 0;
        // append classpath
        if (urls != null && urls.length > 0) {
            // we have a CP to use
            genicArgsString = new String[4 + autoGenICArgsList.size()];

            StringBuffer classpath = new StringBuffer();
            for (int i = 0; i < urls.length; i++) {
                // Ensure path is correct (no %20 for spaces)
                String path = URLUtils.urlToFile(urls[i]).getPath();
                classpath.append(path);
                if (i != (urls.length - 1)) {
                    classpath.append(File.pathSeparator);
                }
            }

            genicArgsString[argIndex++] = "-classpath";
            genicArgsString[argIndex++] = classpath.toString();
        } else {
            // no CP
            genicArgsString = new String[2 + autoGenICArgsList.size()];
        }

        genicArgsString[argIndex++] = "-protocols";
        String listProtocols = "";
        for (Iterator<String> i = protocols.iterator(); i.hasNext();) {
            String protocol = i.next();
            listProtocols = listProtocols + protocol;
            if (i.hasNext()) {
                listProtocols = listProtocols + ",";
            }
        }
        genicArgsString[argIndex++] = listProtocols;

        // Add the extra args
        for (String argument : autoGenICArgsList) {
            genicArgsString[argIndex++] = argument;
        }

        String jonasVersionGenic = getAttributeInManifest(fileName, "Genic-Jonas-Version");
        String listProtocolGenerated = getAttributeInManifest(fileName, "Genic-Jonas-protocols");

        boolean isCurrentProtocolIncludeIntoGenerated = checkCurrentProtocolIncludeIntoGenerated(listProtocolGenerated,
                listProtocols);

        if ((jonasVersionGenic == null) || !jonasVersionGenic.equals(Version.getNumber())
                || !isCurrentProtocolIncludeIntoGenerated) {
            // Print a little explanation before calling GenIC
            if (jonasVersionGenic == null) {
                logger.log(BasicLevel.INFO, "JOnAS version was not found in the '" + fileName
                        + "' manifest file. Auto-generating container classes...");
            } else if (!jonasVersionGenic.equals(Version.getNumber())) {
                logger.log(BasicLevel.INFO, "JOnAS version found in the '" + fileName + "' manifest file :" + jonasVersionGenic
                        + " is different of the current JOnAS version : " + Version.getNumber()
                        + ". Auto-generating container classes...");
            } else if (!isCurrentProtocolIncludeIntoGenerated) {
                logger.log(BasicLevel.INFO, "Current Carol protocol is not included in the protocols found in the '" + fileName
                        + "' manifest file. Auto-generating container classes...");
            }
            try {
                callGenic(fileName, genicArgsString);
            } catch (ServiceException e) {
                logger.log(BasicLevel.ERROR, "Cannot apply GenIC on the file '" + fileName + "' with the args '"
                        + Arrays.asList(genicArgsString) + "'.", e);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public String getContainerContextID(final String containerFileName) {
        return getContainer(containerFileName).getContextId();
    }

    // =====================================================
    // TimerHandle delegation methods
    // =====================================================

    /**
     * {@inheritDoc}
     */
    public Timer getTimer(final JTimerHandleInfo info) {

        // get the Bean TimerService
        JTimerService timerservice = getJTimerService(info);

        // Get the Timer from the list.
        Timer ret = timerservice.getTimerByTime(info.getDuration(), info.getPeriod(), info.getInfo());
        if (ret == null) {
            // The Timer should have been canceled.
            throw new NoSuchObjectLocalException("The Timer should have been canceled");
        }
        return ret;
    }

    /**
     * {@inheritDoc}
     */
    public Timer restartTimer(final JTimerHandleInfo info) {

        // get the Bean TimerService
        JTimerService timerservice = getJTimerService(info);

        // Recreate a Timer with recomputed initial duration.
        long newDuration = info.getDuration() + info.getStartTime() - System.currentTimeMillis();
        if (newDuration < MINIMUM_TIMER_DURATION) {
            newDuration = MINIMUM_TIMER_DURATION;
        }
        // assign new value.
        info.setDuration(newDuration);
        Timer timer = timerservice.createTimer(info.getDuration(), info.getPeriod(), info.getInfo());
        if (timer.getTimeRemaining() > 0) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "timer restarted");
        } else {
            TraceTimer.logger.log(BasicLevel.DEBUG, "timer terminated");
            timer.cancel();
        }
        return timer;
    }

    /**
     * @param info TimerHandle info for TimerService retrieving.
     * @return Returns the associated JTimerService instance.
     */
    private JTimerService getJTimerService(final JTimerHandleInfo info) {

        // First get the Container
        JContainer cont = (JContainer) getContainer(info.getContainerId());
        if (cont == null) {
            TraceTimer.logger.log(BasicLevel.ERROR, "Cannot get container =" + info.getContainerId());
            throw new IllegalStateException("Cannot get container");
        }

        // The the BeanFactory
        JFactory bf = (JFactory) cont.getBeanFactory(info.getBeanId());

        // Finally get the associated TimerService
        JTimerService timerservice = null;
        if (bf instanceof JEntityFactory) {

            // entity bean
            Serializable pk = info.getPk();
            JEntityFactory ef = (JEntityFactory) bf;
            Serializable pks = ef.decodePK(pk);
            if (TraceTimer.isDebug()) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "encoded PK=" + pk);
                TraceTimer.logger.log(BasicLevel.DEBUG, "decoded PK=" + pks);
            }
            JEntitySwitch es = ef.getEJB(pks);
            if (es == null) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "No entity for this pk");
                throw new NoSuchObjectLocalException("No entity for this pk");
            }
            timerservice = (JTimerService) es.getEntityTimerService();
        } else {

            // stateless session or message driven bean
            timerservice = (JTimerService) bf.getTimerService();
        }
        if (timerservice == null) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "Cannot retrieve TimerService");
            throw new IllegalStateException("Cannot retrieve TimerService");
        }
        return timerservice;
    }

    // Admin code (JMX based)
    // ---------------------
    /**
     * Register EJB Container Service MBean.
     * @param service ejb container service to manage
     * @param domainName domain name
     */
    private void registerEjbServiceMBean(final Object service, final String domainName) {
        ObjectName on = JonasObjectName.ejbService(domainName);
        jmxService.registerMBean(service, on);
    }

    /**
     * Unregister EJB Container Service MBean.
     * @param domainName domain name
     */
    private void unregisterEjbServiceMBean(final String domainName) {
        if (jmxService != null) {
            ObjectName on = JonasObjectName.ejbService(domainName);
            jmxService.unregisterMBean(on);
        }
    }

    /**
     * Register an EJBModule MBean.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     * @param container the container object corresponding to the module
     * @param fileName the module's file name
     * @param earUrl if the module is inside a ear, this is the url of the that ear
     * @return the {@link EJBModule} instance.
     */
    private EJBModule registerEJBModuleMBean(final String domainName, final String serverName, final String j2eeappName,
            final String moduleName, final JContainer container, final String fileName, final URL earUrl) {

        ObjectName ejbModuleOn = J2eeObjectName.getEJBModule(domainName, serverName, j2eeappName, moduleName);
        EJBModule ejbModuleMBean = new EJBModule(jmxService.getJmxServer(), ejbModuleOn, container, fileName, moduleName,
                j2eeappName, earUrl);
        ejbModuleMBean.setServer(J2eeObjectName.J2EEServerName(domainName, serverName));
        try {
            jmxService.registerModelMBean(ejbModuleMBean, ejbModuleOn);
        } catch (Exception e) {
            logger.log(BasicLevel.WARN, "Could not register EJBModule MBean", e);
        }
        return ejbModuleMBean;
    }

    /**
     * Registers an EntityBean JSR77 compliant MBean.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     * @param beanName the EJB name
     * @param jef {@link JEntityFactory}
     * @param persist persistency type (CMP/BMP)
     * @return the EntityBean JSR77 {@link ObjectName}
     */
    private String registerEntityBeanMBean(final String domainName, final String serverName, final String j2eeappName,
            final String moduleName, final String beanName, final JEntityFactory jef, final String persist) {

        String entityBeanON = J2eeObjectName.getEntityBeanName(domainName, moduleName, serverName, j2eeappName, beanName);
        EntityBean entityBeanMBean = new EntityBean(entityBeanON, jef, persist, jmxService);
        try {
            jmxService.registerModelMBean(entityBeanMBean, entityBeanON);
        } catch (Exception e) {
            logger.log(BasicLevel.WARN, "Could not register EntityBean MBean", e);
            return null;
        }
        return entityBeanON;
    }

    /**
     * Registers a JSR 77 compliant MBean for a Stateful EJB.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     * @param beanName the EJB name
     * @param jsf {@link JSessionFactory}
     * @return the Stateful JSR 77 MBean {@link ObjectName}
     */
    private String registerStatefulSessionBeanMBean(final String domainName, final String serverName, final String j2eeappName,
            final String moduleName, final String beanName, final JSessionFactory jsf) {

        String ssBeanON = J2eeObjectName.getStatefulSessionBeanName(domainName, moduleName, serverName, j2eeappName, beanName);
        StatefulSessionBean ssBeanMBean = new StatefulSessionBean(ssBeanON, jsf, jmxService);
        try {
            jmxService.registerModelMBean(ssBeanMBean, ssBeanON);
        } catch (Exception e) {
            logger.log(BasicLevel.WARN, "Could not register StatefulSessionBean MBean for " + beanName, e);
            return null;
        }
        return ssBeanON;
    }

    /**
     * Registers a JSR 77 compliant MBean for a Stateless EJB.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     * @param beanName the EJB name
     * @param jsf {@link JStatelessFactory}
     * @return the Stateful JSR 77 MBean {@link ObjectName}
     */
    private String registerStatelessSessionBeanMBean(final String domainName, final String serverName,
            final String j2eeappName, final String moduleName, final String beanName, final JStatelessFactory jsf) {

        String ssBeanON = J2eeObjectName.getStatelessSessionBeanName(domainName, moduleName, serverName, j2eeappName, beanName);
        StatelessSessionBean ssBeanMBean = new StatelessSessionBean(ssBeanON, jsf, jmxService);
        try {
            jmxService.registerModelMBean(ssBeanMBean, ssBeanON);
        } catch (Exception e) {
            logger.log(BasicLevel.WARN, "Could not register StatelessSessionBean MBean" + beanName, e);
            return null;
        }
        return ssBeanON;
    }

    /**
     * Registers a JSR 77 compliant MBean for an MDB.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     * @param beanName the EJB name
     * @param jsf {@link JFactory}
     * @return the MDB JSR 77 MBean {@link ObjectName}
     */
    private String registerMdbBeanMBean(final String domainName, final String serverName, final String j2eeappName,
            final String moduleName, final String beanName, final JFactory jsf) {

        String mdbBeanON = J2eeObjectName.getMessageDrivenBeanName(domainName, moduleName, serverName, j2eeappName, beanName);
        MessageDrivenBean mdbBeanMBean = new MessageDrivenBean(mdbBeanON, jsf, jmxService);
        try {
            jmxService.registerModelMBean(mdbBeanMBean, mdbBeanON);
        } catch (Exception e) {
            logger.log(BasicLevel.WARN, "Could not register MessageDrivenBean MBean" + beanName, e);
            return null;
        }
        return mdbBeanON;
    }

    /**
     * Create and register MBeans for the module and for each EJB in the module.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     * @param container the container object corresponding to the module
     * @param fileName the module's file name
     * @param earUrl if the module is inside a ear, this is the url of the that ear
     * @param dd the module's deployment descriptor
     * @param factoryMap a map containing for each bean name the {@link JFactory} object necessary to construct an EJB MBean
     * @param beanDescMap a Map containing, for each bean name its {@link BeanDesc}
     * @return the module MBean's OBJECT_NAME
     */
    private String registerCompMBeans(final String domainName, final String serverName, final String j2eeappName,
            final String moduleName, final JContainer container, final String fileName, final URL earUrl,
            final DeploymentDesc dd, final Map<String, BeanFactory> factoryMap, final Map<String, BeanDesc> beanDescMap) {

        // Create and register EJBModule MBean
        EJBModule ejbModuleMBean = registerEJBModuleMBean(domainName, serverName, j2eeappName, moduleName, container, fileName,
                earUrl);
        String ejbModuleON = J2eeObjectName.getEJBModuleName(domainName, serverName, j2eeappName, moduleName);
        ejbModuleMBean.setDeploymentDescriptor(dd.getXmlContent());
        ejbModuleMBean.setJonasDeploymentDescriptor(dd.getJOnASXmlContent());

        // Create and register the different Bean MBeans types
        // The keys if factoryMap give the bean Names
        for (String beanName : factoryMap.keySet()) {
            String beanON = null;
            BeanFactory bf = factoryMap.get(beanName);
            BeanDesc beanDesc = beanDescMap.get(beanName);
            if (beanDesc instanceof EntityBmpDesc) {
                beanON = registerEntityBeanMBean(domainName, serverName, j2eeappName, moduleName, beanName,
                        (JEntityFactory) bf, BMP);
            } else if (beanDesc instanceof EntityCmpDesc) {
                beanON = registerEntityBeanMBean(domainName, serverName, j2eeappName, moduleName, beanName,
                        (JEntityFactory) bf, CMP);
            } else if (beanDesc instanceof SessionStatefulDesc) {
                beanON = registerStatefulSessionBeanMBean(domainName, serverName, j2eeappName, moduleName, beanName,
                        (JSessionFactory) bf);
            } else if (beanDesc instanceof SessionStatelessDesc) {
                beanON = registerStatelessSessionBeanMBean(domainName, serverName, j2eeappName, moduleName, beanName,
                        (JStatelessFactory) bf);
            } else if (beanDesc instanceof MessageDrivenDesc) {
                beanON = registerMdbBeanMBean(domainName, serverName, j2eeappName, moduleName, beanName, (JFactory) bf);
            }
            ejbModuleMBean.addEjb(beanON);
        }
        return ejbModuleON;
    }

    /**
     * Unregister MBeans for the module and for each EJB in the module.
     * @param domainName the domain name
     * @param serverName the server name
     * @param j2eeappName the J2EE Application to which the module belongs or null if the module is not inside an application
     * @param moduleName the module name
     */
    private void unregisterCompMBeans(final String domainName, final String serverName, final String j2eeappName,
            final String moduleName) {
        if (jmxService != null) {
            ObjectName onEjbModule = J2eeObjectName.getEJBModule(domainName, serverName, j2eeappName, moduleName);
            MBeanServer mbeanServer = jmxService.getJmxServer();
            // try to remove MBeans corresponding to the EJBs in the module
            ObjectName onEjb = null;
            try {
                String[] onEjbs = (String[]) mbeanServer.getAttribute(onEjbModule, "ejbs");
                for (int i = 0; i < onEjbs.length; i++) {
                    onEjb = new ObjectName(onEjbs[i]);
                    jmxService.unregisterModelMBean(onEjb);
                }
            } catch (Exception e) {
                logger.log(BasicLevel.ERROR, "Could not unregister MBean " + onEjb.toString(), e);
            }

            // Unregister EJBModule MBean
            jmxService.unregisterModelMBean(onEjbModule);
        }
    }

    /**
     * Call GenIC by using a wrapper.
     * @param jarPath path of the jar file
     * @param genicArgs arguments for GenIC
     */
    private void callGenic(final String jarPath, final String[] genicArgs) {
        String[] args;
        if (genicArgs != null) {
            args = new String[genicArgs.length + 1];
            for (int i = 0; i < genicArgs.length; i++) {
                args[i] = genicArgs[i];
            }
            args[genicArgs.length] = jarPath;
        } else {
            args = new String[1];
            args[0] = jarPath;
        }

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Calling GenIC with arguments :" + Arrays.asList(args));
        }
        GenicServiceWrapper.callGenic(args);
    }

    /**
     * @param jmxService the jmxService to set
     */
    public void setJmxService(final JmxService jmxService) {
        this.jmxService = jmxService;
    }

    /**
     * @param securityService the securityService to set
     */
    public void setSecurityService(final SecurityService securityService) {
        this.securityService = securityService;
    }

    /**
     * @param transactionService the transactionService to set
     */
    public void setTransactionService(final TransactionService transactionService) {
        this.transactionService = transactionService;
    }

    /**
     * @param workManagerService the workManagerService to set
     */
    public void setWorkManagerService(final WorkManagerService workManagerService) {
        this.workManagerService = workManagerService;
    }

    /**
     * @param jaxrpcService the jaxrpcService to set
     */
    public void setWebServicesService(final IJAXRPCService jaxrpcService) {
        this.jaxrpcService = jaxrpcService;
    }

    /**
     * @param naming the naming to set
     */
    public void setNaming(final JNamingManager naming) {
        this.naming = naming;
    }

    /**
     * @param componentContextFactory the componentContextFactory to set
     */
    public void setComponentContextFactory(final JComponentContextFactory componentContextFactory) {
        this.componentContextFactory = componentContextFactory;
    }

    /**
     * @param registry the registry to set
     */
    public void setRegistry(final RegistryService registry) {
        this.registryService = registry;
    }

    /**
     * Set the reference on HA service.
     * @param haService the reference on HA service
     */
    public void setHaService(final HaService haService) {
        this.haService = haService;
    }

    /**
     * Set the reference on resource service.
     * @param resService the reference on Resource service
     */
    public void setResourceService(final ResourceService resService) {
        this.resService = resService;
    }

    /**
     * @return the reference on CMI service
     */
    public CmiService getCmiService() {
        return cmiService;
    }

    /**
     * Set the reference on CMI service.
     * @param cmiService the reference on CMI service
     */
    public void setCmiService(final CmiService cmiService) {
        this.cmiService = cmiService;
    }

    /**
     * @param deployerManager the {@link IDeployerManager} to set
     */
    public void setDeployerManager(final IDeployerManager deployerManager) {
        this.deployerManager = deployerManager;
    }

    /**
     * @param versioningService The versioning service to set.
     */
    public void setVersioningService(final VersioningService versioningService) {
        this.versioningService = versioningService;
    }

    /**
     * Sets the versioning service to null.
     */
    public void unsetVersioningService() {
        this.versioningService = null;
    }

    /**
     * @return The versioning service.
     */
    public VersioningService getVersioningService() {
        return this.versioningService;
    }

    /**
     * @return Whether versioning is enabled.
     */
    public boolean isVersioningEnabled() {
        return (versioningService != null && versioningService.isVersioningEnabled());
    }

    /**
     * @return Will EJB monitoring enabled for EJBs deployed in the future?
     */
    public boolean getMonitoringEnabled() {
        return this.monitoringEnabled;
    }

    /**
     * @param monitoringEnabled Whether EJB monitoring will enabled for EJBs deployed in the future.
     */
    public void setMonitoringEnabled(final boolean monitoringEnabled) {
        this.monitoringEnabled = monitoringEnabled;
    }

    /**
     * @return Number of milliseconds after which methods of EJBs deployed in the future will start warning.
     */
    public int getWarningThreshold() {
        return this.warningThreshold;
    }

    /**
     * @param warningThreshold Number of milliseconds after which methods of EJBs deployed in the future will start warning.
     */
    public void setWarningThreshold(final int warningThreshold) {
        if (warningThreshold < 0) {
            throw new IllegalArgumentException("warningThreshold must be positive or 0");
        }
        this.warningThreshold = warningThreshold;
    }

    /**
     * get the total number of calls on all beans of this ejb container
     * @return the total number of calls on all beans of this ejb container
     */
    @SuppressWarnings("unchecked")
    public long getNumberOfCalls() {
        long numberOfCalls = 0;
        try {
            Iterator<ObjectName> statefulBeans = jmxService.getJmxServer().queryNames(
                    new ObjectName(this.getDomainName() + ":j2eeType=StatefulSessionBean,*"), null).iterator();
            while (statefulBeans.hasNext()) {
                ObjectName on = statefulBeans.next();
                try {
                    numberOfCalls += ((Long) jmxService.getJmxServer().getAttribute(on, "numberOfCalls")).longValue();
                } catch (AttributeNotFoundException ignored) {
                    // Ignored, since TODO: EJB3 statistics
                }
            }
            Iterator<ObjectName> statelessBeans = jmxService.getJmxServer().queryNames(
                    new ObjectName(this.getDomainName() + ":j2eeType=StatelessSessionBean,*"), null).iterator();
            while (statelessBeans.hasNext()) {
                ObjectName on = statelessBeans.next();
                try {
                    numberOfCalls += ((Long) jmxService.getJmxServer().getAttribute(on, "numberOfCalls")).longValue();
                } catch (AttributeNotFoundException ignored) {
                    // Ignored, since TODO: EJB3 statistics
                }
            }
        } catch (Exception e) {
            logger.log(BasicLevel.INFO, "Failed getting attribute 'numberOfCalls'", e);
        }
        return numberOfCalls;
    }

    /**
     * get the total time spent in container + business execution for all beans of this ejb container
     * @return the total time spent in container + business execution for all beans of this ejb container
     */
    @SuppressWarnings("unchecked")
    public long getTotalProcessingTime() {
        long totalProcessingTime = 0;
        try {
            Iterator<ObjectName> statefulBeans = jmxService.getJmxServer().queryNames(
                    new ObjectName(this.getDomainName() + ":j2eeType=StatefulSessionBean,*"), null).iterator();
            while (statefulBeans.hasNext()) {
                ObjectName on = statefulBeans.next();
                try {
                    totalProcessingTime += ((Long) jmxService.getJmxServer().getAttribute(on, "totalProcessingTime"))
                            .longValue();
                } catch (AttributeNotFoundException ignored) {
                    // Ignored, since TODO: EJB3 statistics
                }
            }
            Iterator<ObjectName> statelessBeans = jmxService.getJmxServer().queryNames(
                    new ObjectName(this.getDomainName() + ":j2eeType=StatelessSessionBean,*"), null).iterator();
            while (statelessBeans.hasNext()) {
                ObjectName on = statelessBeans.next();
                try {
                    totalProcessingTime += ((Long) jmxService.getJmxServer().getAttribute(on, "totalProcessingTime"))
                            .longValue();
                } catch (AttributeNotFoundException ignored) {
                    // Ignored, since TODO: EJB3 statistics
                }
            }
        } catch (Exception e) {

            logger.log(BasicLevel.INFO, "Failed getting attribute 'totalProcessingTime'", e);
        }
        return totalProcessingTime;
    }

    /**
     * get the total time spent in business execution for all beans of this ejb container
     * @return the total time spent in business execution for all beans of this ejb container
     */
    @SuppressWarnings("unchecked")
    public long getTotalBusinessProcessingTime() {
        long totalBusinessProcessingTime = 0;
        try {

            Iterator<ObjectName> statefulBeans = jmxService.getJmxServer().queryNames(
                    new ObjectName(this.getDomainName() + ":j2eeType=StatefulSessionBean,*"), null).iterator();
            while (statefulBeans.hasNext()) {
                ObjectName on = statefulBeans.next();
                try {
                    totalBusinessProcessingTime += ((Long) jmxService.getJmxServer().getAttribute(on,
                            "totalBusinessProcessingTime")).longValue();
                } catch (AttributeNotFoundException ignored) {
                    // Ignored, since TODO: EJB3 statistics
                }
            }
            Iterator<ObjectName> statelessBeans = jmxService.getJmxServer().queryNames(
                    new ObjectName(this.getDomainName() + ":j2eeType=StatelessSessionBean,*"), null).iterator();
            while (statelessBeans.hasNext()) {
                ObjectName on = statelessBeans.next();
                try {
                    totalBusinessProcessingTime += ((Long) jmxService.getJmxServer().getAttribute(on,
                            "totalBusinessProcessingTime")).longValue();
                } catch (AttributeNotFoundException ignored) {
                    // Ignored, since TODO: EJB3 statistics
                }
            }
        } catch (Exception e) {
            logger.log(BasicLevel.INFO, "Failed getting attribute 'totalBusinessProcessingTime'", e);
        }
        return totalBusinessProcessingTime;
    }

    /**
     * get the average time spent in container + business execution for all beans of this ejb container per request
     * @return the average time spent in container + business execution for all beans of this ejb container per request
     */
    public long getAverageProcessingTime() {
        if (getNumberOfCalls() > 0) {
            return getTotalProcessingTime() / getNumberOfCalls();
        } else {
            return 0;
        }
    }

    /**
     * get the average time spent in business execution for all beans of this ejb container per request
     * @return the average time spent in business execution for all beans of this ejb container per request
     */
    public long getAverageBusinessProcessingTime() {
        if (getNumberOfCalls() > 0) {
            return getTotalBusinessProcessingTime() / getNumberOfCalls();
        } else {
            return 0;
        }
    }

    /**
     * Applies the current monitoring settings to deployed EJBs.
     */
    @SuppressWarnings("unchecked")
    public void applyMonitorSettings(final String scope) {
        Attribute monitoringEnabled = new Attribute("monitoringEnabled", new Boolean(this.monitoringEnabled));
        Attribute warningThreshold = new Attribute("warningThreshold", new Integer(this.warningThreshold));
        try {
            if ("all".equals(scope)) {
                Iterator<ObjectName> statefulBeans = jmxService.getJmxServer().queryNames(
                        new ObjectName(this.getDomainName() + ":j2eeType=StatefulSessionBean,*"), null).iterator();
                while (statefulBeans.hasNext()) {
                    ObjectName on = statefulBeans.next();
                    jmxService.getJmxServer().setAttribute(on, monitoringEnabled);
                    jmxService.getJmxServer().setAttribute(on, warningThreshold);
                }
                Iterator<ObjectName> statelessBeans = jmxService.getJmxServer().queryNames(
                        new ObjectName(this.getDomainName() + ":j2eeType=StatelessSessionBean,*"), null).iterator();
                while (statelessBeans.hasNext()) {
                    ObjectName on = statelessBeans.next();
                    jmxService.getJmxServer().setAttribute(on, monitoringEnabled);
                    jmxService.getJmxServer().setAttribute(on, warningThreshold);
                }
            } else if ("nonDD".equals(scope)) {
                Iterator<ObjectName> statefulBeans = jmxService.getJmxServer().queryNames(
                        new ObjectName(this.getDomainName() + ":j2eeType=StatefulSessionBean,*"), null).iterator();
                while (statefulBeans.hasNext()) {
                    ObjectName on = statefulBeans.next();
                    try {
                        if (!((Boolean) jmxService.getJmxServer().getAttribute(on, "monitoringSettingsDefinedInDD"))) {
                            jmxService.getJmxServer().setAttribute(on, monitoringEnabled);
                            jmxService.getJmxServer().setAttribute(on, warningThreshold);
                        }
                    } catch (AttributeNotFoundException ignored) {
                        // Ignored, since TODO: EJB3 statistics
                    }
                }
                Iterator<ObjectName> statelessBeans = jmxService.getJmxServer().queryNames(
                        new ObjectName(this.getDomainName() + ":j2eeType=StatelessSessionBean,*"), null).iterator();
                while (statelessBeans.hasNext()) {
                    ObjectName on = statelessBeans.next();
                    try {
                        if (!((Boolean) jmxService.getJmxServer().getAttribute(on, "monitoringSettingsDefinedInDD"))) {
                            jmxService.getJmxServer().setAttribute(on, monitoringEnabled);
                            jmxService.getJmxServer().setAttribute(on, warningThreshold);
                        }
                    } catch (AttributeNotFoundException ignored) {
                        // Ignored, since TODO: EJB3 statistics
                    }
                }
            }
        } catch (Exception e) {
            logger.log(BasicLevel.INFO, "Cannot apply monitor settings: " + e.getMessage(), e);
        }
    }

    /**
     * @param eventService IEventService to set.
     */
    public void setEventService(final IEventService eventService) {
        this.eventService = eventService;
    }

    /**
     * @return IEventService in use.
     */
    public IEventService getEventService() {
        return this.eventService;
    }

    /**
     * @return the resource checker manager
     */
    public IResourceCheckerManager getResourceCheckerManager() {
        return resourceCheckerManager;
    }

    /**
     * Sets the resource checker manager.
     * @param resourceCheckerManager the given instance
     */
    public void setResourceCheckerManager(final IResourceCheckerManager resourceCheckerManager) {
        this.resourceCheckerManager = resourceCheckerManager;
    }

    /**
     * Unset the resource checker manager.
     */
    public void unsetResourceCheckerManager() {
        this.resourceCheckerManager = null;
    }

    /**
     * @return work directory for ejb2s.
     */
    protected String getWorkDirectory() {
        return getServerProperties().getWorkDirectory() + File.separator + "ejb2s";
    }
}
