VMware {code} Community
mcaptk
Contributor
Contributor

Using SearchDatastore_Task to filter a list of files

I am developing a plug-in using C#/ASPX (.Net 4.0) and vSphere 5.0.  I want to allow the user to search for files in a known location on the datastore.

I have been looking in the forums for others using SearchDatastore_Task, but I don't see anyone running into this issue.

Using the Managed Object Browser, I can get all of the files every time.  If I use the C# AppUtil class and set up my objects with the same data that I use in the Managed Object Browser, the page hangs until I see the following text within the vSphere Client:  "This program cannot display the webpage"

If I use the Visual Studio debugger, the code enters the "WaitForTask()" method and then hits a Stack Overflow Exception in the ServiceUtil.cs file ("An unhandled exception of type 'System.StackOverflowException' occurred in Vim25Service2010.XmlSerializers.dll").  Specifically in the ServiceUtil.cs file, the error occurs at the following line:

            updateset =

               _connection.Service.WaitForUpdates(

                  _connection.PropCol, version

               );

Below, I have the code that I am using for the Managed Object Browser, followed by the code that I am using for C#.  Has anyone else run into this before and/or is anyone able to see what I may have done wrong?

Note: In both examples below, the HostDatastoreBrowser is type:HostDatastoreBrowser    Value:datastoreBrowser-datastore-63

For the MOB, I use the following and get all 3 ZIP files that are in the folder:

datastorePath (required)

[vzipesxi2-local] c

searchSpec (optional)

<!-- optional -->

<searchSpec>

   <!-- optional -->

   <!-- array start -->

   <!-- optional -->

   <query>

   </query>

   <!-- array end -->

   <!-- optional -->

   <details>

      <fileType>false</fileType>

      <fileSize>true</fileSize>

      <modification>true</modification>

      <fileOwner>false</fileOwner>

   </details>

   <!-- optional -->

   <searchCaseInsensitive>true</searchCaseInsensitive>

   <!-- optional -->

   <!-- array start -->

   <!-- optional -->

   <matchPattern>*.zip</matchPattern>

   <!-- array end -->

   <!-- optional -->

   <sortFoldersFirst>false</sortFoldersFirst>

</searchSpec>

For C#, I use the following, and the last line (WaitForTask) is where the code hangs:

FileQueryFlags fqf = new FileQueryFlags();

fqf.fileOwner = false;

fqf.fileOwnerSpecified = true;

fqf.fileSize = true;

fqf.fileType = false;

fqf.modification = true;

HostDatastoreBrowserSearchSpec hdbss = new HostDatastoreBrowserSearchSpec();

hdbss.query = new FileQuery[] {new FolderFileQuery()};

hdbss.details = fqf;

hdbss.matchPattern = new string[] { "*.zip" };

hdbss.searchCaseInsensitive = true;

hdbss.searchCaseInsensitiveSpecified = true;

hdbss.sortFoldersFirst = false;

hdbss.sortFoldersFirstSpecified = true;

ServiceUtil svcUtil = vsAppUtil.getServiceUtil();

ManagedObjectReference hostDSBrowser = (ManagedObjectReference)svcUtil.GetDynamicProperty(searchDatastoreMor, "browser");

searchDatastorePath = "[vzipesxi2-local] c";

apiTaskMor = vsAppUtil._connection._service.SearchDatastore_Task(hostDSBrowser, searchDatastorePath, hdbss);

apiTaskResult = svcUtil.WaitForTask(apiTaskMor);

NOTE: In reviewing other forum posts, I saw where users where setting the hdbss.query to a new FolderFileQuery() object.  I didn't think that I needed that, but I have added it.  Since then, I get the same results.  I have left the line in here for now, but I am not sure that I need it.  Note that my Managed Object Browser does not have that value set, and it is working.

0 Kudos
1 Reply
mcaptk
Contributor
Contributor

Can I answer my own question?  I am going to put what I have learned here from today... maybe it helps someone else.

I am not 100% positive, but I think there is some type of bug in the ServiceUtility.WaitForTask() for C#.  As I mentioned I was getting a stack overflow exception earlier.  As I continued to investigate, I used the property collector to get the "info" object from the task.  Then I could look at the state property of the info-object.  The state property is a TaskInfoState type, which can only be one of the following {error, success, running, queued}.  With this information, I created my own "Wait For Task" method which will analyze that TaskInfoState property.  In my case, I set up a sleep duration and a retry amount... the two of those will give me a "timeout" duration.  If the TaskInfoState doesn't come to an "end state" {error, success} after the sleep duration and retry amounts have been exceeded, then I can declare a timeout.

After implementing this, I am getting the proper results from the SearchDatastore_Task() call, and I am no longer getting any stack overflow (as I am no longer using the standard WaitForTask() method).

While I'm sure this isn't perfect, this code works for me.  I have pasted the code below:

----------------------------------------------

Calling code looks like this

----------------------------------------------

// Set up your HostDatastoreBrowserSearchSpec as you like... mine is hdbss

apiTaskMor = vsAppUtil._connection._service.SearchDatastore_Task(hostDSBrowser, searchDatastorePath, hdbss);

TaskInfo tInfo;
TaskInfoState infoState;

CustomWaitForTask(apiTaskMor, out tInfo, out infoState, 20, 15000);

----------------------------------------------

Method definition looks like this

----------------------------------------------


/**
* CustomWaitForTask
* The AppUtil's WaitForTask was causing a stack overflow exception.
* Using the TaskInfo object, we can check the state.  We can then
* set a retry count and a sleep time and if the state isn't an
* "end state" {error, success} in a reasonable time, we timeout.
*
* @param  apiTaskMor [ManagedObjectReference] - mor to the task that
*             we are waiting for results
* @param  tInfo [out TaskInfo] - the TaskInfo object from the task
* @param  infoState [out TaskInfoState] - the state of the task
* @param  numRetries [int] - number of retries before timing out
* @param  sleepTime [int] - time in ms for sleeping between timeouts
*
* @return  bool  - True = success; false = error or timeout
*
* <b>GLOBALS</b> :
*
* <b>ASSUMPTIONS</b> :
* Timeout duration (in ms) will equal numRetries x sleepTime
*/
private bool CustomWaitForTask(ManagedObjectReference apiTaskMor,
   out TaskInfo tInfo, out TaskInfoState infoState,
   int numRetries = 10, int sleepTime = 2000)
{
   ServiceUtil svcUtil = vsAppUtil.getServiceUtil();
   ServiceContent svcContent = vsAppUtil._connection._sic;
   ObjectContent[] objTaskInfo;
   tInfo = null;
   while (numRetries >= 0)
   {
      objTaskInfo = svcUtil.GetObjectProperties(svcContent.propertyCollector, apiTaskMor, new String[] { "info" });
      tInfo = (TaskInfo)objTaskInfo[0].propSet[0].val;
      if (tInfo == null)
      {
         throw new ApplicationException("TaskInfo object is null");
      }

      infoState = tInfo.state;
      if (infoState == TaskInfoState.error || infoState == TaskInfoState.success)
      {
         break;
      }

      numRetries--;
      System.Threading.Thread.Sleep(sleepTime);
   }
   return (infoState == TaskInfoState.success ? true : false);
}
0 Kudos