/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 */
package org.jboss.resource.connectionmanager;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import javax.management.Notification;
import javax.management.ObjectName;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.ResourceAdapterAssociation;

import org.jboss.deployment.DeploymentException;
import org.jboss.logging.Logger;
import org.jboss.metadata.MetaData;
import org.jboss.resource.metadata.ConfigPropertyMetaData;
import org.jboss.resource.metadata.ConnectionDefinitionMetaData;
import org.jboss.resource.metadata.ConnectorMetaData;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.util.Classes;
import org.jboss.util.StringPropertyReplacer;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
 * The RARDeployment mbean manages instantiation and configuration of a
 * ManagedConnectionFactory instance. It is intended to be configured
 * primarily by xslt transformation of the ra.xml from a jca adapter.
 * Until that is implemented, it uses the old RARDeployment and RARDeployer
 * mechanism to obtain information from the ra.xml.  Properties for the
 * ManagedConectionFactory should be supplied with their values in the
 * ManagedConnectionFactoryProperties element.
 *
 * @author <a href="toby.allsopp@peace.com">Toby Allsopp</a>
 * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
 * @version $Revision: 1.25.6.5 $
 * @jmx:mbean name="jboss.jca:service=RARDeployment"
 *            extends="org.jboss.system.ServiceMBean, javax.resource.spi.ManagedConnectionFactory"
 */
public class RARDeployment extends ServiceMBeanSupport
   implements RARDeploymentMBean, ManagedConnectionFactory
{
   static final long serialVersionUID = -294341806721616790L;

   public final static String MCF_ATTRIBUTE_CHANGED_NOTIFICATION = "jboss.mcfattributechangednotification";

   private Logger log = Logger.getLogger(getClass());

   //Hack to use previous ra.xml parsing code until xslt deployment is written.
   private ObjectName oldRarDeployment;

   private String rarName;

   private String connectionDefinition;

   private String vendorName;

   private String specVersion;

   private String eisType;

   private String version;

   private String managedConnectionFactoryClass;

   private String connectionFactoryInterface;

   private String connectionFactoryImplClass;

   private String connectionInterface;

   private String connectionImplClass;

   private String transactionSupport;

   private Element managedConnectionFactoryProperties;

   private String authenticationMechanismType;

   private String credentialInterface;

   private boolean reauthenticationSupport;

   private Class mcfClass;

   private ManagedConnectionFactory mcf;

   /**
    * Default managed constructor for RARDeployment mbeans.
    *
    * @jmx.managed-constructor
    */
   public RARDeployment()
   {
   }

   /**
    * The OldRarDeployment attribute refers to a previous-generation RARDeployment.
    * THIS IS A HACK UNTIL XSLT DEPLOYMENT IS WRITTEN
    *
    * @return value of OldRarDeployment
    *
    * @jmx:managed-attribute
    * @todo remove this when xslt based deployment is written.
    */
   public ObjectName getOldRarDeployment()
   {
      return oldRarDeployment;
   }

   /**
    * Set the value of OldRarDeployment
    * @param oldRarDeployment - Value to assign to OldRarDeployment
    *
    * @jmx:managed-attribute
    * @todo remove this when xslt based deployment is written.
    */
   public void setOldRarDeployment(final ObjectName oldRarDeployment)
   {
      this.oldRarDeployment = oldRarDeployment;
   }

   /**
    * The RARName attribute holds the file name of the rar
    *
    * @return the rar name value.
    * @jmx:managed-attribute
    */
   public String getRARName()
   {
      return rarName;
   }

   /**
    * Set the RARName value.
    * 
    * @param rarName The new DisplayName value.
    * @jmx:managed-attribute
    */
   public void setRARName(String rarName)
   {
      this.rarName = rarName;
   }

   /**
    * The connection definition inside the rar,
    * it identifies the unique connection factory
    *
    * @return the rar name value.
    * @jmx:managed-attribute
    */
   public String getConnectionDefinition()
   {
      return connectionDefinition;
   }

   /**
    * Set the connection definition.
    * 
    * @param connectionDefinition - the connection definition
    * @jmx:managed-attribute
    */
   public void setConnectionDefinition(String connectionDefinition)
   {
      this.connectionDefinition = connectionDefinition;
   }

   /**
    * The VendorName attribute holds the VendorName from the ra.xml
    * It should be supplied by xslt from ra.xml
    *
    * @return the VendorName value.
    * @jmx:managed-attribute
    */
   public String getVendorName()
   {
      return vendorName;
   }

   /**
    * Set the VendorName value.
    * @param vendorName The new VendorName value.
    * @jmx:managed-attribute
    */
   public void setVendorName(String vendorName)
   {
      this.vendorName = vendorName;
   }

   /**
    * The SpecVersion attribute holds the SpecVersion from the ra.xml
    * It should be supplied by xslt from ra.xml
    *
    * @return the SpecVersion value.
    * @jmx:managed-attribute
    */
   public String getSpecVersion()
   {
      return specVersion;
   }

   /**
    * Set the SpecVersion value.
    * @param specVersion The new SpecVersion value.
    * @jmx:managed-attribute
    */
   public void setSpecVersion(String specVersion)
   {
      this.specVersion = specVersion;
   }

   /**
    * The EisType attribute holds the EisType from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the EisType value.
    * @jmx:managed-attribute
    */
   public String getEisType()
   {
      return eisType;
   }

   /**
    * Set the EisType value.
    * @param eisType The new EisType value.
    * @jmx:managed-attribute
    */
   public void setEisType(String eisType)
   {
      this.eisType = eisType;
   }

   /**
    * The Version attribute holds the Version from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the Version value.
    * @jmx:managed-attribute
    */
   public String getVersion()
   {
      return version;
   }

   /**
    * Set the Version value.
    * @param version The new Version value.
    * @jmx:managed-attribute
    */
   public void setVersion(String version)
   {
      this.version = version;
   }

   /**
    * The ManagedConnectionFactoryClass attribute holds the ManagedConnectionFactoryClass from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the ManagedConnectionFactoryClass value.
    * @jmx:managed-attribute
    */
   public String getManagedConnectionFactoryClass()
   {
      return managedConnectionFactoryClass;
   }

   /**
    * Set the ManagedConnectionFactoryClass value.
    * @param managedConnectionFactoryClass The new ManagedConnectionFactoryClass value.
    * @jmx:managed-attribute
    */
   public void setManagedConnectionFactoryClass(final String managedConnectionFactoryClass)
   {
      this.managedConnectionFactoryClass = managedConnectionFactoryClass;
   }

   /**
    * The ConnectionFactoryInterface attribute holds the ConnectionFactoryInterface from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the ConnectionFactoryInterface value.
    * @jmx:managed-attribute
    */
   public String getConnectionFactoryInterface()
   {
      return connectionFactoryInterface;
   }

   /**
    * Set the ConnectionFactoryInterface value.
    * @param connectionFactoryInterface The ConnectionFactoryInterface value.
    * @jmx:managed-attribute
    */
   public void setConnectionFactoryInterface(String connectionFactoryInterface)
   {
      this.connectionFactoryInterface = connectionFactoryInterface;
   }

   /**
    * The ConnectionFactoryImplClass attribute holds the ConnectionFactoryImplClass from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the ConnectionFactoryImplClass value.
    * @jmx:managed-attribute
    */
   public String getConnectionFactoryImplClass()
   {
      return connectionFactoryImplClass;
   }

   /**
    * Set the ConnectionFactoryImplClass value.
    * @param connectionFactoryImplClass The ConnectionFactoryImplClass value.
    * @jmx:managed-attribute
    */
   public void setConnectionFactoryImplClass(String connectionFactoryImplClass)
   {
      this.connectionFactoryImplClass = connectionFactoryImplClass;
   }

   /**
    * The ConnectionInterface attribute holds the ConnectionInterface from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the ConnectionInterface value.
    * @jmx:managed-attribute
    */
   public String getConnectionInterface()
   {
      return connectionInterface;
   }

   /**
    * Set the ConnectionInterface value.
    * @param connectionInterface The ConnectionInterface value.
    * @jmx:managed-attribute
    */
   public void setConnectionInterface(String connectionInterface)
   {
      this.connectionInterface = connectionInterface;
   }

   /**
    * The ConnectionImplClass attribute holds the ConnectionImplClass from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the connectionImplClass value.
    * @jmx:managed-attribute
    */
   public String getConnectionImplClass()
   {
      return connectionImplClass;
   }

   /**
    * Set the ConnectionImplClass value.
    * @param connectionImplClass The ConnectionImplClass value.
    * @jmx:managed-attribute
    */
   public void setConnectionImplClass(String connectionImplClass)
   {
      this.connectionImplClass = connectionImplClass;
   }

   /**
    * The TransactionSupport attribute holds the TransactionSupport from the ra.xml.
    * It should be supplied by xslt from ra.xml
    * It is ignored, and choice of ConnectionManager implementations determine
    * transaction support.
    *
    * Get the TransactionSupport value.
    * @return the TransactionSupport value.
    * @jmx:managed-attribute
    */
   public String getTransactionSupport()
   {
      return transactionSupport;
   }

   /**
    * Set the TransactionSupport value.
    * @param transactionSupport The TransactionSupport value.
    * @jmx:managed-attribute
    */
   public void setTransactionSupport(String transactionSupport)
   {
      this.transactionSupport = transactionSupport;
   }

   /**
    * The ManagedConnectionFactoryProperties attribute holds the
    * ManagedConnectionFactoryProperties from the ra.xml, together with
    * user supplied values for all or some of these properties.  This must be
    * supplied as an element in the same format as in ra.xml, wrapped in a
    * properties tag.
    * It should be supplied by xslt from ra.xml merged with an user
    * configuration xml file.
    * An alternative format has a config-property element with attributes for
    * name and type and the value as content.
    *
    * @return the ManagedConnectionFactoryProperties value.
    * @jmx:managed-attribute
    */
   public Element getManagedConnectionFactoryProperties()
   {
      return managedConnectionFactoryProperties;
   }

   /**
    * Set the ManagedConnectionFactoryProperties value.
    * @param managedConnectionFactoryProperties The ManagedConnectionFactoryProperties value.
    * @jmx:managed-attribute
    */
   public void setManagedConnectionFactoryProperties(Element managedConnectionFactoryProperties)
   {
      this.managedConnectionFactoryProperties = managedConnectionFactoryProperties;
   }

   /**
    * The AuthenticationMechanismType attribute holds the AuthenticationMechanismType from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the AuthenticationMechanismType value.
    * @jmx:managed-attribute
    */
   public String getAuthenticationMechanismType()
   {
      return authenticationMechanismType;
   }

   /**
    * Set the AuthenticationMechanismType value.
    * @param authenticationMechanismType The AuthenticationMechanismType value.
    * @jmx:managed-attribute
    */
   public void setAuthenticationMechanismType(String authenticationMechanismType)
   {
      this.authenticationMechanismType = authenticationMechanismType;
   }

   /**
    * The CredentialInterface attribute holds the CredentialInterface from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the CredentialInterface value.
    * @jmx:managed-attribute
    */
   public String getCredentialInterface()
   {
      return credentialInterface;
   }

   /**
    * Set the CredentialInterface value.
    * @param credentialInterface The CredentialInterface value.
    * @jmx:managed-attribute
    */
   public void setCredentialInterface(String credentialInterface)
   {
      this.credentialInterface = credentialInterface;
   }

   /**
    * The ReauthenticationSupport attribute holds the ReauthenticationSupport from the ra.xml.
    * It should be supplied by xslt from ra.xml
    *
    * @return the ReauthenticationSupport value.
    * @jmx:managed-attribute
    */
   public boolean isReauthenticationSupport()
   {
      return reauthenticationSupport;
   }

   /**
    * Set the ReauthenticationSupport value.
    * @param reauthenticationSupport The ReauthenticationSupport value.
    * @jmx:managed-attribute
    */
   public void setReauthenticationSupport(boolean reauthenticationSupport)
   {
      this.reauthenticationSupport = reauthenticationSupport;
   }

   /**
    * The <code>getMcfInstance</code> method returns the
    * ManagedConnectionFactory instance represented by this mbean.
    * It is needed so PasswordCredentials can match up correctly.
    * This will probably have to be implemented as an interceptor when
    * the mcf is directly deployed as an mbean.
    *
    * @return a <code>ManagedConnectionFactory</code> value
    *
    * @jmx.managed-attribute access="read-only" description="Returns ManagedConnectionFactory instance represented by this mbean"
    */
   public ManagedConnectionFactory getMcfInstance()
   {
      return mcf;
   }

   /**
    * Describe <code>startManagedConnectionFactory</code> method here.
    * creates managedConnectionFactory, creates ConnectionFactory, and binds it in jndi.
    * Returns the ManagedConnectionFactory to the ConnectionManager that called us.
    *
    * @todo remove use of oldRarDeployment when xslt based deployment is written.
    */
   protected void startService() throws Exception
   {
      if (mcf != null)
         throw new DeploymentException("Stop the RARDeployment before restarting it");

      ConnectorMetaData cmd = null;
      ConnectionDefinitionMetaData cdmd = null;
      ResourceAdapter resourceAdapter = null;
      if (oldRarDeployment != null)
      {
         try
         {
            resourceAdapter = (ResourceAdapter) getServer().getAttribute(oldRarDeployment, "ResourceAdapter");
            cmd = (ConnectorMetaData) getServer().getAttribute(oldRarDeployment, "MetaData");
            cdmd = cmd.getConnectionDefinition(connectionDefinition);
            if (cdmd == null)
               throw new DeploymentException("ConnectionDefinition '" + connectionDefinition + "' not found in rar '" + rarName + "'");
            setManagedConnectionFactoryClass(cdmd.getManagedConnectionFactoryClass());
            setReauthenticationSupport(cmd.getReauthenticationSupport());
         }
         catch (Exception e)
         {
            throw new DeploymentException("couldn't get oldRarDeployment! " + oldRarDeployment, e);
         }
      }
      try
      {
         mcfClass = Thread.currentThread().getContextClassLoader().loadClass(managedConnectionFactoryClass);
      }
      catch (ClassNotFoundException cnfe)
      {
         log.error("Could not find ManagedConnectionFactory class: " + managedConnectionFactoryClass, cnfe);
         throw new DeploymentException("Could not find ManagedConnectionFactory class: "
               + managedConnectionFactoryClass);
      }
      try
      {
         mcf = (ManagedConnectionFactory) mcfClass.newInstance();
      }
      catch (Exception e)
      {
         log.error("Could not instantiate ManagedConnectionFactory: " + managedConnectionFactoryClass, e);
         throw new DeploymentException("Could not instantiate ManagedConnectionFactory: "
               + managedConnectionFactoryClass);
      }
      if (cmd != null)
      {
         // Set the resource adapter properties
         setMcfProperties(cmd.getProperties(), false);
         // Set the connection definition properties
         setMcfProperties(cdmd.getProperties(), true);
      }
      //set overridden properties;
      setMcfProperties(managedConnectionFactoryProperties);
      
      if (resourceAdapter != null && mcf instanceof ResourceAdapterAssociation)
      {
         ResourceAdapterAssociation raa = (ResourceAdapterAssociation) mcf;
         raa.setResourceAdapter(resourceAdapter);
      }
   }

   /**
    * The <code>stopManagedConnectionFactory</code> method unbinds the ConnectionFactory
    * from jndi, releases the ManagedConnectionFactory instane, and releases the
    * ManagedConnectionFactory class.
    *
    */
   protected void stopService()
   {
      mcf = null;
      mcfClass = null;
   }

   /**
    * The setManagedConnectionFactoryAttribute method can be used to set
    * attributes on the ManagedConnectionFactory from code, without using the
    * xml configuration.
    *
    * @param name a <code>String</code> value
    * @param clazz a <code>Class</code> value
    * @param value an <code>Object</code> value
    *
    * @jmx:managed-operation
    */
   public void setManagedConnectionFactoryAttribute(String name, Class clazz, Object value)
   {
      setManagedConnectionFactoryAttribute(name, clazz, value, false);
   }
   
   protected void setManagedConnectionFactoryAttribute(String name, Class clazz, Object value, boolean mustExist)
   {
      if (name == null || name.length() == 0)
         throw new IllegalArgumentException("Null or empty attribute name " + name);
      String setterName = "set" + Character.toUpperCase(name.charAt(0));
      if (name.length() > 1)
         setterName = setterName.concat(name.substring(1));
      Method setter;
      try
      {
         setter = mcfClass.getMethod(setterName, new Class[] {clazz});
      }
      catch (NoSuchMethodException nsme)
      {
         String error = "The class '" + mcfClass.toString() + "' has no setter for config property '" + name + "'"; 
         if (mustExist)
            throw new IllegalArgumentException(error);
         else
         {
            log.trace(error);
            return;
         }
      }
      try
      {
         setter.invoke(mcf, new Object[] {value});
         log.debug("set property " + name + " to value " + value);
      }
      catch (Exception e)
      {
         String error = "Unable to invoke setter method '" + setter + "' " + "on object '" + mcf + "'"; 
         if (mustExist)
            throw new IllegalArgumentException(error);
         else
         {
            log.trace(error);
            return;
         }
      }
      sendNotification(new Notification(MCF_ATTRIBUTE_CHANGED_NOTIFICATION, getServiceName(),
            getNextNotificationSequenceNumber()));
   }

   /**
    * The <code>getManagedConnectionFactoryAttribute</code> method can be used
    * to examine the managed connection factory properties.
    *
    * @param name a <code>String</code> value
    * @return an <code>Object</code> value
    *
    * @jmx:managed-operation
    */
   public Object getManagedConnectionFactoryAttribute(String name)
   {
      if (name == null || name.length() == 0)
         throw new IllegalArgumentException("Null or empty attribute name " + name);
      String getterName = "get" + Character.toUpperCase(name.charAt(0));
      if (name.length() > 1)
         getterName = getterName.concat(name.substring(1));
      Method getter;
      try
      {
         getter = mcfClass.getMethod(getterName, new Class[] {});
      }
      catch (NoSuchMethodException e)
      {
         String msg = "The class '" + mcfClass + "' has no getter("
            + getterName + ") for config property '" + name + "'";
         log.debug(msg, e);
         throw new IllegalArgumentException(msg);
      }
      try
      {
         Object value = getter.invoke(mcf, new Object[]{});
         log.debug("get property " + name + ": value " + value);
         return value;
      }
      catch (Exception e)
      {
         String msg = "Unable to invoke getter method '" + getter
            + "' " + "on object '" + mcf + "'";
         log.debug(msg, e);
         throw new IllegalArgumentException(msg);
      }
   }

   //ObjectFactory implementation
   //protected methods
   protected void setMcfProperties(Collection properties, boolean mustExist) throws DeploymentException
   {
      for (Iterator i = properties.iterator(); i.hasNext();)
      {
         ConfigPropertyMetaData cpmd = (ConfigPropertyMetaData) i.next();
         String name = cpmd.getName();
         String type = cpmd.getType();
         String value = cpmd.getValue();
         if (name == null || name.length() == 0 || value == null || value.length() == 0)
         {
            log.debug("Not setting config property '" + name + "'");
            continue;
         }
         // see if it is a primitive type first
         Class clazz = Classes.getPrimitiveTypeForName(type);
         if (clazz == null)
         {
            //not primitive, look for it.
            try
            {
               clazz = Thread.currentThread().getContextClassLoader().loadClass(type);
            }
            catch (ClassNotFoundException cnfe)
            {
               log.warn("Unable to find class '" + type + "' for " + "property '" + name + "' - skipping property.");
               continue;
            }
         }
         PropertyEditor pe = PropertyEditorManager.findEditor(clazz);
         if (pe == null)
         {
            log.warn("Unable to find a PropertyEditor for class '" + clazz + "' of property '" + name + "' - "
                  + "skipping property");
            continue;
         }
         value = StringPropertyReplacer.replaceProperties(value);
         log.debug("setting property: " + name + " to value " + value);
         try
         {
            pe.setAsText(value);
         }
         catch (IllegalArgumentException iae)
         {
            log.warn("Value '" + value + "' is not valid for property '" + name + "' of class '" + clazz
                  + "' - skipping " + "property");
            continue;
         }
         Object v = pe.getValue();
         setManagedConnectionFactoryAttribute(name, clazz, v, mustExist);
      }
   }

   protected void setMcfProperties(Element mcfProps) throws DeploymentException
   {
      if (mcfProps == null)
      {
         return;
      } // end of if ()
      // See if the config has disabled property replacement
      boolean replace = true;
      String replaceAttr = mcfProps.getAttribute("replace");
      if (replaceAttr.length() > 0)
         replace = Boolean.valueOf(replaceAttr).booleanValue();
      // the properties that the deployment descriptor says we need to set
      NodeList props = mcfProps.getChildNodes();
      for (int i = 0; i < props.getLength(); i++)
      {
         if (props.item(i).getNodeType() == Node.ELEMENT_NODE)
         {
            Element prop = (Element) props.item(i);
            if (prop.getTagName().equals("config-property"))
            {
               String name = null;
               String type = null;
               String value = null;
               //Support for more friendly config style
               //<config-property name="" type=""></config-property>
               if (prop.hasAttribute("name"))
               {
                  name = prop.getAttribute("name");
                  type = prop.getAttribute("type");
                  value = MetaData.getElementContent(prop);
               } // end of if ()
               else
               {
                  name = MetaData.getElementContent(MetaData.getUniqueChild(prop, "config-property-name"));
                  type = MetaData.getElementContent(MetaData.getUniqueChild(prop, "config-property-type"));
                  value = MetaData.getElementContent(MetaData.getOptionalChild(prop, "config-property-value"));
               } // end of else
               if (name == null || name.length() == 0 || value == null || value.length() == 0)
               {
                  log.debug("Not setting config property '" + name + "'");
                  continue;
               }
               if (type == null || type.length() == 0)
               {
                  // Default to String for convenience.
                  type = "java.lang.String";
               } // end of if ()
               // see if it is a primitive type first
               Class clazz = Classes.getPrimitiveTypeForName(type);
               if (clazz == null)
               {
                  //not primitive, look for it.
                  try
                  {
                     clazz = Thread.currentThread().getContextClassLoader().loadClass(type);
                  }
                  catch (ClassNotFoundException cnfe)
                  {
                     log.warn("Unable to find class '" + type + "' for " + "property '" + name
                           + "' - skipping property.");
                     continue;
                  }
               }
               PropertyEditor pe = PropertyEditorManager.findEditor(clazz);
               if (pe == null)
               {
                  log.warn("Unable to find a PropertyEditor for class '" + clazz + "' of property '" + name + "' - "
                        + "skipping property");
                  continue;
               }
               if (replace == true)
                  value = StringPropertyReplacer.replaceProperties(value);
               log.debug("setting property: " + name + " to value " + value);
               try
               {
                  pe.setAsText(value);
               }
               catch (IllegalArgumentException iae)
               {
                  log.warn("Value '" + value + "' is not valid for property '" + name + "' of class '" + clazz
                        + "' - skipping " + "property");
                  continue;
               }
               Object v = pe.getValue();
               setManagedConnectionFactoryAttribute(name, clazz, v);
            } // end of if ()
         } // end of if ()
      } //end of for
   }

   //ManagedConnectionFactory implementation, used to keep backward compatibility
   // between configs using this mbean and newer xmbean based configs.
   /**
    * Creates a connection factory instance.
    */
   public Object createConnectionFactory() throws ResourceException
   {
      return mcf.createConnectionFactory();
   }

   /**
    * Creates a connection factory instance.
    */
   public Object createConnectionFactory(ConnectionManager cxManager) throws ResourceException
   {
      return mcf.createConnectionFactory(cxManager);
   }

   /**
    * Creates a new ManagedConnection
    */
   public ManagedConnection createManagedConnection(javax.security.auth.Subject subject,
         ConnectionRequestInfo cxRequestInfo) throws ResourceException
   {
      return mcf.createManagedConnection(subject, cxRequestInfo);
   }

   /**
    * Tests object for equality
    */
   public boolean equals(Object other)
   {
      return mcf.equals(other);
   }

   /**
    * Gets the logwriter for this instance.
    */
   public java.io.PrintWriter getLogWriter() throws ResourceException
   {
      return mcf.getLogWriter();
   }

   /**
    * Override toString() so we don't fall fowl an NPE in hashCode
    */
   public String toString()
   {
      StringBuffer buffer = new StringBuffer();
      buffer.append(getClass().getName());
      buffer.append('@');
      buffer.append(Integer.toHexString(System.identityHashCode(this)));
      return buffer.toString();
   }
   
   /**
    * Generates a hashCode for this object
    */
   public int hashCode()
   {
      return mcf.hashCode();
   }

   /**
    * Returns a matching connection from the set.
    */
   public ManagedConnection matchManagedConnections(java.util.Set connectionSet, javax.security.auth.Subject subject,
         ConnectionRequestInfo cxRequestInfo) throws ResourceException
   {
      return mcf.matchManagedConnections(connectionSet, subject, cxRequestInfo);
   }

   /**
    * Sets the logwriter for this instance.
    */
   public void setLogWriter(java.io.PrintWriter out) throws ResourceException
   {
      mcf.setLogWriter(out);
   }
}// RARDeployment