Subscribe to News feed

Microsoft Giveth and Taketh, the mystery of the SPAuditQuery.RowLimit property

Posted at: 2:34 PM on 26 August 2009 by Muhimbi

gift

During a recent optimisation cycle of our Audit Log Viewer (Part of the Muhimbi SharePoint Audit Suite) we noticed the RowLimit property on the SPAuditQuery object. Weird, not sure why we missed this the first time around, but we’ll gladly accept this gift and move on.

The thing is, some of our customers just blindly enable all audit events on all of their site collections and never look back. The record stands, I kid you not, at 848 Million lines of audit data in a single table. As our audit software behaves like a good SharePoint citizen, we access all data through the SharePoint object model and never touch the database directly.

Unfortunately, if a user queries the audit log and removes all filters, the SharePoint object model retrieves all data and happily converts each and every line of audit data into an SPAuditEntry object. This is a relatively slow process that cannot be interrupted and may result in extreme memory and cpu usage.

OK, so back to this shiny RowLimit property. Our log viewer already contained some optimisations to deal with large amounts of data, but being able to set the RowLimit is going to solve the problems of even our most demanding customers….. excellent.

Excellent indeed, everything was working great in our development environment, but during a regression test one of our Test farms was complaining and threw an Exception related to get_RowLimit() not being found…. bugger! We rubbed our eyes, fired up Reflector and compared Microsoft.SharePoint.dll from our Test environment with the one in our Development environment.

RowLimit

As is evident in the screenshot listed above, the version running in our Development environment (on the right) has public members that are not present in the version running in our Test Environment (on the left).

It appears that Microsoft has not documented on which version of the DLL the RowLimit property was introduced (12.0.6219.1000 doesn’t have it, but 12.0.6318.5000 does) so we have decided to detect the availability of the property at runtime to allow our software to auto optimise on systems running the newer version of the DLL.

The code is split up in two methods as the actual line using the RowLimit property cannot be in the same method that is testing its presence. This is related to how .net’s JIT compiler works in the background.

/// <summary>
/// Check presence of RowLimit and set the value
/// </summary>
/// <param name="wssQuery">The query to set the limit on.</param>
public static void SetRowLimit(SPAuditQuery wssQuery)
{
    Type t = typeof(Microsoft.SharePoint.SPAuditQuery);
    PropertyInfo p = t.GetProperty("RowLimit");
 
    if (p != null)
    {
        SetRowLimitInternal(wssQuery);
    }
}
 
/// <summary>
/// Internal method for setting the actual row limit. In its own method
/// to prevent the JIT from tripping over in the SetRowLimit method above.
/// </summary>
/// <param name="wssQuery">The query to set the limit on.</param>
private static void SetRowLimitInternal(SPAuditQuery wssQuery)
{
    wssQuery.RowLimit = MAX_QUERY_ROWS;
}

Labels: , ,

Additional ‘little known’ SharePoint Dispose Guidelines

Posted at: 11:34 AM on 20 August 2009 by Muhimbi

dispose

If you have been a SharePoint developer for more than a day, it will come as no surprise that you need to take great care of manually disposing your objects. Much has been written on this subject, but there are still gaps in the SharePoint Community’s knowledge.

A couple of weeks ago we identified an undocumented requirement to call Dispose() on SPFile streams and today we are documenting a resource leak related to SharePoint’s fancy SiteAdministrationSelector class, which allows users of our Audit Suite to quickly switch between the audit logs for different Site Collections.

During our latest test cycle we were seeing the following line in our SharePoint Trace Logs every time our Audit Log Viewer was invoked, even when the SiteAdministrationSelector was hidden or disabled:

An SPRequest object was not disposed before the end of this thread.  To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.  Due to flags specified at object creation, this will not be freed until processed by garbage collection.  Allocation Id: {F4F8B307-1F2C-4925-9C23-2A1EBED4B475}  To determine where this object was allocated, create a registry key at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings.  Then create a new DWORD named SPRequestStackTrace with the value 1 under this key."

As we are avid users of SPDisposeCheck we were pretty sure we were not leaking any obvious SPSite or SPWeb resources. We tried explicitly disposing the SiteAdministrationSelector in the OnUnload event, but no luck. After several more, and increasingly desperate, attempts we found out that the object that requires disposing is the CurrentItem property, leaving us with the following OnUnload event.
 

protected override void OnUnload(EventArgs e)
{
    try
    {
        // ** Dispose the selected site in the selector. See bug #373
        if(siteCollectionSelector.CurrentItem != null)
            siteCollectionSelector.CurrentItem.Dispose();                
 
        // ** Dispose our global objects
        if (_selectedSite != null && _disposeSelectedSite == true)
            _selectedSite.Dispose();
        if (_selectedWeb != null && _disposeSelectedWeb == true)
            _selectedWeb.Dispose();
    }
    catch (Exception ex)
    {
        ExceptionHelper.ProcessException(ex, this);
    }
}

 

Additional topics regarding the prevention of resource leaks in SharePoint are discussed in section 7.11 of our SharePoint development guidelines. Download your free copy here.

Labels: ,

Solution for layouts.sitemap not accessible for non privileged SharePoint users

Posted at: 12:18 PM on 19 August 2009 by Muhimbi

exception

At Muhimbi we take great pride in the fact that we actually deal with exceptions properly. And when I say properly I mean Not ignoring them, not letting them bubble to the top and make it the users’ problem, but rather catch them, enrich them, log them, take appropriate action and present the user with a friendly message. Oh… and LOG THEM! See, it is so important I mentioned it twice.

During a recent test cycle of our SharePoint Audit Suite, we noticed the following entry in the Event log. The weird thing is that it only happens if the particular Application page was the first page being requested since the Application Pool was last recycled, and only if that request was made by a non privileged (read not a development I can go anywhere administrator)  account.

* Message: The file _app_bin/layouts.sitemap required by XmlSiteMapProvider does not exist.
  - Exception: System.InvalidOperationException
  - StackTrace:    at System.Web.XmlSiteMapProvider.CheckSiteMapFileExists()
   at System.Web.XmlSiteMapProvider.GetConfigDocument()
   at System.Web.XmlSiteMapProvider.BuildSiteMap()
   at Microsoft.SharePoint.Navigation.SPXmlContentMapProvider.BuildSiteMap()
   at System.Web.XmlSiteMapProvider.get_RootNode()
   at Muhimbi.SharePoint.Audit.AuditLogViewer.provider_SiteMapResolve(Object sender, SiteMapResolveEventArgs e)

Fortunately, and surprisingly, the error message is quite descriptive. Our first hunch was that SharePoint is trying to load ‘_app_bin/layouts.sitemap’ using the current user’s credentials. After the first request is successful it caches the file for future requests, quite sensible.

To prove our hypothesis we fired up our favourite troubleshooting tool, Process Monitor (Thanks Mark!) and placed a filter on the file system to only return entries containing ‘.sitemap’. After making the first request it became clear that SharePoint is indeed using the current user’s account to access this file as is evident in the following screenshot.

 

SiteMapException

 

The solution, as it so often is, is to elevate the part of the code that causes the sitemap file to be read. in our case the following bit.

/// <summary>
/// Event handler that allows the breadcrumb to be modified.
/// </summary>
public SiteMapNode provider_SiteMapResolve(object sender, SiteMapResolveEventArgs e)
{
    try
    {
        // ** Irrelevant code removed for the sake of clarity
 
        // ** First request uses the current user's account to load sitemap file, so elevate.
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            SiteMapNode listNode = e.Provider.RootNode.ChildNodes[0].Clone();
            listNode.Url = url;
            listNode.Title = title;
            listNode.ChildNodes = new SiteMapNodeCollection();
 
            pageTitleNode = new SiteMapNode(e.Provider, Guid.NewGuid().ToString());
            pageTitleNode.Title = HttpContext.GetGlobalResourceObject("MuhimbiAuditSharedResources", 
"Page_Title_AuditLogViewer").ToString();
            listNode.ChildNodes.Add(pageTitleNode);
            pageTitleNode.ParentNode = listNode;
        });
    }
    catch (Exception ex)
    {
        ExceptionHelper.ProcessException(ex, this);
    }
 
    return pageTitleNode;
}

 

Another case…err…bug closed, download our full and free SharePoint Development Guidelines.

Labels: ,

Muhimbi’s SharePoint development guidelines available for public download

Posted at: 11:58 AM on 19 May 2009 by Muhimbi

BibleIf it wasn’t for the active SharePoint community, who have filled in the many gaps in Microsoft’s SharePoint documentation, it would be virtually impossible for the developers in our organisation to do any kind of half decent SharePoint development.

Although we have been happily sharing our knowledge and experience on this blog, we feel we have to do something more, which is why we have decided to make our internal SharePoint development guidelines documentation available for download.

Let’s put a big fat disclaimer first: These guidelines have been written with our internal developers, environment and products such as our PDF Converter, URL Shortener and Audit Suite in mind (spot the subtle, yet shameless, plug). There is some valuable information in this document, but not everything may apply to your situation, team or company. Feel free to rip the document apart, rebrand it so it looks like you wrote it, we don’t mind. However, we would appreciate it if you would include a link to this blog posting in any derivative works.

Let’s disclaim a bit more: This is not a SharePoint Tutorial, existing SharePoint development knowledge is assumed, the document is constantly being revised, we don’t guarantee that ANY of the information in the document is accurate, use at your own risk etc. etc.

Now, having said that, this document is serving our internal developers well. I cannot imagine that anyone has memorised it, but it is very useful to read at least once and then use it as a reference to get to the real information.

The following topics are covered:

GuidelinesTOC

Even if you consider yourself a SharePoint expert it is worth a look, particularly chapters 8 Dealing with version numbers and 9 Sharing Libraries and DLLs between SharePoint solutions may be of interest.

Over the next few weeks and months we will discuss each section in a separate blog posting to allow an open discussion to take place.

We would like to thank Brian Farnhill for reviewing an early version of this document in early 2009.

Constructive feedback is always welcomed. Either use the comments section at the bottom of this posting or any of the facilities listed on the Contact Us page. If you find this information valuable then please subscribe to our RSS feed and feel free to evaluate our products.

 

Download the SharePoint Development Guidelines in docx format.

.

Labels: ,

How to reliably detect MOSS or WSS at runtime

Posted at: 5:26 PM on 13 May 2009 by Muhimbi

Our support team faces different SharePoint based ‘customer challenges’ on a daily basis. It will comes as no surprise that no two SharePoint deployments are the same, but sometimes we are really baffled by what we encounter.

Recently we came across a SharePoint environment that had a problem with the fancy ‘Browse’ button that allows users to select a destination library to store documents converted by our PDF Converter for SharePoint. The thing is, this environment was running WSS and not MOSS and as WSS does not support this fancy feature we only display this button on MOSS systems.

PDFConverterScreen3
Shiny Document Library picker

Our MOSS detection logic is based on a common pattern, which is to check for the presence of the MOSS only OssNavigation feature. As this particular WSS environment has this feature installed it was incorrectly detected as a MOSS installation, resulting in problems when someone would press the browse button.

Further investigation shows that checking for OssNavigation is not a reliable manner of detecting MOSS as this feature is also installed by the free Microsoft Search Server 2008 Express, which can be installed on top of WSS.

To cut a long story short, we have updated our solution to check for 2 MOSS specific features before deciding if a SharePoint installation is MOSS or WSS. The code is as follows:

/// <summary>
/// Method to find out if a SharePoint installation is MOSS or WSS 3.0
/// </summary>
public static bool IsMOSS()
{
   
SPFeatureDefinitionCollection features = SPContext.Current.Site.WebApplication.Farm.FeatureDefinitions;
    if (features["OssNavigation"] != null && features["Publishing"] != null)
        return true;
    else
        return false;
}

Case closed…. for now.

Labels: ,