You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by le...@apache.org on 2002/04/22 05:04:27 UTC
cvs commit: jakarta-avalon-excalibur/datasource/src/test/org/apache/avalon/excalibur/datasource/ids/test TableIdGeneratorJdbcTestCase.java TableIdGeneratorJdbcTestCase.xtest TableIdGeneratorMultithreadedJdbcTestCase.java TableIdGeneratorMultithreadedJdbcTestCase.xtest
leif 02/04/21 20:04:27
Modified: datasource build.xml default.properties
Added: datasource/src/java/org/apache/avalon/excalibur/datasource/cluster
AbstractDataSourceCluster.java
DefaultHashedDataSourceCluster.java
DefaultIndexedDataSourceCluster.java
DefaultRoundRobinDataSourceCluster.java
HashedDataSourceCluster.java
IndexedDataSourceCluster.java
RoundRobinDataSourceCluster.java
datasource/src/java/org/apache/avalon/excalibur/datasource/ids
AbstractDataSourceBlockIdGenerator.java
AbstractDataSourceIdGenerator.java
AbstractIdGenerator.java IdException.java
IdGenerator.java SequenceIdGenerator.java
TableIdGenerator.java
datasource/src/test/org/apache/avalon/excalibur/datasource/ids/test
TableIdGeneratorJdbcTestCase.java
TableIdGeneratorJdbcTestCase.xtest
TableIdGeneratorMultithreadedJdbcTestCase.java
TableIdGeneratorMultithreadedJdbcTestCase.xtest
Log:
Moved datasource.ids and datasource.cluster packages into the datasource
subproject.
Revision Changes Path
1.16 +26 -0 jakarta-avalon-excalibur/datasource/build.xml
Index: build.xml
===================================================================
RCS file: /home/cvs/jakarta-avalon-excalibur/datasource/build.xml,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- build.xml 22 Apr 2002 00:44:28 -0000 1.15
+++ build.xml 22 Apr 2002 03:04:26 -0000 1.16
@@ -36,6 +36,7 @@
<pathelement location="${excalibur-logger.jar}"/>
<pathelement location="${excalibur-concurrent.jar}"/>
<pathelement location="${excalibur-collections.jar}"/>
+ <pathelement location="${test.jdbc.driver.jar}"/>
<path refid="project.class.path"/>
</path>
<property name="cp" refid="test.class.path"/>
@@ -213,11 +214,36 @@
<mkdir dir="${build.lib}"/>
+ <!-- excalibur-datasource jar -->
<jar jarfile="${build.lib}/${jar.name}"
basedir="${build.classes}"
compress="${build.compress}"
manifest="${build.conf}/MANIFEST.MF">
<exclude name="**/test/**"/>
+ <exclude name="**/cluster/**"/>
+ <exclude name="**/ids/**"/>
+ <zipfileset dir="${build.conf}" prefix="META-INF/">
+ <include name="LICENSE.txt"/>
+ </zipfileset>
+ </jar>
+
+ <!-- excalibur-datasource-clister jar -->
+ <jar jarfile="${build.lib}/${jar.cluster.name}"
+ basedir="${build.classes}"
+ compress="${build.compress}"
+ manifest="${build.conf}/MANIFEST.MF">
+ <include name="**/cluster/**"/>
+ <zipfileset dir="${build.conf}" prefix="META-INF/">
+ <include name="LICENSE.txt"/>
+ </zipfileset>
+ </jar>
+
+ <!-- excalibur-datasource-ids jar -->
+ <jar jarfile="${build.lib}/${jar.ids.name}"
+ basedir="${build.classes}"
+ compress="${build.compress}"
+ manifest="${build.conf}/MANIFEST.MF">
+ <include name="**/ids/**"/>
<zipfileset dir="${build.conf}" prefix="META-INF/">
<include name="LICENSE.txt"/>
</zipfileset>
1.7 +2 -0 jakarta-avalon-excalibur/datasource/default.properties
Index: default.properties
===================================================================
RCS file: /home/cvs/jakarta-avalon-excalibur/datasource/default.properties,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- default.properties 16 Apr 2002 12:00:40 -0000 1.6
+++ default.properties 22 Apr 2002 03:04:26 -0000 1.7
@@ -109,6 +109,8 @@
# name of jar file
jar.name = ${name}-${version}.jar
+jar.cluster.name = ${name}-cluster-${version}a.jar
+jar.ids.name = ${name}-ids-${version}a.jar
# property indicating directory where all distribution archives are placed
dist.base = distributions
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/AbstractDataSourceCluster.java
Index: AbstractDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.excalibur.datasource.NoValidConnectionException;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentSelector;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public abstract class AbstractDataSourceCluster
extends AbstractLogEnabled
implements Composable, Configurable, Initializable, Disposable, ThreadSafe
{
/** ComponentManager which created this component */
protected ComponentManager m_manager;
protected int m_size;
private String[] m_dataSourceNames;
private ComponentSelector m_dbSelector;
private DataSourceComponent[] m_dataSources;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public AbstractDataSourceCluster()
{
}
/*---------------------------------------------------------------
* AbstractDataSourceCluster Methods
*-------------------------------------------------------------*/
/**
* Returns the number of DataSources in the cluster.
*
* @return size of the cluster.
*/
public int getClusterSize()
{
return m_size;
}
/**
* Gets a Connection to a database given an index.
*
* @param index Index of the DataSource for which a connection is to be returned.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
public Connection getConnectionForIndex( int index ) throws SQLException
{
if( ( index < 0 ) || ( index >= m_size ) )
{
throw new NoValidConnectionException(
"index (" + index + ") must be in the range 0 to " + ( m_size - 1 ) );
}
return m_dataSources[ index ].getConnection();
}
/*---------------------------------------------------------------
* Composable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to tell the component which ComponentManager
* is controlling it.
*
* @param ComponentManager which curently owns the component.
*/
public void compose( ComponentManager manager )
{
m_manager = manager;
}
/*---------------------------------------------------------------
* Configurable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to configure the component.
*
* @param configuration configuration info used to setup the component.
*
* @throws ConfigurationException if there are any problems with the configuration.
*/
public void configure( Configuration configuration ) throws ConfigurationException
{
// Get the size
m_size = configuration.getAttributeAsInteger( "size" );
if( m_size < 1 )
{
throw new ConfigurationException(
"Invalid value (" + m_size + ") for size attribute." );
}
// Read in the data source names.
m_dataSourceNames = new String[ m_size ];
Configuration[] dataSourceConfigs = configuration.getChildren( "dbpool" );
for( int i = 0; i < dataSourceConfigs.length; i++ )
{
int index = dataSourceConfigs[ i ].getAttributeAsInteger( "index" );
if( ( index < 0 ) || ( index >= m_size ) )
{
throw new ConfigurationException( "The dbpool with index=\"" + index +
"\" is invalid. Index must be in the range 0 to " + ( m_size - 1 ) );
}
if( m_dataSourceNames[ index ] != null )
{
throw new ConfigurationException( "Only one dbpool with index=\"" + index +
"\" can be defined." );
}
m_dataSourceNames[ index ] = dataSourceConfigs[ i ].getValue();
}
// Make sure that all of the dbpools were defined
for( int i = 0; i < m_dataSourceNames.length; i++ )
{
if( m_dataSourceNames[ i ] == null )
{
throw new ConfigurationException( "Expected a dbpool with index=\"" + i + "\"" );
}
}
}
/*---------------------------------------------------------------
* Initializable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to initialize the component.
*
* @throws Exception if there were any problems durring initialization.
*/
public void initialize() throws Exception
{
// Get references to a data sources
m_dbSelector =
(ComponentSelector)m_manager.lookup( DataSourceComponent.ROLE + "ClusterSelector" );
m_dataSources = new DataSourceComponent[ m_size ];
for( int i = 0; i < m_dataSourceNames.length; i++ )
{
m_dataSources[ i ] = (DataSourceComponent)m_dbSelector.select( m_dataSourceNames[ i ] );
}
}
/*---------------------------------------------------------------
* Disposable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to dispose the component.
*/
public void dispose()
{
// Free up the data source
if( m_dbSelector != null )
{
if( m_dataSources != null )
{
for( int i = 0; i < m_dataSources.length; i++ )
{
if( m_dataSources[ i ] != null )
{
m_dbSelector.release( m_dataSources[ i ] );
}
}
m_dataSources = null;
}
m_manager.release( m_dbSelector );
m_dbSelector = null;
}
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/DefaultHashedDataSourceCluster.java
Index: DefaultHashedDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.avalon.excalibur.datasource.NoValidConnectionException;
/**
* The DefaultHashedDataSourceCluster requires that the user specify an object or a hashCode
* which will be used consistantly select a member DataSource form a cluster for each connection
* request. Calls to getConnection() will throw an exception. Components which make use of
* this class must call either the getConnectionForHashObject( Object hashObject) or the
* getConnectionForHashCode( int hashCode ) methods instead.
* <p>
* This form of Clustering is useful in cases where data can be reliably accessed in a repeatable
* manner. For example a web site's visitor information could be accessed by using a String
* containing the visitor's username as a hashObject. This would allow visitor information to be
* spread across several database servers.
* <p>
* The Configuration for a 2 database cluster is like this:
*
* <pre>
* <datasources>
* <hashed-cluster name="mydb-cluster" size="2">
* <dbpool index="0">mydb-0</dbpool>
* <dbpool index="1">mydb-1</dbpool>
* </hashed-cluster>
* </datasources>
* <cluster-datasources>
* <jdbc name="mydb-0">
* <pool-controller min="1" max="10"/>
* <auto-commit>true</auto-commit>
* <driver>com.database.jdbc.JdbcDriver</driver>
* <dburl>jdbc:driver://host0/mydb</dburl>
* <user>username</user>
* <password>password</password>
* </jdbc>
* <jdbc name="mydb-1">
* <pool-controller min="1" max="10"/>
* <auto-commit>true</auto-commit>
* <driver>com.database.jdbc.JdbcDriver</driver>
* <dburl>jdbc:driver://host1/mydb</dburl>
* <user>username</user>
* <password>password</password>
* </jdbc>
* </cluster-datasources>
* </pre>
*
* With the following roles declaration:
*
* <pre>
* <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
* shorthand="datasources"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* <hint shorthand="j2ee" class="org.apache.avalon.excalibur.datasource.J2eeDataSource"/>
* <hint shorthand="hashed-cluster"
* class="org.apache.avalon.excalibur.datasource.cluster.DefaultHashedDataSourceCluster"/>
* </role>
* <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentClusterSelector"
* shorthand="cluster-datasources"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* <hint shorthand="j2ee" class="org.apache.avalon.excalibur.datasource.J2eeDataSource"/>
* </role>
* </pre>
*
* A hashed-cluster definition enforces that the configuration specify a size. This size must
* equal the number of datasources referenced as being members of the cluster. Any datasource can
* be a member of the cluster.
* <p>
* The hashed-cluster can be obtained in the same manner as a non-clustered datasource. The only
* difference is in how it is used. The HashedDataSourceCluster requires that the caller provide
* an object or a hashCode to use when requesting a connection.
* <p>
* The following code demonstrates a change that can be made to database enabled components so that
* they will be able to work with both HashedDataSourceCluster DataSources and regular
* DataSources.
* <p>
* old:
* <pre>
* Connection connection = m_dataSource.getConnection();
* </pre>
*
* new:
* <pre>
* Connection connection;
* if ( m_dataSource instanceof HashedDataSourceCluster )
* {
* connection = ((HashedDataSourceCluster)m_dataSource).getConnectionForHashObject( hashObject );
* }
* else
* {
* connection = m_dataSource.getConnection();
* }
* </pre>
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public class DefaultHashedDataSourceCluster
extends AbstractDataSourceCluster
implements HashedDataSourceCluster
{
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public DefaultHashedDataSourceCluster()
{
}
/*---------------------------------------------------------------
* DataSourceComponent Methods
*-------------------------------------------------------------*/
/**
* Not supported in this component. Will throw a NoValidConnectionException.
*/
public Connection getConnection() throws SQLException
{
throw new NoValidConnectionException(
"getConnection() should not be called for a " + getClass().getName() + ". " +
"Please verify your configuration." );
}
/*---------------------------------------------------------------
* HashedDataSourceCluster Methods
*-------------------------------------------------------------*/
// public int getClusterSize()
// declared in AbstractDataSourceCluster
/**
* Gets a Connection to a database given a hash object.
*
* @param hashObject Object whose hashCode will be used to select which of the Clusted
* DataSources will be provide a Connection.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
public Connection getConnectionForHashObject( Object hashObject ) throws SQLException
{
return getConnectionForIndex( getIndexForHashObject( hashObject ) );
}
/**
* Gets a Connection to a database given a hash code.
*
* @param hashCode HashCode which will be used to select which of the Clusted
* DataSources will be provide a Connection.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
public Connection getConnectionForHashCode( int hashCode ) throws SQLException
{
return getConnectionForIndex( getIndexForHashCode( hashCode ) );
}
// public Connection getConnectionForIndex( int index ) throws SQLException
// declared in AbstractDataSourceCluster
/**
* Gets the index which will be resolved for a given hashCode. This can be used
* by user code to optimize the use of DataSource Clusters.
* <p>
* Subclasses can override this method to get different behavior.
* <p>
* By default the index = getIndexForHashCode( hashObject.hashCode() )
*
* @param hashObject Object whose hashCode will be used to select which of the Clusted
* DataSources will be provide a Connection.
*/
public int getIndexForHashObject( Object hashObject )
{
return getIndexForHashCode( hashObject.hashCode() );
}
/**
* Gets the index which will be resolved for a given hashCode. This can be used
* by user code to optimize the use of DataSource Clusters.
* <p>
* Subclasses can override this method to get different behavior.
* <p>
* By default the index = hashCode % getClusterSize()
*
* @param hashCode HashCode which will be used to select which of the Clusted
* DataSources will be provide a Connection.
*/
public int getIndexForHashCode( int hashCode )
{
// DEVELOPER WARNING:
// If you change the way the hashCode is calculated, you WILL BREAK
// things for existing users, so please do so only after much thought.
// Hash code may be negative, Make them all positive by using the unsigned int value.
long lHashCode = ( (long)hashCode ) & 0xffffffffL;
return (int)( lHashCode % getClusterSize() );
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/DefaultIndexedDataSourceCluster.java
Index: DefaultIndexedDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.avalon.excalibur.datasource.NoValidConnectionException;
/**
* The DefaultIndexedDataSourceCluster requires that the user implement their own method of
* selecting which DataSource in the cluster to use for each connection request. Calls to
* getConnection() will throw an exception. Components which make use of this class must call
* the getConnectionForIndex(int index) method instead.
* <p>
* The Configuration for a 2 database cluster is like this:
*
* <pre>
* <datasources>
* <indexed-cluster name="mydb-cluster" size="2">
* <dbpool index="0">mydb-0</dbpool>
* <dbpool index="1">mydb-1</dbpool>
* </indexed-cluster>
* </datasources>
* <cluster-datasources>
* <jdbc name="mydb-0">
* <pool-controller min="1" max="10"/>
* <auto-commit>true</auto-commit>
* <driver>com.database.jdbc.JdbcDriver</driver>
* <dburl>jdbc:driver://host0/mydb</dburl>
* <user>username</user>
* <password>password</password>
* </jdbc>
* <jdbc name="mydb-1">
* <pool-controller min="1" max="10"/>
* <auto-commit>true</auto-commit>
* <driver>com.database.jdbc.JdbcDriver</driver>
* <dburl>jdbc:driver://host1/mydb</dburl>
* <user>username</user>
* <password>password</password>
* </jdbc>
* </cluster-datasources>
* </pre>
*
* With the following roles declaration:
*
* <pre>
* <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
* shorthand="datasources"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* <hint shorthand="j2ee" class="org.apache.avalon.excalibur.datasource.J2eeDataSource"/>
* <hint shorthand="indexed-cluster"
* class="org.apache.avalon.excalibur.datasource.cluster.DefaultIndexedDataSourceCluster"/>
* </role>
* <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentClusterSelector"
* shorthand="cluster-datasources"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* <hint shorthand="j2ee" class="org.apache.avalon.excalibur.datasource.J2eeDataSource"/>
* </role>
* </pre>
*
* An indexed-cluster definition enforces that the configuration specify a size. This size must
* equal the number of datasources referenced as being members of the cluster. Any datasource can
* be a member of the cluster.
* <p>
* The indexed-cluster can be obtained in the same manner as a non-clustered datasource. The only
* difference is in how it is used. The IndexedDataSourceCluster requires that the caller specify
* the index of the cluster member to use when requesting a connection.
* <p>
* The following code demonstrates a change that can be made to database enabled components so that
* they will be able to work with both IndexedDataSourceCluster DataSources and regular
* DataSources.
* <p>
* old:
* <pre>
* Connection connection = m_dataSource.getConnection();
* </pre>
*
* new:
* <pre>
* Connection connection;
* if ( m_dataSource instanceof IndexedDataSourceCluster )
* {
* connection = ((IndexedDataSourceCluster)m_dataSource).getConnectionForIndex( index );
* }
* else
* {
* connection = m_dataSource.getConnection();
* }
* </pre>
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public class DefaultIndexedDataSourceCluster
extends AbstractDataSourceCluster
implements IndexedDataSourceCluster
{
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public DefaultIndexedDataSourceCluster()
{
}
/*---------------------------------------------------------------
* DataSourceComponent Methods
*-------------------------------------------------------------*/
/**
* Not supported in this component. Will throw a NoValidConnectionException.
*/
public Connection getConnection() throws SQLException
{
throw new NoValidConnectionException(
"getConnection() should not be called for a " + getClass().getName() + ". " +
"Please verify your configuration." );
}
/*---------------------------------------------------------------
* IndexedDataSourceCluster Methods
*-------------------------------------------------------------*/
// public int getClusterSize()
// declared in AbstractDataSourceCluster
// public Connection getConnectionForIndex( int index ) throws SQLException
// declared in AbstractDataSourceCluster
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/DefaultRoundRobinDataSourceCluster.java
Index: DefaultRoundRobinDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.avalon.excalibur.datasource.NoValidConnectionException;
/**
* The DefaultRoundRobinDataSourceCluster allows the user to specify a cluster of DataSources
* which all act as one. The Cluster works by cycling through its member DataSources returning
* a connection from a different one with each call to getConnection().
* <p>
* This form of Clustering has the benefit that it can be used by components without requiring
* any changes. But care must be taken as to the kind of data written or read from the database.
* Wich this clustering method, there is no control over which DataSource will provide a
* connection for any given call.
* <p>
* Round Robin Clusters are useful in cases where lots of read-only data needs to be accessed and
* multiple copies of the data can be stored on different database servers to balance load.
* <p>
* The Configuration for a 2 database cluster is like this:
*
* <pre>
* <datasources>
* <roundrobin-cluster name="mydb-cluster" size="2">
* <dbpool index="0">mydb-0</dbpool>
* <dbpool index="1">mydb-1</dbpool>
* </roundrobin-cluster>
* </datasources>
* <cluster-datasources>
* <jdbc name="mydb-0">
* <pool-controller min="1" max="10"/>
* <auto-commit>true</auto-commit>
* <driver>com.database.jdbc.JdbcDriver</driver>
* <dburl>jdbc:driver://host0/mydb</dburl>
* <user>username</user>
* <password>password</password>
* </jdbc>
* <jdbc name="mydb-1">
* <pool-controller min="1" max="10"/>
* <auto-commit>true</auto-commit>
* <driver>com.database.jdbc.JdbcDriver</driver>
* <dburl>jdbc:driver://host1/mydb</dburl>
* <user>username</user>
* <password>password</password>
* </jdbc>
* </cluster-datasources>
* </pre>
*
* With the following roles declaration:
*
* <pre>
* <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
* shorthand="datasources"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* <hint shorthand="j2ee" class="org.apache.avalon.excalibur.datasource.J2eeDataSource"/>
* <hint shorthand="roundrobin-cluster"
* class="org.apache.avalon.excalibur.datasource.cluster.DefaultRoundRobinDataSourceCluster"/>
* </role>
* <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentClusterSelector"
* shorthand="cluster-datasources"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* <hint shorthand="j2ee" class="org.apache.avalon.excalibur.datasource.J2eeDataSource"/>
* </role>
* </pre>
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public class DefaultRoundRobinDataSourceCluster
extends AbstractDataSourceCluster
implements RoundRobinDataSourceCluster
{
private Object m_semaphore = new Object();
private int m_nextIndex;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public DefaultRoundRobinDataSourceCluster()
{
}
/*---------------------------------------------------------------
* DataSourceComponent Methods
*-------------------------------------------------------------*/
/**
* Returns a Connection to one of the Cluster's member DataSources.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
public Connection getConnection() throws SQLException
{
int index;
synchronized( m_semaphore )
{
index = m_nextIndex;
if( ( ++m_nextIndex ) >= m_size )
{
m_nextIndex = 0;
}
}
return getConnectionForIndex( index );
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/HashedDataSourceCluster.java
Index: HashedDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public interface HashedDataSourceCluster
extends DataSourceComponent
{
/**
* The name of the role for convenience
*/
String ROLE = "org.apache.avalon.excalibur.datasource.cluster.HashedDataSourceCluster";
/**
* Returns the number of DataSources in the cluster.
*
* @return size of the cluster.
*/
int getClusterSize();
/**
* Gets a Connection to a database given a hash object.
*
* @param hashObject Object whose hashCode will be used to select which of the Clusted
* DataSources will be provide a Connection.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
Connection getConnectionForHashObject( Object hashObject ) throws SQLException;
/**
* Gets a Connection to a database given a hash code.
*
* @param hashCode HashCode which will be used to select which of the Clusted
* DataSources will be provide a Connection.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
Connection getConnectionForHashCode( int hashCode ) throws SQLException;
/**
* Gets a Connection to a database given an index.
*
* @param index Index of the DataSource for which a connection is to be returned.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
Connection getConnectionForIndex( int index ) throws SQLException;
/**
* Gets the index which will be resolved for a given hashCode. This can be used
* by user code to optimize the use of DataSource Clusters.
*
* @param hashObject Object whose hashCode will be used to select which of the Clusted
* DataSources will be provide a Connection.
*/
int getIndexForHashObject( Object hashObject );
/**
* Gets the index which will be resolved for a given hashCode. This can be used
* by user code to optimize the use of DataSource Clusters.
*
* @param hashCode HashCode which will be used to select which of the Clusted
* DataSources will be provide a Connection.
*/
int getIndexForHashCode( int hashCode );
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/IndexedDataSourceCluster.java
Index: IndexedDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public interface IndexedDataSourceCluster
extends DataSourceComponent
{
/**
* The name of the role for convenience
*/
String ROLE = "com.silveregg.util.datasource.cluster.IndexedDataSourceCluster";
/**
* Returns the number of DataSources in the cluster.
*
* @return size of the cluster.
*/
int getClusterSize();
/**
* Gets a Connection to a database given an index.
*
* @param index Index of the DataSource for which a connection is to be returned.
*
* @throws NoValidConnectionException when there is no valid Connection wrapper
* available in the classloader or when the index is not valid.
*
* @throws NoAvailableConnectionException when there are no more available
* Connections in the pool.
*/
Connection getConnectionForIndex( int index ) throws SQLException;
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/cluster/RoundRobinDataSourceCluster.java
Index: RoundRobinDataSourceCluster.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.cluster;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public interface RoundRobinDataSourceCluster
extends DataSourceComponent
{
/**
* The name of the role for convenience
*/
String ROLE = "org.apache.avalon.excalibur.datasource.cluster.RoundRobinDataSourceCluster";
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/AbstractDataSourceBlockIdGenerator.java
Index: AbstractDataSourceBlockIdGenerator.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import java.math.BigDecimal;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
/**
* The AbstractDataSourceBlockIdGenerator allocates blocks of ids from a DataSource
* and then provides them as needed. This is useful in reducing communication with
* the DataSource.
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public abstract class AbstractDataSourceBlockIdGenerator
extends AbstractDataSourceIdGenerator
{
/**
* The first id in a batch of Ids loaded in from the DataSource.
*/
private BigDecimal m_firstBigDecimal;
/**
* The first id in a batch of Ids loaded in from the DataSource.
*/
private long m_firstLong;
/**
* The number of ids loaded in each block.
*/
private int m_blockSize;
/**
* The number of ids which have been allocated from the current block.
*/
private int m_allocated;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public AbstractDataSourceBlockIdGenerator()
{
}
/*---------------------------------------------------------------
* Methods
*-------------------------------------------------------------*/
/**
* Allocates a block, of the given size, of ids from the database.
*
* @param blockSize number of Ids which are to be allocated.
*
* @return The first id in the allocated block.
*
* @throws IdException if there it was not possible to allocate a block of ids.
*/
protected abstract BigDecimal allocateBigDecimalIdBlock( int blockSize )
throws IdException;
/**
* Allocates a block, of the given size, of ids from the database.
*
* @param blockSize number of Ids which are to be allocated.
*
* @return The first id in the allocated block.
*
* @throws IdException if there it was not possible to allocate a block of ids.
*/
protected abstract long allocateLongIdBlock( int blockSize )
throws IdException;
/*---------------------------------------------------------------
* AbstractIdGenerator Methods
*-------------------------------------------------------------*/
/**
* Gets the next id as a Big Decimal. This method will only be called
* when synchronized and when the data type is configured to be BigDecimal.
*
* @return the next id as a BigDecimal.
*
* @throws IdException if an Id could not be allocated for any reason.
*/
protected BigDecimal getNextBigDecimalIdInner()
throws IdException
{
if( m_allocated >= m_blockSize )
{
// Need to allocate a new batch of ids
try
{
m_firstBigDecimal = allocateBigDecimalIdBlock( m_blockSize );
// Reset the allocated count
m_allocated = 0;
}
catch( IdException e )
{
// Set the allocated count to signal that there are not any ids available.
m_allocated = Integer.MAX_VALUE;
throw e;
}
}
// We know that at least one id is available.
// Get an id out of the currently allocated block.
BigDecimal id = m_firstBigDecimal.add( new BigDecimal( m_allocated ) );
m_allocated++;
return id;
}
/**
* Gets the next id as a long. This method will only be called
* when synchronized and when the data type is configured to be long.
*
* @return the next id as a long.
*
* @throws IdException if an Id could not be allocated for any reason.
*/
protected long getNextLongIdInner()
throws IdException
{
if( m_allocated >= m_blockSize )
{
// Need to allocate a new batch of ids
try
{
m_firstLong = allocateLongIdBlock( m_blockSize );
// Reset the allocated count
m_allocated = 0;
}
catch( IdException e )
{
// Set the allocated count to signal that there are not any ids available.
m_allocated = Integer.MAX_VALUE;
throw e;
}
}
// We know that at least one id is available.
// Get an id out of the currently allocated block.
long id = m_firstLong + m_allocated;
if( id < 0 )
{
// The value wrapped
String msg = "No more Ids are available, the maximum long value has been reached.";
getLogger().error( msg );
throw new IdException( msg );
}
m_allocated++;
return id;
}
/*---------------------------------------------------------------
* Configurable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to configure the component.
*
* @param configuration configuration info used to setup the component.
*
* @throws ConfigurationException if there are any problems with the configuration.
*/
public void configure( Configuration configuration )
throws ConfigurationException
{
super.configure( configuration );
// Obtain the block size.
m_blockSize = configuration.getAttributeAsInteger( "block-size", 10 );
}
/*---------------------------------------------------------------
* Initializable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to initialize the component.
*
* @throws Exception if there were any problems durring initialization.
*/
public void initialize()
throws Exception
{
super.initialize();
// Set the state so that the first request for an id will load in a block of ids.
m_allocated = Integer.MAX_VALUE;
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/AbstractDataSourceIdGenerator.java
Index: AbstractDataSourceIdGenerator.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentSelector;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public abstract class AbstractDataSourceIdGenerator
extends AbstractIdGenerator
implements IdGenerator, Composable, Configurable, Initializable, Disposable, ThreadSafe
{
protected static final int DBTYPE_STANDARD = 0;
protected static final int DBTYPE_MYSQL = 1;
/** ComponentManager which created this component */
protected ComponentManager m_manager;
private String m_dataSourceName;
private ComponentSelector m_dbSelector;
protected DataSourceComponent m_dataSource;
protected int m_dbType;
/**
* Number of allocated Ids remaining before another block must be allocated.
*/
protected int m_allocated;
protected long m_nextId;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public AbstractDataSourceIdGenerator()
{
}
/*---------------------------------------------------------------
* Methods
*-------------------------------------------------------------*/
/**
* Allocates a connection for the caller. The connection must be closed by the caller
* when no longer needed.
*
* @return an open DB connection.
*
* @throws SQLException if the connection can not be obtained for any reason.
*/
protected Connection getConnection()
throws SQLException
{
return m_dataSource.getConnection();
}
/*---------------------------------------------------------------
* Composable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to tell the component which ComponentManager
* is controlling it.
*
* @param ComponentManager which curently owns the component.
*/
public void compose( ComponentManager manager )
{
m_manager = manager;
}
/*---------------------------------------------------------------
* Configurable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to configure the component.
*
* @param configuration configuration info used to setup the component.
*
* @throws ConfigurationException if there are any problems with the configuration.
*/
public void configure( Configuration configuration )
throws ConfigurationException
{
// Obtain the big-decimals flag.
setUseBigDecimals( configuration.getAttributeAsBoolean( "big-decimals", false ) );
// Obtain a reference to the configured DataSource
m_dataSourceName = configuration.getChild( "dbpool" ).getValue();
}
/*---------------------------------------------------------------
* Initializable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to initialize the component.
*
* @throws Exception if there were any problems durring initialization.
*/
public void initialize()
throws Exception
{
// Get a reference to a data source
m_dbSelector = (ComponentSelector)m_manager.lookup( DataSourceComponent.ROLE + "Selector" );
m_dataSource = (DataSourceComponent)m_dbSelector.select( m_dataSourceName );
// Resolve the type of database that is being used.
try
{
Connection conn = getConnection();
try
{
Statement statement = conn.createStatement();
String className = statement.getClass().getName();
if( className.indexOf( "mysql" ) > 0 )
{
m_dbType = DBTYPE_MYSQL;
}
else
{
m_dbType = DBTYPE_STANDARD;
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to open connection to resolve database type.", e );
}
}
/*---------------------------------------------------------------
* Disposable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to dispose the component.
*/
public void dispose()
{
// Free up the data source
if( m_dbSelector != null )
{
if( m_dataSource != null )
{
m_dbSelector.release( m_dataSource );
m_dataSource = null;
}
m_manager.release( m_dbSelector );
m_dbSelector = null;
}
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/AbstractIdGenerator.java
Index: AbstractIdGenerator.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import java.math.BigDecimal;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public abstract class AbstractIdGenerator
extends AbstractLogEnabled
implements IdGenerator, ThreadSafe
{
private static final BigDecimal BIG_DECIMAL_MAX_LONG = new BigDecimal( Long.MAX_VALUE );
/**
* Used to manage internal synchronization.
*/
private Object m_semaphore = new Object();
/**
* Data type for the Id Pool.
*/
private boolean m_useBigDecimals;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public AbstractIdGenerator()
{
}
/*---------------------------------------------------------------
* Methods
*-------------------------------------------------------------*/
/**
* Gets the next id as a Big Decimal. This method will only be called
* when synchronized and when the data type is configured to be BigDecimal.
*
* @return the next id as a BigDecimal.
*
* @throws IdException if an Id could not be allocated for any reason.
*/
protected abstract BigDecimal getNextBigDecimalIdInner()
throws IdException;
/**
* Gets the next id as a long. This method will only be called
* when synchronized and when the data type is configured to be long.
*
* @return the next id as a long.
*
* @throws IdException if an Id could not be allocated for any reason.
*/
protected abstract long getNextLongIdInner()
throws IdException;
/**
* By default, the IdGenerator will operate using a backend datatype of type long. This
* is the most efficient, however it does not allow for Ids that are larger than
* Long.MAX_VALUE. To allow very large Ids, it is necessary to make use of the BigDecimal
* data storage type. This method should only be called durring initialization.
*
* @param useBigDecimals True to set BigDecimal as the internal data type.
*/
protected final void setUseBigDecimals( boolean useBigDecimals )
{
m_useBigDecimals = useBigDecimals;
}
/**
* Returns true if the internal data type is using BigDecimals, false if it is using longs.
*/
protected final boolean isUsingBigDecimals()
{
return m_useBigDecimals;
}
/**
* Gets the next Long Id constraining the value to be less than the specified maxId.
*
* @throws IdException if the next id is larger than the specified maxId.
*/
protected final long getNextLongIdChecked( long maxId )
throws IdException
{
long nextId;
if( m_useBigDecimals )
{
// Use BigDecimal data type
BigDecimal bd;
synchronized( m_semaphore )
{
bd = getNextBigDecimalIdInner();
}
// Make sure that the Big Decimal value can be assigned to a long before continuing.
if( bd.compareTo( BIG_DECIMAL_MAX_LONG ) > 0 )
{
String msg = "Unable to provide an id. The next id would " +
"be greater than the id data type allows.";
getLogger().error( msg );
throw new IdException( msg );
}
nextId = bd.longValue();
}
else
{
// Use long data type
synchronized( m_semaphore )
{
nextId = getNextLongIdInner();
}
}
// Make sure that the id is valid for the requested data type.
if( nextId > maxId )
{
String msg = "Unable to provide an id. The next id would " +
"be greater than the id data type allows.";
getLogger().error( msg );
throw new IdException( msg );
}
return nextId;
}
/*---------------------------------------------------------------
* IdGenerator Methods
*-------------------------------------------------------------*/
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*/
public final BigDecimal getNextBigDecimalId()
throws IdException
{
BigDecimal bd;
if( m_useBigDecimals )
{
// Use BigDecimal data type
synchronized( m_semaphore )
{
bd = getNextBigDecimalIdInner();
}
}
else
{
// Use long data type
synchronized( m_semaphore )
{
bd = new BigDecimal( getNextLongIdInner() );
}
}
return bd;
}
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IdException if the next id is outside of the range of valid longs.
*/
public final long getNextLongId()
throws IdException
{
return getNextLongIdChecked( Long.MAX_VALUE );
}
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IdException if the next id is outside of the range of valid integers.
*/
public final int getNextIntegerId()
throws IdException
{
return (int)getNextLongIdChecked( Integer.MAX_VALUE );
}
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IdException if the next id is outside of the range of valid shorts.
*/
public final short getNextShortId()
throws IdException
{
return (short)getNextLongIdChecked( Short.MAX_VALUE );
}
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IdException if the next id is outside of the range of valid bytes.
*/
public final byte getNextByteId()
throws IdException
{
return (byte)getNextLongIdChecked( Byte.MAX_VALUE );
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/IdException.java
Index: IdException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import org.apache.avalon.framework.CascadingException;
/**
* Thrown when it was not possible to allocate an Id.
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public class IdException
extends CascadingException
{
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
/**
* Construct a new IdException instance.
*
* @param message The detail message for this exception.
*/
public IdException( String message )
{
super( message );
}
/**
* Construct a new IdException instance.
*
* @param message The detail message for this exception.
* @param throwable The root cause of the exception.
*/
public IdException( String message, Throwable throwable )
{
super( message, throwable );
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/IdGenerator.java
Index: IdGenerator.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import java.math.BigDecimal;
import org.apache.avalon.framework.component.Component;
/**
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public interface IdGenerator
extends Component
{
/**
* The name of the role for convenience
*/
String ROLE = "org.apache.avalon.excalibur.datasource.ids.IdGenerator";
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*/
BigDecimal getNextBigDecimalId()
throws IdException;
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IllegalStateException if the next id is outside of the range of valid longs.
*/
long getNextLongId()
throws IdException;
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IllegalStateException if the next id is outside of the range of valid integers.
*/
int getNextIntegerId()
throws IdException;
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IllegalStateException if the next id is outside of the range of valid shorts.
*/
short getNextShortId()
throws IdException;
/**
* Returns the next Id from the pool.
*
* @return the next Id.
*
* @throws IllegalStateException if the next id is outside of the range of valid bytes.
*/
byte getNextByteId()
throws IdException;
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/SequenceIdGenerator.java
Index: SequenceIdGenerator.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
/**
* The SequenceIdGenerator requests each Id using a sequence in a database. While not
* actually pooling batches of Ids like other IdGenerator implementations, making use of this class
* does make code compatable with other IdGenerators on a configuration basis.
* <p>
* The Configuration to use a SequenceIdGenerator look like the following:
* <pre>
* <id-generators>
* <sequence name="user-ids" logger="cm.ids">
* <dbpool>user-db</dbpool>
* <query>SELECT NEXTVAL('category_ids')</query>
* </sequence>
* </id-generators>
* </pre>
* Where user-db is the name of a DataSource configured in a datasources element, and query is
* any query which will return a single id while maintaining state so that successive calls
* will continue to return incremented ids.
* <p>
*
* With the following roles declaration:
* <pre>
* <role name="org.apache.avalon.excalibur.datasource.ids.IdGeneratorSelector"
* shorthand="id-generators"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="sequence"
* class="org.apache.avalon.excalibur.datasource.ids.SequenceIdGenerator"/>
* </role>
* </pre>
*
* To configure your component to use the IdGenerator declared above, its configuration should look
* something like the following:
* <pre>
* <user-service logger="cm">
* <dbpool>user-db</dbpool>
* <id-generator>user-ids</id-generator>
* </user-service>
* </pre>
*
* Your component obtains a reference to an IdGenerator using the same method as it obtains a
* DataSource, by making use of a ComponentSelector.
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public class SequenceIdGenerator
extends AbstractDataSourceIdGenerator
{
private String m_query;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public SequenceIdGenerator()
{
}
/*---------------------------------------------------------------
* AbstractIdGenerator Methods
*-------------------------------------------------------------*/
/**
* Gets the next id as a Big Decimal. This method will only be called
* when synchronized and when the data type is configured to be BigDecimal.
*
* @return the next id as a BigDecimal.
*
* @throws IdException if an Id could not be allocated for any reason.
*/
protected BigDecimal getNextBigDecimalIdInner()
throws IdException
{
if( getLogger().isDebugEnabled() )
{
getLogger().debug( "Requesting an Id using query: " + m_query );
}
try
{
Connection conn = getConnection();
try
{
PreparedStatement stmt = conn.prepareStatement( m_query );
ResultSet rs = stmt.executeQuery();
if( rs.next() )
{
return rs.getBigDecimal( 1 );
}
else
{
String msg = "Query for Id did not return a value";
getLogger().error( msg );
throw new IdException( msg );
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
throw new IdException( "Unable to allocate an Id", e );
}
}
/**
* Gets the next id as a long. This method will only be called
* when synchronized and when the data type is configured to be long.
*
* @return the next id as a long.
*
* @throws IdException if an Id could not be allocated for any reason.
*/
protected long getNextLongIdInner()
throws IdException
{
if( getLogger().isDebugEnabled() )
{
getLogger().debug( "Requesting an Id using query: " + m_query );
}
try
{
Connection conn = getConnection();
try
{
PreparedStatement stmt = conn.prepareStatement( m_query );
ResultSet rs = stmt.executeQuery();
if( rs.next() )
{
return rs.getLong( 1 );
}
else
{
String msg = "Query for Id did not return a value";
getLogger().error( msg );
throw new IdException( msg );
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
String msg = "Unable to allocate an Id";
getLogger().error( msg );
throw new IdException( msg );
}
}
/*---------------------------------------------------------------
* Configurable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to configure the component.
*
* @param configuration configuration info used to setup the component.
*
* @throws ConfigurationException if there are any problems with the configuration.
*/
public void configure( Configuration configuration )
throws ConfigurationException
{
super.configure( configuration );
// Obtain the query to use to obtain an id from a sequence.
m_query = configuration.getChild( "query" ).getValue();
}
}
1.1 jakarta-avalon-excalibur/datasource/src/java/org/apache/avalon/excalibur/datasource/ids/TableIdGenerator.java
Index: TableIdGenerator.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
/**
* The TableIdGenerator requests blocks of ids from a Database table. The table consists of two
* columns one called <code>table_name</code> of type CHAR or VARCHAR, and the second called
* <code>next_id</code> of an integer type large enough to hold your largest ids.
* <p>
* The Configuration to use a TableIdGenerator looks like the following:
* <pre>
* <id-generators>
* <table name="user-ids" big-decimals="true" block-size="1" table="ids"
* key-table="event-type" logger="cm.ids">
* <dbpool>user-db</dbpool>
* </table>
* </id-generators>
* </pre>
* Where user-db is the name of a DataSource configured in a datasources element, block-size is
* the number if ids that are allocated with each query to the databse (defaults to "10"),
* table is the name of the table which contains the ids (defaults to "ids"), and key-table is
* the table_name of the row from which the block of ids are allocated (defaults to "id").
* <p>
*
* With the following roles declaration:
* <pre>
* <role name="org.apache.avalon.excalibur.datasource.ids.IdGeneratorSelector"
* shorthand="id-generators"
* default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="table"
* class="org.apache.avalon.excalibur.datasource.ids.TableIdGenerator"/>
* </role>
* </pre>
*
* To configure your component to use the IdGenerator declared above, its configuration should look
* something like the following:
* <pre>
* <user-service logger="cm">
* <dbpool>user-db</dbpool>
* <id-generator>user-ids</id-generator>
* </user-service>
* </pre>
*
* Your component obtains a reference to an IdGenerator using the same method as it obtains a
* DataSource, by making use of a ComponentSelector.
* <p>
* Depending on your database, the ids table should look something like the following:
* <pre>
* CREATE TABLE ids (
* table_name varchar(16) NOT NULL,
* next_id INTEGER NOT NULL,
* PRIMARY KEY (table_name)
* );
* </pre>
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/04/22 03:04:27 $
* @since 4.1
*/
public class TableIdGenerator
extends AbstractDataSourceBlockIdGenerator
{
/**
* The name of the table containing the ids.
*/
private String m_table;
/**
* TableName used to reference which ids to allocate.
*/
private String m_tableName;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public TableIdGenerator()
{
}
/*---------------------------------------------------------------
* Methods
*-------------------------------------------------------------*/
/**
* Allocates a block of ids of the given size and returns the first id.
*
* @param blockSize number of ids to allocate.
* @param useBigDecimals returns the first id as a BigDecimal if true, otherwise as a Long.
*
* @return either a Long or a BigDecimal depending on the value of useBigDecimals
*
* @throws IdException if a block of ids can not be allocated.
*/
private Object allocateIdBlock( int blockSize, boolean useBigDecimals )
throws IdException
{
if( getLogger().isDebugEnabled() )
{
getLogger().debug( "Allocating a new block of " + blockSize +
" ids for key_table " + m_tableName + "." );
}
try
{
Connection conn = getConnection();
try
{
// Turn off auto commit so that we are working in a transaction,
// but keep the old value.
boolean oldAutoCommit = conn.getAutoCommit();
conn.setAutoCommit( false );
try
{
int oldIsolation = conn.getTransactionIsolation();
conn.setTransactionIsolation( Connection.TRANSACTION_SERIALIZABLE );
try
{
try
{
Statement stmt = conn.createStatement();
int tries = 0;
// May run into conflicts with other processes, so try this up to 50
// times before giving up.
while( tries < 50 )
{
// Get the nextId from the table
ResultSet rs = stmt.executeQuery(
"SELECT next_id FROM " + m_table + " WHERE table_name = '" + m_tableName + "'" );
if( !rs.next() )
{
// The row does not exist.
String msg = "Unable to allocate a block of Ids, no row with table_name='" +
m_tableName + "' exists in the " + m_table + " table.";
getLogger().error( msg );
conn.rollback();
throw new IdException( msg );
}
// Get the next_id using the appropriate data type.
Object nextId;
if( useBigDecimals )
{
nextId = rs.getBigDecimal( 1 );
}
else
{
nextId = new Long( rs.getLong( 1 ) );
}
// Update the value of next_id in the database so it reflects the full block
// being allocated. If another process has done the same thing, then this
// will throw an exception due to transaction isolation.
try
{
int updated = stmt.executeUpdate( "UPDATE " + m_table +
" SET next_id = next_id + " + blockSize +
" WHERE table_name = '" + m_tableName + "'" );
if( updated >= 1 )
{
// Update was successful.
conn.commit();
// Return the next id obtained above.
return nextId;
}
else
{
// May have been a transaction confict. Try again.
if( getLogger().isDebugEnabled() )
{
getLogger().debug(
"Update resulted in no rows being changed." );
}
}
}
catch( SQLException e )
{
// Assume that this was caused by a transaction conflict. Try again.
if( getLogger().isDebugEnabled() )
{
getLogger().debug(
"Encountered an exception attempting to update the " +
m_table + " table. May be a transaction confict. " +
"Trying again: " + e.getMessage() );
}
}
// If we got here, then we failed, roll back the connection so we can
// try again.
conn.rollback();
tries++;
}
// If we got here then we ran out of tries.
getLogger().error( "Unable to allocate a block of Ids. Too many retries." );
return null;
}
catch( SQLException e )
{
// Need this catch so that the connection can be rolled back before
// the transaction is set in the finally block.
String msg = "Unable to allocate a block of Ids.";
getLogger().error( msg, e );
// Rollback after the error is logged so that any problems rolling back
// will not prevent the error from being logged.
conn.rollback();
throw new IdException( msg, e );
}
}
finally
{
// Restore the isolation level
conn.setTransactionIsolation( oldIsolation );
}
}
finally
{
// restore Auto commit
conn.setAutoCommit( oldAutoCommit );
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
String msg = "Unable to allocate a block of Ids.";
getLogger().error( msg, e );
throw new IdException( msg, e );
}
}
/**
* Allocates a block of ids of the given size and returns the first id.
* MySQL does not support transactions so this method handles synchronization
* by making use of table locking.
*
* @param blockSize number of ids to allocate.
* @param useBigDecimals returns the first id as a BigDecimal if true, otherwise as a Long.
*
* @return either a Long or a BigDecimal depending on the value of useBigDecimals
*
* @throws IdException if a block of ids can not be allocated.
*/
private Object allocateIdBlockMySQL( int blockSize, boolean useBigDecimals )
throws IdException
{
if( getLogger().isDebugEnabled() )
{
getLogger().debug( "Allocating a new block of " + blockSize + " ids." );
}
try
{
Connection conn = getConnection();
try
{
Statement stmt = conn.createStatement();
// Obtain a lock on the table
stmt.executeUpdate( "LOCK TABLES " + m_table + " WRITE" );
try
{
// Get the nextId from the table
ResultSet rs = stmt.executeQuery(
"SELECT next_id FROM " + m_table + " WHERE table_name = '" + m_tableName + "'" );
if( !rs.next() )
{
// The row does not exist.
String msg = "Unable to allocate a block of Ids, no row with table_name='" +
m_tableName + "' exists in the " + m_table + " table.";
getLogger().error( msg );
throw new IdException( msg );
}
// Get the next_id using the appropriate data type.
Object nextId;
Object nextSavedId;
if( useBigDecimals )
{
BigDecimal id = rs.getBigDecimal( 1 );
nextId = id;
nextSavedId = id.add( new BigDecimal( blockSize ) );
}
else
{
Long id = new Long( rs.getLong( 1 ) );
nextId = id;
nextSavedId = new Long( id.longValue() + blockSize );
}
// Update the value of next_id in the database so it reflects the full block
// being allocated.
//
// Queries that set next_id = next_id + 10 do not work with large decimal values on MySQL 3.23.31
int updated = stmt.executeUpdate( "UPDATE " + m_table +
" SET next_id = " + nextSavedId +
" WHERE table_name = '" + m_tableName + "'" );
if( updated >= 1 )
{
// Return the next id obtained above.
return nextId;
}
else
{
String msg = "Update resulted in no rows being changed.";
getLogger().error( msg );
throw new IdException( msg );
}
}
finally
{
// Make absolutely sure that the lock is always released.
stmt.executeUpdate( "UNLOCK TABLES" );
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
String msg = "Unable to allocate a block of Ids.";
getLogger().error( msg, e );
throw new IdException( msg, e );
}
}
/*---------------------------------------------------------------
* AbstractDataSourceBlockIdGenerator Methods
*-------------------------------------------------------------*/
/**
* Allocates a block, of the given size, of ids from the database.
*
* @param blockSize number of Ids which are to be allocated.
*
* @return The first id in the allocated block.
*
* @throws IdException if there it was not possible to allocate a block of ids.
*/
protected BigDecimal allocateBigDecimalIdBlock( int blockSize )
throws IdException
{
BigDecimal id;
switch( m_dbType )
{
case AbstractDataSourceIdGenerator.DBTYPE_MYSQL:
id = (BigDecimal)allocateIdBlockMySQL( blockSize, true );
break;
default:
id = (BigDecimal)allocateIdBlock( blockSize, true );
}
return id;
}
/**
* Allocates a block, of the given size, of ids from the database.
*
* @param blockSize number of Ids which are to be allocated.
*
* @return The first id in the allocated block.
*
* @throws IdException if there it was not possible to allocate a block of ids.
*/
protected long allocateLongIdBlock( int blockSize )
throws IdException
{
Long id;
switch( m_dbType )
{
case AbstractDataSourceIdGenerator.DBTYPE_MYSQL:
id = (Long)allocateIdBlockMySQL( blockSize, false );
break;
default:
id = (Long)allocateIdBlock( blockSize, false );
}
return id.longValue();
}
/*---------------------------------------------------------------
* Configurable Methods
*-------------------------------------------------------------*/
/**
* Called by the Container to configure the component.
*
* @param configuration configuration info used to setup the component.
*
* @throws ConfigurationException if there are any problems with the configuration.
*/
public void configure( Configuration configuration )
throws ConfigurationException
{
super.configure( configuration );
// Obtain the table name.
m_table = configuration.getAttribute( "table", "ids" );
// Obtain the key-table.
m_tableName = configuration.getAttribute( "key-table", "id" );
}
}
1.1 jakarta-avalon-excalibur/datasource/src/test/org/apache/avalon/excalibur/datasource/ids/test/TableIdGeneratorJdbcTestCase.java
Index: TableIdGeneratorJdbcTestCase.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids.test;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.excalibur.datasource.ids.IdException;
import org.apache.avalon.excalibur.datasource.ids.IdGenerator;
import org.apache.avalon.excalibur.testcase.ExcaliburTestCase;
import org.apache.avalon.framework.component.ComponentSelector;
/**
* Test the TableIdGenerator Component.
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
*/
public class TableIdGeneratorJdbcTestCase
extends ExcaliburTestCase
{
private ComponentSelector m_dbSelector;
private DataSourceComponent m_dataSource;
private ComponentSelector m_idGeneratorSelector;
private IdGenerator m_idGenerator;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public TableIdGeneratorJdbcTestCase( String name )
{
super( name );
// Set the priority for default log output.
m_logPriority = org.apache.log.Priority.INFO;
}
/*---------------------------------------------------------------
* TestCase Methods
*-------------------------------------------------------------*/
public void setUp() throws Exception
{
super.setUp();
// Get a reference to a data source
m_dbSelector = (ComponentSelector)manager.lookup( DataSourceComponent.ROLE + "Selector" );
m_dataSource = (DataSourceComponent)m_dbSelector.select( "test-db" );
// We need to initialize an ids table in the database for these tests.
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
// Try to drop the table. It may not exist and throw an exception.
getLogger().debug( "Attempting to drop old ids table" );
try
{
statement.executeUpdate( "DROP TABLE ids" );
}
catch( SQLException e )
{
// The table was probably just not there. Ignore this.
}
// Create the table that we will use in this test.
// Different depending on the db. Please add new statements as new databases are
// tested.
getLogger().debug( "Create new ids table" );
statement.executeUpdate(
"CREATE TABLE ids ( " +
"table_name varchar(16) NOT NULL, " +
"next_id DECIMAL(30) NOT NULL, " +
"PRIMARY KEY (table_name))" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to initialize database for test.", e );
fail( "Unable to initialize database for test. " + e );
}
// Get a reference to an IdGenerator Selector.
// Individual IdGenerators are obtained in the tests.
m_idGeneratorSelector = (ComponentSelector)manager.lookup( IdGenerator.ROLE + "Selector" );
}
public void tearDown() throws Exception
{
// Free up the IdGenerator Selector
if( m_idGeneratorSelector != null )
{
manager.release( m_idGeneratorSelector );
m_dbSelector = null;
}
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
// Delete the table that we will use in this test.
getLogger().debug( "Drop ids table" );
statement.executeUpdate( "DROP TABLE ids" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to cleanup database after test.", e );
// Want to continue
}
// Free up the data source
if( m_dbSelector != null )
{
if( m_dataSource != null )
{
m_dbSelector.release( m_dataSource );
m_dataSource = null;
}
manager.release( m_dbSelector );
m_dbSelector = null;
}
super.tearDown();
}
/*---------------------------------------------------------------
* Test Cases
*-------------------------------------------------------------*/
public void testNonExistingTableName() throws Exception
{
getLogger().info( "testNonExistingTableName" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testNonExistingTableName" );
try
{
try
{
int id = idGenerator.getNextIntegerId();
fail( "Should not have gotten an id" );
}
catch( IdException e )
{
// Got the expected error.
}
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testSimpleRequestIdsSize1() throws Exception
{
getLogger().info( "testSimpleRequestIdsSize1" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize1" );
try
{
int testCount = 100;
// Initialize the counter in the database.
initializeNextLongId( "test", 1 );
for( int i = 1; i <= testCount; i++ )
{
int id = idGenerator.getNextIntegerId();
assertEquals( "The returned id was not what was expected.", i, id );
}
assertEquals( "The next_id column in the database did not have the expected value.",
testCount + 1, peekNextLongId( "test" ) );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testSimpleRequestIdsSize10() throws Exception
{
getLogger().info( "testSimpleRequestIdsSize10" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize10" );
try
{
int testCount = 100;
// Initialize the counter in the database.
initializeNextLongId( "test", 1 );
for( int i = 1; i <= testCount; i++ )
{
int id = idGenerator.getNextIntegerId();
assertEquals( "The returned id was not what was expected.", i, id );
}
assertEquals( "The next_id column in the database did not have the expected value.",
testCount + 1, peekNextLongId( "test" ) );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testSimpleRequestIdsSize100() throws Exception
{
getLogger().info( "testSimpleRequestIdsSize100" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize100" );
try
{
int testCount = 100;
// Initialize the counter in the database.
initializeNextLongId( "test", 1 );
for( int i = 1; i <= testCount; i++ )
{
int id = idGenerator.getNextIntegerId();
assertEquals( "The returned id was not what was expected.", i, id );
}
assertEquals( "The next_id column in the database did not have the expected value.",
testCount + 1, peekNextLongId( "test" ) );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testBigDecimalRequestIdsSize10() throws Exception
{
getLogger().info( "testBigDecimalRequestIdsSize10" );
if( isBigDecimalImplemented() )
{
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testBigDecimalRequestIdsSize10" );
try
{
int testCount = 100;
BigDecimal initial = new BigDecimal( Long.MAX_VALUE + "00" );
// Initialize the counter in the database.
initializeNextBigDecimalId( "test", initial );
for( int i = 0; i < testCount; i++ )
{
BigDecimal id = idGenerator.getNextBigDecimalId();
assertEquals( "The returned id was not what was expected.",
initial.add( new BigDecimal( i ) ), id );
}
assertEquals( "The next_id column in the database did not have the expected value.",
initial.add( new BigDecimal( testCount ) ), peekNextBigDecimalId( "test" ) );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
else
{
getLogger().warn( "Test Skipped because BigDecimals are not implemented in current driver." );
}
}
public void testMaxByteIds() throws Exception
{
getLogger().info( "testMaxByteIds" );
IdGenerator idGenerator = (IdGenerator)m_idGeneratorSelector.select( "ids-testMaxByteIds" );
try
{
int testCount = 100;
long max = Byte.MAX_VALUE;
long initial = max - testCount;
// Initialize the counter in the database.
initializeNextLongId( "test", initial );
for( int i = 0; i <= testCount; i++ )
{
byte id = idGenerator.getNextByteId();
assertEquals( "The returned id was not what was expected.", i + initial, id );
}
// Next one should throw an exception
try
{
byte id = idGenerator.getNextByteId();
fail( "Should not have gotten an id: " + id );
}
catch( IdException e )
{
// Good. Got the exception.
}
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testMaxShortIds() throws Exception
{
getLogger().info( "testMaxShortIds" );
IdGenerator idGenerator = (IdGenerator)m_idGeneratorSelector.select( "ids-testMaxShortIds" );
try
{
int testCount = 100;
long max = Short.MAX_VALUE;
long initial = max - testCount;
// Initialize the counter in the database.
initializeNextLongId( "test", initial );
for( int i = 0; i <= testCount; i++ )
{
short id = idGenerator.getNextShortId();
assertEquals( "The returned id was not what was expected.", i + initial, id );
}
// Next one should throw an exception
try
{
short id = idGenerator.getNextShortId();
fail( "Should not have gotten an id: " + id );
}
catch( IdException e )
{
// Good. Got the exception.
}
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testMaxIntegerIds() throws Exception
{
getLogger().info( "testMaxIntegerIds" );
IdGenerator idGenerator = (IdGenerator)m_idGeneratorSelector.select( "ids-testMaxIntegerIds" );
try
{
int testCount = 100;
long max = Integer.MAX_VALUE;
long initial = max - testCount;
// Initialize the counter in the database.
initializeNextLongId( "test", initial );
for( int i = 0; i <= testCount; i++ )
{
int id = idGenerator.getNextIntegerId();
assertEquals( "The returned id was not what was expected.", i + initial, id );
}
// Next one should throw an exception
try
{
int id = idGenerator.getNextIntegerId();
fail( "Should not have gotten an id: " + id );
}
catch( IdException e )
{
// Good. Got the exception.
}
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testMaxLongIds() throws Exception
{
getLogger().info( "testMaxLongIds" );
IdGenerator idGenerator = (IdGenerator)m_idGeneratorSelector.select( "ids-testMaxLongIds" );
try
{
int testCount = 100;
long max = Long.MAX_VALUE;
long initial = max - testCount;
// Initialize the counter in the database.
initializeNextLongId( "test", initial );
for( int i = 0; i <= testCount; i++ )
{
long id = idGenerator.getNextLongId();
assertEquals( "The returned id was not what was expected.", i + initial, id );
}
// Next one should throw an exception
try
{
long id = idGenerator.getNextLongId();
fail( "Should not have gotten an id: " + id );
}
catch( IdException e )
{
// Good. Got the exception.
}
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
/*---------------------------------------------------------------
* Utilitity Methods
*-------------------------------------------------------------*/
/**
* Tests to see whether or not the current DataSource supports BigDecimal
*/
private boolean isBigDecimalImplemented()
{
String tableName = "foorbar_table";
// Add a row that can be selected.
initializeNextLongId( tableName, 1 );
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
"WHERE table_name = '" + tableName + "'" );
if( rs.next() )
{
BigDecimal id = rs.getBigDecimal( 1 );
}
else
{
fail( tableName + " row not in ids table." );
return false; // for compiler
}
}
finally
{
conn.close();
}
// Implemented
return true;
}
catch( SQLException e )
{
if( e.toString().toLowerCase().indexOf( "implemented" ) > 0 )
{
// Not implemented
return false;
}
getLogEnabledLogger().error( "Unable to test for BigDecimal support.", e );
fail( "Unable to test for BigDecimal support. " + e );
return false; // for compiler
}
}
private void initializeNextBigDecimalId( String tableName, BigDecimal nextId )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
statement.executeUpdate( "INSERT INTO ids (table_name, next_id) VALUES ('" +
tableName + "', " + nextId.toString() + ")" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to initialize next_id.", e );
fail( "Unable to initialize next_id. " + e );
}
}
private void initializeNextLongId( String tableName, long nextId )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
statement.executeUpdate( "INSERT INTO ids (table_name, next_id) VALUES ('" +
tableName + "', " + nextId + ")" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to initialize next_id.", e );
fail( "Unable to initialize next_id. " + e );
}
}
private BigDecimal peekNextBigDecimalId( String tableName )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
"WHERE table_name = '" + tableName + "'" );
if( rs.next() )
{
return rs.getBigDecimal( 1 );
}
else
{
fail( tableName + " row not in ids table." );
return null; // for compiler
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to peek next_id.", e );
fail( "Unable to peek next_id. " + e );
return null; // for compiler
}
}
private long peekNextLongId( String tableName )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
"WHERE table_name = '" + tableName + "'" );
if( rs.next() )
{
return rs.getLong( 1 );
}
else
{
fail( tableName + " row not in ids table." );
return -1; // for compiler
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogger().error( "Unable to peek next_id.", e );
fail( "Unable to peek next_id. " + e );
return -1; // for compiler
}
}
}
1.1 jakarta-avalon-excalibur/datasource/src/test/org/apache/avalon/excalibur/datasource/ids/test/TableIdGeneratorJdbcTestCase.xtest
Index: TableIdGeneratorJdbcTestCase.xtest
===================================================================
<testcase>
<annotation>
<![CDATA[
<title>TableIdGenerator Tests</title>
<para>
This series of tests excersizes the TableIdGenerator provided by Excalibur.
The configuration is specified in the file located in
<parameter>jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/datasource/ids/test/TableIdGeneratorJdbcTestCase.xtext</parameter>.
</para>
]]>
</annotation>
<!-- =================================================================== -->
<!-- LogKit Configuration. -->
<!-- =================================================================== -->
<logkit>
<factories>
<factory type="stream"
class="org.apache.avalon.excalibur.logger.factory.StreamTargetFactory"/>
<factory type="file" class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/>
</factories>
<targets>
<stream id="console">
<stream>System.out</stream>
<format type="avalon">
%7.7{priority} %5.5{time} [%8.8{category}] (%{context}): %{message}\n%{throwable}
</format>
</stream>
<file id="file">
<filename>TEST-org.apache.avalon.excalibur.datasource.ids.test.TableIdGeneratorJdbcTestCase.log</filename>
<format type="extended">
%7.7{priority} %5.5{time} [%8.8{category}] (%{context}): %{message}\n%{throwable}
</format>
</file>
</targets>
<categories>
<category name="jdbc" log-level="INFO">
<log-target id-ref="console"/>
<log-target id-ref="file"/>
</category>
<category name="id-gen" log-level="INFO">
<log-target id-ref="console"/>
<log-target id-ref="file"/>
</category>
</categories>
</logkit>
<!-- =================================================================== -->
<!-- Roles Configuration. -->
<!-- =================================================================== -->
<roles>
<role name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
shorthand="datasources"
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
<hint shorthand="jdbc"
class="org.apache.avalon.excalibur.datasource.ResourceLimitingJdbcDataSource"/>
</role>
<role name="org.apache.avalon.excalibur.datasource.ids.IdGeneratorSelector"
shorthand="id-generators"
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
<hint shorthand="table"
class="org.apache.avalon.excalibur.datasource.ids.TableIdGenerator"/>
</role>
<role name="org.apache.avalon.excalibur.datasource.ids.IdGenerator"
shorthand="id-generator"
default-class="org.apache.avalon.excalibur.datasource.ids.TableIdGenerator"/>
</roles>
<!-- =================================================================== -->
<!-- Component Configuration. -->
<!-- =================================================================== -->
<components>
<datasources>
<jdbc name="test-db" logger="jdbc">
<pool-controller min="1" max="10"/>
<auto-commit>true</auto-commit>
<driver>@test.jdbc.driver@</driver>
<dburl>@test.jdbc.url@</dburl>
<user>@test.jdbc.user@</user>
<password>@test.jdbc.password@</password>
</jdbc>
</datasources>
<id-generators>
<table name="ids-testNonExistingTableName" block-size="1" table="ids"
key-table="does-not-exist" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testSimpleRequestIdsSize1" block-size="1" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testSimpleRequestIdsSize10" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testSimpleRequestIdsSize100" block-size="100" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testBigDecimalRequestIdsSize10" big-decimals="true"
block-size="10" table="ids" key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxByteIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxShortIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxIntegerIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxLongIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
</id-generators>
</components>
</testcase>
1.1 jakarta-avalon-excalibur/datasource/src/test/org/apache/avalon/excalibur/datasource/ids/test/TableIdGeneratorMultithreadedJdbcTestCase.java
Index: TableIdGeneratorMultithreadedJdbcTestCase.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.excalibur.datasource.ids.test;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.excalibur.datasource.ids.IdGenerator;
import org.apache.avalon.excalibur.testcase.CascadingAssertionFailedError;
import org.apache.avalon.excalibur.testcase.ExcaliburTestCase;
import org.apache.avalon.excalibur.testcase.LatchedThreadGroup;
import org.apache.avalon.framework.component.ComponentSelector;
/**
* Test the TableIdGenerator Component.
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
*/
public class TableIdGeneratorMultithreadedJdbcTestCase
extends ExcaliburTestCase
{
private static final String TABLE_KEY = "test";
private static final int ID_COUNT = 1000;
private static final int THREAD_COUNT = 50;
private ComponentSelector m_dbSelector;
private DataSourceComponent m_dataSource;
private ComponentSelector m_idGeneratorSelector;
private Object m_semaphore = new Object();
private IdGenerator m_idGenerator;
private int m_perThreadGets;
private HashMap m_ids;
private Throwable m_throwable;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public TableIdGeneratorMultithreadedJdbcTestCase( String name )
{
super( name );
// Set the priority for default log output.
m_logPriority = org.apache.log.Priority.INFO;
}
/*---------------------------------------------------------------
* TestCase Methods
*-------------------------------------------------------------*/
public void setUp() throws Exception
{
super.setUp();
// Get a reference to a data source
m_dbSelector = (ComponentSelector)manager.lookup( DataSourceComponent.ROLE + "Selector" );
m_dataSource = (DataSourceComponent)m_dbSelector.select( "test-db" );
// We need to initialize an ids table in the database for these tests.
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
// Try to drop the table. It may not exist and throw an exception.
getLogEnabledLogger().debug( "Attempting to drop old ids table" );
try
{
statement.executeUpdate( "DROP TABLE ids" );
}
catch( SQLException e )
{
// The table was probably just not there. Ignore this.
}
// Create the table that we will use in this test.
// Different depending on the db. Please add new statements as new databases are
// tested.
getLogEnabledLogger().debug( "Create new ids table" );
statement.executeUpdate(
"CREATE TABLE ids ( " +
"table_name varchar(16) NOT NULL, " +
"next_id DECIMAL(30) NOT NULL, " +
"PRIMARY KEY (table_name))" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogEnabledLogger().error( "Unable to initialize database for test.", e );
fail( "Unable to initialize database for test. " + e );
}
// Get a reference to an IdGenerator Selector.
// Individual IdGenerators are obtained in the tests.
m_idGeneratorSelector = (ComponentSelector)manager.lookup( IdGenerator.ROLE + "Selector" );
}
public void tearDown() throws Exception
{
// Free up the IdGenerator Selector
if( m_idGeneratorSelector != null )
{
manager.release( m_idGeneratorSelector );
m_dbSelector = null;
}
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
// Delete the table that we will use in this test.
getLogEnabledLogger().debug( "Drop ids table" );
statement.executeUpdate( "DROP TABLE ids" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogEnabledLogger().error( "Unable to cleanup database after test.", e );
// Want to continue
}
// Free up the data source
if( m_dbSelector != null )
{
if( m_dataSource != null )
{
m_dbSelector.release( m_dataSource );
m_dataSource = null;
}
manager.release( m_dbSelector );
m_dbSelector = null;
}
super.tearDown();
}
/*---------------------------------------------------------------
* Test Cases
*-------------------------------------------------------------*/
public void testSimpleRequestIdsSize1() throws Exception
{
getLogEnabledLogger().info( "testSimpleRequestIdsSize1" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize1" );
try
{
long firstId = 1;
int idCount = ID_COUNT;
int threadCount = THREAD_COUNT;
// Initialize the counter in the database.
initializeNextLongId( TABLE_KEY, firstId );
generalTestCase( idGenerator, firstId, idCount, threadCount );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testSimpleRequestIdsSize10() throws Exception
{
getLogEnabledLogger().info( "testSimpleRequestIdsSize10" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize10" );
try
{
long firstId = 1;
int idCount = ID_COUNT;
int threadCount = THREAD_COUNT;
// Initialize the counter in the database.
initializeNextLongId( TABLE_KEY, firstId );
generalTestCase( idGenerator, firstId, idCount, threadCount );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testSimpleRequestIdsSize100() throws Exception
{
getLogEnabledLogger().info( "testSimpleRequestIdsSize100" );
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize100" );
try
{
long firstId = 1;
int idCount = ID_COUNT;
int threadCount = THREAD_COUNT;
// Initialize the counter in the database.
initializeNextLongId( TABLE_KEY, firstId );
generalTestCase( idGenerator, firstId, idCount, threadCount );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
public void testBigDecimalRequestIdsSize10() throws Exception
{
getLogEnabledLogger().info( "testBigDecimalRequestIdsSize10" );
if( isBigDecimalImplemented() )
{
IdGenerator idGenerator =
(IdGenerator)m_idGeneratorSelector.select( "ids-testBigDecimalRequestIdsSize10" );
try
{
long firstId = 1;
int idCount = ID_COUNT;
int threadCount = THREAD_COUNT;
// Initialize the counter in the database.
initializeNextLongId( TABLE_KEY, firstId );
generalTestCase( idGenerator, firstId, idCount, threadCount );
}
finally
{
m_idGeneratorSelector.release( idGenerator );
}
}
else
{
getLogEnabledLogger().warn( "Test Skipped because BigDecimals are not implemented in current driver." );
}
}
/*---------------------------------------------------------------
* Utilitity Methods
*-------------------------------------------------------------*/
/**
* General multithreaded test of an IdGenerator
*
* @param idGenerator the Id Generator to test.
* @param firstId the first Id that is expected to be returned by the Id Generator.
* @param idCount the number of ids to request in the test.
* @param threadCount the number of threads to use to test the Id Generator.
*/
private void generalTestCase( final IdGenerator idGenerator,
final long firstId,
final int idCount,
final int threadCount )
{
if( idCount % threadCount != 0 )
{
fail( "idCount must be evenly divisible by threadCount" );
}
m_idGenerator = idGenerator;
m_perThreadGets = idCount / threadCount;
m_ids = new HashMap();
// Create the runnable which will be used by the test.
Runnable runnable = new Runnable()
{
public void run()
{
boolean duplicatesFound = false;
for( int i = 0; i < m_perThreadGets; i++ )
{
try
{
long id = m_idGenerator.getNextLongId();
synchronized( m_semaphore )
{
Long lId = new Long( id );
// Make sure this id has not already been seen
if( m_ids.get( lId ) != null )
{
getLogEnabledLogger().error( "Obtained a duplicate id: " + id );
duplicatesFound = true;
}
else
{
// Store a reference to this id
m_ids.put( lId, lId );
}
}
}
catch( Throwable t )
{
synchronized( m_semaphore )
{
if( m_throwable == null )
{
m_throwable = t;
}
}
return;
}
}
if( duplicatesFound )
{
fail( "IdGenerator returned duplicate ids." );
}
}
};
LatchedThreadGroup group = new LatchedThreadGroup( runnable, threadCount );
group.enableLogging( getLogEnabledLogger() );
// Run the test.
long duration;
try
{
duration = group.go();
}
catch( Throwable t )
{
// Throwable could have been thrown by one of the tests.
if( m_throwable == null )
{
m_throwable = t;
}
duration = 0;
}
if( m_throwable != null )
{
throw new CascadingAssertionFailedError( "Exception in test thread.", m_throwable );
}
// Make sure that all of the expected ids were obtained
for( int i = 0; i < idCount; i++ )
{
Long id = new Long( firstId + i );
assertTrue( "The IdGenerator did not return an expected id (" + id + ")",
m_ids.get( id ) != null );
}
getLogEnabledLogger().info( "It took " + duration + "ms. for " + threadCount +
" threads to allocate " + idCount + " ids." );
assertEquals( "The next_id column in the database did not have the expected value.",
firstId + idCount, peekNextLongId( TABLE_KEY ) );
}
/**
* Tests to see whether or not the current DataSource supports BigDecimal
*/
private boolean isBigDecimalImplemented()
{
String tableName = "foorbar_table";
// Add a row that can be selected.
initializeNextLongId( tableName, 1 );
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
"WHERE table_name = '" + tableName + "'" );
if( rs.next() )
{
BigDecimal id = rs.getBigDecimal( 1 );
}
else
{
fail( tableName + " row not in ids table." );
return false; // for compiler
}
}
finally
{
conn.close();
}
// Implemented
return true;
}
catch( SQLException e )
{
if( e.toString().toLowerCase().indexOf( "implemented" ) > 0 )
{
// Not implemented
return false;
}
getLogEnabledLogger().error( "Unable to test for BigDecimal support.", e );
fail( "Unable to test for BigDecimal support. " + e );
return false; // for compiler
}
}
private void initializeNextBigDecimalId( String tableName, BigDecimal nextId )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
statement.executeUpdate( "INSERT INTO ids (table_name, next_id) VALUES ('" +
tableName + "', " + nextId.toString() + ")" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogEnabledLogger().error( "Unable to initialize next_id.", e );
fail( "Unable to initialize next_id. " + e );
}
}
private void initializeNextLongId( String tableName, long nextId )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
statement.executeUpdate( "INSERT INTO ids (table_name, next_id) VALUES ('" +
tableName + "', " + nextId + ")" );
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogEnabledLogger().error( "Unable to initialize next_id.", e );
fail( "Unable to initialize next_id. " + e );
}
}
private BigDecimal peekNextBigDecimalId( String tableName )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
"WHERE table_name = '" + tableName + "'" );
if( rs.next() )
{
return rs.getBigDecimal( 1 );
}
else
{
fail( tableName + " row not in ids table." );
return null; // for compiler
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogEnabledLogger().error( "Unable to peek next_id.", e );
fail( "Unable to peek next_id. " + e );
return null; // for compiler
}
}
private long peekNextLongId( String tableName )
{
try
{
Connection conn = m_dataSource.getConnection();
try
{
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
"WHERE table_name = '" + tableName + "'" );
if( rs.next() )
{
return rs.getLong( 1 );
}
else
{
fail( tableName + " row not in ids table." );
return -1; // for compiler
}
}
finally
{
conn.close();
}
}
catch( SQLException e )
{
getLogEnabledLogger().error( "Unable to peek next_id.", e );
fail( "Unable to peek next_id. " + e );
return -1; // for compiler
}
}
}
1.1 jakarta-avalon-excalibur/datasource/src/test/org/apache/avalon/excalibur/datasource/ids/test/TableIdGeneratorMultithreadedJdbcTestCase.xtest
Index: TableIdGeneratorMultithreadedJdbcTestCase.xtest
===================================================================
<testcase>
<annotation>
<![CDATA[
<title>TableIdGenerator Tests</title>
<para>
This series of tests excersizes the TableIdGenerator provided by Excalibur in a multithreaded context.
The configuration is specified in the file located in
<parameter>jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/datasource/ids/test/TableIdGeneratorMultithreadedJdbcTestCase.xtext</parameter>.
</para>
]]>
</annotation>
<!-- =================================================================== -->
<!-- LogKit Configuration. -->
<!-- =================================================================== -->
<logkit>
<factories>
<factory type="stream"
class="org.apache.avalon.excalibur.logger.factory.StreamTargetFactory"/>
<factory type="file" class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/>
</factories>
<targets>
<stream id="console">
<stream>System.out</stream>
<format type="avalon">
%7.7{priority} %5.5{time} [%8.8{category}] (%{context}): %{message}\n%{throwable}
</format>
</stream>
<file id="file">
<filename>TEST-org.apache.avalon.excalibur.datasource.ids.test.TableIdGeneratorMultithreadedJdbcTestCase.log</filename>
<format type="extended">
%7.7{priority} %5.5{time} [%8.8{category}] (%{context}): %{message}\n%{throwable}
</format>
</file>
</targets>
<categories>
<category name="jdbc" log-level="INFO">
<log-target id-ref="console"/>
<log-target id-ref="file"/>
</category>
<category name="id-gen" log-level="INFO">
<log-target id-ref="console"/>
<log-target id-ref="file"/>
</category>
</categories>
</logkit>
<!-- =================================================================== -->
<!-- Roles Configuration. -->
<!-- =================================================================== -->
<roles>
<role name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
shorthand="datasources"
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
<hint shorthand="jdbc"
class="org.apache.avalon.excalibur.datasource.ResourceLimitingJdbcDataSource"/>
</role>
<role name="org.apache.avalon.excalibur.datasource.ids.IdGeneratorSelector"
shorthand="id-generators"
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
<hint shorthand="table"
class="org.apache.avalon.excalibur.datasource.ids.TableIdGenerator"/>
</role>
<role name="org.apache.avalon.excalibur.datasource.ids.IdGenerator"
shorthand="id-generator"
default-class="org.apache.avalon.excalibur.datasource.ids.TableIdGenerator"/>
</roles>
<!-- =================================================================== -->
<!-- Component Configuration. -->
<!-- =================================================================== -->
<components>
<datasources>
<jdbc name="test-db" logger="jdbc">
<pool-controller min="1" max="10"/>
<auto-commit>true</auto-commit>
<driver>@test.jdbc.driver@</driver>
<dburl>@test.jdbc.url@</dburl>
<user>@test.jdbc.user@</user>
<password>@test.jdbc.password@</password>
</jdbc>
</datasources>
<id-generators>
<table name="ids-testNonExistingTableName" block-size="1" table="ids"
key-table="does-not-exist" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testSimpleRequestIdsSize1" block-size="1" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testSimpleRequestIdsSize10" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testSimpleRequestIdsSize100" block-size="100" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testBigDecimalRequestIdsSize10" big-decimals="true"
block-size="10" table="ids" key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxByteIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxShortIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxIntegerIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
<table name="ids-testMaxLongIds" block-size="10" table="ids"
key-table="test" logger="id-gen">
<dbpool>test-db</dbpool>
</table>
</id-generators>
</components>
</testcase>
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>