/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2010 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: Rar.java 19800 2010-04-30 10:07:59Z durieuxp $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.resource.internal;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import java.util.jar.JarEntry;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.resource.Referenceable;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ManagedConnectionFactory;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.ResourceAdapterAssociation;
import javax.resource.spi.ResourceAllocationException;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.deployment.ejb.ActivationConfigPropertyDesc;
import org.ow2.jonas.deployment.rar.AdminobjectDesc;
import org.ow2.jonas.deployment.rar.AuthenticationMechanismDesc;
import org.ow2.jonas.deployment.rar.ConfigPropertyDesc;
import org.ow2.jonas.deployment.rar.ConnectionDefinitionDesc;
import org.ow2.jonas.deployment.rar.ConnectorDesc;
import org.ow2.jonas.deployment.rar.InboundResourceadapterDesc;
import org.ow2.jonas.deployment.rar.JonasActivationspecDesc;
import org.ow2.jonas.deployment.rar.JonasAdminobjectDesc;
import org.ow2.jonas.deployment.rar.JonasConnectionDefinitionDesc;
import org.ow2.jonas.deployment.rar.JonasConnectorDesc;
import org.ow2.jonas.deployment.rar.MessageadapterDesc;
import org.ow2.jonas.deployment.rar.MessagelistenerDesc;
import org.ow2.jonas.deployment.rar.OutboundResourceadapterDesc;
import org.ow2.jonas.deployment.rar.RarDeploymentDesc;
import org.ow2.jonas.deployment.rar.RequiredConfigPropertyDesc;
import org.ow2.jonas.deployment.rar.ResourceadapterDesc;
import org.ow2.jonas.deployment.rar.TmConfigPropertyDesc;
import org.ow2.jonas.deployment.rar.lib.RarDeploymentDescManager;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.bootstrap.JProp;
import org.ow2.jonas.lib.bootstrap.loader.JClassLoader;
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.util.JJarFile;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.lib.util.ModuleNamingUtils;
import org.ow2.jonas.resource.ResourceServiceException;
import org.ow2.jonas.resource.internal.cm.ConnectionManagerImpl;
import org.ow2.jonas.resource.internal.cm.ConnectionManagerPoolParams;
import org.ow2.jonas.resource.internal.mbean.JCAActivationSpec;
import org.ow2.jonas.resource.internal.mbean.JCAAdminObject;
import org.ow2.jonas.resource.internal.mbean.JCAResource;
import org.ow2.jonas.resource.internal.mbean.ResourceAdapterModule;
import org.ow2.jonas.service.ServiceException;
import org.ow2.jonas.tm.TransactionManager;
import org.ow2.util.url.URLUtils;

/**
 * Rar object.
 * @author Eric Hardesty
 */
public class Rar implements org.ow2.jonas.resource.Rar {

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

    /**
     * Pool infomation logger
     */
    private static Logger poolLogger = Log.getLogger(Log.JONAS_JCA_PREFIX + ".pool");

    /**
     * Config property setter logger
     */
    private static Logger setterLogger = Log.getLogger(Log.JONAS_JCA_PREFIX + ".setters");

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

    String rarFileName = null;
    ClassLoader curLoader = null;
    boolean isInEar;
    URL earUrl = null;
    Context rCtx = null;
    String jDomain = null;
    String jServer = null;
    Vector jndinames = new Vector();

    String lnkJndiName = "";
    String lnkRarFilename = "";

    JCAResource jcaResourceMBean = null;
    String JCAResourceName = null;

    // J2EE CA 1.5 objects
    private ResourceBootstrapContext bootCtx = null;

    // Properties for inits

    // JCA resource service configuration parameters
    public static final String CLASS = "jonas.service.resource.class";
    public static final String JNDI_NAME = "jndiname";
    public static final String RAR_FILENAME = "rarfilename";
    public static final String LNK_JNDI_NAME = "lnkjndiname";
    public static final String LNK_RAR_FILENAME = "lnkrarfilename";
    public static final String OBJ_TYPE = "objtype";
    public static final String RESOURCE_LIST = "jonas.service.resource.resources";

    public static final int DEF_WRK_THREADS = 5;
    public static final int DEF_EXEC_TIME = 0;

    public static final String JCD = "JCD";
    public static final String JAS = "JAS";
    public static final String JAO = "JAO";

    public String objectName = null;
    public String pathName = null;
    public ResourceAdapter resAdp = null;
    public ConnectorDesc raConn = null;
    public JonasConnectorDesc lnkJonasConn = null;
    public JonasConnectorDesc jonasConn = null;

    public String xmlContent = null;
    public String jonasXmlContent = null;


    private class ConfigObj {
        public String type;
        public int offset;
        public String jndiName;
        public String rarFilename;
        public String lnkJndiName;
        public String lnkRarFilename;
        public String interfaceStr;
        public String classStr;
        public Object factory;
        public ConnectionManager cm;
        public boolean basicPass;
        public boolean defaultAS;
        public List reqConfigProps;
        public ConfigObj(final String fType, final int off, final String jndi, final String fName,
                          final String intStr, final String clsStr, final Object fact) {
            type = fType;
            offset = off;
            jndiName = jndi;
            rarFilename = fName;
            interfaceStr = intStr;
            classStr = clsStr;
            factory = fact;
            cm = null;
            reqConfigProps = null;
            lnkJndiName = "";
            lnkRarFilename = "";
            defaultAS = false;
        }
    }

    /**
     * Hold list of default ActivationSpecs
     */
    private static Vector defaultAS = new Vector();

    private Hashtable cfgObjs = new Hashtable();

    /**
     * The transaction manager in the server
     */
    private TransactionManager tm = null;

    /** JMX Service */
    private JmxService jmx = null;

    /**
     * JNDI Context
     */
    private Context ictx = null;

    /**
     * Resource Utility factory
     */
    private ResourceUtility ru = null;

    /**
     * JOnAS Generic RA 
     */
    private boolean jonasGeneric = false;

    /**
     * - Get the loggers
     * - Get the global jndi context
     * - Get the list of the resource adapters. The list is reachable in the
     * -  context parameter under the name RESOURCE_LIST.
     * - Get the transaction manager into the jndi
     * - Set the XML validation property
     */
    public Rar() {
        curLoader = Thread.currentThread().getContextClassLoader();

        ru = new ResourceUtility(null, logger, setterLogger, manageLogger);
    }

    /**
     * - Get the loggers
     * - Get the global jndi context
     * - Get the list of the resource adapters. The list is reachable in the
     * -  context parameter under the name RESOURCE_LIST.
     * - Get the transaction manager into the jndi
     * - Set the XML validation property
     */
    public Rar(final Context ctx,
               final String jDom,
               final String jServ,
               final ResourceBootstrapContext btCtx,
               final TransactionManager txManager,
               final JmxService jmxService) {

        try {
            rarFileName = (String) ctx.lookup("rarFileName");
            isInEar = ((Boolean) ctx.lookup("isInEar")).booleanValue();
            if (isInEar) {
                earUrl = (URL) ctx.lookup("earUrl");
            }
            curLoader = (ClassLoader) ctx.lookup("classloader");
        } 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);
        }

        bootCtx = btCtx;
        rCtx = ctx;
        jDomain = jDom;
        jServer = jServ;

        tm = txManager;
        jmx = jmxService;

        ru = new ResourceUtility(jmx, logger, setterLogger, manageLogger);
    }

    /**
     * @param tm the {@link TransactionManager} to set
     */
    public void setTransactionManager(final TransactionManager tm) {
        this.tm = tm;
    }

    /**
     * @param jmx the {@link JmxService} to set
     */
    public void setJMXService(final JmxService jmx) {
        this.jmx = jmx;
    }

    /**
     * @param context the initial {@link Context} to set
     */
    public void setInitialContext(final Context context) {
        this.ictx = context;
    }

    /**
     * Process the resource adapter.  This Resource Adapter is configured via
     * xml files in the rar file
     */
    public Context processRar(final String domainName, final JOnASResourceService rserv) throws Exception {

        String onRar = null;
        // Get the ra.xml and jonas-ra.xml descriptions
        RarDeploymentDesc radesc = RarDeploymentDescManager.getInstance(rCtx);
        ConnectorDesc conn = radesc.getConnectorDesc();
        JonasConnectorDesc jConn = radesc.getJonasConnectorDesc();
        xmlContent = radesc.getXmlContent();
        jonasXmlContent = radesc.getJOnASXmlContent();
        ConnectionManager cm = null;
        ManagedConnectionFactory mcf = null;

        if (conn == null && jConn == null) {
            logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName
                                         + ") must be a valid RAR file.");
            throw new Exception("resource input file incorrect");
        }
        logger.log(BasicLevel.DEBUG, "Process Resource " + rarFileName);

        // Figure out if this Rar is a generic jonas rar
        // maybe we should do more checking ?
        if (rarFileName.indexOf("JOnAS_jdbc") > 0) {
            jonasGeneric = true;
            logger.log(BasicLevel.INFO, rarFileName + " is generic");
        }

        // Check if rarlink is specified.
        String rarVal = jConn.getRarlink();
        if (rarVal != null && rarVal.length() > 0) {
            // rarlink is specified.
            // If it has been processed, then get the correct
            // Connector object and continue.
            // if it has not been processed, then add this rar to the
            // deferred list and it will be processed at end of the rar files.
            lnkJndiName = rarVal;
            conn = rserv.getConnectorDesc(lnkJndiName);
            if (conn == null) {
                return rCtx;
            }
            ConfigObj co = (ConfigObj) rserv.getConfigObject(lnkJndiName);
            if (co == null) {
                logger.log(BasicLevel.ERROR, "ConfigObject " + lnkJndiName + "  not found.");
                throw new Exception("ConfigObject not found");
            }
            lnkRarFilename = co.rarFilename;
            xmlContent = rserv.getXmlContent(lnkJndiName);
            // Get the config parameters
            lnkJonasConn = rserv.getJonasConnectorDesc(lnkJndiName);
        } else if (conn == null) {
            // No rarlink specified.
            // make sure that the connector object is valid.
            logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName
                    + ") is not valid.");
            throw new Exception("resource input file incorrect: no ra.xml file");
        }
        bldSecurityTable(lnkJonasConn, jConn);
        raConn = conn;
        jonasConn = jConn;

        // Setup the global pool parameters
        ConnectionManagerPoolParams cmpp =
                ru.configurePoolParams(jConn.getPoolParamsDesc(),
                                       jConn.getJdbcConnParamsDesc(),
                                       null);

        // Process the RAR file adding each of the jar files into the classloader and if the
        // native-lib is defined then place all other files into that directory.

        extractJars(rarFileName, jConn, rserv.getRarsWorkDirectory());

        // Process the config values
        ResourceadapterDesc ra = conn.getResourceadapterDesc();

        // Check Authentication Mechanisms, must if some exist then BasicPassword
        // must be present
        boolean basicPass = true;
        List authList = ra.getAuthenticationMechanismList();
        if (authList != null && authList.size() > 0) {
            basicPass = false;
            for (Iterator i = authList.iterator(); i.hasNext();) {
                AuthenticationMechanismDesc am = (AuthenticationMechanismDesc) i.next();
                if (am.getAuthenticationMechanismType().equalsIgnoreCase("BasicPassword")) {
                    basicPass = true;
                    break;
                }
            }
            if (!basicPass) {
                logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName + ") doesn't contain an AuthenticationMechanismType that is supported by JOnAS(BasicPassword).");
                throw new Exception("No AuthenticationMechanismType that is supported by JOnAS(BasicPassword).");
            }
        }

        ConfigPropertyDesc [] cfgRaJonas = null;

        String logEnabled = null;
        if (jConn.getLogEnabled() != null) {
            logEnabled = jConn.getLogEnabled().trim();
        }
        String logTopic = null;
        if (jConn.getLogTopic() != null) {
            logTopic = jConn.getLogTopic().trim();
        }

        Referenceable cf = null;
        String jndiName = null;
        String mcfc = null;
        ConfigObj cObj = null;

        if (lnkJonasConn != null) {
            cfgRaJonas = ru.buildConfigProperty(ra.getConfigPropertyList(),
                                                jConn.getJonasConfigPropertyList(),
                                                lnkJonasConn.getJonasConfigPropertyList());
        } else {
            cfgRaJonas = ru.buildConfigProperty(ra.getConfigPropertyList(),
                                                jConn.getJonasConfigPropertyList(),
                                                null);
        }

        Properties tmProp = new Properties();
        if (jConn.getTmParamsDesc() != null) {
            List tmParams = jConn.getTmParamsDesc().getTmConfigPropertyList();
            if (tmParams != null) {
                for (Iterator i = tmParams.iterator(); i.hasNext();) {
                    TmConfigPropertyDesc tpd = (TmConfigPropertyDesc) i.next();
                    String tpdVal = tpd.getTmConfigPropertyValue();
                    if (tpdVal == null) {
                        tpdVal = "";
                    }
                    tmProp.setProperty(tpd.getTmConfigPropertyName(), tpdVal);
                }
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "TM Properties: " + tmProp);
                }
            }
        }

        String specVersion = conn.getSpecVersion();
        if (specVersion.equals("1.0")) {
            if (!jConn.isSetup()) {
                logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName
                        + ") must be a valid RAR file, there must be jonas-ra.xml file.");
                throw new Exception("resource input file incorrect: no jonas-ra.xml file");
            }
            jndiName = jConn.getJndiName().trim();
            logger.log(BasicLevel.DEBUG, jndiName);
            onRar = registerRarMBean(cfgRaJonas, specVersion, jndiName, domainName, null, rarVal);
            cm = ru.createConnectionManager(ra.getTransactionSupport(), tm,
                                            logger, poolLogger, jndiName, jonasGeneric);
            // Only need to build an MCF for a 1.0 resource adapter
            mcfc = ra.getManagedconnectionfactoryClass();
            mcf = (ManagedConnectionFactory) ru.processMCF(cmpp, curLoader, rarFileName,
                                                           mcfc,
                                                           jndiName, logEnabled, logTopic,
                                                           cfgRaJonas, resAdp);
            ((ConnectionManagerImpl) cm).setResourceAdapter(mcf, cmpp);

            cf = (Referenceable) mcf.createConnectionFactory(cm);

            // Add entry to list of configured objects
            cObj = new ConfigObj(JCD, 0, jndiName, rarFileName, null, mcfc, cf);
            cObj.cm = cm;
            cObj.basicPass = basicPass;

            cfgObjs.put(jndiName, cObj);

            jndinames.add(jndiName);

            ru.registerMBean(cf, jndiName, onRar, rarFileName, conn, jonasConn, JCD, 0,
                             jcaResourceMBean, JCAResourceName, jDomain, jServer, ictx,
                             buildProp(cfgRaJonas), "", (ConnectionManagerImpl)cm);
            ((ConnectionManagerImpl) cm).setXAName(ru.getJcaMcfName(jndiName));
            ((ConnectionManagerImpl) cm).registerXAResource(tmProp);

        } else if (conn.getSpecVersion().equals("1.5")) {
            String raStr = conn.getResourceadapterDesc().getResourceadapterClass().trim();
            boolean isResAdapt = false;
            logger.log(BasicLevel.DEBUG, "Starting deployment of " + rarFileName);
            if (raStr != null && raStr.length() > 0) {
                isResAdapt = true;

                // Create an instance of the RA and start it
                // --------------------------------------------------

                final String adapterClassname = raStr;
                final ConfigPropertyDesc[] configuration = cfgRaJonas;

                IExecution<ResourceAdapter> exec = new IExecution<ResourceAdapter>() {
                    public ResourceAdapter execute() throws Exception {

                        // Instantiate the resource adapter class
                        Class<? extends ResourceAdapter> adapterClass = curLoader.loadClass(adapterClassname)
                                                                                 .asSubclass(ResourceAdapter.class);
                        ResourceAdapter adapter = adapterClass.newInstance();

                        // Inject values
                        ru.processSetters(adapterClass, adapter, rarFileName, configuration);

                        // Call ResourceAdapter start method
                        adapter.start(bootCtx);

                        return adapter;
                    }
                };

                // Execute within the specified ClassLoader
                ExecutionResult<ResourceAdapter> execResult = RunnableHelper.execute(curLoader, exec);

                // Manage possible errors
                if (execResult.hasException()) {
                    logger.log(BasicLevel.ERROR, "Rar: Error from resource (" + rarFileName + ") start method.");
                    throw new Exception("Error from start method. ", execResult.getException());
                }

                // Store managed RA instance
                resAdp = execResult.getResult();

            }

            onRar = registerRarMBean(cfgRaJonas, specVersion, null, domainName, conn.getResourceadapter().getResourceadapterClass(), rarVal);

            // Loop thru ConnectionDefinition(outbound) factories
            OutboundResourceadapterDesc outRa = ra.getOutboundResourceadapterDesc();
            List cdList = null;
            if (outRa != null) {
                cdList = outRa.getConnectionDefinitionList();
            }
            ConnectionDefinitionDesc conDef = null;
            JonasConnectionDefinitionDesc jConDef = null;
            String id = null;
            int idOffset = -1;
            if (cdList != null) {
                if (cdList.size() > 0 &&  !jConn.isSetup()) {
                    logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName
                            + ") must be a valid RAR file, there must be jonas-ra.xml file.");
                    throw new Exception("resource input file incorrect: no jonas-ra.xml file");
                }
                for (int cd = 0; cd < cdList.size(); cd++) {
                    conDef = (ConnectionDefinitionDesc) cdList.get(cd);
                    id = conDef.getId();
                    idOffset++;
                    jConDef = (JonasConnectionDefinitionDesc)
                               ru.getJonasXML(jConn, id, idOffset, JCD);


                    if (jConDef.getLogEnabled() != null) {
                        logEnabled = jConDef.getLogEnabled().trim();
                    }
                    if (jConDef.getLogTopic() != null) {
                        logTopic = jConDef.getLogTopic().trim();
                    }
                    ConfigPropertyDesc [] cfgCdDesc =
                                 ru.buildConfigProperty(conDef.getConfigPropertyList(),
                                                        jConDef.getJonasConfigPropertyList(),
                                                        null);
                    ConnectionManagerPoolParams pool =
                        ru.configurePoolParams(jConDef.getPoolParamsDesc(),
                                               jConDef.getJdbcConnParamsDesc(),
                                               cmpp);
                    jndiName = jConDef.getJndiName().trim();
                    cm = ru.createConnectionManager(outRa.getTransactionSupport(), tm,
                                                    logger, poolLogger, jndiName, false);

                    mcfc = conDef.getManagedconnectionfactoryClass();
                    mcf = (ManagedConnectionFactory)
                        ru.processMCF(pool, curLoader, rarFileName,
                                                   mcfc, jndiName, logEnabled, logTopic,
                                                   cfgCdDesc, resAdp);
                    ((ConnectionManagerImpl) cm).setResourceAdapter(mcf, pool);

                    cf = (Referenceable) mcf.createConnectionFactory(cm);

                    // Add entry to list of configured objects
                    cObj = new ConfigObj(JCD, idOffset, jndiName, rarFileName, null, mcfc, cf);
                    cObj.cm = cm;
                    cObj.basicPass = basicPass;

                    cfgObjs.put(jndiName, cObj);

                    jndinames.add(jndiName);

                    String desc = "";
                    List descList = jConDef.getDescriptionList();
                    if (descList != null) {
                        for (int i = 0; i < descList.size(); i++) {
                            String tmp = (String) descList.get(i);
                            desc = desc + tmp;
                        }
                    }
                    ru.registerMBean(cf, jndiName, onRar, rarFileName, conn, jonasConn, JCD, idOffset,
                            jcaResourceMBean, JCAResourceName, jDomain, jServer, ictx,
                            buildProp(cfgCdDesc), desc, (ConnectionManagerImpl)cm);

                    ((ConnectionManagerImpl) cm).setXAName(ru.getJcaMcfName(jndiName));
                    ((ConnectionManagerImpl) cm).registerXAResource(tmProp);
                }
            }

            // We only want to do anything with Inbound or Adminobjects if the Resourceadapter
            // class was specified, otherwise it is a 1.0 RAR with a 1.5 ra.xml
            if (isResAdapt) {
                // Loop thru Messagelisteners (inbound) factories
                InboundResourceadapterDesc inAdapt = ra.getInboundResourceadapterDesc();
                MessageadapterDesc msgAdapt = null;
                List mlList = null;
                if (inAdapt != null) {
                    msgAdapt = inAdapt.getMessageadapterDesc();
                    mlList = null;
                    if (msgAdapt != null) {
                        mlList = msgAdapt.getMessagelistenerList();
                    }
                }
                MessagelistenerDesc msgList = null;
                JonasActivationspecDesc jAct = null;
                id = null;
                idOffset = -1;
                if (mlList != null) {
                    if (mlList.size() > 0 && !jConn.isSetup()) {
                        logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName
                                + ") must be a valid RAR file, there must be jonas-ra.xml file.");
                        throw new Exception("resource input file incorrect: no jonas-ra.xml file");
                    }
                    for (int ml = 0; ml < mlList.size(); ml++) {
                        msgList = (MessagelistenerDesc) mlList.get(ml);
                        id = msgList.getId();
                        idOffset++;
                        jAct = (JonasActivationspecDesc)
                                ru.getJonasXML(jConn, id, idOffset, JAS);

                        processJAS(rarFileName, msgList, jAct, idOffset);
                    }
                }

                // Loop thru Adminobjects
                List aoList = ra.getAdminobjectList();
                AdminobjectDesc admObj = null;
                JonasAdminobjectDesc jAObj = null;
                id = null;
                idOffset = -1;
                if (aoList != null) {
                    if (aoList.size() > 0 && !jConn.isSetup()) {
                        logger.log(BasicLevel.ERROR, "aoList = " + aoList);
                        logger.log(BasicLevel.ERROR, "Rar.processRar: Resource (" + rarFileName
                                + ") must be a valid RAR file, there must be jonas-ra.xml file.");
                        throw new Exception("resource input file incorrect: no jonas-ra.xml file");
                    }
                    for (int ao = 0; ao < aoList.size(); ao++) {
                        admObj = (AdminobjectDesc) aoList.get(ao);
                        id = admObj.getId();
                        idOffset++;
                        jAObj = (JonasAdminobjectDesc)
                                 ru.getJonasXML(jConn, id, idOffset, JAO);

                        ConfigPropertyDesc [] cfgCdDesc =
                                     ru.buildConfigProperty(admObj.getConfigPropertyList(),
                                                            jAObj.getJonasConfigPropertyList(),
                                                            null);

                        processJAO(rarFileName, admObj, jAObj, cfgCdDesc, idOffset);
                    }
                }
            }
        } else {
            logger.log(BasicLevel.ERROR, "ResourceService.createRA: Resource ("
                                         + rarFileName
                                         + ") must be specify a valid specification of 1.0 or 1.5.");
            throw new Exception("resource input file incorrect: invalid specification support(only 1.0 & 1.5 are valid)");
        }

        logger.log(BasicLevel.INFO, new File(rarFileName).getName() + " available");
        try {
            rCtx.rebind("onRar", onRar);
            rCtx.rebind("deployed", new Boolean(true));
        } catch (Exception ex) {
            String err = "Error while getting parameter from context param.";
            logger.log(BasicLevel.ERROR, err + ex.getMessage());
            throw new ResourceServiceException(err, ex);
        }
        return rCtx;
    }



    /**
     * Register the Rar.
     * @param cfgRaJonas ConfigPropertyDesc []
     * @param specVersion String
     * @param resourceAdapterClassname ra classname (only for 1.5 connectors)
     * @return String jndiName of the Rar
     * @throws Exception thrown
     */
    private String registerRarMBean(final ConfigPropertyDesc [] cfgRaJonas, final String specVersion
            , final String jndiName, final String domainName
            , final String resourceAdapterClassname, final String rarLink) throws Exception {

        // --------------------------
        // Register MBeans cf. JSR 77
        // --------------------------

        // The current server name.
        // Maybe it should be received as a method parameter (similar to domainName).
        String serverName = jServer;

        String raName = null; // ResourceAdapter name
        Properties props = buildProp(cfgRaJonas);

        // MBean resources registering
        URL rarURL = (new File(rarFileName)).toURL();
        String jmxRaName = rarURL.getFile().replace(':', '|');

        // ResourceAdapterModule MBean
        // ---------------------------
        String appName = null;
        if (isInEar) {
            appName = ModuleNamingUtils.fromURL(earUrl);
        }

        // ResourceAdapter Module name
        String resourceAdapterModuleName = buildModuleName(jmxRaName, appName);

        ObjectName onResourceAdapterModule =
            J2eeObjectName.getResourceAdapterModule(jDomain,
                    jServer,
                    appName,
                    resourceAdapterModuleName);

        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "ObjectName created for ResourceAdapterModule: " + onResourceAdapterModule.toString());
        }

        ResourceAdapterModule raModuleMBean = new ResourceAdapterModule(onResourceAdapterModule,
                isInEar, earUrl);
        raModuleMBean.setFileName(jmxRaName);
        raModuleMBean.setRarURL(rarURL);
        raModuleMBean.setDeploymentDescriptor(xmlContent);
        raModuleMBean.setJonasDeploymentDescriptor(jonasXmlContent);
        raModuleMBean.setServer(J2eeObjectName.J2EEServerName(domainName, serverName));
        jmx.registerModelMBean(raModuleMBean, onResourceAdapterModule);
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "ResourceAdapterModule MBean created");
        }

        // ResourceAdapter MBean
        // ---------------------
        raName = resourceAdapterModuleName;
        ObjectName onResourceAdapter = J2eeObjectName.getResourceAdapter(jDomain,
                resourceAdapterModuleName,
                appName,
                jServer,
                raName);
        org.ow2.jonas.resource.internal.mbean.ResourceAdapter raMBean =
            new org.ow2.jonas.resource.internal.mbean.ResourceAdapter(onResourceAdapter.toString(),
                    props,
                    jndiName,
                    jmxRaName,
                    isInEar,
                    earUrl,
                    specVersion,
                    rarLink);
        raMBean.setResourceAdapterClassname(resourceAdapterClassname);
        jmx.registerModelMBean(raMBean, onResourceAdapter);
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "ResourceAdapter ManagedBean created");
        }

        // Update the ResourceAdapterModule MBean with the ResourceAdapter MBean's OBJECT_NAME
        raModuleMBean.setResourceAdapter(onResourceAdapter.toString());
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "ResourceAdapterModule ManagedBean updated");
        }

        // JCA Resource implemented by this ResourceAdapter
        // ------------------------------------------------
        JCAResourceName = raName; // TO BE IMPLEMENTED
        ObjectName onJCAResource =
            J2eeObjectName.getJCAResource(jDomain,
                    jServer,
                    raName,
                    JCAResourceName);
        jcaResourceMBean = new JCAResource(onJCAResource.toString());
        jmx.registerModelMBean(jcaResourceMBean, onJCAResource);
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "JCAResource J2EEResource created");
        }

        // Update the ResourceAdapter MBean with the JCAResource MBean's OBJECT_NAME
        raMBean.setJcaResource(onJCAResource.toString());
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "JCAResource J2EEResource updated");
        }

        return onResourceAdapterModule.toString();
    }


    /**
     * Unregister the resource adapter.
     *
     * @throws Exception thrown
     */
    public void unRegister(final String domainName) throws Exception {

        for (int i = 0; i < jndinames.size(); i++) {
            String jName = (String) jndinames.get(i);
            ConfigObj co = (ConfigObj) cfgObjs.get(jName);
            try {
                // Unbind from jndi
                ictx.unbind(jName);
            } catch (NamingException ex) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "ResourceService: cannot unbind jndiname of "
                            + jName + " for ResourceAdapter "
                            + rarFileName, ex);
                }
                throw new ServiceException("Cannot unregister", ex);
            }

            if (co.defaultAS) {
                removeDefaultAS(jName);
            }
            if (co != null) {  // Add for now, take out for final
                if (co.cm != null) {
                    // The connection manger must stop managing the RA
                    ((ConnectionManagerImpl) co.cm).cleanResourceAdapter();
                }
            }

        }
        cfgObjs.clear();
        jndinames.clear();

        // --------------------------
        // unregister MBeans cf. JSR 77
        // --------------------------
        // MBean resourcesource registering
        String jmxRaName = (new File(rarFileName)).toURL().getFile().replace(':', '|');

        // Construct ResourceAdapterModule MBean name
        // ------------------------------------------
        String appName = null;
        if (isInEar) {
            appName = ModuleNamingUtils.fromURL(earUrl);
        }
        // ResourceAdapter Module name
        String resourceAdapaterModuleName = buildModuleName(jmxRaName, appName);
        ObjectName onResourceAdapterModule =
            J2eeObjectName.getResourceAdapterModule(jDomain,
                    jServer,
                    appName,
                    resourceAdapaterModuleName);
        MBeanServer mbeanServer = jmx.getJmxServer();
        try {
            String[] resourceAdapterNames = (String[]) mbeanServer.getAttribute(onResourceAdapterModule, "resourceAdapters");

            // Unregister ResourceAdapterModule MBean
            jmx.unregisterModelMBean(onResourceAdapterModule);

            // Unregister ResourceAdapters
            for (int i = 0; i < resourceAdapterNames.length; i++) {
                String resourceAdapterName = resourceAdapterNames[i];
                ObjectName onResourceAdapater = new ObjectName(resourceAdapterName);
                // Determine this ResourceAdapter's JCAResource
                String jcaResourceName = (String) mbeanServer.getAttribute(onResourceAdapater, "jcaResource");
                // Update the list of resources in the J2EEServer MBean with the JCAResource MBean's OBJECT_NAME
                jmx.unregisterModelMBean(onResourceAdapater);
                ObjectName onJCAResource = new ObjectName(jcaResourceName);
                String [] connectionFactoriesNames = (String []) mbeanServer.getAttribute(onJCAResource, "connectionFactories");
                // Unregister the JCA ConnectionFactories of this JCAResource
                for (int j = 0; j < connectionFactoriesNames.length; j++) {
                    String connectionFactoryName = connectionFactoriesNames[j];
                    ObjectName onJCAConnectionFactory = new ObjectName(connectionFactoryName);
                    String managedConnectionFactoryName = (String) mbeanServer.getAttribute(onJCAConnectionFactory, "managedConnectionFactory");
                    jmx.unregisterModelMBean(onJCAConnectionFactory);
                    jmx.unregisterModelMBean(new ObjectName(managedConnectionFactoryName));
                }
                String [] adminObjectNames = (String []) mbeanServer.getAttribute(onJCAResource, "adminObjects");
                // Unregister the JCA AdminObjects of this JCAResource
                for (int j = 0; j < adminObjectNames.length; j++) {
                    String adminObjectName = adminObjectNames[j];
                    ObjectName onJCAAdminObject = new ObjectName(adminObjectName);
                    jmx.unregisterModelMBean(onJCAAdminObject);
                }
                String [] activationSpecNames = (String []) mbeanServer.getAttribute(onJCAResource, "activationSpecs");
                // Unregister the JCA ActivationSpecs of this JCAResource
                for (int j = 0; j < activationSpecNames.length; j++) {
                    String activationSpecName = activationSpecNames[j];
                    ObjectName onJCAActivationSpec = new ObjectName(activationSpecName);
                    jmx.unregisterModelMBean(onJCAActivationSpec);
                }
                jmx.unregisterModelMBean(onJCAResource);
            }
        } catch (MalformedObjectNameException ma) {
            logger.log(BasicLevel.ERROR, "Cannot cleanly unregister RAR: ", ma);
        } catch (MBeanRegistrationException mr) {
            logger.log(BasicLevel.ERROR, "Cannot cleanly unregister RAR: ", mr);
        } catch (InstanceNotFoundException infe) {
            logger.log(BasicLevel.ERROR, "Cannot cleanly unregister RAR: ", infe);
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Cannot cleanly unregister RAR: ", e);
        }

        //   Call Resourceadapter stop
        if (resAdp != null) {
            // Stops the ResourceAdapter in an execution block
            IExecution<Void> exec = new IExecution<Void>() {
                public Void execute() throws Exception, Error {
                    resAdp.stop();
                    return null;
                }
            };

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

            // Throw an ServiceException if needed
            if (result.hasException()) {
                throw result.getException();
            }
        }

    }

    //--------------------------------------------------------------
    //           Other Method
    //--------------------------------------------------------------

    /**
     * get the ConfigObj matching the jndiname specified
     *
     * @param jndiName String of jndi name
     * @return Object ConfigObj associated with the jndi name
     */
    public Object getConfigObj(final String jndiName) {
        synchronized (cfgObjs) {
            ConfigObj co = (ConfigObj) cfgObjs.get(jndiName);
            return co;
        }
    }

    /**
     * get the ResourceAdapter Connector object for this Rar
     *
     * @return ConnectorDesc associated resource adapter connector object
     */
    public ConnectorDesc getConnectorDesc() {
        return raConn;
    }

    /**
     * set the ResourceAdapter Connector object for this Rar
     *
     * @param cd ConnectorDesc to associate with
     */
    public void setConnectorDesc(final ConnectorDesc cd) {
        raConn = cd;
    }

    /**
     * get the Xml Content for this Rar
     *
     * @return String Xml content of this RAR
     */
    public String getXmlContent() {
        return xmlContent;
    }

    /**
     * get the JonasConnector object for this Rar
     *
     * @return JonasConnectorDesc associated JonasConnector
     */
    public JonasConnectorDesc getJonasConnectorDesc() {
        return jonasConn;
    }

    /**
     * set the JonasConnector object for this Rar
     *
     * @param jcd JonasConnectorDesc to associate with
     */
    public void setJonasConnectorDesc(final JonasConnectorDesc jcd) {
        jonasConn = jcd;
    }

    public Object getFactory(final String jndiname) {
        ConfigObj co = (ConfigObj) cfgObjs.get(jndiname);
        return (co == null ? null : co.factory );
    }

    public String getInterface(final String jndiname) {
        ConfigObj co = (ConfigObj) cfgObjs.get(jndiname);
        return (co == null ? null : co.interfaceStr );
    }

    public Vector getJndinames() {
        return jndinames;
    }

    public ResourceAdapter getResourceAdapter() {
        return resAdp;
    }

    public void configureAS(final ActivationSpec as, final List acp, final List jacp, final String jndiname,
                             final String ejbName) throws Exception {
        ConfigPropertyDesc [] asCp = ru.buildConfigProperty(null, acp, jacp);

        // set resource adapter if not already done
        if (((ResourceAdapterAssociation) as).getResourceAdapter() == null) {
                ((ResourceAdapterAssociation) as).setResourceAdapter(resAdp);
        }
        ru.processSetters(as.getClass(), as, ejbName, asCp);
        validateAS(as, jndiname, acp, jacp);
    }

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

    private void validateAS(final ActivationSpec as, final String jndiname,
                             final List acp, final List jacp) throws Exception {
        boolean found = false;
        String reqName = null;
        RequiredConfigPropertyDesc rcProp = null;
        ActivationConfigPropertyDesc acProp = null;
        // Check that the req properties have been satisfied
        ConfigObj co = (ConfigObj) cfgObjs.get(jndiname);
        for (Iterator r = co.reqConfigProps.iterator(); r.hasNext();) {
            rcProp = (RequiredConfigPropertyDesc) r.next();
            found = false;
            reqName = rcProp.getConfigPropertyName();
            if (acp != null) {
                for (Iterator i = acp.iterator(); i.hasNext();) {
                    acProp = (ActivationConfigPropertyDesc) i.next();
                    if (reqName.equals(acProp.getActivationConfigPropertyName())) {
                        found = true;
                        break;
                    }
                }
            }
            if (!found && jacp != null) {
                for (Iterator i = jacp.iterator(); i.hasNext();) {
                    acProp = (ActivationConfigPropertyDesc) i.next();
                    if (reqName.equals(acProp.getActivationConfigPropertyName())) {
                        found = true;
                        break;
                    }
                }
            }
            if (!found) {
                logger.log(BasicLevel.ERROR, "Required property " + reqName + " not specified ");
                throw new ResourceAllocationException("Required property " + reqName + " not specified ");
            }
        }

        // Call validate
        try {
            as.validate();
        } catch (UnsupportedOperationException uoe) {
            // treat this as no error from validate
        } catch (Exception ex) {
            logger.log(BasicLevel.ERROR, "Error from ActivationSpec.validate(). " + ex);
            throw new ResourceAllocationException("Error from ActivationSpec.validate(). ", ex);
        }

    }

    /**
     * Build Security Table
     */
    private void bldSecurityTable(final JonasConnectorDesc lnkJCon, final JonasConnectorDesc jCon) {
        // TODO
    }

    /**
     *
     * @param rarFileName String of rar file
     * @param appName the name of the application
     * @return String of module name
     */
    public static String buildModuleName(final String rarFileName, final String appName) {
        String sName = null;
        try {
            sName = new File(rarFileName).getName();
            int iPos = sName.lastIndexOf('.');
            if (iPos > -1) {
                sName = sName.substring(0, iPos);
            }
        } catch (NullPointerException e) {
            // none action
        }
        if (appName != null) {
            StringBuffer sbName = new StringBuffer(appName);
            sbName.append(".");
            sbName.append(sName);
            sName = sbName.toString();
        }

        return sName;
    }

    /**
     * Create the factory specified.
     *
     * @param jndiName name to bind the factory
     * @param rarObjectName unique name to match rar
     * @param factoryOffset factory offset in the to create
     * @param factoryType factory type to create
     * @param conn ConnectionDesc object to use for creation
     * @param jConn JonasConnectionDesc object to use for creation
     * @return Object factory created
     * @throws Exception if an error occurs
     */
    public Object createFactory(final String jndiName,
                                final String rarObjectName,
                                final int factoryOffset,
                                final String factoryType,
                                final ConnectorDesc conn,
                                final JonasConnectorDesc jConn)
                 throws Exception {

        ResourceUtility ru = new ResourceUtility(null, logger, setterLogger, manageLogger);
        Object factory = null;
        ManagedConnectionFactory mcf = null;
        ResourceadapterDesc ra = conn.getResourceadapterDesc();
        /**
         * Can currently assume Basic Password support since the RAR must have already been
         * deployed correctly
         */

        /**
         * Needed if a 1.5 rar and ResourceAdapterAssociation is implemented
         */
        ResourceAdapter resAdp = null;

        String logEnabled = null;
        if (jConn.getLogEnabled() != null) {
            logEnabled = jConn.getLogEnabled().trim();
        }
        String logTopic = null;
        if (jConn.getLogTopic() != null) {
            logTopic = jConn.getLogTopic().trim();
        }
        String specVersion = conn.getSpecVersion();

        ConnectionManager cm = null;
        String jBase = null;
        try {
            jBase = System.getProperty("jonas.base");
            if (tm != null) {
                // We're inside a JOnAS instance (Server)
                cm = ru.createConnectionManager(ra.getTransactionSupport(),
                                                tm,
                                                logger,
                                                poolLogger, jndiName, false);
            }
            // cm is null in Client case (no TM)
        } catch (Exception ex) {
            logger.log(BasicLevel.ERROR, "Use default ConnectionManager: " + rarObjectName);
        }

        // Build the global config and pool values
        ConfigPropertyDesc [] cfgRaJonas =
                     ru.buildConfigProperty(ra.getConfigPropertyList(),
                                            jConn.getJonasConfigPropertyList(),
                                            null);
        ConnectionManagerPoolParams cmpp = ru.configurePoolParams(jConn.getPoolParamsDesc(),
                                                                  jConn.getJdbcConnParamsDesc(),
                                                                  null);
        Properties tmProp = new Properties();
        if (jConn.getTmParamsDesc() != null) {
            List tmParams = jConn.getTmParamsDesc().getTmConfigPropertyList();
            if (tmParams != null) {
                for (Iterator i = tmParams.iterator(); i.hasNext();) {
                    TmConfigPropertyDesc tpd = (TmConfigPropertyDesc) i.next();
                    String tpdVal = tpd.getTmConfigPropertyValue();
                    if (tpdVal == null) {
                        tpdVal = "";
                    }
                    tmProp.setProperty(tpd.getTmConfigPropertyName(), tpdVal);
                }
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "TM Properties: " + tmProp);
                }
            }
        }

        if (specVersion.equals("1.0")) {
            // Only need to build an MCF for a 1.0 resource adapter
            mcf = (ManagedConnectionFactory) ru.processMCF(cmpp, curLoader, rarObjectName,
                                                           ra.getManagedconnectionfactoryClass(),
                                                           jndiName, logEnabled, logTopic,
                                                           cfgRaJonas, resAdp);
            if (cm != null) {
                // Server
                ((ConnectionManagerImpl) cm).setResourceAdapter(mcf, cmpp);
                factory = mcf.createConnectionFactory(cm);
            } else {
                // Client
                factory = mcf.createConnectionFactory();
            }

        } else if (specVersion.equals("1.5")) {
            // Need to figure out how to call RA.start() under some circumstances
            boolean callStart = false;

            String raStr = conn.getResourceadapterDesc().getResourceadapterClass().trim();
            if (raStr != null && raStr.length() > 0) {

                // Instantiate the resource adapter class
                Class raClass = curLoader.loadClass(raStr);
                resAdp = (ResourceAdapter) raClass.newInstance();

                ru.processSetters(raClass, resAdp, rarObjectName, cfgRaJonas);

                // Call Resourceadapter start method
                try {
                    if (callStart) {
                        resAdp.start(bootCtx);
                    }
                } catch (Exception ex) {
                    logger.log(BasicLevel.ERROR, "Rar: Error from resource (" + rarObjectName
                                                 + ") start method.");
                    throw new Exception("Error from start method. " + ex);
                } catch (Throwable th) {
                    logger.log(BasicLevel.ERROR, "Rar: Error from resource (" + rarObjectName
                                                 + ") start method.");
                    throw new Exception("Error from start method. ", th);
                }
            }

            // Determine type
            if (factoryType.equals(ResourceUtility.JCD)) {
                OutboundResourceadapterDesc outRa = ra.getOutboundResourceadapterDesc();
                List cdList = null;
                if (outRa != null) {
                    cdList = outRa.getConnectionDefinitionList();
                }
                ConnectionDefinitionDesc conDef = null;
                JonasConnectionDefinitionDesc jConDef = null;
                String id = null;
                if (cdList != null) {
                    conDef = (ConnectionDefinitionDesc) cdList.get(factoryOffset);
                    id = conDef.getId();
                    jConDef = (JonasConnectionDefinitionDesc)
                                  ru.getJonasXML(jConn, id, factoryOffset, ResourceUtility.JCD);

                    if (jConDef.getLogEnabled() != null) {
                        logEnabled = jConDef.getLogEnabled().trim();
                    }
                    if (jConDef.getLogTopic() != null) {
                        logTopic = jConDef.getLogTopic().trim();
                    }

                    // Create Config Property List
                    ConfigPropertyDesc [] cfgCdDesc =
                                 ru.buildConfigProperty(conDef.getConfigPropertyList(),
                                                        jConDef.getJonasConfigPropertyList(),
                                                        null);

                    // Only need to build an MCF for a 1.0 resource adapter
                    mcf = (ManagedConnectionFactory) ru.processMCF(cmpp, curLoader, rarObjectName,
                                                                   conDef.getManagedconnectionfactoryClass(),
                                                                   jndiName, logEnabled, logTopic,
                                                                   cfgCdDesc, resAdp);
                    if (cm != null) {
                        // Server
                        ConnectionManagerPoolParams pool =
                              ru.configurePoolParams(jConDef.getPoolParamsDesc(),
                                                     jConDef.getJdbcConnParamsDesc(),
                                                     cmpp);
                        ((ConnectionManagerImpl) cm).setResourceAdapter(mcf, pool);
                        factory = mcf.createConnectionFactory(cm);
                    } else {
                        // Client
                        factory = mcf.createConnectionFactory();
                    }

                }
            } else if (factoryType.equals(ResourceUtility.JAO)) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Cannot lookup remote admin object of " + jndiName);
                }
                return null;
            } else if (factoryType.equals(ResourceUtility.JAS)) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Cannot lookup remote activationspec of " + jndiName);
                }
                return null;
            }
        }

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "cf = " + factory);
        }
        // Need to register the RM with JOTM
        if (factoryType.equals(ResourceUtility.JCD) && cm != null) {
            // Call CM to register with JOTM
            ((ConnectionManagerImpl) cm).setXAName(ru.getJcaMcfName(jndiName));
            ((ConnectionManagerImpl) cm).registerXAResource(tmProp);
        }
        return factory;
    }

    /**
     * add a default AS to the list, the first one deployed is the default
     * if others are added then they will become default if this one is undeployed
     * @param jndiName jndi name to add
     */
    public static void addDefaultAS(final String jndiName) {
        defaultAS.add(jndiName);
    }

    /**
     * return the current default ActivationSpec
     * @return String default ActivationSpec name
     */
    public static String getDefaultAS() {
        String ret = null;
        try {
            ret = (String) defaultAS.firstElement();
        } catch (Exception ex) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "No default activationspec");
            }
        }
        return ret;
    }

    /**
     * remove the specified jndiName from the default AS list
     * @param jndiName jndi name to remove
     */
    public static void removeDefaultAS(final String jndiName) {
        try {
            defaultAS.remove(jndiName);
        } catch (Exception ex) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Unable to remove default activationspec");
            }
        }
    }

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

    /**
     * Build Properties from the RA configuration
     */
    private Properties buildProp(final ConfigPropertyDesc[] cfgRaJonas) {
        Properties ret = new Properties();
        if (cfgRaJonas != null) {
            for (int i = 0; i < cfgRaJonas.length; i++) {
                String val = cfgRaJonas[i].getConfigPropertyValue() != null ? cfgRaJonas[i].getConfigPropertyValue()
                        : "";
                ret.setProperty(cfgRaJonas[i].getConfigPropertyName(), val);
            }
        }
        return ret;
    }

    /**
     * Process the specified rar, extracting all the jars files to be included
     * in the classloader and all the native files needed by the rar.
     * @param resFileName rar file to process
     * @param jonasConn the jonas-ra.xml properties
     * @param rarsWorkDirectory the rars working directory
     * @throws ResourceServiceException if an error occurs during
     * the extracting.
     */
    private void extractJars(final String resFileName,
                             final JonasConnectorDesc jonasConn,
                             final String rarsWorkDirectory)
                 throws ResourceServiceException {

        String jarPath = rarsWorkDirectory + File.separator + "jonas" + File.separator;
        String binPath = null;
        if (jonasConn.getNativeLib() != null  && jonasConn.getNativeLib().trim().length() > 0) {
            binPath = jonasConn.getNativeLib().trim() + File.separator;
            if (!(binPath.startsWith("/") || binPath.startsWith("\\") || binPath.charAt(1) == ':')) {
                binPath = JProp.getJonasBase() + File.separator + binPath;
            }
        }

        // Get RAR name (seems to be the RA short name)
        String rarName = resFileName;
        if (rarName.endsWith(".rar")) {
            rarName = resFileName.substring(0, resFileName.lastIndexOf('.'));
        }

        int off = rarName.lastIndexOf(File.separator);
        rarName = rarName.substring(++off);
        if (File.separatorChar == '\\') {
            off = rarName.lastIndexOf("/");
            rarName = rarName.substring(++off);
        }

        // Place the rar URL itself in first position (relative to the jars
        // included in the rar) in the ClassLoader
        // Note: this brutal casting works in all case (it was at least working before my change :) )
        JClassLoader loader = (JClassLoader) curLoader;
        loader.addURL(URLUtils.fileToURL(new File(resFileName)));

        JJarFile jjar = null;
        try {
            jjar = new JJarFile(resFileName);
            List<JarEntry> entries = Collections.list(jjar.entries());
            for (JarEntry je : entries) {

                if (!je.isDirectory() && je.getName().endsWith(".jar")) {

                    // This is a jar file
                    // Unpack it in a dedicated work directory
                    String fileName = constructFilename(jarPath, rarName, je);
                    jjar.extract(je, fileName);

                    // Add the jar library to the ClassLoader
                    loader.addURL(URLUtils.fileToURL(new File(fileName)));

                } else if (!je.isDirectory() && !je.getName().startsWith("META-INF") && (binPath != null)) {

                    // This is a native library, unpack it in a known location (known before hand by the JVM)
                    String fileName = constructFilename(binPath, null, je);
                    jjar.extract(je, fileName);
                }
            }
        } catch (Exception ex) {
            String err = "Error while extracting the files from " + resFileName;
            logger.log(BasicLevel.ERROR, err, ex);
            throw new ResourceServiceException(err, ex);
        } finally {
            if (jjar != null) {
                try {
                    jjar.close();
                } catch (IOException e) {
                    // Ignored
                }
            }
        }

    }

    private String constructFilename(final String path,
                                     final String rarName,
                                     final JarEntry je) throws Exception {

        File filename = new File(path, je.getName());

        // Create directory structure up to filename
        File parent = filename.getParentFile();
        if (!parent.exists()) {
            parent.mkdirs();
        }

        // Construct temp name for jar files from the rar
        if (rarName != null) {
            String name = "_" + rarName + "_" + filename.getName();
            filename = new File(parent, name);
        }
        return filename.getAbsolutePath();
    }

    private void processJAO(final String rarFilename, final AdminobjectDesc aObj, final JonasAdminobjectDesc jao,
                            final ConfigPropertyDesc [] cfgRaJonas, final int idOff) throws Exception {

        ConfigObj cObj = null;
        String jndiName = jao.getJndiName();
        String aoDesc = "";
        for (Iterator r = jao.getDescriptionList().iterator(); r.hasNext();) {
            String desc = (String) r.next();
            aoDesc += desc + " ";
        }
        if (jndiName == null || jndiName.length() == 0) {
            logger.log(BasicLevel.ERROR,
                  "Rar: jndi-name not set in jonas-ra.xml for Adminobject: " + idOff);
            throw new Exception("configuration file incorrect");
        }

        // Instantiate into the currect loader
        String admObj = aObj.getAdminobjectClass();
        Class aoClass = curLoader.loadClass(admObj);
        Object ao = aoClass.newInstance();

        ru.processSetters(aoClass, ao, rarFileName, cfgRaJonas);

        jndinames.add(jndiName);
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "jndiName=" + jndiName);
        }

        // Add entry to list of configured objects
        cObj = new ConfigObj(JAO, idOff, jndiName, rarFileName, aObj.getAdminobjectInterface(), admObj, ao);

        cfgObjs.put(jndiName, cObj);
        //
        // Register into Jndi
        try {
            ictx.rebind(jndiName, ao);

        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Rar: Cannot register ResourceAdapter with the name "
                                         + jndiName);
            logger.log(BasicLevel.ERROR, "Rar: Exception caught : " + e);
            throw new Exception("Error binding jndiName: " + jndiName, e.getCause());
        }

        // --------------------------
        // Register MBeans cf. JSR 77
        // --------------------------

        // Available Adminobjects
        // ------------------------------------------------------------
        // Adminobject MBean
        // -----------------------
        String jcaAdminobjectName = jndiName;
        Properties p = buildProp(cfgRaJonas);
        ObjectName onJCAAdminobject =
            J2eeObjectName.getJCAAdminObject(jDomain,
                    JCAResourceName,
                    jServer,
                    jcaAdminobjectName);
        JCAAdminObject jcaAdminobjectMBean = new JCAAdminObject(onJCAAdminobject.toString(), jndiName, aoDesc, p);
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "JCAAdminObject created");
        }
        jmx.registerModelMBean(jcaAdminobjectMBean, onJCAAdminobject);

        // Update the list of connection factories in the JCAResource MBean with the JCAConnectionFactory
        // MBean's OBJECT_NAME
        jcaResourceMBean.setAdminObjects(onJCAAdminobject.toString());


    }

    private void processJAS(final String rarFilename, final MessagelistenerDesc mlst, final JonasActivationspecDesc jas,
                             final int idOff) throws Exception {

        ConfigObj cObj = null;
        String jndiName = jas.getJndiName();
        String asDesc = "";
        for (Iterator r = jas.getDescriptionList().iterator(); r.hasNext();) {
            String desc = (String) r.next();
            asDesc += desc + " ";
        }
        if (jndiName == null || jndiName.length() == 0) {
            logger.log(BasicLevel.ERROR,
                "Rar: jndi-name not set in jonas-ra.xml for Activationspec: " + idOff);
            throw new Exception("configuration file incorrect");
        }

        // Instantiate into the currect loader
        String mlIntf = mlst.getMessagelistenerType();
        String actSpec = mlst.getActivationspecDesc().getActivationspecClass();
        Class asClass = curLoader.loadClass(actSpec);
        ActivationSpec as =
            (ActivationSpec) asClass.newInstance();

        jndinames.add(jndiName);
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "jndiName=" + jndiName);
        }

        // Add entry to list of configured objects
        cObj = new ConfigObj(JAS, idOff, jndiName, rarFilename, mlIntf, actSpec, as);
        cObj.reqConfigProps = mlst.getActivationspecDesc().getRequiredConfigPropertyList();
        if (jas.getDefaultAS() != null && jas.getDefaultAS().equals("true")) {
            cObj.defaultAS = true;
            addDefaultAS(jndiName);
        }

        cfgObjs.put(jndiName, cObj);
        //
        // Register into Jndi
        try {
            ictx.rebind(jndiName, as);

        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Rar.processJAS: Cannot register ResourceAdapter with the name " + jndiName);
            logger.log(BasicLevel.ERROR, "Rar.processJAS: Exception caught : " + e);
            throw new Exception("Error binding jndiName: " + jndiName, e.getCause());
        }

        // --------------------------
        // Register MBeans cf. JSR 77
        // --------------------------

        // Available ActivationSpecs
        // ------------------------------------------------------------
        // ActivationSpec MBean
        // -----------------------
        String jcaActivationSpecName = jndiName;
        ObjectName onJCAActivationSpec =
            J2eeObjectName.getJCAActivationSpec(jDomain,
                    JCAResourceName,
                    jServer,
                    jcaActivationSpecName);
        JCAActivationSpec jcaActivationSpecMBean = new JCAActivationSpec(onJCAActivationSpec.toString(), jndiName, asDesc, cObj.reqConfigProps);
        if (manageLogger.isLoggable(BasicLevel.DEBUG)) {
            manageLogger.log(BasicLevel.DEBUG, "JCAActivationSpec created");
        }
        jmx.registerModelMBean(jcaActivationSpecMBean, onJCAActivationSpec);

        // Update the list of connection factories in the JCAResource MBean with the JCAConnectionFactory
        // MBean's OBJECT_NAME
        jcaResourceMBean.setActivationSpecs(onJCAActivationSpec.toString());

    }

}


