Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Connection Pool in a Static Library

3.79/5 (5 votes)
1 Jan 2007CPOL5 min read 1   1K  
Connection Pool in a Static Library

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.

Image 1

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.

Image 2

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.

Image 3

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.

Image 4

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.

Image 5

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.

C++
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.

C++
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.

C++
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.

C++
CConnectionAdapter::CConnectionAdapter()
{
	CoInitialize(NULL);

	char* connstr = NULL;
	CRegistryFacade *registry = new CRegistryFacade;
	registry->getKeyFromRegistry("ConnectionString", &connstr);
	if (NULL == connstr || 0 == strlen(connstr))
	{
                //Failed to retrieve the connection string
                //Write default settings to the registry and use them
		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.

C++
//An instance of the pool factory has to be achieved
CPoolFactory *factory = CPoolFactory::getPoolFactory(); 
//Get an instance of the pool
CPool *pool = factory->getPool();
//Retrieve a connection from the pool
CResource *res = NULL;
res = pool->aquire();
//Release the connection when it is no more needed 
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)