leif        02/02/08 04:27:32

  Added:       src/scratchpad/org/apache/avalon/excalibur/datasource/ids
                        AbstractDataSourceBlockIdGenerator.java
                        AbstractDataSourceIdGenerator.java
                        AbstractIdGenerator.java IdException.java
                        IdGenerator.java SequenceIdGenerator.java
                        TableIdGenerator.java
               src/scratchpad/org/apache/avalon/excalibur/datasource/ids/test
                        TableIdGeneratorJdbcTestCase.java
                        TableIdGeneratorJdbcTestCase.xtest
  Log:
  Initial stab at IdGenerators
  
  Revision  Changes    Path
  1.1                  
jakarta-avalon-excalibur/src/scratchpad/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 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 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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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;
          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/src/scratchpad/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.math.BigDecimal;
  import java.sql.Connection;
  import java.sql.SQLException;
  
  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.Composable;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentSelector;
  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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @since 4.1
   */
  public abstract class AbstractDataSourceIdGenerator
      extends AbstractIdGenerator
      implements IdGenerator, Composable, Configurable, Initializable, 
Disposable, ThreadSafe
  {
      /** ComponentManager which created this component */
      protected ComponentManager    m_manager;
  
      private   String              m_dataSourceName;
      private   ComponentSelector   m_dbSelector;
      protected DataSourceComponent m_dataSource;
      
      /**
       * 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 );
      }
  
      /*---------------------------------------------------------------
       * 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/src/scratchpad/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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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/src/scratchpad/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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @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/src/scratchpad/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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @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.
       *
       * @returns the next Id.
       */
      BigDecimal getNextBigDecimalId()
          throws IdException;
      
      /**
       * Returns the next Id from the pool.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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.
       *
       * @returns 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/src/scratchpad/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>
   *   &lt;id-generators&gt;
   *       &lt;sequence name="user-ids" logger="cm.ids"&gt;
   *           &lt;dbpool&gt;user-db&lt;/dbpool&gt;
   *           &lt;query&gt;SELECT NEXTVAL('category_ids')&lt;/query&gt;
   *       &lt;/sequence&gt;
   *   &lt;/id-generators&gt;
   * </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>
   *   &lt;role 
name="org.apache.avalon.excalibur.datasource.ids.IdGeneratorSelector"
   *         shorthand="id-generators"
   *         
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector"&gt;
   *       &lt;hint shorthand="sequence"
   *             
class="org.apache.avalon.excalibur.datasource.ids.SequenceIdGenerator"/&gt;
   *   &lt;/role&gt;
   * </pre>
   *
   * To configure your component to use the IdGenerator declared above, its 
configuration should look
   *  something like the following:
   * <pre>
   *   &lt;user-service logger="cm"&gt;
   *       &lt;dbpool&gt;user-db&lt;/dbpool&gt;
   *       &lt;id-generator&gt;user-ids&lt;/id-generator&gt;
   *   &lt;/user-service&gt;
   * </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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @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.
       *
       * @returns 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.
       *
       * @returns 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/src/scratchpad/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.Statement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  
  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>
   *   &lt;id-generators&gt;
   *       &lt;table name="user-ids" big-decimals="true" block-size="1" 
table="ids"
   *           key-table="event-type" logger="cm.ids"&gt;
   *           &lt;dbpool&gt;user-db&lt;/dbpool&gt;
   *       &lt;/table&gt;
   *   &lt;/id-generators&gt;
   * </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>
   *   &lt;role 
name="org.apache.avalon.excalibur.datasource.ids.IdGeneratorSelector"
   *         shorthand="id-generators"
   *         
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector"&gt;
   *       &lt;hint shorthand="table"
   *             
class="org.apache.avalon.excalibur.datasource.ids.TableIdGenerator"/&gt;
   *   &lt;/role&gt;
   * </pre>
   *
   * To configure your component to use the IdGenerator declared above, its 
configuration should look
   *  something like the following:
   * <pre>
   *   &lt;user-service logger="cm"&gt;
   *       &lt;dbpool&gt;user-db&lt;/dbpool&gt;
   *       &lt;id-generator&gt;user-ids&lt;/id-generator&gt;
   *   &lt;/user-service&gt;
   * </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:[EMAIL PROTECTED]">Leif Mortenson</a>
   * @version CVS $Revision: 1.1 $ $Date: 2002/02/08 12:27:32 $
   * @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.
       *
       * @returns 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." );
          }
          
          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 );
          }
      }
      
      /*---------------------------------------------------------------
       * AbstractDataSourceBlockIdGenerator Methods
       *-------------------------------------------------------------*/
      /**
       * Allocates a block, of the given size, of ids from the database.
       *
       * @param blockSize number of Ids which are to be allocated.
       *
       * @returns 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
      {
          return (BigDecimal)allocateIdBlock( blockSize, true );
      }
      
      /**
       * Allocates a block, of the given size, of ids from the database.
       *
       * @param blockSize number of Ids which are to be allocated.
       *
       * @returns 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
      {
          return ((Long)allocateIdBlock( blockSize, false )).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/src/scratchpad/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:[EMAIL PROTECTED]">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-size-does-not-exist" );
          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-size-1" );
          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-size-10" );
          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-size-100" );
          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" );
          
          IdGenerator idGenerator = (IdGenerator)m_idGeneratorSelector.select( 
"ids-size-10-bd" );
          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 );
          }
      }
      
      public void testMaxByteIds() throws Exception
      {
          getLogger().info( "testMaxByteIds" );
          
          IdGenerator idGenerator = (IdGenerator)m_idGeneratorSelector.select( 
"ids-size-10" );
          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-size-10" );
          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-size-10" );
          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-size-10" );
          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
       *-------------------------------------------------------------*/
      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( "next_id 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( "next_id 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/src/scratchpad/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="DEBUG">
                  <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-size-does-not-exist" block-size="1" table="ids"
                     key-table="does-not-exist" logger="id-gen">
                  <dbpool>test-db</dbpool>
              </table>
              
              <table name="ids-size-1" block-size="1" table="ids" 
key-table="test" logger="id-gen">
                  <dbpool>test-db</dbpool>
              </table>
              
              <table name="ids-size-10" block-size="10" table="ids" 
key-table="test" logger="id-gen">
                  <dbpool>test-db</dbpool>
              </table>
              
              <table name="ids-size-100" block-size="100" table="ids" 
key-table="test"
                     logger="id-gen">
                  <dbpool>test-db</dbpool>
              </table>
              
              <table name="ids-size-10-bd" big-decimals="true" block-size="10" 
table="ids"
                     key-table="test" logger="id-gen">
                  <dbpool>test-db</dbpool>
              </table>
          </id-generators>
      </components>
  </testcase>
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to