For any class containing pooled-object creation methods, a stub file is generated that contains the object evaluation interface and its methods. The Object Pool Manager makes callbacks to these methods to determine criteria for matching and creating objects.
You must complete the method stubs in this interface, which is named IGXObjectEvaluation in C++ extensions or IJavaObjectEvaluation in Java extensions. For example, in PoolSampleExt, the object evaluation method stubs are found in ConnManager.cpp.
Most or all of the object evaluation methods use the following parameters:
Implementing MatchObject( )
The Object Pool Manager makes a callback to MatchObject( ) to ask if a given physical object matches a given virtual object. Given these two objects, you determine whether they are considered a match, and you set the return value accordingly to TRUE or FALSE.
C++ Example
In PoolSampleExt, MatchObject( ) is implemented in ConnManager.cpp as shown in the following code. The matching criteria differ for TXCONN_POOL, which contains transactional connection objects created by CreateTxConnection( ).
// Stub method bodies for interface: IGXObjectEvaluation
HRESULT
ConnManager::MatchObject(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pVirtual,
/* [in] */ IGXObject *pPhysical,
/* [out] */ BOOL *pMatches)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Depending on poolName, determine if the objects match:
// Navigate to the introspection interface of the
// virtual & physical objects, get their attributes,
// and see if they match.
// CONN_POOL: match on userid & passwd
// TXCONN_POOL: match on userid, passwd AND flags
// (match criteria is stricter than CONN_POOL)
if (!strcmp(poolName, "CONN_POOL"))
{
*pMatches = FALSE;
IInfo* pV = NULL;
IInfo* pP = NULL;
// Navigate to IInfo interfaces
if((((hr=pVirtual->QueryInterface(IID_IInfo,
(LPVOID*)&pV))==GXE_SUCCESS)&&(pV))
&&(((hr=pPhysical->QueryInterface(IID_IInfo,
(LPVOID*)&pP))==GXE_SUCCESS)&&(pP)))
{
ULONG PUid = 0;
ULONG VUid = 0;
char PPasswd[256];
char VPasswd[256];
// Get uid, passwd for both virtual
// & physical objects
if(hr==GXE_SUCCESS)
hr=pV->GetUid(&VUid);
if(hr==GXE_SUCCESS)
hr=pV->GetPasswd(VPasswd, 256);
if(hr==GXE_SUCCESS)
hr=pP->GetUid(&PUid);
if(hr==GXE_SUCCESS)
hr=pP->GetPasswd(PPasswd, 256);
// Check if userid & password match
if ((hr==GXE_SUCCESS)
&& (PUid==VUid)
&& (!strcmp(PPasswd, VPasswd)))
*pMatches = TRUE;
}
// Release interfaces
if (pP)
pP->Release();
if (pV)
pV->Release();
}
else // TXCONN_POOL
...
Implementing CreateObject( )
The Object Pool Manager makes a callback to CreateObject( ) to ask you to create a physical object that matches the given virtual object.
This callback may occur for two reasons:
If CreateObject( ) fails to create a physical object, it means a critical resource is unavailable. As a result, the Object Pool Manager will not queue the associated request but will return a FAILURE status immediately.
Note the Out parameter for CreateObject( ):
/* [out] */ IGXPoolObject **ppPhysical)
The previous line of code creates an instance of a coclass that is marked as poolable. This coclass implements the IGXPoolObject interface, which is automatically code-generated for you.
C++ Example
In PoolSampleExt, CreateObject( ) is implemented in ConnManager.cpp as shown in the following code. The creation logic differs depending on the pool name.
HRESULT
ConnManager::CreateObject(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pVirtual,
/* [out] */ IGXPoolObject **ppPhysical)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Depending on the poolName, navigate to the
// introspection interface of the virtual object,
// get the encapsulated attributes,
// and create the appropriate physical object with the
// same attributes.
if (!strcmp(poolName, "CONN_POOL"))
{
IInfo* pV = NULL;
IFakeConnection* pConn = NULL;
ULONG uid = 0;
ULONG flags = 0;
char passwd[256];
// Navigate to IInfo interface
if(((hr=pVirtual->QueryInterface(IID_IInfo,
(LPVOID*)&pV))==GXE_SUCCESS)&&(pV))
{
// Get the uid, passwd, flags
if(hr==GXE_SUCCESS)
hr=pV->GetUid(&uid);
if(hr==GXE_SUCCESS)
hr=pV->GetPasswd(passwd, 256);
if(hr==GXE_SUCCESS)
hr=pV->GetFlags(&flags);
}
// Create a matching Connection object.
if (hr==GXE_SUCCESS)
{
hr = CreateConnection(uid, passwd, flags, &pConn);
// QueryInterface to IGXPoolObject
if((hr==GXE_SUCCESS)&& pConn)
hr=pConn->QueryInterface(IID_IGXPoolObject,
(LPVOID*)ppPhysical);
}
// Release interfaces
if (pV)
pV->Release();
if (pConn)
pConn->Release();
}
else // TXCONN_POOL
...
Implementing StealObject( )
The Object Pool Manager makes a callback to this method to ask if a given physical object can be destroyed. Destroying the object makes room in the pool to create another object that matches a given virtual object. Set the return value to TRUE or FALSE depending on the physical object to be replaced and the virtual object to be reserved.
In most cases, you may replace any physical object, so you should return TRUE. In rare cases, the object may be an important resource that you want to prevent from being replaced. Return FALSE in this case. Returning FALSE does not guarantee that the object will never be destroyed. Instead, a value of FALSE effectively raises that object's priority. The Object Pool Manager will pass over this object when searching for objects to destroy.
If you return FALSE, the request will probably be queued until another object is returned to the pool.
C++ Example
In PoolSampleExt, StealObject( ) is implemented in ConnManager.cpp as shown in the following code. The implementation means that any object is equally eligible to be replaced.
HRESULT
ConnManager::StealObject(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pVirtual,
/* [in] */ IGXObject *pPhysical,
/* [out] */ BOOL *pCanSteal)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Allow any connection to be replaced.
*pCanSteal = TRUE;
return hr;
}
Implementing ReleaseObject( )
The Object Pool Manager makes a callback to ReleaseObject( ) to ask you to dispose of any resource a physical object might be holding. The physical object will never be used again. This callback may occur if an object needs to be created but the pool is at its maximum size.
However, do not use ReleaseObject( ) to actually destroy the object. The object is automatically destroyed like any other COM object, when its reference count drops to zero.
For pooled objects, use ReleaseObject( ) for time-consuming actions such as closing network connections. If the destructor is used for time-consuming tasks instead of ReleaseObject( ), other requesting threads will be blocked.
It is recommended that you isolate time-consuming actions in a separate method of the physical object. You can use a state flag to ensure that the method executes its logic. A non-pooled instance of the object can then call this logic from the destructor, and a pooled instance can call the logic from ReleaseObject( ).
C++ Example
In PoolSampleExt, ReleaseObject( ) is implemented in ConnManager.cpp as follows. The reason parameter does nothing but is reserved for future use.
HRESULT
ConnManager::ReleaseObject(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pPhysical,
/* [in] */ unsigned long reason)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Would have closed any backend connection here.
// Nothing to do in this dummy implementation.
return hr;
}
Implementing InitObject( )
The Object Pool Manager makes a callback to InitObject( ) whenever the given physical object is reserved against the given virtual object; that is, after a successful match, creation, or replacement. Use InitObject( ) to make any initializations on the physical object that are specific to the given virtual object.
For example, suppose you have a connection object. For this object to match exactly with a given virtual object, both the userid/password and any connection settings must match. If you assume that allocating a network connection is time consuming, it is more efficient to do so in the object constructor or in the CreateObject( ) method. In that case, you would return TRUE from MatchObject( ) as long as the userid/password match.
By contrast, use InitObject( ) to initialize the settings that take little processing time. This breakup of actions will ensure maximum pooling efficiency.
C++ Example
In PoolSampleExt, InitObject( ) is implemented in ConnManager.cpp as follows.
HRESULT
ConnManager::InitObject(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pVirtual,
/* [in] */ IGXObject *pPhysical)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Remember: the match criteria for objects in
// CONN_POOL did not include flag settings.
// We therefore need to restore the flag settings
// of the physical object, to match those of the
// virtual object.
// For CONN_POOL objects, do flag settings here.
if (!strcmp(poolName, "CONN_POOL"))
{
// Get to IInfo interface
IInfo* pV = NULL;
if(((hr=pVirtual->QueryInterface(IID_IInfo,
(LPVOID*)&pV))==GXE_SUCCESS)&&(pV))
{
ULONG flags = 0;
hr=pV->GetFlags(&flags);
// Get Connection implementation
IFakeConnection* pIConn=NULL;
if(((hr=pPhysical->QueryInterface
(IID_IFakeConnection,(LPVOID*)&pIConn))
==GXE_SUCCESS)&&(pIConn))
{
// Set flags
pIConn->SetFlags(flags);
}
}
}
return hr;
}
Implementing UninitObject( )
The Object Pool Manager makes a callback to UninitObject( ) whenever the given physical object is returned to the given pool. Use this method as a complement to InitObject( ). Do not use UninitObject( ) nor the object destructor for time-consuming actions such as closing network connections; use ReleaseObject( ) instead.
C++ Example
In PoolSampleExt, UninitObject( ) is implemented in ConnManager.cpp as follows.
HRESULT
ConnManager::UninitObject(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pPhysical)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Nothing to do.
return hr;
}
Implementing GetHint( )
The Object Pool Manager makes a callback to GetHint( ) to ask for a hint string for the given virtual object. This hint string is used to group objects in the pool, to limit MatchObject( ) searches to this group. Using GetHint( ) may improve performance if the MatchObject( ) method is time-consuming. In most cases, however, return an empty string.
C++ Example
In PoolSampleExt, GetHint( ) is implemented in ConnManager.cpp as shown in the following code:
HRESULT
ConnManager::GetHint(
/* [in] */ LPSTR poolName,
/* [in] */ IGXObject *pVirtual,
/* [out] */ LPSTR hint,
/* [in] */ unsigned long Size)
{
HRESULT hr=GXE_SUCCESS;
//***
//*** Provide your implementation here
//*** and remove the GXASSERT below
//***
// Don't want to provide any hint.
strcpy(hint, "");
return hr;
}