/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007-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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id: EasyBeansService.java 19727 2010-04-22 11:27:00Z benoitf $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ejb.easybeans;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.spi.PersistenceProvider;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.ow2.easybeans.api.EZBConfigurationExtension;
import org.ow2.easybeans.api.EZBContainer;
import org.ow2.easybeans.api.EZBServer;
import org.ow2.easybeans.api.naming.EZBNamingStrategy;
import org.ow2.easybeans.component.api.EZBComponent;
import org.ow2.easybeans.component.itf.ICmiComponent;
import org.ow2.easybeans.container.ManagementPool;
import org.ow2.easybeans.container.mdb.helper.MDBResourceAdapterHelper;
import org.ow2.easybeans.deployment.InjectionHolder;
import org.ow2.easybeans.deployment.api.EZBInjectionHolder;
import org.ow2.easybeans.jmx.MBeansHelper;
import org.ow2.easybeans.loader.EasyBeansClassLoader;
import org.ow2.easybeans.naming.interceptors.ENCManager;
import org.ow2.easybeans.osgi.archive.BundleArchiveFactory;
import org.ow2.easybeans.osgi.extension.EasyBeansOSGiExtension;
import org.ow2.easybeans.osgi.extension.OSGiBindingFactory;
import org.ow2.easybeans.persistence.PersistenceUnitManager;
import org.ow2.easybeans.persistence.api.EZBPersistenceUnitManager;
import org.ow2.easybeans.persistence.api.PersistenceXmlFileAnalyzerException;
import org.ow2.easybeans.persistence.xml.JPersistenceUnitInfo;
import org.ow2.easybeans.persistence.xml.JPersistenceUnitInfoHelper;
import org.ow2.easybeans.persistence.xml.PersistenceXmlFileAnalyzer;
import org.ow2.easybeans.proxy.binding.BindingManager;
import org.ow2.easybeans.resolver.ApplicationJNDIResolver;
import org.ow2.easybeans.resolver.api.EZBApplicationJNDIResolver;
import org.ow2.easybeans.resolver.api.EZBJNDIResolver;
import org.ow2.easybeans.security.propagation.context.SecurityCurrent;
import org.ow2.easybeans.server.Embedded;
import org.ow2.easybeans.server.EmbeddedConfigurator;
import org.ow2.easybeans.server.EmbeddedException;
import org.ow2.jonas.cmi.CmiService;
import org.ow2.jonas.ejb3.IEasyBeansService;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.jndi.checker.api.IResourceCheckerManager;
import org.ow2.jonas.lib.bootstrap.JProp;
import org.ow2.jonas.lib.bootstrap.LoaderManager;
import org.ow2.jonas.lib.execution.ExecutionResult;
import org.ow2.jonas.lib.execution.IExecution;
import org.ow2.jonas.lib.execution.RunnableHelper;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.lib.work.DeployerLog;
import org.ow2.jonas.lib.work.DeployerLogException;
import org.ow2.jonas.management.J2EEServerService;
import org.ow2.jonas.naming.JNamingManager;
import org.ow2.jonas.resource.ResourceService;
import org.ow2.jonas.service.ServiceException;
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.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.EARDeployable;
import org.ow2.util.ee.deploy.api.deployable.EJB3Deployable;
import org.ow2.util.ee.deploy.api.deployable.LibDeployable;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.event.api.IEventService;
import org.ow2.util.jmx.api.ICommonsModelerExtService;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
import org.ow2.util.pool.impl.enhanced.ReusableThreadPoolFactory;
import org.ow2.util.url.URLUtils;

/**
 * Implementation of the service that runs the EasyBeans EJB3 container.
 * @author Florent Benoit
 *         Contributors:
 *              S. Ali Tokmen (versioning)
 */
public class EasyBeansService extends AbsServiceImpl implements IEasyBeansService {

    /**
     * Name of the configuration file of EasyBeans for JOnAS.
     */
    public static final String EASYBEANS_CONFIG_FILE = "easybeans-jonas.xml";

    /**
     * Name of the configuration file of EasyBeans for JOnAS with a support of clustering.
     */
    public static final String EASYBEANS_CLUSTER_CONFIG_FILE = "easybeans-cluster-jonas.xml";

    /**
     * Limit for management pool using work manager.
     */
    private static final int MANAGEMENTPOOL_WORKMANAGER_LIMIT = 30;

    /**
     * The name of the JONAS_BASE directory.
     */
    protected static final String JONAS_BASE = JProp.getJonasBase();

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(EasyBeansService.class);

    /**
     * URL of the configuration file.
     */
    private URL xmlConfigurationURL = null;

    /**
     * URL of the extra configuration file for clustering.
     */
    private URL cmiXMLConfigurationURL = null;

    /**
     * Embedded instance.
     */
    private Embedded embedded = null;

    /**
     * Reference to the deployer.
     */
    private EasyBeansDeployer easyBeansDeployer = null;

    /**
     * Reference to the JMX service.
     */
    private JmxService jmxService = null;

    /**
     * Working directory for EJB3.
     */
    private File workEjb3sFile;

    /**
     * Reference to the MBeans library. This one register J2EEServer. It's only
     * here because we need to be started <b>after</b>.
     */
    private J2EEServerService j2eeServer = null;

    /**
     * Reference to the resource service (used by MDB only).
     * This is then an optional service.
     */
    private ResourceService resourceService = null;

    /**
     * External Classloader.
     */
    private ClassLoader extClassLoader;

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

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

    /**
     * Reference to the CMI service.
     */
    private CmiService cmiService = null;

    /**
     * OSGi Bundle context.
     */
    private BundleContext bundleContext = null;

    /**
     * Reference to the Naming Manager of JOnAS.
     */
    private static JNamingManager namingManager = null;

    /**
     * OSGi Service Registration.
     */
    private ServiceRegistration embeddedServiceRegistration = null;

    /**
     * OSGi bundle factory.
     */
    private BundleArchiveFactory bundleArchiveFactory = null;

    /**
     * OSGi BindingFactory (used to register beans as services).
     */
    private OSGiBindingFactory bindingFactory = null;

    /**
     * The EventService.
     */
    private IEventService eventService = null;

    /**
     * The CommonsModelerExtService.
     */
    private ICommonsModelerExtService commonsModelerExtService = null;

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

    /**
     * <code>true</code> if cmi is configured.
     */
    private boolean cmiConfigured = false;

    /**
     * Work Manager.
     */
    private WorkManagerService workManagerService = null;


    /**
     * Persistence Providers.
     */
    private List<PersistenceProvider> persistenceProviders = null;

    /**
     * Constructor in OSGi mode. It provides the bundle context.
     * @param bundleContext the given bundle context.
     */
    public EasyBeansService(final BundleContext bundleContext) {
        this.bundleContext = bundleContext;
        this.persistenceProviders = new ArrayList<PersistenceProvider>();

        // Create the EasyBeans deployer
        easyBeansDeployer = new EasyBeansDeployer();
        // Create the EasyBeans Embedded
        embedded = new Embedded();
    }

    /**
     * Abstract start-up method to be implemented by sub-classes.
     * @throws ServiceException service start-up failed
     */
    @Override
    protected void doStart() throws ServiceException {
        initWorkingDirectory();

        // EasyBeans should use the JOnAS domain name and server name
        MBeansHelper.setDomainName(jmxService.getDomainName());
        MBeansHelper.setServerName(jmxService.getJonasServerName());


        // register mbeans-descriptors
        jmxService.loadDescriptors(getClass().getPackage().getName(), getClass().getClassLoader());

        // Register MBean
        try {
            jmxService.registerModelMBean(this, JonasObjectName.ejb3Service(getDomainName()));
        } catch (Exception e) {
            logger.warn("Cannot register MBean for EJB3 service", e);
        }

        File configurationFile = new File(JONAS_BASE + File.separator + "conf" + File.separator + EASYBEANS_CONFIG_FILE);
        if (!configurationFile.exists()) {
            throw new ServiceException("The configuration file '" + configurationFile + "' was not found in the classloader");
        }

        // Get URL
        xmlConfigurationURL = URLUtils.fileToURL(configurationFile);

        // Clustering requires an extra configuration
        if (cmiService != null && cmiService.isStarted() && !cmiConfigured) {
            // Get URL
            cmiXMLConfigurationURL = bundleContext.getBundle().getResource(EASYBEANS_CLUSTER_CONFIG_FILE);
            if (cmiXMLConfigurationURL == null) {
                throw new ServiceException("The configuration file '" + EASYBEANS_CLUSTER_CONFIG_FILE
                        + "' was not found in the classloader");
            }
            cmiConfigured = true;
        }

        // get apps ClassLoader
        try {
            LoaderManager lm = LoaderManager.getInstance();
            extClassLoader = lm.getExternalLoader();
        } catch (Exception e) {
            //logger.error("Cannot get the Applications ClassLoader from EAR Container Service: " + e);
            throw new ServiceException("Cannot get the Applications ClassLoader from EAR Container Service", e);
        }

        // Create Finder for Resource Adapter
        JOnASResourceAdapterFinder resourceAdapterFinder = new JOnASResourceAdapterFinder();
        resourceAdapterFinder.setEasyBeansService(this);

        try {
            // Set value
            MDBResourceAdapterHelper.setResourceAdapterFinder(resourceAdapterFinder);

            // Init EasyBeans ENC Manager
            // TODO : Get the InterceptorClass if it has already be set
            ENCManager.setInterceptorClass(JOnASENCInterceptor.class);
        } catch (IllegalStateException e) {
            // Do nothing
            logger.debug("Interceptor class may have already be initialized before", e);
        }

        // Sets the JOnAS 5 Security wrapper.
        try {
            SecurityCurrent.setSecurityCurrent(new JOnASSecurityCurrent());
        } catch (IllegalStateException e) {
            logger.debug("SecurityCurrent may have already be initialized before", e);
        }

        // Configure some dialects.
        configureExtraDialects();


       // Sets the Management Pool
       ManagementPool managementPool = new ManagementPool(ReusableThreadPoolFactory.createWorkManagerThreadPool(workManagerService.getWorkManager(), MANAGEMENTPOOL_WORKMANAGER_LIMIT));
       embedded.setManagementThreadPool(managementPool);


        // Provides a Map of instances to be injected
        final Map<String, Object> context = new HashMap<String, Object>();
        context.put("event-service", this.eventService);
        context.put("modeler-service", this.commonsModelerExtService);

        // Start the EasyBeans server in an execution block
        IExecution<Void> startExec = new IExecution<Void>() {
            public Void execute() throws EmbeddedException {
                List<URL> configurations = new ArrayList<URL>();

                // Load core components ...
                URL coreComponentsURL = Embedded.class.getResource("easybeans-core.xml");
                configurations.add(coreComponentsURL);
                // ... and the JOnAS XML configuration
                configurations.add(xmlConfigurationURL);

                if (cmiXMLConfigurationURL != null) {
                    // Add the cmi component to the given embedded instance
                    configurations.add(cmiXMLConfigurationURL);
                }

                // Initialize the instance with the set of configuration files
                EmbeddedConfigurator.init(embedded, configurations, context);

                // Do not load the easybeans' core components during startup  (already added)
                embedded.getServerConfig().setAddEmbeddedComponents(false);

                // Start it
                embedded.start();
                return null;
            }
        };

        // Execute
        ExecutionResult<Void> startExecResult = RunnableHelper.execute(getClass().getClassLoader(), startExec);

        // Throw an ServiceException if needed
        if (startExecResult.hasException()) {
            logger.error("Cannot start the EasyBeans server", startExecResult.getException());
            throw new ServiceException("Cannot start the EasyBeans Server", startExecResult.getException());
        }

        /**
         * Enable OSGi extension of EasyBeans if there is a bundlecontext
         */
        if (bundleContext != null) {
            IExecution<Void> addOSGiExtension = new IExecution<Void>() {
                public Void execute() throws EmbeddedException {
                    // Add extension factory
                    EasyBeansOSGiExtension extension = new EasyBeansOSGiExtension();
                    extension.setBundleContext(bundleContext);
                    embedded.getServerConfig().addExtensionFactory(extension);

                    ArchiveManager am = ArchiveManager.getInstance();
                    bundleArchiveFactory = new BundleArchiveFactory();
                    am.addFactory(bundleArchiveFactory);

                    // Add OSGi BF
                    // The EjbJars will now be exposed as OSGi services
                    bindingFactory = new OSGiBindingFactory();
                    BindingManager.getInstance().registerFactory(bindingFactory);

                    return null;
                }
            };

            // Execute
            ExecutionResult<Void> addOSGiExtensionResult = RunnableHelper
                    .execute(getClass().getClassLoader(), addOSGiExtension);
            // Throw an ServiceException if needed
            if (addOSGiExtensionResult.hasException()) {
                logger.error("Cannot start the EasyBeans server", startExecResult.getException());
                throw new ServiceException("Cannot start the EasyBeans Server", startExecResult.getException());
            }
        }

        // Register the OSGi service if the server is RUNNING
        if (j2eeServer.isRunning()) {
            registerEmbeddedService();
        }

        // Set the deployer
        easyBeansDeployer.setEmbedded(embedded);
        easyBeansDeployer.setServerProperties(getServerProperties());
        easyBeansDeployer.setAppsClassLoader(extClassLoader);
        easyBeansDeployer.setWorkDirectory(getWorkDirectory());

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

    }

    /**
     * Abstract method for service stopping to be implemented by sub-classes.
     * @throws ServiceException service stopping failed
     */
    @Override
    protected void doStop() throws ServiceException {

        // Unregister MBean
        try {
            jmxService.unregisterModelMBean(JonasObjectName.ejb3Service(getDomainName()));
        } catch (Exception e) {
            logger.debug("Cannot unregister MBean for EJB3 service", e);
        }

        // Undeploy EJB3s
        easyBeansDeployer.stop();

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

        // Remove OSGi extension
        if (bundleContext != null) {
            IExecution<Void> removeOSGiExtension = new IExecution<Void>() {
                public Void execute() throws EmbeddedException {
                    if (bundleArchiveFactory != null) {
                        ArchiveManager am = ArchiveManager.getInstance();
                        am.removeFactory(bundleArchiveFactory);
                        bundleArchiveFactory = null;
                    }

                    // Unregister service
                    if (embeddedServiceRegistration != null) {
                        embeddedServiceRegistration.unregister();
                        embeddedServiceRegistration = null;
                    }

                    // Do not forget to unregister the OSGi BF
                    BindingManager.getInstance().unregisterFactory(bindingFactory);

                    return null;
                }
            };

            // Execute
            ExecutionResult<Void> removeOSGiExtensionResult = RunnableHelper.execute(getClass().getClassLoader(),
                    removeOSGiExtension);
            // Throw an ServiceException if needed
            if (removeOSGiExtensionResult.hasException()) {
                throw new ServiceException("Cannot stop EasyBeans", removeOSGiExtensionResult.getException());
            }

        }

        // Stop the EasyBeans server
        if (embedded.isStarted()) {
            try {
                // Stop the EasyBeans server in an execution block
                IExecution<Void> stopExec = new IExecution<Void>() {
                    public Void execute() throws EmbeddedException {
                        embedded.stop();
                        return null;
                    }
                };

                // Execute
                ExecutionResult<Void> stopExecResult = RunnableHelper.execute(getClass().getClassLoader(), stopExec);

                // Throw an ServiceException if needed
                if (stopExecResult.hasException()) {
                    throw new EmbeddedException(stopExecResult.getException());
                }

            } catch (EmbeddedException e) {
                throw new ServiceException("Cannot stop the EasyBeans component", e);
            }
        }
    }

    /**
     * Configure some dialects that may be used with some databases used by JOnAS.
     */
    @SuppressWarnings("unchecked")
    protected void configureExtraDialects() {
        // Add Hibernate dialect
        try {
            Class<?> hibernateDialectClass = Embedded.class.getClassLoader().loadClass("org.hibernate.dialect.DialectFactory");
            Field mapper = hibernateDialectClass.getDeclaredField("MAPPERS");
            mapper.setAccessible(true);
            Map<String, Object> map = null;
            map = (Map<String, Object>) mapper.get(null);
            Class<?> versionInsensitiveMapperClass = Embedded.class.getClassLoader().loadClass(
                    "org.hibernate.dialect.DialectFactory$VersionInsensitiveMapper");
            Constructor<?> c = versionInsensitiveMapperClass.getConstructor(String.class);
            Object dialect = c.newInstance("org.hibernate.dialect.Oracle9iDialect");
            map.put("Oracle9i Enterprise Edition", dialect);
            mapper.setAccessible(false);
        } catch (Exception e) {
            logger.debug("Cannot configure some dialects used by Hibernate", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public EZBServer getEasyBeansServer() {
        return embedded;
    }

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

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

    /**
     * Returns the NamingManger.
     * @return the NamingManger
     */
    public static JNamingManager getNamingManager() {
        return namingManager;
    }

    /**
     * Sets the NamingManger.
     * @param naming the NamingManger to set
     */
    public void setNamingManager(final JNamingManager naming) {
        namingManager = naming;
    }

    /**
     * @param resourceService the resourceService to set
     */
    public void setResourceService(final ResourceService resourceService) {
        this.resourceService = resourceService;
    }

    /**
     * @return the resourceService
     */
    public ResourceService getResourceService() {
        return resourceService;
    }

    /**
     * Set a reference to the CMI service.
     * @param cmiService a reference to the CMI service
     */
    public void setCmiService(final CmiService cmiService) {
        if (!cmiConfigured) {
            cmiXMLConfigurationURL = bundleContext.getBundle().getResource(EASYBEANS_CLUSTER_CONFIG_FILE);
            if (cmiXMLConfigurationURL == null) {
                throw new ServiceException("The configuration file '" + EASYBEANS_CLUSTER_CONFIG_FILE
                        + "' was not found in the classloader");
            }
            // Start the EasyBeans server in an execution block
            IExecution<Void> startExec = new IExecution<Void>() {
                public Void execute() throws Exception {
                    List<URL> configurations = new ArrayList<URL>();;
                    configurations.add(cmiXMLConfigurationURL);
                 // Provides a Map of instances to be injected
                    final Map<String, Object> context = new HashMap<String, Object>();
                    context.put("event-service", eventService);
                    context.put("config", embedded.getServerConfig());
                    // Initialize the instance with the set of configuration files
                    EmbeddedConfigurator.configure(embedded, configurations, context);
                    /**
                     * Start cmi component.
                     */
                    EZBComponent cmiComponent = embedded.getComponent(ICmiComponent.class);
                    cmiComponent.init();
                    cmiComponent.start();
                    return null;
                }
            };
            // Execute
            ExecutionResult<Void> startExecResult = RunnableHelper.execute(getClass().getClassLoader(), startExec);

            // Throw a ServiceException if needed
            if (startExecResult.hasException()) {
                logger.error("Cannot reconfigure the EasyBeans server", startExecResult.getException());
                throw new ServiceException("Cannot reconfigure the EasyBeans Server", startExecResult.getException());
            }
            cmiConfigured = true;
        }
        this.cmiService = cmiService;
    }

    /**
     * Return a reference to the CMI service.
     * @return a reference to the CMI service
     */
    public CmiService getCmiService() {
        return cmiService;
    }

    /**
     * Adds the given container.
     * @param ejbContainer the EJB3 bundle container to add
     */
    public void addContainer(final EZBContainer ejbContainer) {
        embedded.addContainer(ejbContainer);
    }

    /**
     * Remove the given container.
     * @param ejbContainer the given container
     */
    public void removeContainer(final EZBContainer ejbContainer) {
        embedded.removeContainer(ejbContainer);
    }

    /**
     * Create working directory for EJB3.
     */
    protected void initWorkingDirectory() {
        if (workEjb3sFile == null) {
            // Create $JONAS_BASE/work/ejb3s directory file
            workEjb3sFile = new File(getWorkDirectory() + File.separator + getServerProperties().getServerName());
            workEjb3sFile.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(workEjb3sFile.getPath() + File.separator + getServerProperties().getServerName() + ".log");

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

        try {
            // Create the logger
            deployerLog = new DeployerLog(fileLog);
            easyBeansDeployer.setDeployerLog(deployerLog);
            CleanTask cleanTask = new EJB3CleanTask(this, deployerLog);

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

    /**
     * Test if the specified unpack name is already deployed or not. This method
     * is defined in the {@link IEasyBeansService} interface.
     * @param unpackName the name of the EJB3 file.
     * @return true if the EJB3 is deployed, else false.
     */
    public boolean isEJB3DeployedByWorkName(final String unpackName) {
        Map<URL, EJB3Deployable> ejb3s = easyBeansDeployer.getEJB3s();
        for (EJB3Deployable ejb3Deployable : ejb3s.values()) {
            try {
                File unpackedFile = URLUtils.urlToFile(ejb3Deployable.getArchive().getURL());
                if (unpackName.equals(unpackedFile.getName())) {
                    return true;
                }
            } catch (ArchiveException e) {
                logger.debug("Cannot retrieve the name of the unpacked ear {0}", unpackName);
            }

        }
        return false;
    }

    /**
     * Register Embedded as an OSGi service.
     */
    public void registerEmbeddedService() {
        if (embeddedServiceRegistration == null) {
            embeddedServiceRegistration = bundleContext.registerService(Embedded.class.getName(), embedded, null);
        }
    }

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

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

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

    /**
     * @param j2eeServer the j2eeServer to set
     */
    public void setJ2EEServer(final J2EEServerService j2eeServer) {
        this.j2eeServer = j2eeServer;
    }

    /**
     * Add a new extension to Embedded, that will be used for the next EZBContainer creation.
     * @param extension Configuration extension to be added
     */
    public void addConfigurationExtension(final EZBConfigurationExtension extension) {
        embedded.getServerConfig().addExtensionFactory(extension);
    }

    /**
     * Disengage the given extension
     * @param extension
     */
    public void removeConfigurationExtension(final EZBConfigurationExtension extension) {
        // TODO add a removeExtensionFactory method
        //embedded.getServerConfig().removeExtensionFactory(extension);
    }

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

    /**
     * @param commonsModelerExtService the CommonsModelerExtService to set
     */
    public void setCommonsModelerExtService(final ICommonsModelerExtService commonsModelerExtService) {
        this.commonsModelerExtService = commonsModelerExtService;
    }

    /**
     * Allow to build a classloader that provide JPA classtransformers and bytecode modifications.
     * @param urls the array of URLs to use
     * @param parentClassLoader the parent classloader
     * @return a classloader.
     */
    public ClassLoader buildByteCodeEnhancementClassLoader(final URL[] urls, final ClassLoader parentClassLoader) {
        return new EasyBeansClassLoader(urls, parentClassLoader);
    }

    /**
     * @param persistenceUnitManager the Persistence Unit Manager (if any)
     * @param jndiResolver the JNDI resolver (if any)
     * @return a new Injection holder.
     */
    public EZBInjectionHolder buildInjectionHolder(final EZBPersistenceUnitManager persistenceUnitManager, final EZBJNDIResolver jndiResolver) {
        InjectionHolder injectionHolder = new InjectionHolder();
        injectionHolder.setPersistenceUnitManager(persistenceUnitManager);
        injectionHolder.setJNDIResolver(jndiResolver);
        return injectionHolder;
    }

    /**
     * @return a new JNDI application resolver.
     */
    public EZBApplicationJNDIResolver buildApplicationJNDIResolver() {
        return new ApplicationJNDIResolver();
    }

    /**
     * Gets the persistence unit manager for the given EAR and classloader.
     *
     * @param earDeployable  the ear deployable
     * @param appClassLoader the classloader used as deployable
     * @return the given persistence unit manager
     */
    public EZBPersistenceUnitManager getPersistenceUnitManager(final EARDeployable earDeployable,
                                                                  final ClassLoader appClassLoader) throws PersistenceXmlFileAnalyzerException {
        // Analyze libraries to detect persistence archive (only once for now
        // and for all libraries)
        // Get libraries of this EAR
        List<LibDeployable> libs = earDeployable.getLibDeployables();
        PersistenceUnitManager persistenceUnitManager = null;
        for (LibDeployable lib : libs) {
            PersistenceUnitManager builtPersistenceUnitManager = null;
            try {
                JPersistenceUnitInfo[] persistenceUnitInfos =
                        PersistenceXmlFileAnalyzer.analyzePersistenceXmlFile(lib.getArchive());
                if (persistenceUnitInfos != null) {
                    builtPersistenceUnitManager =
                            PersistenceXmlFileAnalyzer.loadPersistenceProvider(persistenceUnitInfos, appClassLoader);
                }
            } catch (PersistenceXmlFileAnalyzerException e) {
                throw new IllegalStateException("Failure when analyzing the persistence.xml file", e);
            }

            // Existing manager and new manager found
            if (persistenceUnitManager != null) {
                if (builtPersistenceUnitManager != null) {
                    // Add the persistence unit infos to the existing
                    // persistence unit manager
                    persistenceUnitManager.addExtraPersistenceUnitInfos(builtPersistenceUnitManager.getPersistenceUnitInfos());
                }
            } else {
                // New persistence manager use the built manager
                persistenceUnitManager = builtPersistenceUnitManager;
            }
        }
        return persistenceUnitManager;
    }

    /**
     * Gets the persistence unit manager for the given archive and classloader.
     * @param archive  the archive
     * @param classLoader the classloader used to analyze persistence
     * @return the given persistence unit manager
     */
    public EZBPersistenceUnitManager getPersistenceUnitManager(final IArchive archive,
                                                               final ClassLoader classLoader) throws PersistenceXmlFileAnalyzerException {
        PersistenceUnitManager persistenceUnitManager = null;
        JPersistenceUnitInfo[] persistenceUnitInfos = PersistenceXmlFileAnalyzer.analyzePersistenceXmlFile(archive);
        if (persistenceUnitInfos != null) {
            persistenceUnitManager = PersistenceXmlFileAnalyzer.loadPersistenceProvider(persistenceUnitInfos, classLoader);
        }
        return persistenceUnitManager;

    }

    /**
     * Build a new Strategy for the given prefix and the old strategy.
     * @param prefix the given prefix
     * @param oldNamingStrategy the strategy
     * @return the new strategy
     */
    public EZBNamingStrategy getNamingStrategy(final String prefix, final EZBNamingStrategy oldNamingStrategy) {
        return new PrefixedNamingStrategy(prefix, oldNamingStrategy);
    }

    /**
     * @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;
    }

    /**
     * Sets the work manager service instance.
     * @param workManagerService the given instance
     */
    public void setWorkManagerService(final WorkManagerService workManagerService) {
        this.workManagerService = workManagerService;
    }


    /**
     * Remove the given persistence provider.
     * @param persistenceProvider the persistence provider
     */
    public synchronized void removePersistenceProvider(final PersistenceProvider persistenceProvider) {
        this.persistenceProviders.remove(persistenceProvider);
        notifyDefaultPersistenceProvider();
    }

    /**
     * Adds the given persistence provider.
     * @param persistenceProvider the persistence provider
     */
    public synchronized void addPersistenceProvider(final PersistenceProvider persistenceProvider) {
        this.persistenceProviders.add(persistenceProvider);
        notifyDefaultPersistenceProvider();
    }



    /**
     * Notify EasyBeans with the new default Persistence Provider.
     */
    private void notifyDefaultPersistenceProvider() {
        JPersistenceUnitInfo defaultPersistenceunitInfo = new JPersistenceUnitInfo();
        // Update the default persistence provider if there is one
        if (persistenceProviders.size() > 0) {
            defaultPersistenceunitInfo.setPersistenceProviderClassName(persistenceProviders.get(0).getClass().getName());
            defaultPersistenceunitInfo.setPersistenceProvider(persistenceProviders.get(0));
        }
        JPersistenceUnitInfoHelper.setDefaultPersistenceunitInfo(defaultPersistenceunitInfo);
    }

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

}
