Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

694 lines
22 KiB

  1. //=================================================================================================
  2. //
  3. // Copyright (c) 2000-2001 Microsoft Corporation, All Rights Reserved
  4. //
  5. // assoc.cpp -- Rule-based association class
  6. //
  7. // This class allows for the creation of a specific type of rule-based associations. Consider
  8. // this example:
  9. //
  10. // CAssociation MyThisComputerPhysicalFixedDisk(
  11. // L"ThisComputerPhysicalFixedDisk",
  12. // L"Root\\default",
  13. // L"ThisComputer",
  14. // L"PhysicalFixedDisk",
  15. // L"GroupComponent",
  16. // L"PartComponent"
  17. // ) ;
  18. //
  19. // This declaration is saying that there is a class named "ThisComputerPhysicalFixedDisk" which
  20. // resides in the "root\default" namespace. It is an association between the "ThisComputer"
  21. // class, and the "PhysicalFixedDisk" class. The "ThisComputer" value goes into the
  22. // "GroupComponent" property of the "ThisComputerPhysicalFixedDisk" class, and the
  23. // "PhysicalFixedDisk" value goes in the "PartComponent" property of the
  24. // "ThisComputerPhysicalFixedDisk" class.
  25. //
  26. // Some notes:
  27. // - This class will take all the instances of the left class ("ThisComputer" in the example
  28. // above) and relate them to ALL instances of the right class ("PhysicalFixedDisk" in the example
  29. // above). So, if there are 3 instances of the left class, and 4 instances of the right class,
  30. // this association class will return 12 instances.
  31. //
  32. // - When choosing which of the two classes should be the left class, choose the class that is
  33. // likely to have fewer instances. This will result in less memory being used, and instances
  34. // being sent back to the client sooner.
  35. //
  36. // - CAssociation supports ExecQuery, GetObject, and EnumerateInstances.
  37. //
  38. // - CAssociation is designed to be derived from. For example, if your association needs to
  39. // support DeleteInstance, ExecMethod, or PutInstance, create a class that derives from
  40. // CAssociation, and add the appropriate methods. Also, various methods such as
  41. // LoadPropertyValues and AreRelated may be useful for further customization.
  42. //
  43. // - The two endpoint classes can be dynamic, static, or abstract. CAssociation will do a deep
  44. // enumeration (actually a query, which is always deep) to retrieve the instances.
  45. //
  46. // - When calling the endpoint classes, CAssociation will use per property gets, and queries
  47. // with Select clauses and/or Where statements. If the endpoint classes support per-property
  48. // gets or queries, this will result in better performance for the associaton class.
  49. //
  50. // - The association class and both endpoints must all be in the same namespace.
  51. //
  52. // See also: CBinding (binding.cpp) for a different type of rule-based association.
  53. //
  54. //=================================================================================================
  55. #include "precomp.h"
  56. #include "Assoc.h"
  57. #include <helper.h>
  58. /////////////////////////////////////////////////////////////////////
  59. //
  60. // Function: CAssociation::CAssociation
  61. //
  62. // Constructor.
  63. //
  64. /////////////////////////////////////////////////////////////////////
  65. CAssociation::CAssociation(
  66. LPCWSTR pwszClassName,
  67. LPCWSTR pwszNamespaceName,
  68. LPCWSTR pwszLeftClassName,
  69. LPCWSTR pwszRightClassName,
  70. LPCWSTR pwszLeftPropertyName,
  71. LPCWSTR pwszRightPropertyName
  72. ) : Provider(pwszClassName, pwszNamespaceName)
  73. {
  74. // Save off the class and property names
  75. m_sLeftClassName = pwszLeftClassName;
  76. m_sRightClassName = pwszRightClassName;
  77. m_sLeftPropertyName = pwszLeftPropertyName;
  78. m_sRightPropertyName = pwszRightPropertyName;
  79. }
  80. /////////////////////////////////////////////////////////////////////
  81. //
  82. // Function: CAssociation::~CAssociation
  83. //
  84. // Destructor
  85. //
  86. /////////////////////////////////////////////////////////////////////
  87. CAssociation::~CAssociation()
  88. {
  89. }
  90. /////////////////////////////////////////////////////////////////////
  91. //
  92. // Function: CAssociation::ExecQuery
  93. //
  94. // This routine will optimize on queries of the form:
  95. // WHERE prop1 = value1 [ or prop1 = value2 ...]
  96. //
  97. // This type of query is commonly seen when doing an ASSOCIATORS or
  98. // REFERENCES query against one of the endpoint classes.
  99. //
  100. // This routine will also optimize on queries of the form:
  101. // WHERE prop1 = value1 [ or prop1 = value2 ...] AND
  102. // prop2 = value3 [ or prop2 = value4 ...]
  103. //
  104. // It will NOT optmize on queries of the form:
  105. // WHERE prop1 <> value1
  106. // WHERE prop1 > value1
  107. // WHERE prop1 = value1 OR prop2 = value2
  108. //
  109. /////////////////////////////////////////////////////////////////////
  110. HRESULT CAssociation::ExecQuery(
  111. MethodContext* pMethodContext,
  112. CFrameworkQuery &pQuery,
  113. long lFlags
  114. )
  115. {
  116. HRESULT hr = WBEM_S_NO_ERROR;
  117. TRefPointerCollection<CInstance> lefts;
  118. CHStringArray sLeftPaths, sRightPaths;
  119. // Look for WHERE m_sLeftPropertyName=value1
  120. pQuery.GetValuesForProp ( m_sLeftPropertyName, sLeftPaths ) ;
  121. // Look for WHERE m_sRightPropertyName=value1
  122. pQuery.GetValuesForProp ( m_sRightPropertyName, sRightPaths ) ;
  123. if (sLeftPaths.GetSize() == 0)
  124. {
  125. // They didn't ask for a specific set of left instances. However,
  126. // it may be that we can figure out what left instances we need
  127. // by looking at what right instances they requested. CAssociation
  128. // doesn't do this, but CBinding does.
  129. CHStringArray sRightWheres;
  130. bool bHadRights = sRightPaths.GetSize() > 0;
  131. MakeWhere(sRightPaths, sRightWheres);
  132. // If we used to have a list of RightWheres, and MakeWhere discarded
  133. // them all as unusable, then there aren't going to be any
  134. // instances that match the query.
  135. if (!bHadRights || sRightPaths.GetSize() > 0)
  136. {
  137. // GetLeftInstances populates lefts using a sRightWheres
  138. // to construct a query.
  139. hr = GetLeftInstances(pMethodContext, lefts, sRightWheres);
  140. }
  141. }
  142. else
  143. {
  144. // For each sLeftPaths that is valid, create an entry in lefts by
  145. // doing a GetObject on the sLeftPaths entry.
  146. hr = ValidateLeftObjectPaths(pMethodContext, sLeftPaths, lefts);
  147. }
  148. // If we failed, or if there are no instances on the left, there's
  149. // no point in continuing.
  150. if (SUCCEEDED(hr) && lefts.GetSize() > 0)
  151. {
  152. // If the where clause didn't specify any value for the right property
  153. if (sRightPaths.GetSize() == 0)
  154. {
  155. // We may be able to use the information from the already retrieved
  156. // left instances to limit which instances we retrieve from the right.
  157. // CAssociation doesn't do this, but CBinding does.
  158. CHStringArray sLeftWheres;
  159. hr = FindWhere(lefts, sLeftWheres);
  160. if (SUCCEEDED(hr))
  161. {
  162. // GetRightInstances takes the 'lefts' and rubs all the
  163. // rights against them creating instances where appropriate
  164. hr = GetRightInstances(pMethodContext, &lefts, sLeftWheres);
  165. }
  166. }
  167. else
  168. {
  169. // They gave us a list of object paths for the righthand property
  170. TRefPointerCollection<CInstance> rights;
  171. // For each sRightPaths that is valid, create an instance
  172. hr = ValidateRightObjectPaths(pMethodContext, sRightPaths, lefts);
  173. }
  174. }
  175. return hr;
  176. }
  177. /////////////////////////////////////////////////////////////////////
  178. //
  179. // Function: CAssociation::GetObject
  180. //
  181. // Verify the exist of the specified association class instance.
  182. //
  183. /////////////////////////////////////////////////////////////////////
  184. HRESULT CAssociation::GetObject(
  185. CInstance* pInstance,
  186. long lFlags,
  187. CFrameworkQuery &pQuery
  188. )
  189. {
  190. HRESULT hr = WBEM_E_NOT_FOUND;
  191. CHString sLeftPath, sRightPath;
  192. // Get the two endpoints to verify
  193. if (pInstance->GetCHString(m_sLeftPropertyName, sLeftPath ) &&
  194. pInstance->GetCHString(m_sRightPropertyName, sRightPath ) )
  195. {
  196. CInstancePtr pLeft, pRight;
  197. // Try to get the objects
  198. if (
  199. SUCCEEDED(hr = RetrieveLeftInstance(
  200. sLeftPath,
  201. &pLeft,
  202. pInstance->GetMethodContext())
  203. ) &&
  204. SUCCEEDED(hr = RetrieveRightInstance(
  205. sRightPath,
  206. &pRight,
  207. pInstance->GetMethodContext())
  208. )
  209. )
  210. {
  211. hr = WBEM_E_NOT_FOUND;
  212. // So, the end points exist. Are they derived from or equal
  213. // to the classes we are working with?
  214. CHString sLeftClass, sRightClass;
  215. pLeft->GetCHString(L"__Class", sLeftClass);
  216. pRight->GetCHString(L"__Class", sRightClass);
  217. bool bDerived = IsDerivedFrom(
  218. m_sLeftClassName,
  219. sLeftClass,
  220. pInstance->GetMethodContext()
  221. );
  222. if (bDerived)
  223. {
  224. bDerived = IsDerivedFrom(
  225. m_sRightClassName,
  226. sRightClass,
  227. pInstance->GetMethodContext()
  228. );
  229. }
  230. if (bDerived)
  231. {
  232. // Just because two instances are valid and derive from the right class,
  233. // doesn't mean they are related. Do any other checks.
  234. if (AreRelated(pLeft, pRight))
  235. {
  236. // CBinding and CAssoc don't populate any additional properties, but
  237. // an overload of one of these classes might.
  238. hr = LoadPropertyValues(pInstance, pLeft, pRight);
  239. }
  240. }
  241. }
  242. }
  243. return hr;
  244. }
  245. /////////////////////////////////////////////////////////////////////
  246. //
  247. // Function: CAssociation::EnumerateInstances
  248. //
  249. // Return all instances of the association class
  250. //
  251. /////////////////////////////////////////////////////////////////////
  252. HRESULT CAssociation::EnumerateInstances(
  253. MethodContext *pMethodContext,
  254. long lFlags /*= 0L*/
  255. )
  256. {
  257. HRESULT hr = WBEM_S_NO_ERROR;
  258. TRefPointerCollection<CInstance> lefts;
  259. CHStringArray sWheres;
  260. // GetLeftInstances populates lefts
  261. if (SUCCEEDED(hr = GetLeftInstances(pMethodContext, lefts, sWheres)))
  262. {
  263. // We may be able to use the information from the already retrieved
  264. // left instances to limit which instances we retrieve from the right.
  265. // CAssociation doesn't do this, but CBinding does.
  266. FindWhere(lefts, sWheres);
  267. // GetRightInstances takes the 'lefts' and rubs all the
  268. // rights against them
  269. hr = GetRightInstances(pMethodContext, &lefts, sWheres);
  270. }
  271. return hr;
  272. }
  273. /////////////////////////////////////////////////////////////////////
  274. //
  275. // Function: CAssociation::GetRightInstances
  276. //
  277. // For each instance of the righthand class retrieved, call
  278. // CAssociation::StaticEnumerationCallback.
  279. //
  280. /////////////////////////////////////////////////////////////////////
  281. HRESULT CAssociation::GetRightInstances(
  282. MethodContext *pMethodContext,
  283. TRefPointerCollection<CInstance> *lefts,
  284. const CHStringArray &sLeftWheres
  285. )
  286. {
  287. CHString sQuery;
  288. sQuery.Format(L"SELECT __RELPATH FROM %s", m_sRightClassName);
  289. // 'StaticEnumerationCallback' will get called once for each instance
  290. // returned from the query
  291. HRESULT hr = CWbemProviderGlue::GetInstancesByQueryAsynch(
  292. sQuery,
  293. this,
  294. StaticEnumerationCallback,
  295. GetNamespace(),
  296. pMethodContext,
  297. lefts);
  298. return hr;
  299. }
  300. /////////////////////////////////////////////////////////////////////
  301. //
  302. // Function: CAssociation::StaticEnumerationCallback
  303. //
  304. // Put the 'this' pointer back, and call CAssociation::EnumerationCallback
  305. //
  306. /////////////////////////////////////////////////////////////////////
  307. HRESULT WINAPI CAssociation::StaticEnumerationCallback(
  308. Provider* pThat,
  309. CInstance* pInstance,
  310. MethodContext* pContext,
  311. void* pUserData
  312. )
  313. {
  314. HRESULT hr;
  315. CAssociation *pThis = (CAssociation *) pThat;
  316. if (pThis)
  317. {
  318. hr = pThis->EnumerationCallback(pInstance, pContext, pUserData);
  319. }
  320. else
  321. {
  322. hr = WBEM_S_NO_ERROR;
  323. }
  324. return hr;
  325. }
  326. /////////////////////////////////////////////////////////////////////
  327. //
  328. // Function: CAssociation::EnumerationCallback
  329. //
  330. // Take the righthand instance that was passed in and pair it
  331. // with each of the left hand instances.
  332. //
  333. /////////////////////////////////////////////////////////////////////
  334. HRESULT CAssociation::EnumerationCallback(
  335. CInstance *pRight,
  336. MethodContext *pMethodContext,
  337. void *pUserData
  338. )
  339. {
  340. HRESULT hr = WBEM_E_FAILED;
  341. CInstancePtr pLeft;
  342. REFPTRCOLLECTION_POSITION posLeft;
  343. CHString sLeftPath, sRightPath;
  344. // Cast for userdata back to what it is
  345. TRefPointerCollection<CInstance> *pLefts = (TRefPointerCollection<CInstance> *)pUserData;
  346. if (pLefts->BeginEnum(posLeft))
  347. {
  348. hr = WBEM_S_NO_ERROR;
  349. // Walk all the pLefts
  350. for (pLeft.Attach(pLefts->GetNext(posLeft)) ;
  351. (SUCCEEDED(hr)) && (pLeft != NULL) ;
  352. pLeft.Attach(pLefts->GetNext(posLeft)) )
  353. {
  354. // Compare it to the current pRight
  355. if(AreRelated(pLeft, pRight))
  356. {
  357. // We have a winner. Populate the properties and send it back.
  358. if (GetLocalInstancePath(pLeft, sLeftPath) &&
  359. GetLocalInstancePath(pRight, sRightPath))
  360. {
  361. CInstancePtr pNewAssoc(CreateNewInstance(pMethodContext), false);
  362. if (pNewAssoc->SetCHString(m_sLeftPropertyName, sLeftPath) &&
  363. pNewAssoc->SetCHString(m_sRightPropertyName, sRightPath) )
  364. {
  365. if (SUCCEEDED(hr = LoadPropertyValues(pNewAssoc, pLeft, pRight)))
  366. {
  367. hr = pNewAssoc->Commit();
  368. }
  369. }
  370. }
  371. }
  372. }
  373. pLefts->EndEnum();
  374. }
  375. return hr;
  376. }
  377. /////////////////////////////////////////////////////////////////////
  378. //
  379. // Function: CAssociation::ValidateLeftObjectPaths
  380. //
  381. // Populate the lefts array by doing GetObjects on the object paths
  382. // passed in sPaths.
  383. //
  384. /////////////////////////////////////////////////////////////////////
  385. HRESULT CAssociation::ValidateLeftObjectPaths(
  386. MethodContext *pMethodContext,
  387. const CHStringArray &sPaths,
  388. TRefPointerCollection<CInstance> &lefts
  389. )
  390. {
  391. CInstancePtr pInstance;
  392. // Walk the object paths
  393. for (DWORD x=0; x < sPaths.GetSize(); x++)
  394. {
  395. ParsedObjectPath *pParsedPath = NULL;
  396. CObjectPathParser objpathParser;
  397. CHString sPath(sPaths[x]);
  398. // Parse the object path
  399. int nStatus = objpathParser.Parse( sPath, &pParsedPath );
  400. if ( 0 == nStatus )
  401. {
  402. OnDeleteObj<ParsedObjectPath *,
  403. CObjectPathParser,
  404. void(CObjectPathParser::*)(ParsedObjectPath *),
  405. &CObjectPathParser::Free> ReleaseMe(&objpathParser,pParsedPath);
  406. // Is this class derived from or equal to the lefthand class?
  407. bool bDerived = false;
  408. bDerived = IsDerivedFrom(
  409. m_sLeftClassName,
  410. pParsedPath->m_pClass,
  411. pMethodContext
  412. );
  413. // Make sure this is an absolute path
  414. if (pParsedPath->m_dwNumNamespaces == 0)
  415. {
  416. sPath = L"\\\\.\\" + GetNamespace() + L':' + sPath;
  417. }
  418. if (bDerived)
  419. {
  420. // See if it is valid. Note that we DON'T send back an error just because
  421. // we can't find one of the object paths.
  422. if (SUCCEEDED(RetrieveLeftInstance(sPath, &pInstance, pMethodContext)))
  423. {
  424. // Yup, add it to the list
  425. lefts.Add(pInstance);
  426. }
  427. }
  428. }
  429. }
  430. return WBEM_S_NO_ERROR;
  431. }
  432. /////////////////////////////////////////////////////////////////////
  433. //
  434. // Function: CAssociation::ValidateRightObjectPaths
  435. //
  436. // Retrieve the righthand instances by doing GetObjects on the object
  437. // paths passed in sPaths. Pass them to EnumerationCallback.
  438. //
  439. /////////////////////////////////////////////////////////////////////
  440. HRESULT CAssociation::ValidateRightObjectPaths(
  441. MethodContext *pMethodContext,
  442. const CHStringArray &sPaths,
  443. TRefPointerCollection<CInstance> &lefts
  444. )
  445. {
  446. HRESULT hr = WBEM_S_NO_ERROR;;
  447. CInstancePtr pInstance;
  448. // Walk the object paths
  449. for (DWORD x=0;
  450. (x < sPaths.GetSize()) && SUCCEEDED(hr);
  451. x++)
  452. {
  453. ParsedObjectPath *pParsedPath = NULL;
  454. CObjectPathParser objpathParser;
  455. CHString sPath(sPaths[x]);
  456. int nStatus = objpathParser.Parse( sPath, &pParsedPath );
  457. if ( 0 == nStatus )
  458. {
  459. OnDeleteObj<ParsedObjectPath *,
  460. CObjectPathParser,
  461. void(CObjectPathParser::*)(ParsedObjectPath *),
  462. &CObjectPathParser::Free> ReleaseMe(&objpathParser,pParsedPath);
  463. bool bDerived = false;
  464. // Make sure this object path is at least related to us
  465. bDerived = IsDerivedFrom(
  466. m_sRightClassName,
  467. pParsedPath->m_pClass,
  468. pMethodContext
  469. );
  470. // Make sure this is an absolute path
  471. if (pParsedPath->m_dwNumNamespaces == 0)
  472. {
  473. sPath = L"\\\\.\\" + GetNamespace() + L':' + sPath;
  474. }
  475. if (bDerived)
  476. {
  477. // See if it is valid. Note that we DON'T send back an error just because
  478. // we can't find one of the object paths.
  479. if (SUCCEEDED(RetrieveRightInstance(sPath, &pInstance, pMethodContext)))
  480. {
  481. hr = EnumerationCallback(pInstance, pMethodContext, &lefts);
  482. }
  483. }
  484. }
  485. }
  486. return hr;
  487. }
  488. /////////////////////////////////////////////////////////////////////
  489. //
  490. // Function: CAssociation::GetLeftInstances
  491. //
  492. // Retrieve all the lefthand instances and store them in lefts
  493. //
  494. /////////////////////////////////////////////////////////////////////
  495. HRESULT CAssociation::GetLeftInstances(
  496. MethodContext *pMethodContext,
  497. TRefPointerCollection<CInstance> &lefts,
  498. const CHStringArray &sRightWheres
  499. )
  500. {
  501. CHString sQuery;
  502. sQuery.Format(L"SELECT __RELPATH FROM %s", m_sLeftClassName);
  503. return CWbemProviderGlue::GetInstancesByQuery(sQuery, &lefts, pMethodContext, GetNamespace());
  504. }
  505. /////////////////////////////////////////////////////////////////////
  506. //
  507. // Function: CAssociation::RetrieveLeftInstance
  508. //
  509. // Retrieve a specific lefthand instance. Use per-property gets
  510. // to only request the keys for maximum performance.
  511. //
  512. /////////////////////////////////////////////////////////////////////
  513. HRESULT CAssociation::RetrieveLeftInstance(
  514. LPCWSTR lpwszObjPath,
  515. CInstance **ppInstance,
  516. MethodContext *pMethodContext
  517. )
  518. {
  519. return CWbemProviderGlue::GetInstanceKeysByPath(lpwszObjPath, ppInstance, pMethodContext);
  520. }
  521. /////////////////////////////////////////////////////////////////////
  522. //
  523. // Function: CAssociation::RetrieveRightInstance
  524. //
  525. // Retrieve a specific righthand instance. Use per-property gets
  526. // to only request the keys for maximum performance.
  527. //
  528. /////////////////////////////////////////////////////////////////////
  529. HRESULT CAssociation::RetrieveRightInstance(
  530. LPCWSTR lpwszObjPath,
  531. CInstance **ppInstance,
  532. MethodContext *pMethodContext
  533. )
  534. {
  535. return CWbemProviderGlue::GetInstanceKeysByPath(lpwszObjPath, ppInstance, pMethodContext);
  536. }
  537. /////////////////////////////////////////////////////////////////////
  538. //
  539. // Function: CAssociation::IsInstance
  540. //
  541. // See whether the specified CInstance is an Instance object, or a
  542. // Class object.
  543. //
  544. /////////////////////////////////////////////////////////////////////
  545. bool CAssociation::IsInstance(const CInstance *pInstance)
  546. {
  547. DWORD dwGenus = 0;
  548. pInstance->GetDWORD(L"__Genus", dwGenus);
  549. return dwGenus == WBEM_GENUS_INSTANCE;
  550. }
  551. /////////////////////////////////////////////////////////////////////
  552. //
  553. // Function: CAssociation::IsDerivedFrom
  554. //
  555. // See whether the specified class is derived from or equal
  556. // to the class we are working with. Specifically, does
  557. // pszDerivedClassName derive from pszBaseClassName?
  558. //
  559. /////////////////////////////////////////////////////////////////////
  560. bool CAssociation::IsDerivedFrom(
  561. LPCWSTR pszBaseClassName,
  562. LPCWSTR pszDerivedClassName,
  563. MethodContext *pMethodContext
  564. )
  565. {
  566. // First let's see if they are equal. CWbemProviderGlue::IsDerivedFrom
  567. // doesn't check for this case
  568. bool bDerived = _wcsicmp(pszBaseClassName, pszDerivedClassName) == 0;
  569. if (!bDerived)
  570. {
  571. bDerived = CWbemProviderGlue::IsDerivedFrom(
  572. pszBaseClassName,
  573. pszDerivedClassName,
  574. pMethodContext,
  575. GetNamespace()
  576. );
  577. }
  578. return bDerived;
  579. }