/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2008 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$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.resource.internal;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

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 javax.transaction.xa.XAException;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.configuration.DeploymentPlanDeployer;
import org.ow2.jonas.deployment.rar.ConnectorDesc;
import org.ow2.jonas.deployment.rar.JonasConnectorDesc;
import org.ow2.jonas.deployment.rar.wrapper.RarManagerWrapper;
import org.ow2.jonas.jmx.JmxService;
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.management.javaee.J2eeObjectName;
import org.ow2.jonas.lib.naming.ComponentContext;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.registry.RegistryService;
import org.ow2.jonas.resource.ResourceService;
import org.ow2.jonas.resource.ResourceServiceException;
import org.ow2.jonas.resource.internal.mbean.ArchiveConfigMBean;
import org.ow2.jonas.resource.internal.mbean.RarConfigMBean;
import org.ow2.jonas.service.ServiceException;
import org.ow2.jonas.tm.TransactionManager;
import org.ow2.jonas.tm.TransactionService;
import org.ow2.jonas.workmanager.WorkManagerService;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.url.URLUtils;

/**
 * JCA resource service implementation.
 *
 * @author Philippe Coq Contributor(s): JOnAS 2.4 Sebastien Chassande-Barrioz
 *         (sebastien.chassande@inrialpes.fr) JOnAS 3.0 Eric Hardesty
 *         (Eric.Hardesty@bull.com) JOnAS 4.0 Adriana Danes (JSR 77 + use of
 *         Jakarta Modeler Component :
 *         http://jakarta.apache.org/commons/modeler) Eric Hardesty (J2CA 1.5)
 *
 */
public class JOnASResourceService extends AbsServiceImpl implements
ResourceService, JOnASResourceServiceMBean {

    /**
     * Main logger.
     */
    private static Logger logger = Log.getLogger(Log.JONAS_JCA_PREFIX + ".process");

    /**
     * Management logger.
     */
    private static Logger manageLogger = Log.getLogger(Log.JONAS_JCA_PREFIX + ".management");

    /**
     * RAR deployer.
     */
    private RARDeployer rarDeployer = null;

    /**
     * List of RAR files that cannot be deployed now because
     * another RAR must be deployed first.
     */
    private ArrayList<Context> delayedRAs = new ArrayList<Context>();

    /**
     * The transaction service for this server.
     */
    private TransactionService transactionService = null;

    /**
     * The transaction manager for this server.
     */
    private TransactionManager tm = null;

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

    /**
     * List of resource names.
     */
    private List<String> resourceNames = null;

    /**
     * Associate a Rar with its jndi name.
     */
    private static Hashtable<String, Rar> jndiName2RA = new Hashtable<String, Rar>();

    /**
     * Associate a filename to an RAR object.
     */
    private static Hashtable<String, Rar> fileName2RA = new Hashtable<String, Rar>();

    // J2EE CA 1.5 objects
    /**
     * Work Manager for the Resource service.
     */
    private WorkManager workMgr = null;

    /**
     * BootstrapContext for the Resource service.
     */
    private ResourceBootstrapContext bootCtx = null;

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

    /**
     * The name of the working apps directory.
     */
    public static final String WORK_RARS_DIR = JProp.getWorkDir()
    + File.separator + "rars";

    /**
     * External class loader.
     */
    private ClassLoader extClassLoader;

    /**
     * Need a WorkManager.
     */
    private WorkManagerService workManagerService;

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

    /**
     * Registry Service.
     */
    private RegistryService registryService;

    /**
     * The JOnAS deployment plan deployer service.
     */
    private DeploymentPlanDeployer deploymentPlanDeployer = null;

    /**
     * Initial Context for Naming.
     */
    private Context ictx = null;

    /**
     * Default constructor for ResourceService.
     */
    public JOnASResourceService() {
        resourceNames = new ArrayList<String>();

        // create RAR deployer
        this.rarDeployer = new RARDeployer();
    }

    /**
     * @param validate Use a validating XML parser ?
     */
    public void setParsingwithvalidation(final boolean validate) {
        RarManagerWrapper.setParsingWithValidation(validate);
    }

    /**
     * Start the Resource service.
     * @throws ServiceException if the startup failed.
     */
    @Override
    public void doStart() throws ServiceException {
        // Get the InitialContext in an execution block
        IExecution<InitialContext> ictxGetter = new IExecution<InitialContext>() {
            public InitialContext execute() throws Exception {
                return getRegistryService().getRegistryContext();
            }
        };

        // Execute
        ExecutionResult<InitialContext> ictxResult = RunnableHelper.execute(
                getClass().getClassLoader(), ictxGetter);
        // Throw an ServiceException if needed
        if (ictxResult.hasException()) {
            logger.log(BasicLevel.ERROR,
                    "Cannot create initial context when Resource service initializing");
            throw new ServiceException(
                    "Cannot create initial context when Resource service initializing",
                    ictxResult.getException());
        }
        // Store the ref
        ictx = ictxResult.getResult();

        // Get Loader Manager
        try {
            // FIXME This will not be usable as is in OSGi, as JONAS_ROOT maybe
            // not set, and at least, it's no more the task of the LoaderManager
            // to set-up the Class hierarchy
            LoaderManager lm = LoaderManager.getInstance();
            extClassLoader = lm.getExternalLoader();
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR,
                    "Cannot get the Applications ClassLoader from RAR Container Service: "
                    + e);
            throw new ServiceException(
                    "Cannot get the Applications ClassLoader from RAR Container Service",
                    e);
        }

        // Settings on the deployer
        rarDeployer.setResourceService(this);

        // Register it
        deployerManager.register(rarDeployer);

        // finish initialization
        tm = transactionService.getTransactionManager();
        workMgr = workManagerService.getWorkManager();
        try {
            bootCtx = new ResourceBootstrapContext(workMgr, transactionService
                    .getXATerminator());
        } catch (XAException e) {
            logger.log(BasicLevel.ERROR,
                    "Unable to get an XATerminator from the TransactionService");
            throw new ServiceException(
                    "Unable to get an XATerminator from the TransactionService",
                    e);
        }

        // creates each resource
        String rarFileName = null;
        ComponentContext ctx = null;
        for (int i = 0; i < resourceNames.size(); i++) {
            rarFileName = resourceNames.get(i);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "rarFileName=" + rarFileName);
            }
            try {
                ctx = new ComponentContext(rarFileName);
                ctx.rebind("rarFileName", rarFileName);
                ctx.rebind("isInEar", Boolean.valueOf(false));
                ctx.rebind("classloader", extClassLoader);
                createResourceAdapter(ctx);
            } catch (Exception e) {
                logger.log(BasicLevel.ERROR, "JOnAS: Cannot create resource: "
                        + rarFileName + " exception: " + e);
                e.printStackTrace();
            }
        }

        jmxService.loadDescriptors(getClass().getPackage().getName(),
                getClass().getClassLoader());

        // Create and register the Resource Service MBean
        String domainName = getDomainName();
        jmxService.registerMBean(this, JonasObjectName
                .resourceService(domainName));
        // Create and register the archive configuration MBeans
        try {
            jmxService.registerModelMBean(new ArchiveConfigMBean(),
                    JonasObjectName.ArchiveConfig(domainName));
            jmxService.registerModelMBean(new RarConfigMBean(), JonasObjectName
                    .RarConfig(domainName));
        } catch (Exception e) {
            e.printStackTrace();
            logger.log(BasicLevel.WARN,
                    "ResourceService: Can't register MBeans for archive configuration"
                    + e);
            return;
        }

        try {
            deploymentPlanDeployer.deploy("jdbc-resource-adapters");
        } catch (DeployerException e) {
            logger.log(BasicLevel.ERROR, "Cannot deploy JDBC resource adapters");
        }

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

    /**
     * Stop the Resource service.
     *
     * @throws ServiceException
     *             if the stop failed.
     */
    @Override
    public void doStop() throws ServiceException {
        // Undeploy RARs
        rarDeployer.stop();

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

        if (jmxService != null) {
            // unregister resource MBeans
            // unregister Archive Config MBeans
            String domainName = getDomainName();
            jmxService.unregisterModelMBean(JonasObjectName.ArchiveConfig(domainName));
            jmxService.unregisterModelMBean(JonasObjectName.RarConfig(domainName));
            jmxService.unregisterMBean(JonasObjectName.resourceService(domainName));
        }

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

    // IMPLEMENTATION OF 'ResourceService' INTERFACE //

    /**
     * Create a new resource adapter.
     * This Resource Adapter is configured via xml files in the rar file.
     * @param ctx Context to use for deploying an RAR
     * @return Sting resource objectName
     * @throws MalformedURLException
     */
    public synchronized String createResourceAdapter(final Context ctx)
    throws ResourceServiceException, NamingException, MalformedURLException {
        String ron = createRA(ctx);
        if (ron == null) {
            if (!delayedRAs.contains(ctx)) {
                delayedRAs.add(ctx);
            }
            return null;
        }

        int count = 1;
        while (count > 0 && delayedRAs.size() > 0) {
            ArrayList<Context> processedRAs = new ArrayList<Context>();
            count = 0;
            try {
                for (Context cc : delayedRAs) {
                    String on = createRA(cc);
                    if (on != null) {
                        count++;
                        if (!processedRAs.contains(cc)) {
                            processedRAs.add(cc);
                        }
                    }
                }
            } catch (Exception e) {
                logger.log(BasicLevel.WARN, "delayed creation of RA failed:" + e);
                e.printStackTrace();
            }
            // Remove processed RAs from the list
            for (Context cc : processedRAs) {
                int index = delayedRAs.indexOf(cc);
                if (index > -1) {
                    delayedRAs.remove(index);
                }
            }
        }

        return ron;
    }

    /**
     * Create a new resource adapter. This Resource Adapter is configured via
     * xml files in the rar file
     *
     * @param ctx
     *            Context to use for deploying an RAR
     * @return Sting resource objectName or null if could not be deployed.
     * @throws MalformedURLException
     */
    private String createRA(final Context ctx) throws ResourceServiceException,
    NamingException, MalformedURLException {

        // Parameters :
        // rarFileName, isInEar, class loader and possible AltDD and earUrl
        String rarFileName;
        try {
            rarFileName = (String) ctx.lookup("rarFileName");
            ctx.rebind("deployed", Boolean.valueOf(false));
        } catch (Exception ex) {
            String err = "Error while getting parameter from context param.";
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            throw new ResourceServiceException(err, ex);
        }

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, rarFileName);
        }
        if (!rarFileName.endsWith(".rar")) {
            rarFileName += ".rar";
            ctx.rebind("rarFileName", rarFileName);
        }

        // Determine if the RAR file exists
        File f = new File(rarFileName);
        if (!f.exists()) {
            logger.log(BasicLevel.ERROR, "createResourceAdapter: " + rarFileName + " not found");
            throw new NamingException(rarFileName + " not found");
        }

        URL rarUrl = f.toURL();

        final Rar rar = new Rar(ctx, getDomainName(), getJonasServerName(),
                bootCtx, this.tm, this.jmxService);

        rar.setInitialContext(ictx);

        try {
            final JOnASResourceService resourceService = this;
            // Process the resource adapter in an execution block
            IExecution<Context> exec = new IExecution<Context>() {
                public Context execute() throws Exception {
                    return rar.processRar(getDomainName(), resourceService);
                }
            };

            // Execute
            ExecutionResult<Context> execResult = RunnableHelper.execute(
                    getClass().getClassLoader(), exec);
            // Throw an ServiceException if needed
            if (execResult.hasException()) {
                logger.log(BasicLevel.ERROR, "execResult failed:"
                        + execResult.getException());
                throw execResult.getException();
            }
            // Store the ref
            Context ctxRar = execResult.getResult();
        } catch (NullPointerException n) {
            logger.log(BasicLevel.ERROR, "NPE while processing RAR");
            n.printStackTrace();
            throw new ResourceServiceException("NPE while processing RAR", n);
        } catch (Exception ex) {
            // Exception error in processing unregister
            String err = "Error processing Rar: " + ex.getMessage();
            try {
                rar.unRegister(getDomainName());
            } catch (Exception exc) {
                err = err + "  Unregister also failed with " + exc.getMessage();
            }
            if (ex.getMessage() != null
                    && ex.getMessage().indexOf("no jonas-ra.xml") > 0) {
                throw new ResourceServiceException("", ex);
            }
            logger.log(BasicLevel.ERROR, err);
            throw new ResourceServiceException(err, ex);
        }

        boolean isDeployed = false;
        try {
            isDeployed = ((Boolean) ctx.lookup("deployed")).booleanValue();
        } catch (Exception ex) {
            String err = "Error while getting parameter(isDeployed) from context param.";
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            throw new ResourceServiceException(err, ex);
        }

        if (!isDeployed) {
            // Not deployed. Add it to the list.
            logger.log(BasicLevel.DEBUG, "Still not deployed:" + ctx);
            return null;
        }

        Vector jNames = rar.getJndinames();
        if (jNames != null) {
            for (int i = 0; i < jNames.size(); i++) {
                jndiName2RA.put((String) jNames.get(i), rar);
            }
        }

        // Processed and deployed rar, so add it to our lists
        fileName2RA.put(rarUrl.getPath(), rar);

        String onRar = null;
        try {
            onRar = (String) ctx.lookup("onRar");
        } catch (Exception ex) {
            String err = "Error while getting parameter(onRar) from context param.";
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            throw new ResourceServiceException(err, ex);
        }

        return onRar;
    }

    /**
     * Deploy the given rars of an ear file with the specified parent
     * classloader (ear classloader). (This method is only used for for ear
     * applications).
     *
     * @param ctx
     *            the context containing the configuration to deploy the rars.<BR>
     *            This context contains the following parameters :<BR> - urls
     *            the list of the urls of the rars to deploy.<BR> - earRootURL
     *            the URL of the ear application file.<BR> - earClassLoader the
     *            ear classLoader of the j2ee app.<BR> - altDDs the optional
     *            URI of deployment descriptor.<BR>
     * @throws ResourceServiceException
     *             if an error occurs during the deployment.
     */
    public void deployRars(final Context ctx) throws ResourceServiceException {

        // Gets the parameters from the context :
        // - urls the list of the urls of the rars to deploy.
        // - earRootURL the URL of the ear application file.
        // - earClassLoader the ear classLoader of the j2ee app.
        // - altDDs the optional URI of deployment descriptor.
        URL[] urls = null;
        URL earUrl = null;
        ClassLoader earClassLoader = null;
        URL[] altDDs = null;
        try {
            urls = (URL[]) ctx.lookup("urls");
            earUrl = (URL) ctx.lookup("earUrl");
            earClassLoader = (ClassLoader) ctx.lookup("earClassLoader");
            altDDs = (URL[]) ctx.lookup("altDDs");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param ";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new ResourceServiceException(err, e);
        }

        // Deploy all the rars of the ear application.
        for (int i = 0; i < urls.length; i++) {
            // Get the name of a rar to deploy.
            String fileName = URLUtils.urlToFile(urls[i]).getPath();
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Deploy rar '" + 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("rarFileName", fileName);
                contctx.rebind("isInEar", Boolean.valueOf(true));
                contctx.rebind("earUrl", earUrl);
                if (altDDs[i] != null) {
                    contctx.rebind("altDD", altDDs[i]);
                }
                contctx.rebind("classloader", earClassLoader);
                createResourceAdapter(contctx);
            } catch (Exception e) {
                // A rar is corrupted so undeploy all the deployed rar
                // of the ear application.
                logger.log(BasicLevel.ERROR, "Error when deploying '"
                        + fileName + "'");
                logger.log(BasicLevel.ERROR, e.getMessage());
                logger.log(BasicLevel.ERROR,
                "Undeploy rar of the ear application");

                for (int j = 0; j < i; j++) {
                    String rarFileName = urls[j].getFile();
                    try {
                        // Try to undeploy a rar of the ear application.
                        ComponentContext compctx = new ComponentContext(
                                rarFileName);
                        compctx.rebind("rarFileName", rarFileName);
                        compctx.rebind("isInEar", new Boolean(true));
                        contctx.rebind("earUrl", earUrl);
                        unRegisterRar(compctx);
                    } catch (Exception ex) {
                        // Cannot undeploy a rar of the ear application
                        // So there is an error message.
                        logger.log(BasicLevel.ERROR, "Error when undeploying '"
                                + rarFileName + "'");
                        logger.log(BasicLevel.ERROR, ex.getMessage());
                        logger.log(BasicLevel.ERROR,
                        "Cannot undeploy rar of the ear application");
                    }

                }
                throw new ResourceServiceException(
                        "Error during the deployment", e);
            }
        }
    }

    /**
     * Undeploy the given rars of an ear file. (This method is only used for the
     * ear applications).
     * @param urls the list of the urls of the rars to undeploy.
     * @param earUrl the URL of the associated EAR file
     */
    public void unDeployRars(final URL[] urls, final URL earUrl) {
        for (int i = 0; i < urls.length; i++) {
            String fileName = urls[i].getFile();
            if (fileName2RA.containsKey(fileName)) {
                try {
                    // Try to undeploy a rar of the ear application.
                    ComponentContext compctx = new ComponentContext(fileName);
                    compctx.rebind("rarFileName", fileName);
                    compctx.rebind("isInEar", Boolean.valueOf(true));
                    compctx.rebind("earUrl", earUrl);
                    unRegisterRar(compctx);
                } catch (Exception ex) {
                    logger.log(BasicLevel.ERROR, "Cannot undeploy resource: "
                            + fileName + " " + ex);
                }
            } else {
                logger.log(BasicLevel.ERROR,
                        "Cannot remove the non-existant rar '"
                        + fileName + "'");
            }
        }
    }

    /**
     * Unregister the resource adapter.
     * @param ctx Context to use for unregistering an RAR
     * @throws Exception error encountered
     */
    public void unRegisterRar(final Context ctx) throws Exception {
        String rarFileName;
        try {
            rarFileName = (String) ctx.lookup("rarFileName");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param.";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new ResourceServiceException(err, e);
        } catch (Exception ex) {
            String err = "Error while getting parameter from context param.";
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            throw new ResourceServiceException(err, ex);
        }

        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG,
                    "TEST Unregister MBeans for RAR in file: " + rarFileName);
        }

        String rarPath = (new File(rarFileName)).toURL().getPath();
        Rar rar = fileName2RA.get(rarPath);
        // rar.unregister clear the Rar JNDI names Vector, so we have to clone it
        // Otherwise, we won't remove any JNDI names from the jndiName2RA Map
        // Leading to strange behavior during start/stop/start sequence
        Vector jNames = (Vector) rar.getJndinames().clone();
        rar.unRegister(getDomainName());

        // Remove rars from hashtables
        for (int i = 0; i < jNames.size(); i++) {
            jndiName2RA.remove(jNames.get(i));
        }
        fileName2RA.remove(rarPath);
        resourceNames.remove(normalizePath(rarFileName));

        logger.log(BasicLevel.DEBUG, "Unregistering RAR: "
                + rarFileName);
    }

    /**
     * Return the JDBC ResourceAdapter MBean ObjectNames deployed in the current
     * server. The JDBC ResourceAdapters have a 'properties' attribute containing
     * the following properties set (not null and not empty): 'dsClass', 'URL'.
     *
     * @return The found MBean ObjectNames or null if no JDBC ResourceAdapter
     *         MBean registered for the current server in the current domain.
     * @throws Exception
     *             The ResourceAdapter MBeans checking failed.
     */
    public ObjectName[] getJDBCResourceAdapaters() throws Exception {
        ObjectName[] result = null;
        ObjectName raOns = J2eeObjectName.getResourceAdapters(getDomainName(),
                getJonasServerName());
        MBeanServer mbeanServer = jmxService.getJmxServer();
        Set<ObjectName> ons = mbeanServer.queryNames(raOns, null);
        if (ons.isEmpty()) {
            return null;
        }
        // Got ResourceAdapters for this server
        ArrayList<ObjectName> al = new ArrayList<ObjectName>();

        for (Iterator<ObjectName> it = ons.iterator(); it.hasNext();) {
            ObjectName aRaOn = it.next();
            Properties props = (Properties) mbeanServer.getAttribute(aRaOn,
            "properties");
            String dsClassValue = props.getProperty("dsClass");
            String urlValue = props.getProperty("URL");
            if (dsClassValue != null && dsClassValue.length() != 0
                    && urlValue != null && urlValue.length() != 0) {
                // This is a well configured JDBC ResourceAdapter
                al.add(aRaOn);
            }
        }
        int nbJDBCResourceAdapaters = al.size();
        if (nbJDBCResourceAdapaters == 0) {
            return null;
        } else {
            result = new ObjectName[nbJDBCResourceAdapaters];
            for (int i = 0; i < nbJDBCResourceAdapaters; i++) {
                result[i] = al.get(i);
            }
        }
        return result;
    }

    /**
     * Return the JDBC ResourceAdapter MBean OBJECT_NAME deployed in the current
     * server having the 'jndiName' attribute value equal to the given jndiName.
     *
     * @param jndiName A DataSource JNDI name we are looking for.
     * @return The found MBean OBJECT_NAME or null if none of the JDBC
     *         ResourceAdapter MBean have the given JNDI name.
     * @throws Exception
     *             The ResourceAdapter MBeans checking failed.
     */
    public String getJDBCResourceAdapater(final String jndiName)
    throws Exception {
        String result = null;
        ObjectName[] raOns = getJDBCResourceAdapaters();
        ObjectName raOn = null;
        String raJndiName = null;
        MBeanServer mbeanServer = jmxService.getJmxServer();
        if (raOns != null) {
            for (int i = 0; i < raOns.length; i++) {
                raOn = raOns[i];
                raJndiName = (String) mbeanServer.getAttribute(raOn, "jndiName");
                if (jndiName.equals(raJndiName)) {
                    // found the JDBC RA MBean having the given jndi name
                    return raOn.toString();
                }
            }
        }
        return result;
    }

    // --------------------------------------------------------------
    // MBean Methods
    // --------------------------------------------------------------

    /**
     * @return Integer Total Number of Resources available in JOnAS
     */
    public Integer getCurrentNumberOfResource() {
        return getCurrentNumberOfRars();
    }

    /**
     * @return Integer Total Number of Rars available in JOnAS
     */
    public Integer getCurrentNumberOfRars() {
        return Integer.valueOf(fileName2RA.size());
    }

    /**
     * @return the list of RAR files deployed
     */
    public List<String> getDeployedRars() {
        ArrayList<String> al = new ArrayList<String>();
        String rarFileName = null;
        Enumeration<String> keys = fileName2RA.keys();
        while (keys.hasMoreElements()) {
            rarFileName = keys.nextElement();
            try {
                al.add((new File(rarFileName)).toURL().getPath());
            } catch (Exception e) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Unable to add rarfile "
                            + rarFileName + " to arraylist");
                }
            }
        }
        return al;
    }

    /**
     * Deploy the resource adapter
     *
     * @param fileName
     *            the name of the rar file.
     * @throws Exception
     *             if unable to deploy the rar
     * @return String ObjectName of the deployed rar
     */
    public String deployRar(final String fileName)
    throws ResourceServiceException {
        Context ctx = null;
        String onRar = null;
        try {
            ctx = new ComponentContext(fileName);
            ctx.rebind("rarFileName", fileName);
            ctx.rebind("isInEar", Boolean.valueOf(false));
            ctx.rebind("classloader", extClassLoader);
            onRar = createResourceAdapter(ctx);
        } catch (Exception e) {
            String err = "Error when deploying the rar file: " + fileName;
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            e.printStackTrace();
            throw new ResourceServiceException(err, e);
        }
        return onRar;
    }

    /**
     * Test if the specified filename is already deployed or not.
     *
     * @param fileName
     *            the name of the rar file.
     * @return true if the rar is deployed, else false.
     */
    public Boolean isRarDeployed(final String fileName) {
        return Boolean.valueOf(isRarLoaded(fileName));
    }

    /**
     * Test if the specified unpack name is already deployed or not. This method
     * is defined in the ResourceService interface.
     * @param unpackName the name of the RAR file.
     * @return true if the RAR is deployed, else false.
     */
    public boolean isRarDeployedByUnpackName(final String unpackName) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "entering for unpackName= "
                    + unpackName);
        }
        /*
         * // for each rar loaded Enumeration lc = ears.elements(); while
         * (lc.hasMoreElements()) { Ear ear = (Ear) lc.nextElement(); // get
         * unpack name of the ear String deployedUnpackName = new
         * File(ear.getUnpackName()).getName() ; logger.log(BasicLevel.DEBUG,
         * "deployedUnpackName=" + deployedUnpackName);
         *
         * if (deployedUnpackName.equals(unpackName)) { return true; } // else,
         * go to the next loop } // not found
         */
        return false;
    }

    /**
     * Undeploy the resource adapter.
     * @param fileName the name of the rar file.
     * @throws Exception if not able to undeploy the rar
     */
    public void unDeployRar(final String filename) throws Exception {
        String fileName = filename;
        /*
         * We have only the name of the file, not its associated path, so we
         * look in the current directory and in the rar applications directory
         */

        boolean found = true;
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Trying to undeploy: " + fileName
                    + " with the following deployed:" + fileName2RA);
        }

        try {
            // Determine if the RAR is in list
            File f = new File(fileName);
            if (!fileName2RA.containsKey(f.toURL().getPath())) {
                found = false;
            }

            if (!found) {
                String err = "Cannot undeploy the rar '" + fileName
                + "', it is not deployed.";
                logger.log(BasicLevel.ERROR, err);
                throw new ResourceServiceException(err);
            }
        } catch (Exception ex) {
            String err = "Error trying to undeployRarMBean " + fileName;
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            throw new ResourceServiceException(err, ex);
        }

        // We've got the file, now we bind the params
        ComponentContext compctx = null;
        try {
            compctx = new ComponentContext(fileName);
            compctx.rebind("rarFileName", fileName);
            compctx.rebind("isInEar", Boolean.valueOf(false));
            // Call the function
            unRegisterRar(compctx);
        } catch (NamingException e) {
            String err = "Error when binding parameters";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new ResourceServiceException(err, e);
        } catch (Exception ex) {
            String err = "Error when unRegistering rar " + fileName;
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            ex.printStackTrace();
            throw new ResourceServiceException(err, ex);
        }

    }

    /**
     * Test if the specified filename is already deployed or not.
     * @param fileName the name of the rar file.
     * @return true if the rar is deployed, else false.
     */
    public boolean isRarLoaded(final String fileName) {

        String updateName = null;
        boolean isLoaded = false;
        try {
            if (fileName2RA.containsKey(fileName)) {
                isLoaded = true;
            }
        } catch (Exception e) {
            String err = "Cannot determine if the rar is deployed or not";
            logger.log(BasicLevel.ERROR, err);
            return false;
        }

        return isLoaded;
    }

    // --------------------------------------------------------------
    // Private Methods
    // --------------------------------------------------------------

    /**
     * Normalize the path for the platform.
     *
     * @param path
     *            the path to normalize
     * @return String normalized path
     */
    private String normalizePath(final String path) {
        String ret = null;
        if (File.separatorChar == '/') {
            ret = path.replace('\\', File.separatorChar);
        } else {
            ret = path.replace('/', File.separatorChar);
        }
        return ret;
    }

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

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

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

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

    /**
     * Sets the JOnAS deployment plan deployer service.
     * @param deploymentPlanDeployer the JOnAS deployment plan deployer service.
     */
    public void setDeploymentPlanDeployer(final DeploymentPlanDeployer deploymentPlanDeployer) {
        this.deploymentPlanDeployer = deploymentPlanDeployer;
    }

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

    /**
     * Returns the registry service.
     *
     * @return The registry service
     */
    private RegistryService getRegistryService() {
        return registryService;
    }

    /**
     * get the Rar matching the jndiNname will be used in EJB container to
     * deploy an MDB
     *
     * @param jndiName
     *            jndi name to lookup
     * @return Rar Rar object for the specified jndi name
     */
    public org.ow2.jonas.resource.Rar getRar(final String jndiName) {
        synchronized (jndiName2RA) {
            Rar ra = jndiName2RA.get(jndiName);
            return ra;
        }
    }

    /**
     * get the RAR ConnectorDesc object matching the JNDI name.
     * @param jndiName the JNDI name to lookup
     * @return ConnectorDesc matching ConnectorDesc
     */
    public ConnectorDesc getConnectorDesc(final String jndiName) {
        synchronized (jndiName2RA) {
            Rar rar = jndiName2RA.get(jndiName);
            if (rar != null) {
                return (rar.getConnectorDesc());
            }
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Can't find " + jndiName + " in "
                        + jndiName2RA);
            }
            return null;
        }
    }

    /**
     * get the RAR JonasConnectorDesc object matching the JNDI name.
     * @param jndiName the JNDI name to lookup
     * @return JonasConnectorDesc matching JonasConnectorDesc
     */
    public JonasConnectorDesc getJonasConnectorDesc(final String jndiName) {
        synchronized (jndiName2RA) {
            Rar rar = jndiName2RA.get(jndiName);
            if (rar != null) {
                return (rar.getJonasConnectorDesc());
            }
            return null;
        }
    }

    /**
     * get the ResourceAdapter matching the jndiName will be used in
     * ResourceObjectJNDIHandler class at lookup time.
     * @param jndiName to lookup ResourceObject
     * @return Object resourceObject corresponding to jndiName
     */
    public static Object getResourceObject(final String jndiName) {
        Rar ra = null;
        synchronized (jndiName2RA) {
            ra = jndiName2RA.get(jndiName);
        }
        if (ra != null) {
            return (ra.getFactory(jndiName));
        }
        return null;
    }

    /**
     * get the ResourceAdapter Connector object matching the jndiName.
     * @param jndiName String of jNDI name
     * @return String for the jndiName
     */
    public String getXmlContent(final String jndiName) {
        synchronized (jndiName2RA) {
            Rar rar = jndiName2RA.get(jndiName);
            if (rar != null) {
                return (rar.getXmlContent());
            }
            return null;
        }
    }

    /**
     * get the ConfigObj object matching the jndiName.
     * @param jndiName String of JNDI name
     * @return Object for the jndiName
     */
    public Object getConfigObject(final String jndiName) {
        Rar ra = null;
        synchronized (jndiName2RA) {
            ra = jndiName2RA.get(jndiName);
        }
        if (ra != null) {
            return (ra.getConfigObj(jndiName));
        }
        return null;
    }

}
