Introduction
When establishing a connection to a database, we all know that it can be a time consuming process. The reason for using a connection pool is therefore primary because of latency for that particular process. A connection pool holds a bundle of connections, which never will be released β unless the pool is closed. During the design of a connection pool, it is important to introduce an abstraction layer, which will allow different kinds of connections and even different kinds of pools. The major approach for doing this will be to introduce a few patterns. All patterns are described in [1] and are summarized with quotations from [1] below.
Abstract Factory
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Use the Abstract Factory pattern when a system should be independent of how its products are created, composed, and represented; a system should be configured with one of multiple families of products; a family of related product objects is designed to be used together, and you need to enforce this constraint or you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations.
Singleton
Ensure a class only has one instance, and provide a global point of access to it.
Use the Singleton pattern when there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point or when the sole instance should be extensible by sub classing, and clients should be able to use an extended instance without modifying their code.
Factory Methods
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Use the Factory Method pattern when a class can't anticipate the class of objects it must create; a class wants its subclasses to specify the objects it creates or classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.
Adapter
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Use the Adapter pattern when you want to use an existing class, and its interface does not match the one you need; you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces or (object adapter only) you need to use several existing subclasses, but it's impractical to adapt their interface by sub classing every one. An object adapter can adapt the interface of its parent class.
The Construction of the Pool
By the use of these patterns, it is possible to combine them and get a design that fits the connection pool problem. This pattern is named pooling and is in general described in [2]. The CPool
and CResource
are both abstracts. They are used to make an abstraction layer that will make it possible to reuse the connection pool with different connections and even different pools seen from the CPoolFactory
point of view. The concrete level is reached when going one level down. This level holds the implementation of each abstract
object.
The CPoolFactory
is a singleton [1] and it will always return an object of the type CPool
. The CPool
will always return an object of the type CRe<code>
source. The initialization of both objects is made in the concrete layer and it is therefore hidden to the clients by the abstract
layer. This gives us the benefits of reuse instead of re-implement because itβs only necessary to make a new implementation of the CConnectionAdapter
, when connecting to another database. When that is done, it is necessary to make sure that the pool instantiates the correct connection β but that is easy as well. The interface still dictates that it returns a CResource
. This means that the new implementation must implement it as well to prevent violating the derived contracts. Therefore it is only necessary to make changes where the previous implementation is created in the pool, because both implementations use the CResource
abstraction β nothing has to be changed in the client.
The Implementation in C++
When the pool is created, it fetches the initial size from the database and creates the initial number of connections. This is all done the constructor of the CConnectionPool
class.
CConnectionPool::CConnectionPool()
{
m_pool.clear();
m_initSize = 0;
long connstr = 0;
CRegistryFacade *registry = new CRegistryFacade;
registry->getKeyFromRegistry("InitSize", &connstr);
if (0 == connstr)
{
registry->setKeyToRegistry("InitSize", "10");
registry->getKeyFromRegistry("InitSize", &connstr);
}
delete registry;
m_initSize = connstr;
for (int i=0; i<m_initSize; i++)
{
CConnectionAdapter * resource = new CConnectionAdapter;
resource->setInUse(false);
m_pool.push_back(resource);
}
}
The CConnectionPool
class has a special method called CConnectionPool::findNewConnection()
that is used to find a new available connection in the bool and the method is used by the CConnectionPool::aquire()
method.
CResource* CConnectionPool::findNewConnection()
{
bool found = false;
CResource *entry;
list<CResource*>::iterator ite;
ite=m_pool.begin();
while(ite!=m_pool.end() && !found) {
entry = *ite;
CConnectionAdapter * adapter =
dynamic_cast<CConnectionAdapter*>(entry);
if (! adapter->getInUse()) {
adapter->setInUse(true);
found = true;
}
ite++;
}
return entry;
}
Connections are achieved and released with the CConnectionPool::aquire()
and CConnectionPool::release(CResource* res)
methods. These methods are also made accessible through the abstract
layer and can be found at the CPool
class.
CResource* CConnectionPool::aquire()
{
CResource * resource = NULL;
if (NULL == (resource = findNewConnection())) {
resource = new CConnectionAdapter;
m_pool.push_back(resource);
}
return resource;
}
void CConnectionPool::release(CResource* res)
{
bool found = false;
list<CResource*>::iterator ite;
ite=m_pool.begin();
while(ite!=m_pool.end() && !found) {
CResource* entry = *ite;
if (entry->operator == (*res)) {
CConnectionAdapter *adapter =
dynamic_cast<CConnectionAdapter*>(entry);
adapter->setInUse(false);
found = true;
}
ite++;
}
}
The ADO connection is encapsulated in the CConnectionAdapter
class. The primary responsibility of this class is to hold the connection, but it holds other information such as a boolean which tells whether it is in use or not and a unique identifier among others.
CConnectionAdapter::CConnectionAdapter()
{
CoInitialize(NULL);
char* connstr = NULL;
CRegistryFacade *registry = new CRegistryFacade;
registry->getKeyFromRegistry("ConnectionString", &connstr);
if (NULL == connstr || 0 == strlen(connstr))
{
registry->setKeyToRegistry
("ConnectionString", "DATABASE=db;DSN=dsn;UID=sa;PWD=sa");
if (NULL != connstr) {
free(connstr);
connstr = NULL;
}
registry->getKeyFromRegistry("ConnectionString", &connstr);
}
delete registry;
m_connection.CreateInstance(__uuidof(Connection));
m_connection->CursorLocation = adUseServer;
m_connection->Open( connstr, L"", L"", adConnectUnspecified);
m_currentlyUsed = true;
CoCreateGuid (&m_id);
}
When creating an instance of the CConnectionAdapter
class, it fetches the connection string from the registry (that currently is unencrypted!), establishes a connection and sets the unique identifier. The class has a CConnectionAdapter::getConnection()
method, which gives a pointer to the current connection.
Using The Pool
When using this connection pool, it offers an out of the box usage, where all settings such as the connection string and the initial size are stored in the registry. It also offers the possibility of creating another connection pool based on the abstraction layer provided by this library. In this way, it is possible to make a connection pool, which does support Oracle connections as well if needed.
CPoolFactory *factory = CPoolFactory::getPoolFactory();
CPool *pool = factory->getPool();
CResource *res = NULL;
res = pool->aquire();
pool->release(res);
Please notice that the library is based on the Microsoft Data Access Component (MDAC). All MDAC independent implementation is encapsulated in the CConnectionAdapter
class.
References
[1] Design patterns: elements of reusable Object-oriented software
By Erich Gamma, Richard Helm, Ralph Johnson, John Vissides
Addison Wesley, 1995
ISBN: 0-201-63361-2
[2] Pattern-Oriented Software Architecture
Vol. 3: Patterns for Resource Management
By Michael Kircher, Prashant Jain
Wiley, Oct. 1995
ISBN: 0-470-84525-2
History
- 1st January, 2007: Initial post