//****************************************************************************** // // AGGREG.CPP // // Copyright (C) 1996-1999 Microsoft Corporation // //****************************************************************************** #include "precomp.h" #include "ess.h" #include "aggreg.h" IWbemClassObject* CEventAggregator::mstatic_pClass = NULL; CEventAggregator::CEventAggregator(CEssNamespace* pNamespace, CAbstractEventSink* pDest) : COwnedEventSink(pDest), m_aProperties(NULL), m_pNamespace(pNamespace), m_lNumProperties(0), m_pHavingTree(NULL) { } CEventAggregator::~CEventAggregator() { delete [] m_aProperties; delete m_pHavingTree; } HRESULT CEventAggregator::SetQueryExpression(CContextMetaData* pMeta, QL_LEVEL_1_RPN_EXPRESSION* pExpr) { HRESULT hres = WBEM_S_NO_ERROR; // Check that, if aggregation is required, tolerance is specified // ============================================================== if(!pExpr->bAggregated) { return WBEM_E_CRITICAL_ERROR; // internal error } if(pExpr->bAggregated && pExpr->AggregationTolerance.m_bExact) { ERRORTRACE((LOG_ESS, "Event aggregation query specified GROUP BY, but " "not GROUP WITHIN. This query is invalid and will not be acted " "upon\n")); return WBEM_E_MISSING_GROUP_WITHIN; } m_fTolerance = pExpr->AggregationTolerance.m_fTolerance; if(m_fTolerance < 0) return WBEM_E_INVALID_PARAMETER; // Check that all properties are valid // =================================== if(pExpr->bAggregateAll) { ERRORTRACE((LOG_ESS, "Aggregating based on all properties of an event " "is not supported\n")); return WBEM_E_MISSING_AGGREGATION_LIST; } // Get the class // ============= _IWmiObject* pClass = NULL; pMeta->GetClass(pExpr->bsClassName, &pClass); if(pClass == NULL) { return WBEM_E_INVALID_CLASS; } CReleaseMe rm1(pClass); // Allocate the array to hold property names // ========================================= delete [] m_aProperties; m_lNumProperties = pExpr->nNumAggregatedProperties; m_aProperties = new CPropertyName[m_lNumProperties]; if(m_aProperties == NULL) return WBEM_E_OUT_OF_MEMORY; int i; for(i = 0; i < pExpr->nNumAggregatedProperties; i++) { CPropertyName& PropName = pExpr->pAggregatedPropertyNames[i]; // Check existence // =============== CIMTYPE ct; if(FAILED(pClass->Get((LPWSTR)PropName.GetStringAt(0), 0, NULL, &ct, NULL))) { ERRORTRACE((LOG_ESS, "Invalid aggregation property %S --- not a " "member of class %S\n", (LPWSTR)PropName.GetStringAt(0), pExpr->bsClassName)); return WBEM_E_INVALID_PROPERTY; } if(PropName.GetNumElements() > 1 && ct != CIM_OBJECT) { return WBEM_E_PROPERTY_NOT_AN_OBJECT; } if(PropName.GetNumElements() == 1 && ct == CIM_OBJECT) { return WBEM_E_AGGREGATING_BY_OBJECT; } m_aProperties[i] = PropName; } // Initialize post-evaluator with the data from the HAVING clause // ============================================================== QL_LEVEL_1_RPN_EXPRESSION* pHavingExpr = _new QL_LEVEL_1_RPN_EXPRESSION; if(pHavingExpr == NULL) return WBEM_E_OUT_OF_MEMORY; pHavingExpr->SetClassName(L"__AggregateEvent"); for(i = 0; i < pExpr->nNumHavingTokens; i++) { pHavingExpr->AddToken(pExpr->pArrayOfHavingTokens[i]); } delete m_pHavingTree; m_pHavingTree = new CEvalTree; if(m_pHavingTree == NULL) return WBEM_E_OUT_OF_MEMORY; hres = m_pHavingTree->CreateFromQuery(pMeta, pHavingExpr, 0); delete pHavingExpr; return hres; } HRESULT CEventAggregator::Initialize(IWbemServices* pNamespace) { return pNamespace->GetObject( CWbemBSTR( L"__AggregateEvent" ), 0, GetCurrentEssContext(), &mstatic_pClass, NULL ); } HRESULT CEventAggregator::Shutdown() { if(mstatic_pClass) mstatic_pClass->Release(); mstatic_pClass = NULL; return WBEM_S_NO_ERROR; } HRESULT CEventAggregator::Indicate(long lNumEvents, IWbemEvent** apEvents, CEventContext* pContext) { HRESULT hresGlobal = S_OK; // // Note: we are going to lose the event's security context, but that is OK, // since the security check has already been done. // for(long i = 0; i < lNumEvents; i++) { HRESULT hres = Process(apEvents[i]); if(FAILED(hres)) hresGlobal = hres; } return hresGlobal; } HRESULT CEventAggregator::Process(IWbemEvent* pEvent) { // Compute the event's aggregation vector // ====================================== CVarVector* pvv = _new CVarVector; if(pvv == NULL) return WBEM_E_OUT_OF_MEMORY; HRESULT hres = ComputeAggregationVector(pEvent, *pvv); if(FAILED(hres)) { delete pvv; return hres; } // Add event to the right bucket, creating one if needed. // THIS CALL ACQUIRES pvv!!! // ====================================================== CBucket* pCreatedBucket = NULL; hres = AddEventToBucket(pEvent, pvv, &pCreatedBucket); if(FAILED(hres)) { return hres; } if(pCreatedBucket) { // Create a timer instruction to empty this bucket // =============================================== CBucketInstruction* pInst = _new CBucketInstruction(this, pCreatedBucket, m_fTolerance); if(pInst == NULL) return WBEM_E_OUT_OF_MEMORY; pInst->AddRef(); hres = m_pNamespace->GetTimerGenerator().Set(pInst, CWbemTime::GetZero()); pInst->Release(); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed to schedule aggregation instruction %p" "\n", pInst)); return hres; } } return S_OK; } HRESULT CEventAggregator::AddEventToBucket(IWbemEvent* pEvent, ACQUIRE CVarVector* pvv, CBucket** ppCreatedBucket) { // Search for a matching bucket // ============================ CInCritSec ics(&m_cs); BOOL bFound = FALSE; for(int i = 0; i < m_apBuckets.GetSize(); i++) { CBucket* pBucket = m_apBuckets[i]; if(pBucket->CompareTo(*pvv)) { HRESULT hres = pBucket->AddEvent(pEvent); delete pvv; *ppCreatedBucket = NULL; return hres; } } // Need a _new bucket // ================== CBucket* pBucket = _new CBucket(pEvent, pvv); // takes over pvv if(pBucket == NULL) { delete pvv; return WBEM_E_OUT_OF_MEMORY; } if(m_apBuckets.Add(pBucket) < 0) { delete pBucket; return WBEM_E_OUT_OF_MEMORY; } *ppCreatedBucket = pBucket; return S_OK; } HRESULT CEventAggregator::ComputeAggregationVector( IN IWbemEvent* pEvent, OUT CVarVector& vv) { HRESULT hres; IWbemPropertySource* pPropSource = NULL; if(FAILED(pEvent->QueryInterface(IID_IWbemPropertySource, (void**)&pPropSource))) { return E_NOINTERFACE; } CReleaseMe rm1(pPropSource); // Go through all the properties and add their values to the array // =============================================================== for(int i = 0; i < m_lNumProperties; i++) { CPropertyName& PropName = m_aProperties[i]; // Get the value // ============= VARIANT v; VariantInit(&v); hres = pPropSource->GetPropertyValue(&PropName, 0, NULL, &v); if(FAILED(hres)) return hres; // Add it to the array // =================== CVar* pVar = _new CVar; if(pVar == NULL) return WBEM_E_OUT_OF_MEMORY; pVar->SetVariant(&v); VariantClear(&v); if(vv.Add(pVar) < 0) // ACQUIRES pVar { delete pVar; return WBEM_E_OUT_OF_MEMORY; } } return WBEM_S_NO_ERROR; } HRESULT CEventAggregator::PostponeDispatchFirstBucket() { HRESULT hres; // // Construct the aggregate event while locked // IWbemEvent* pAggEvent = NULL; { CInCritSec ics(&m_cs); if(m_apBuckets.GetSize() == 0) return WBEM_S_FALSE; hres = m_apBuckets[0]->MakeAggregateEvent(&pAggEvent); if(FAILED(hres)) return hres; m_apBuckets.RemoveAt(0); } CReleaseMe rm1(pAggEvent); return FireEvent(pAggEvent, false); } HRESULT CEventAggregator::DispatchBucket(CBucket* pBucket) { // Search for the bucket // ===================== IWbemEvent* pAggEvent = NULL; { CInCritSec ics(&m_cs); BOOL bFound = FALSE; for(int i = 0; i < m_apBuckets.GetSize(); i++) { if(m_apBuckets[i] == pBucket) { // Found it. Construct its event // ============================= HRESULT hres = pBucket->MakeAggregateEvent(&pAggEvent); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Could not create an aggregate event: " "%X\n", hres)); return hres; } // Delete the bucket // ================= m_apBuckets.RemoveAt(i); break; } } } if(pAggEvent == NULL) { // No bucket! // ========== return WBEM_E_CRITICAL_ERROR; // internal error } CReleaseMe rm1(pAggEvent); // // We can fire this event directly on this thread, as it is ours // return FireEvent(pAggEvent, true); } HRESULT CEventAggregator::FireEvent(IWbemClassObject* pAggEvent, bool bRightNow) { // Constructed aggregate. Decorate it // ================================== HRESULT hrDec = m_pNamespace->DecorateObject(pAggEvent); if (FAILED(hrDec)) return hrDec; // Check HAVING query // ================== BOOL bResult; CSortedArray aTrues; IWbemObjectAccess* pAccess; pAggEvent->QueryInterface(IID_IWbemObjectAccess, (void**)&pAccess); if(FAILED(m_pHavingTree->Evaluate(pAccess, aTrues))) { bResult = FALSE; } else { bResult = (aTrues.Size() > 0); } pAccess->Release(); if(bResult) { // Get destination pointer, protecting from Deactivation // ===================================================== CAbstractEventSink* pDest = NULL; { CInCritSec ics(&m_cs); pDest = m_pOwner; if(pDest) pDest->AddRef(); } if(pDest) { if(bRightNow) pDest->Indicate(1, &pAggEvent, NULL); else PostponeIndicate(pDest, pAggEvent); pDest->Release(); } } return WBEM_S_NO_ERROR; } HRESULT CEventAggregator::PostponeIndicate(CAbstractEventSink* pDest, IWbemEvent* pEvent) { CPostponedList* pList = GetCurrentPostponedEventList(); if(pList == NULL) return pDest->Indicate(1, &pEvent, NULL); CPostponedIndicate* pReq = new CPostponedIndicate(pDest, pEvent); if(pReq == NULL) return WBEM_E_OUT_OF_MEMORY; return pList->AddRequest( m_pNamespace, pReq ); } HRESULT CEventAggregator::Deactivate(bool bFire) { HRESULT hres; // // First remove all timer instructions that may still be scheduled. // Timer instructions have a ref-count on us (and therefore our owner), // so we may not disconnect until we are done // CAggregatorInstructionTest Test(this); CTimerGenerator& Generator = m_pNamespace->GetTimerGenerator(); hres = Generator.Remove(&Test); // // If requested, fire all buckets, albeit not right now // if(bFire) PostponeFireAllBuckets(); Disconnect(); return hres; } HRESULT CEventAggregator::PostponeFireAllBuckets() { HRESULT hres; while((hres = PostponeDispatchFirstBucket()) != S_FALSE); if(FAILED(hres)) return hres; else return WBEM_S_NO_ERROR; } HRESULT CEventAggregator::CopyStateTo(CEventAggregator* pDest) { CInCritSec ics(&m_cs); for(int i = 0; i < m_apBuckets.GetSize(); i++) { CBucket* pNewBucket = m_apBuckets[i]->Clone(); if(pNewBucket == NULL) return WBEM_E_OUT_OF_MEMORY; if(pDest->m_apBuckets.Add(pNewBucket) < 0) { delete pNewBucket; return WBEM_E_OUT_OF_MEMORY; } } return S_OK; } CEventAggregator::CBucket::CBucket(IWbemEvent* pEvent, CVarVector* pvvData) : m_pvvData(pvvData), m_dwCount(1), m_pRepresentative(NULL) { pEvent->Clone(&m_pRepresentative); } CEventAggregator::CBucket::~CBucket() { delete m_pvvData; if(m_pRepresentative) m_pRepresentative->Release(); } BOOL CEventAggregator::CBucket::CompareTo(CVarVector& vv) { return m_pvvData->CompareTo(vv, TRUE); } HRESULT CEventAggregator::CBucket::AddEvent(IWbemEvent* pEvent) { // Just increment the number of events in the bucket // ================================================= m_dwCount++; return WBEM_S_NO_ERROR; } HRESULT CEventAggregator::CBucket::MakeAggregateEvent( IWbemClassObject** ppAggregateEvent) NOCS { HRESULT hres; // Create an instance of the aggregate event class // =============================================== if(mstatic_pClass == NULL) return WBEM_E_SHUTTING_DOWN; IWbemClassObject* pAgg; hres = mstatic_pClass->SpawnInstance(0, &pAgg); if(FAILED(hres)) return hres; // Fill in the number of events in the bucket // ========================================== VARIANT v; VariantInit(&v); V_VT(&v) = VT_I4; V_I4(&v) = (long)m_dwCount; hres = pAgg->Put(L"NumberOfEvents", 0, &v, NULL); if(FAILED(hres)) { pAgg->Release(); return hres; } // Fill in the representative // ========================== V_VT(&v) = VT_EMBEDDED_OBJECT; V_EMBEDDED_OBJECT(&v) = m_pRepresentative; hres = pAgg->Put(L"Representative", 0, &v, NULL); if(FAILED(hres)) { pAgg->Release(); return hres; } *ppAggregateEvent = pAgg; return WBEM_S_NO_ERROR; } CEventAggregator::CBucket* CEventAggregator::CBucket::Clone() { CVarVector* pNewVv = new CVarVector(*m_pvvData); if(pNewVv == NULL) return NULL; CBucket* pNewBucket = new CBucket(m_pRepresentative, pNewVv); if(pNewBucket == NULL) { delete pNewVv; return NULL; } pNewBucket->m_dwCount = m_dwCount; return pNewBucket; } CEventAggregator::CBucketInstruction::CBucketInstruction( CEventAggregator* pAggregator, CBucket* pBucket, double fSTimeout) : m_pAggregator(pAggregator), m_pBucket(pBucket), m_lRefCount(0) { m_Interval.SetMilliseconds(fSTimeout * 1000); m_pAggregator->AddRef(); } CEventAggregator::CBucketInstruction::~CBucketInstruction() { m_pAggregator->Release(); } void CEventAggregator::CBucketInstruction::AddRef() { InterlockedIncrement(&m_lRefCount); } void CEventAggregator::CBucketInstruction::Release() { if(InterlockedDecrement(&m_lRefCount) == 0) delete this; } int CEventAggregator::CBucketInstruction::GetInstructionType() { return INSTTYPE_AGGREGATION; } CWbemTime CEventAggregator::CBucketInstruction::GetNextFiringTime( CWbemTime LastFiringTime, OUT long* plFiringCount) const { // Only fires once. // ================ return CWbemTime::GetInfinity(); } CWbemTime CEventAggregator::CBucketInstruction::GetFirstFiringTime() const { // In "interval" ms from now // ========================= return CWbemTime::GetCurrentTime() + m_Interval; } HRESULT CEventAggregator::CBucketInstruction::Fire(long lNumTimes, CWbemTime NextFiringTime) { m_pAggregator->DispatchBucket(m_pBucket); return WBEM_S_NO_ERROR; } BOOL CEventAggregator::CAggregatorInstructionTest:: operator()( CTimerInstruction* pToTest) { if(pToTest->GetInstructionType() == INSTTYPE_AGGREGATION) { CBucketInstruction* pInst = (CBucketInstruction*)pToTest; return (pInst->GetAggregator() == m_pAgg); } else return FALSE; }