org.apache.avalon.excalibur.datasource.cluster.DefaultHashedDataSourceCluster Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.
*
* 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.
*
* The Configuration for a 2 database cluster is like this:
*
*
* <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>
*
*
* With the following roles declaration:
*
*
* <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>
*
*
* 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.
*
* 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.
*
* 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.
*
* old:
*
* Connection connection = m_dataSource.getConnection();
*
*
* new:
*
* Connection connection;
* if ( m_dataSource instanceof HashedDataSourceCluster )
* {
* connection = ((HashedDataSourceCluster)m_dataSource).getConnectionForHashObject( hashObject );
* }
* else
* {
* connection = m_dataSource.getConnection();
* }
*
*
* @avalon.component
* @avalon.service type=org.apache.avalon.excalibur.datasource.DataSourceComponent
* @avalon.service type=HashedDataSourceCluster
* @x-avalon.info name=hash-db-cluster
* @x-avalon.lifestyle type=singleton
*
* @author Avalon Development Team
* @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:20 $
* @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 NoValidConnectionException 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 NoValidConnectionException 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.
*
* Subclasses can override this method to get different behavior.
*
* 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.
*
* Subclasses can override this method to get different behavior.
*
* 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() );
}
}