Figure 3 Minimum RD Interfaces
//Types
typedef DWORD RESTYPID;
typedef DWORD RESID;
typedef LPOLESTR SRESID;
typedef LPCOLESTR constSRESID;
typedef DWORD RESOURCERATING;
typedef long TIMEINSECS;
typedef DWORD INSTID;
typedef DWORD TRANSID;
//Interfaces
IDispenserManager : public IUnknown
{
HRESULT RegisterDispenser(
const IDispenserDriver* pDispenserDriver,
const WCHAR* szDispenserName,
IHolder** ppIHolder);
HRESULT GetContext(INSTID* pInstId,TRANSID* pTransId);
interface IHolder: public IUnknown
{
HRESULT AllocResource(RESTYPID ResTypId,RESID* pResId);
HRESULT FreeResource(const RESID ResId);
HRESULT TrackResource(const RESID ResId);
HRESULT TrackResourceS(const SRESID SResId);
HRESULT UntrackResource(const RESID ResId,const BOOL fDestroy);
HRESULT UntrackResourceS(const SRESID SResId, const BOOL fDestroy);
HRESULT Close();
HRESULT RequestDestroyResource(const RESID ResId);
}
interface IDispenserDriver : public IUnknown
{
HRESULT CreateResource(
const RESTYPID ResTypId,
RESID* pResId,
TIMEINSECS* pSecsFreeBeforeDestroy);
HRESULT RateResource(
const RESTYPID ResTypId,
const RESID ResId,
const BOOL fRequiresTransactionEnlistment,
RESOURCERATING* pRating);
HRESULT EnlistResource(const RESID ResId,const TRANSID TransId);
HRESULT ResetResource(const RESID ResId);
HRESULT DestroyResource(const RESID ResId);
}
Figure 4 CRDisp Class Declaration
/////////////////////////////////////////////////////////////////////////////////
//RDisp.h : Declaration of the CRDisp resource dispenser class.
//
//DESCRIPTION
// Declaration of C++ resource dispenser class. Implements IIDispenserDriver,
//IRDisp, and IRDispAdmin interfaces.
//
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef __RDISP_H_
#define __RDISP_H_
// disable warning C4786: symbol greater than 255 characters
#pragma warning(disable: 4786)
#include "StdAfx.h"
#include "resource.h" // main symbols
#include <map>
#include <fstream>
using namespace std;
#include "..\include\mtxdm.h"
//create a map of ResourceIDs and their status
typedef map<long, bool> ResourceMap;
//interface IDispenserDriver;
//interface IHolder;
//utility for releasing pointers
#define SafeRelease(pUnk) {if (pUnk){pUnk -> Release();pUnk = NULL; }}
// CRDisp
class ATL_NO_VTABLE CRDisp :
public IDispenserDriver,
public IRDisp,
public IRDispAdmin,
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CRDisp, &CLSID_CoRDisp>
{
private:
//data members
IHolder * m_pHolder;
IDispenserManager * m_pDispMan;
IUnknown* m_pUnkMarshaler;
ofstream m_fLog;
long m_lResourceTimeout;
//critical sections:
//used to serialize access to instance data, namely ResourceMap
CComAutoCriticalSection m_CS;
//Set containing all resources
ResourceMap m_ResourceMap;
public:
const static RESTYPID m_DefaultResourceTypeID;
const static long m_lDefaultTimeout;
//must be singleton
DECLARE_CLASSFACTORY_SINGLETON(CRDisp);
DECLARE_PROTECT_FINAL_CONSTRUCT();
DECLARE_REGISTRY_RESOURCEID(IDR_RDISP);
DECLARE_NOT_AGGREGATABLE(CRDisp);
//const/dest
CRDisp();
~CRDisp();
HRESULT FinalConstruct();
void FinalRelease();
//COM interface map
BEGIN_COM_MAP(CRDisp)
COM_INTERFACE_ENTRY(IRDisp)
COM_INTERFACE_ENTRY(IRDispAdmin)
COM_INTERFACE_ENTRY(IDispenserDriver)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler)
END_COM_MAP()
//IRDisp
STDMETHODIMP Connect(BSTR strConnectInfo, long* lResourceID);
STDMETHODIMP Disconnect(long lResourceID);
STDMETHODIMP ScheduleJob(long lResourceID, long lJobID);
STDMETHODIMP CancelJob(long lResourceID, long lJobID);
//IRDispAdmin
STDMETHODIMP DestroyInactive(void);
STDMETHODIMP SetTimeout(long lTimeout);
STDMETHODIMP GetInventoryStatus( long* lSize, long* lInUse);
//IDipenserDriver
STDMETHODIMP CreateResource(const RESTYPID ResourceTypeID,
RESID* pResourceID,
TIMEINSECS* pSecsFreeBeforeDestroy);
STDMETHODIMP RateResource(const RESTYPID ResourceTypeID,
const RESID ResourceID,
const BOOL fRequiresTransactionEnlistment,
RESOURCERATING* pRating);
STDMETHODIMP EnlistResource(const RESID ResourceID, const TRANSID TransID);
STDMETHODIMP ResetResource(const RESID ResourceID);
STDMETHODIMP DestroyResource(const RESID ResourceID);
STDMETHODIMP DestroyResourceS(const SRESID sResourceID);
};
#endif //__RDISP_H_
Figure 5 CRDisp Class, IDispenserDriver Interface Implementation
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::CreateResource(), implements IDispenserDriver::CreateResource()
//
//DESCRIPTION:
//Called by the dispenser manager to create brand new resource.
//
STDMETHODIMP CRDisp::CreateResource (
/*[in]*/ const RESTYPID ResourceTypeID,
/*[out]*/ RESID* pResourceID,
/*[out]*/ TIMEINSECS* pSecsFreeBeforeDestroy)
{
if (ResourceTypeID != m_DefaultResourceTypeID)
{
//invalid Resource Type requested, return E_FAIL
return E_FAIL;
};
//
//get the resource manager pointer, no-op for us since we use DLL
//
//create new resource
bool bSuccess = RM_Connect("RDisp Disp Manager", (long*)pResourceID);
//set timeout
*pSecsFreeBeforeDestroy = (TIMEINSECS)m_lResourceTimeout;
//lock the CS
m_CS.Lock();
//Add the resourceID into map and mark it as 'active'
m_ResourceMap[*pResourceID] = true;
ATLTRACE(_T("CRDisp::CreateResource()...creating new resource.\n"));
m_fLog << "CRDisp::CreateResource()...resource :" << (long)*pResourceID
<< " created."<< endl;
//release lock and return
m_CS.Unlock();
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::RateResource(), implements IDispenserDriver::RateResource()
//
//DESCRIPTION:
//Called by the dispenser manager to rate an already created resource.
//
STDMETHODIMP CRDisp::RateResource(
/*[in]*/ const RESTYPID ResourceTypeID,
/*[in]*/ const RESID ResourceID,
/*[in]*/ const BOOL fRequiresTransactionEnlistment,
/*[out]*/ RESOURCERATING* pRating)
{
ATLTRACE(_T("CRDisp::RateResource()...Rating resource %ld.\n"),
(long)ResourceID);
//init
*pRating = 0;
//first, make sure we have the correct RESTYPID
if(ResourceTypeID != m_DefaultResourceTypeID)
{
//unusable resource, Resource Type mismatch
*pRating = 0;
return S_OK;
};
if (!fRequiresTransactionEnlistment)
{
//if already enlisted on the right transaction, and has the same
//IP address, assign perfect rating
*pRating = 100;
}
else
{
// not enlisted, but exists
*pRating = 25;
}
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////////
//CRDisp::EnlistResource(), implements IDispenserDriver::EnlistResource()
//
//DESCRIPTION:
//Called by the dispenser manager when attempting to enlist new resource in the
//client's transaction.
//The same happens when the resource from general inventory is used.
//
STDMETHODIMP CRDisp::EnlistResource(
/*[in]*/ const RESID ResourceID,
/*[in]*/ const TRANSID TransId)
{
ATLTRACE(_T("CRDisp::EnlistResource()...Enlisting Resource%ld.\n"),
(long)ResourceID );
m_fLog << "CRDisp::EnlistResource()...Enlisting resource: "
<< (long)ResourceID << endl;
//this is non-transactional ResDisp, return S_FALSE
return S_FALSE;
}
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::ResetResource(),implements IDispenserDriver::ResetResource()
//
//DESCRIPTION:
//Called by the dispenser manager to reset the resouce before returning it to the
//pool.
//
STDMETHODIMP CRDisp::ResetResource(/*[in]*/ const RESID ResourceID)
{
ATLTRACE(_T("CRDisp::ResetResource()...Resetting resource: %ld.\n"), ResourceID );
m_fLog << "CRDisp::ResetResource()...Resetting resource: "
<< (long)ResourceID << endl;
//
//get the resource manager pointer, no-op for us since we use DLL
//
//reset the connection
bool bSuccess = RM_ResetConnection(ResourceID);
if (bSuccess)
{
m_CS.Lock();
m_ResourceMap[ResourceID] = false;
m_CS.Unlock();
return S_OK;
}
else
{
return S_FALSE;
};
}
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::DestroyResource(), implements IDispenserDriver::DestroyResource().
//
//DESCRIPTION:
//Called by the dispenser manager when destroying the resource.
//
STDMETHODIMP CRDisp::DestroyResource(/*[in]*/ const RESID ResourceID)
{
HRESULT hr;
ATLTRACE(_T("CRDisp::DestroyResource()...Destroying resource: %ld.\n"), (long)ResourceID);
m_fLog << "CRDisp::DestroyResource()...Destroying resource:"
<< (long)ResourceID << endl;
//get the RM pointer, no-op for us since we use DLL
//instruct RM to close the connection:
if (RM_Disconnect((long)ResourceID) )
{
//make sure that no other thread is accessing instance data
m_CS.Lock();
//remove resourceID from the set of resources
m_ResourceMap.erase(ResourceID);
//unlock and release
m_CS.Unlock();
hr = S_OK;
}
else
{
hr = S_FALSE;
};
return hr;
}
//////////////////////////////////////////////////////////////////////////////////
//CRDisp::DestroyResourceS(), implements IDispenserDriver::DestroyResourceS().
//
//DESCRIPTION:
//This method behaves exactly the same as DestroyResource() except it uses string
//representation of SRESID. Since we do not use SRESIDs, return E_NOTIMPL.
//
STDMETHODIMP CRDisp::DestroyResourceS(/*[in]*/ const SRESID sResourceID)
{
ATLTRACE(_T("CRDisp::DestroyResourceS()\n"));
m_fLog << "CRDisp::DestroyResourceS()" << endl;
return E_NOTIMPL;
}
Figure 6 CRDisp Class, IRDisp Interface Implementation
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::Connect(), implements IRDisp::Connect()
//
//DESCRIPTION:
//Called by clients to create new resource: connection to the scheduling system.
//
//
STDMETHODIMP CRDisp::Connect(BSTR strConnectInfo, long* lResourceID)
{
HRESULT hr;
//get the lock
m_CS.Lock();
ATLTRACE(_T("CRDisp::Connect()\n"));
m_fLog << "CRDisp::Connect()" << endl;
//init
*lResourceID = 0;
//call Holder to create resource:
if (m_pDispMan)
{
if (!m_pHolder)
{
//no holder - return error
ATLTRACE(_T("CRDisp::Connect(), no holder present.\n"));
//release lock and return
m_CS.Unlock();
return E_INVALIDARG;
};
*lResourceID = NULL;
hr = m_pHolder->AllocResource(m_DefaultResourceTypeID,
(RESID *)lResourceID);
if(FAILED(hr))
{
ATLTRACE(_T("AllocResource failed! Error code %x\n"), hr);
};
}
else
{
//No DispMan, hence no MTS, create the resource directly
m_fLog << "CRDisp::Connect()...No DispMan" << endl;
TIMEINSECS timeout = 0;
hr = CreateResource(m_DefaultResourceTypeID,
(RESID*)lResourceID,&timeout);
};
//release lock and return
m_CS.Unlock();
return hr;
}
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::Disconnect(), implements IRDisp::Disconnect()
//
//DESCRIPTION:
//
//
STDMETHODIMP CRDisp::Disconnect(long lResourceID)
{
ATLTRACE(_T("CRDisp::Disonnect()\n"));
m_fLog << "CRDisp::Disonnect()...resource ID: " << lResourceID << endl;
HRESULT hr;
//aquire a lock
m_CS.Lock();
if (m_pDispMan)
{
if (!m_pHolder)
{
ATLTRACE(_T("CRDisp::Disconnect(), no holder present.\n"));
m_fLog << "CRDisp::no holder present." << endl;
//release lock
m_CS.Unlock();
return E_INVALIDARG;
};
if (m_pDispMan)
{
hr = m_pHolder->FreeResource(lResourceID);
if (!SUCCEEDED(hr))
{
ATLTRACE(_T("FreeResource failed with error %x\n"), hr);
m_fLog << "FreeResource failed. " << hr << endl;
};
}
}
else
{
//No DispMan, i.e. no MTS, therefore destroy resource directly
m_fLog << "CRDisp::no DispMan present." << endl;
hr = DestroyResource(lResourceID);
};
//release lock
m_CS.Unlock();
return hr;
}
//////////////////////////////////////////////////////////////////////////////////
//CRDisp::ScheduleJob(), implements IRDisp::ScheduleJob()
//
//DESCRIPTION:
//Performs actual task of adding a new job from the scheduling subsystem.
//
//
STDMETHODIMP CRDisp::ScheduleJob(/*[in]*/ long lResourceID, /*[in]*/ long lJobID)
{
//
//retrieve the RM pointer, no-op since we use RM as DLL
//
bool bSuccess = RM_ScheduleJob(lResourceID, lJobID);
if (bSuccess)
{
return S_OK;
}
else
{
return S_FALSE;
};
}
//////////////////////////////////////////////////////////////////////////////////
//CRDisp::CancelJob(), implements IRDisp::CancelJob()
//
//DESCRIPTION:
//Performs actual task of canceling a job from the scheduling subsystem.
//
//
STDMETHODIMP CRDisp::CancelJob(/*[in]*/ long lResourceID, /*[in]*/ long lJobID)
{
//
//retrieve the RM pointer, no-op since we use RM as DLL
//
bool bSuccess = RM_CancelJob(lResourceID, lJobID);
if (bSuccess)
{
return S_OK;
}
else
{
return S_FALSE;
};
}
Figure 7 RD::Connect Pseudocode
RD::Connect(/*IN*/ BSTR strConnectionString, /*out*/ long lResourceID)
{
HRESULT hr;
//Lock critical section to protect the instance data
m_CS.Lock();
ATLTRACE(_T("in CRDisp::Connect()\n"));
//reset resource ID
*lResourceID = 0;
RESTYPID myResourceType;
//decide what resource type this connection represents and set myResourceType
//value accordingly
//If there is a dispenser manager present (i.e. we are running under MTS)
//must use holder object to allocate the inventory.
if (m_pDispMan)
{
//If no holder object, refuse to allocate resource
//because this happens only when the RD is getting ready to shut down.
if (!m_pHolder)
{
//release lock and return
m_CS.Unlock();
return E_INVALIDARG;
}
else
{
//call Holder
hr = m_pHolder->AllocResource(myResourceType, (RESID*)lResourceID);
if (!SUCCEEDED(hr))
{
//error
ATLTRACE(_T("Failed to allocate resource, error: %x\n"), hr);
};
};
}
else
{
//not running under MTS: create the resource directly:
//set timeout
TIMEINSECS timeout;
//call CreateResource() method
hr = CreateResource(myResourceType,(RESID*)lResourceID, &Timeout);
};
//release lock and return
m_CS.Unlock();
return hr;
}
Figure 8 CRDisp Class, IRDispAdmin Interface Implementation
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::GetInventoryStatus(), implements IRDispAdmin::GetInventoryStatus().
//
//DESCRIPTION:
//Walks through the map of all allocated resources and returns the map size,
//and number of active resources, i.e.:
//lSize = lInUse + (general and enlisted inventory)
//
STDMETHODIMP CRDisp::GetInventoryStatus(/*[out]*/ long* lSize,
/*[out]*/ long* lInUse)
{
ATLTRACE(_T("CRDisp::GetInventoryStatus()\n"));
m_fLog << "CRDisp::GetInventoryStatus()" << endl;
//initialize
*lSize = 0;
*lInUse = 0;
//get the CS lock so that no other thread starts inserting, or
//removing elements while we're using the map.
m_CS.Lock();
ResourceMap::iterator it;
for (it = m_ResourceMap.begin(); it != m_ResourceMap.end(); ++it)
{
(*lSize)++;
if (it->second) (*lInUse)++;
};
//release lock and return
m_CS.Unlock();
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////////
//CRDisp::DestroyInactive(...), implements IRDispAdmin::DestroyInactive().
//
//DESCRIPTION:
//Releases holders general and enlisted inventory.
//
STDMETHODIMP CRDisp::DestroyInactive(void)
{
HRESULT hr;
if(m_pDispMan)
{
if(!m_pHolder)
{
//no holder, therefore no pool
ATLTRACE(_T("CRDisp::DestroyInactive(), no holder present.\n"));
return E_FAIL;
};
}
else
{
//no dispenser manager, therefore no holder and no pool
ATLTRACE(_T("CRDisp::DestroyInactive(), no DispMan present.\n"));
//release lock and return
return E_FAIL;
};
//lock cs
m_CS.Lock();
ATLTRACE(_T("CRDisp::DestroyInactive()\n"));
m_fLog << "CRDisp::DestroyInactive()" << endl;
//Walk through the resource map and identify the resource to be deleted.
//Note that we need to do this in two steps since the RequestDestroyResource()
//will call CRDisp::DestroyResource(), which immediately removes the resource id
//from the map.
//define the set
set <long> InactiveSet;
ResourceMap::iterator it1;
for (it1 = m_ResourceMap.begin(); it1 != m_ResourceMap.end(); ++it1)
{
if (it1->second == false)
{
//inactive resource, add it to the rest of inactive resources
InactiveSet.insert(it1->first);
};
};
//now actually delete the resources
set<long>::iterator it2;
for (it2 = InactiveSet.begin(); it2 != InactiveSet.end(); ++it2)
{
hr = m_pHolder->RequestDestroyResource((RESID)*it2);
if (!SUCCEEDED(hr))
{
m_fLog << "Cound not destroy resource: " << *it2 << endl;
};
};
//release lock and return
m_CS.Unlock();
return hr;
}
//////////////////////////////////////////////////////////////////////////////////
//CRDisp::SetTimeout(...)
//
//DESCRIPTION:
//Sets timeout for how long all new resources can 'hang out' in general and
//enlisted inventory before being destroyed.
//
STDMETHODIMP CRDisp::SetTimeout(long lTimeout)
{
ATLTRACE(_T("CRDisp::SetTimeout()...Setting resource timeout to: %ld.\n"), lTimeout);
m_fLog << "CRDisp::SetTimeout()......Setting resource timeout to: "
<< lTimeout << endl;
if ( lTimeout < 0)
{
return E_INVALIDARG;
}
else
{
m_lResourceTimeout = lTimeout;
return S_OK;
};
}
Figure 9 CRDisp Class Implementation: Constructors, Destructors
#include "stdafx.h"
#include <initguid.h>
#include <cstdlib>
#include <ctime>
#include <set>
#include "ResourceDisp.h"
#include "RDisp.h"
//Resource Manager
#include "..\include\RM.h"
//include holder, and dispenser driver definitions
#include "..\include\mtxdm_i.c" //shipped with MTS SDK
//Set default Resource Type ID
const RESTYPID CRDisp::m_DefaultResourceTypeID = (RESTYPID)123;
//Set default resource timeout to 30 sec.
const long CRDisp::m_lDefaultTimeout = 30;
//constructors/destructors
CRDisp::CRDisp()
{
//reset all instance variables
m_pDispMan = NULL;
m_pHolder = NULL;
m_pUnkMarshaler = NULL;
}
CRDisp::~CRDisp(){}
/////////////////////////////////////////////////////////////////////////////////
//HRESULT CRDisp::FinalConstruct(...)
//
//DESCRIPTION:
//
HRESULT CRDisp::FinalConstruct()
{
ATLTRACE(_T("CRDisp::FinalConstruct()\n"));
HRESULT hr;
//obtain pointer to dispenser manager (DispMan)
hr = GetDispenserManager(&m_pDispMan);
if (SUCCEEDED(hr))
{
//we're running under MTS, register resource and receive holder
IDispenserDriver * pDriver;
hr = GetUnknown()->QueryInterface(IID_IDispenserDriver,
(void **)&pDriver);
if (SUCCEEDED(hr))
{
//Register and receive holder
hr = m_pDispMan->RegisterDispenser(pDriver, L"RDisp Dispenser", &m_pHolder);
if (FAILED(hr))
{
ATLTRACE(_T("Could not register with DispMan.\n"));
};
}
else
{
ATLTRACE(_T("CQI for IDispenserDriver failed.\n"));
};
}
else
{
//no DispMan, we are running outside of MTS
};
//
// get a connection to RM, since we use DLL, no-op
//
//initialize instance variables
m_ResourceMap.clear();
m_lResourceTimeout = m_lDefaultTimeout;
m_fLog.open("ResourceDisp.log");
//aggregate the freethreaded marshaler
hr = CoCreateFreeThreadedMarshaler(GetUnknown(), &m_pUnkMarshaler);
return hr;
}
//////////////////////////////////////////////////////////////////////////////////
//void CRDisp::FinalRelease()
//
//DESCRIPTION:
//Clean-up before exiting.
//Note that we have to manually free all resource handles that were not released
//properly by calling .DestroyResource(). The DispMan will not do this
//automatically.
//
void CRDisp::FinalRelease()
{
ATLTRACE(_T("CRDisp::FinalRelease()\n"));
m_fLog << "CRDisp::FinalRelease()" << endl;
HRESULT hr;
//instruct holder to release remaining inventory:
while( !m_ResourceMap.empty() )
{
//note: don't use IHolder to remove unreleased resources, holder can
//release only general, or enlisted inventory, and NOT
//active resources.
//Use direct DestroyResource() call instead
hr = DestroyResource((RESID)(*m_ResourceMap.begin()).first);
};
//empty the set
m_ResourceMap.clear();
//now, release pointers
SafeRelease(m_pHolder);
SafeRelease(m_pDispMan);
SafeRelease(m_pUnkMarshaler);
}