Channels ▼
RSS

JVM Languages

JDBC Fast Connection Failover with Oracle RAC


Configuring a Tomcat Data Source

Tomcat data source configuration is done through a Resource element in the Context configuration. Tomcat has built-in support for database resources using the Apache DBCP implementation; however a custom resource factory can be used to setup the Oracle UCP implementation. Oracle makes this configuration easy by providing an implemention of the JNDI javax.naming.spi.ObjectFactory interface via the oracle.ucp.jdbc.PoolDataSourceImpl class which allows Tomcat to instantiate and configure the UCP data source.

The attributes used on the Resource element will be passed to the PoolDataSourceImpl during construction to setup the connection pool. The Javadocs for the oracle.ucp.jdbc.PoolDataSource define the various configuration strings as constants for reference. Listing 2 shows a sample configuration. The UCP data source will be available to deployed applications just like a normal Tomcat DBCP configured data source and requires no changes to the application code.

<Resource name="salesDatasource" 
	auth="Container"
	factory="oracle.ucp.jdbc.PoolDataSourceImpl"
	type="oracle.ucp.jdbc.PoolDataSource"
	description="FCF Sales Datasource"
	connectionFactoryClassName="oracle.jdbc.pool.OracleDataSource"
	connectionWaitTimeout="30"
	minPoolSize="5"
	maxPoolSize="25"
	inactiveConnectionTimeout="20"
	timeoutCheckInterval="60"
	fastConnectionFailoverEnabled="true"
	onsConfiguration="nodes=sales1node1.foo.com:6151,sales1node2.foo.com:6151"
	user="bsmith" 
	password="secret"
	url="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=sales1node1.foo.com)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=sales1node2.foo.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=sales.foo.com)))" 
	connectionPoolName="salesConnectionPool" 
	validateConnectionOnBorrow="true"
	sqlForValidateConnection="select 1 from DUAL"
/>

Listing 2

Configuring a Spring Data Source

Spring data source configuration is normally done through a bean definition in an application context XML file. Most applications again use the Apache DBCP implementation but the UCP implementation can be used by configuring a factory bean with the oracle.ucp.jdbc.PoolDataSourceFactory class and setting properties on the resulting oracle.ucp.jdbc.PoolDataSource.

You should be aware that a Spring configured UCP is not destroyed when the bean context is shutdown which is a problem if the Spring application is redeployed in a container such as Tomcat. The recommended way to correct this behavior is to use the destroy-method attribute of the Spring bean definition; however the oracle.ucp.jdbc.PoolDataSource doesn't expose any suitable target method. Listing 3 presents a custom bean post processor which identifies data source instances and properly removes them from the UCP manager when an application context is shutdown. In a standalone Spring application, this functionality isn't required because the UCP will use Java finalizers to perform the pool cleanup.


package org.mpilone.jdbc;

import java.util.*;

import oracle.ucp.UniversalConnectionPoolException;
import oracle.ucp.admin.UniversalConnectionPoolManager;
import oracle.ucp.admin.UniversalConnectionPoolManagerImpl;
import oracle.ucp.jdbc.PoolDataSource;

import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;

/**
 * <p>
 * A simple bean post processor that identified {@link PoolDataSource}s which
 * should be shutdown in the Universal Connection Pool (UCP) manager. The data
 * sources will be shutdown when the application context is shutdown. By default
 * this implementation destroys all instances of {@link PoolDataSource}s found
 * in the context. To use this class, simply create an instance in the bean 
 * configuration.
 * </p>
 * 
 * @author mpilone
 */
public class PoolDataSourceBeanPostProcessor implements
    DestructionAwareBeanPostProcessor
{
  /**
   * The data sources to target when performing the destroy post processing.
   */
  private List<PoolDataSource> mTargetDataSources =
      new ArrayList<PoolDataSource>();

  /*
   * (non-Javadoc)
   * @see
   * org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
   * #postProcessBeforeDestruction(java.lang.Object, java.lang.String)
   */
  @Override
  public void postProcessBeforeDestruction(Object bean, String beanName)
      throws BeansException
  {
    if (PoolDataSource.class.isAssignableFrom(bean.getClass()))
    {
      try
      {
        UniversalConnectionPoolManager mgr =
            UniversalConnectionPoolManagerImpl
                .getUniversalConnectionPoolManager();

        PoolDataSource ds = (PoolDataSource) bean;
        String poolName = ds.getConnectionPoolName();

        if (isTargeted(poolName))
        {
          mgr.stopConnectionPool(poolName);
          mgr.destroyConnectionPool(poolName);
        }
      }
      catch (UniversalConnectionPoolException ex)
      {
        throw new FatalBeanException("Unable to destroy UCP data source", ex);
      }
    }
  }

  /**
   * Checks to see if the given pool is targeted by this post processor.
   * 
   * @param poolName
   *          the name of the pool
   * @return true if the pool is targeted, false otherwise
   */
  private boolean isTargeted(String poolName)
  {
    if (poolName == null)
    {
      return false;
    }

    // If we have specific data sources, only return
    // true if one of the targets matches the pool name.
    boolean targeted = mTargetDataSources.isEmpty();
    if (!targeted)
    {
      for (Iterator<PoolDataSource> iter = mTargetDataSources.iterator(); iter
          .hasNext() &&
          !targeted;)
      {
        PoolDataSource ds = iter.next();
        targeted = ds.getConnectionPoolName().equals(poolName);
      }
    }

    return targeted;
  }

  /*
   * (non-Javadoc)
   * @seeorg.springframework.beans.factory.config.BeanPostProcessor#
   * postProcessAfterInitialization(java.lang.Object, java.lang.String)
   */
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException
  {
    return bean;
  }

  /*
   * (non-Javadoc)
   * @seeorg.springframework.beans.factory.config.BeanPostProcessor#
   * postProcessBeforeInitialization(java.lang.Object, java.lang.String)
   */
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException
  {
    return bean;
  }

  /**
   * Gets the data sources to target when performing the destroy post processing
   * or an empty list if all are targeted.
   * 
   * @return the data sources to target or an empty list
   */
  public List<PoolDataSource> getTargetDataSources()
  {
    return mTargetDataSources;
  }

  /**
   * Sets the the data sources to target when performing the destroy post
   * processing. If not set, all {@link PoolDataSource} instances will be
   * targeted.
   * 
   * @param targetDataSources
   *          the data sources to target
   */
  public void setTargetDataSources(List<PoolDataSource> targetDataSources)
  {
    mTargetDataSources = targetDataSources;
  }
}

Listing 3

Listing 4 shows the Spring bean configuration of the PoolDataSource and includes the PoolDataSourceBeanPostProcessor to support clean context shutdowns. Just like in the Tomcat case, the UCP data source will now be available to deployed applications as a configured data source and requires no changes to the application code.


<bean class="org.mpilone.jdbc.PoolDataSourceBeanPostProcessor" />

<bean id="salesDataSource" class="oracle.ucp.jdbc.PoolDataSourceFactory" factory-method="getPoolDataSource">
	<property name="URL" value="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=sales1node1.foo.com)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=sales1node2.foo.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=sales.foo.com)))"/>
	<property name="user" value="bsmith"/>
	<property name="password" value="secret"/>
	<property name="connectionFactoryClassName" value="oracle.jdbc.pool.OracleDataSource"/>
	<property name="connectionPoolName" value="salesConnectionPool"/>
	<property name="connectionWaitTimeout" value="30"/>
	<property name="minPoolSize" value="5"/>
	<property name="maxPoolSize" value="25"/>
	<property name="inactiveConnectionTimeout" value="20"/>
	<property name="timeoutCheckInterval" value="60"/>
	<property name="fastConnectionFailoverEnabled" value="true"/>
	<property name="ONSConfiguration" value="nodes=sales1node1.foo.com:6151,sales1node2.foo.com:6151"/>
	<property name="validateConnectionOnBorrow" value="true"/>
	<property name="SQLForValidateConnection" value="select 1 from DUAL"/>
</bean>

Listing 4


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video