ASP.NET, Coding, Sitecore

Investingation application pool crash due to StackOverflow exception in Sitecore

A web application crashing with StackOverflow exception is quite the pain to resolve. We had a customer, experiencing random app pool crashed, and had to figure out what caused it. When dealing with a StackOverflow exception, nothing in logged in the Sitecore log files, and the only thing showing the error, is the Windows Event log (c00000fd). The solution was built on a Sitecore CMS 8.2, with Active directory module installed.

Active Directory Module in Sitecore

We previously had crash issues within the AD module, when the AD contained nested circular roles. When Sitecore access a user with nested circular roles, a StackOverflow exception is thrown. Therefore, we tested their AD for circular roles. To do this we used a PowerShell script querying the AD for these roles. This version is slightly modified from Richard Muellers version from 2011:

Function CheckNesting ($Group, $Parents)
{
 # Recursive function to enumerate group members of a group.
 # $Group is the group whose membership is being evaluated.
 # $Parents is an array of all parent groups of $Group.
 # $Count is the number of groups involved in circular nesting.
 # $GroupMembers is the hash table of all groups and their group members.
 # $Count and $GroupMembers must have script scope.
 # If any group member matches any of the parents, we have
 # detected an instance of circular nesting.

# Enumerate all group members of $Group.
 ForEach ($Member In $Script:GroupMembers[$Group])
 {
 Write-Host "Testing Group: $Member"
 # Check if this group matches any parent group.
 ForEach ($Parent In $Parents)
 {
 If ($Member -eq $Parent)
 {
 Write-Host "Circular Nested Group: $Parent"
 $Script:Count = $Script:Count + 1
 # Avoid infinite loop.
 Return
 }
 }
 # Check all group members for group membership.
 If ($Script:GroupMembers.ContainsKey($Member))
 {
 # Add this member to array of parent groups.
 # However, this is not a parent for siblings.
 # Recursively call function to find nested groups.
 $Temp = $Parents
 CheckNesting $Member ($Temp += $Member)
 }
 }
}

# Hash table of groups and their direct group members.
$GroupMembers = @{}

# Search entire domain.
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Root = $Domain.GetDirectoryEntry()
$Searcher = [System.DirectoryServices.DirectorySearcher]$Root

$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
$Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
$Searcher.PropertiesToLoad.Add("member") > $Null

# Filter on all group objects.
$Searcher.Filter = "(objectCategory=group)"
$Results = $Searcher.FindAll()

# Enumerate groups and populate Hash table. The key value will be
# the Distinguished Name of the group. The item value will be an array
# of the Distinguished Names of all members of the group that are groups.
# The item value starts out as an empty array, since we don't know yet
# which members are groups.
ForEach ($Group In $Results)
{
 $DN = [string]$Group.properties.Item("distinguishedName")
 $Script:GroupMembers.Add($DN, @())
}

# Enumerate the groups again to populate the item value arrays.
# Now we can check each member to see if it is a group.
ForEach ($Group In $Results)
{
 $DN = [string]$Group.properties.Item("distinguishedName")
 $Members = @($Group.properties.Item("member"))
 # Enumerate the members of the group.
 ForEach ($Member In $Members)
 {
 # Check if the member is a group.
 If ($Script:GroupMembers.ContainsKey($Member))
 {
 # Add the Distinguished Name of this member to the item value array.
 $Script:GroupMembers[$DN] += $Member
 }
 }
}

# Count the number of circular nested groups found.
$Script:Count = 0
# Retrieve array of all groups in the domain.
$Groups = $Script:GroupMembers.Keys
# Enumerate all groups and check group membership of each.
Write-Host "Domain: $Domain"
Write-Host "Root: $Root"
Write-Host "Searcher: $Searcher"

ForEach ($Group In $Groups)
{
 # Check group membership for circular nesting.
 CheckNesting $Group @($Group)
}

Write-Host "Number of circular nested groups found = $Script:Count"

After running this script, we could rule out the Active Directory Module and circular nested roles.

Number of circular nested groups found = 0

Using recursive metodes in custom code.

Another thing that could cause a StackOverflow exception, is recursive calls in custom code. However the solution was quite large in terms of code and classes. Therefore, I used a C# script to identify recursive calls and LinkPad to run it. The Script need Mono.Cecil.dll to run, and can be found here: Mono.Cecil

const string AssemblyFilePath = @"{AssemblyFilePath}";

void Main(){
 var assembly = ModuleDefinition.ReadModule(AssemblyFilePath);
 var calls =
     (from type in assembly.Types 
     from caller in type.Methods
     where caller != null && caller.Body != null
     from instruction in caller.Body.Instructions
     where instruction.OpCode == OpCodes.Call
     let callee = instruction.Operand as MethodReference
     select new { type, caller, callee }).Distinct();

var directRecursiveCalls =
     from call in calls
     where call.callee == call.caller
     select call.caller;

foreach (var method in directRecursiveCalls)
     Debug.WriteLine(method.DeclaringType.Namespace + "." + method.DeclaringType.Name + "." + method.Name + " calls itself");
}

This script iterates through assembly files and locate recursive calls:

Results for recursive calls in Assembly.Kernel.dll: 6
Assembly.Kernel.Providers.ActivityStream.SingleActivityFetcher.GetActivityRootItem
Assembly.Kernel.Pipelines.Events.Development.SerializeEvent.SerializeItemAndParent 
Assembly.Kernel.Localisation.Client.ResourceManager.GenerateAppFile
Assembly.Kernel.HttpHandlers.MenuService.GetParent
Assembly.Kernel.HttpHandlers.MenuService.GetParentOfSelf 
Assembly.Kernel.Helpers.SitecoreHelper.GetDescendants

This will output all direct recursive calls in the assembly, and make it a lot faster to check if any of them could be causing a StackOverflow exception. However, in our case, this wasn’t the issue.

Creating a memory dump on the time of crash

Out last resort was to attach a tool to the w3wp.exe process and create a dump file when the application crashed on a StackOverflow exception. This can cause the application to run slow and might not be possible in a production environment.

Sitecore Support suggested we use a lightweight tool called ProcDump. Using this tool was a lot less performance consuming than other tools we tried.  We attached it and set it up to collect a memory dump on first chance of a StackOverflow exception:

procdump -e 1 -f "System.StackOverflowException" -ma [Name or PID]

Take a look at the link above to see how to use ProcDump.

Looking through the memory dump, it was clear a save event in Sitecore was resaving and kept firing the save event. In a save event we were adjusting ItemSecurity, with this:

item.Editing.BeginEdit();
item.Security.SetAccessRules(accessRules);
item.Editing.EndEdit(false, true);

This has worked for a long time in the solution, but we recently upgraded it to Sitecore 8.2, and apparently the method SetAccessRules() now saves the item itself.

To fix it Sitecore support suggested we wrapped it in a event disabler, because we are modifying more fields than security:

using(new EventDisabler()){
    item.Editing.BeginEdit();
    item.Security.SetAccessRules(accessRules);
    item.Editing.EndEdit(false, true);
}

After being through all these differnt approches, the memory dump showed us exactly where to look. After identifying where the issue was it didn’t take long to fix and prevent it from happening again. The memory dump is the easiest way to locate a StackOverflow exception, however, it might not always be possible in a production environment.

The other approaches might be useful in other scenarios as well. The AD circular nested roles were nearly impossible to find the first time we encountered it. This script would have helped us a lot back then.

 

Advertisements
Sitecore

Sitecore Express Migration Tool 2.0 skipping Security and Configuration steps

I recently got to know this wonderful tool when upgrading an old Sitecore version. The tool itself allows you to migrate any Sitecore 6.6 or Sitecore 7.2 versions to Sitecore 8.2. It also includes support for migrating Web Forms for Marketers.

My first experience was upgrading a 7.2 to 8.2. and within a day, the solution was fully upgraded and ready for testers. However, my newest attempt at upgrading a 6.6 solution gave a bit trouble. We decided to run at test upgrade to see if everything was migrated as it should, locally first.

All seemed fine, and we ran the migration again, but from using the production environments databases. This time the tool skipped both Security and Configuration. LEaving all users and roles behind. After some debugging and searching, I found that this was a know issue with the Express Tool:

The tool skips security and configuration steps if at least one migration was completed for the same source version (137519). Source

We needed to reset the tool in order to be able re-run the migration. To reset the tool you have to delete the Database file, created during the first run of the tool:

  • Migration.db

It is located in the root folder of the tools. The database file stores information on previous migrations, and somehow Sitecore skips Security and Configuration, if you already migrated the source version once before.

With the tool reset, the migration works perfectly again 🙂

 

 

 

Sitecore

Sitecore Intranet Platform? What happened to it?

If you search for Sitecore Intranet, within the Sitecore regime, you will find the old sdn pages about the intranet portal, with a message saying :

The Sitecore Intranet Platform has been discontinued.

Sitecore officially discontinued the SIP product, after the last release October 22, 2013. The last supported version from Sitecore is Sitecore Intranet 4.1, running on a Sitecore 7.x installation. However, at Oxygen A/S, some of our largest customers were using the Sitecore Intranet Platform throughout their entire organization with multiple integrations. Their employees have one point of entry to most of their systems, the Intranet.

Because of this, we started to develop the Sitecore Intranet platform, upgrade it, remove old deprecated Sitecore elements to replace with newer technology, updated work processes and added much needed features. Today, the intranet runs on Sitecore 8.2.

We have updated it, following these four keywords.

  • Easy
  • Accessible
  • Social
  • Integrated

One of the main feature we have added, is the Social wall:

Oxygen itranet

The wall is where the employee can communicate and share knowledge with each other. They can upload videos, images and direct their messages to groups, users or teams if needed.

Another cool new feature is the groups. The groups in Oxygen Intranet are made both for professional and social purposes. This is an effective way to share knowledge with a project group, but it can also be used in non-work related contexts like film or sport groups.

Group

The new Oxygen Intranet comes with a lot more features than mentioned here. This post was to clarify for Sitecore developers / Marketeers what happened to the old SIP (Sitecore Intranet Portal). If you would like to know more about the Oxygen Intranet, have a look at it here.

 

Sitecore

Getting standard Sitecore fields as a list?

I had a task where I needed to get field names present on an item. By getting all fields on an item using Item.Fields, I could easily convert this list to a list of strings, containing the name of the field:

var fieldNames = contextItem.Fields.Select(field => field.Name).ToList();

In my case the client would provide a exclude pipe-separated list of item names which we didn’t need for this operation:

Exlucde

Removing these values:

var excludedFields = templateFieldValue.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
// Removed excludes.
foreach (string fieldName in excludedFields)
{
     fieldNames.RemoveAll(x => x.ToLower().Equals(fieldName.ToLower()));
}

However, this will leave you with a list of custom fields for the item, along with standard fields from Sitecore. In our case we needed to have a complete list of custom fields, without Sitecores standard fields.

To achieve this, Sitecore has a static class which contains all of the standard fields for Sitecore:

Sitecore.FieldIDs

This class has 82 fields, which return a Sitecore.Data.ID. I needed a list of those IDs, so I could filter my field names, and remove all unneeded fields. To get this list you can use reflection:

/// <summary>
/// Use reflection to get all sitecore standard field ids from FieldIDs class
/// </summary>
/// <returns>List of Sitecore standard fields IDs</returns>
private static List<ID> GetStandardSitecoreFields()
{
     Type myType = typeof(FieldIDs);
     FieldInfo[] fields = myType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
     return fields.Select(field => (ID) field.GetValue(myType)).ToList();
}

With this list you can easily remove Sitecore standard fields, when using Item.Fields:

var fieldNames = contextItem.Fields.Where(f => !sitecoreStandardFieldIDs.Contains(f.ID)).ToList().Select(field => field.Name).ToList();
ASP.NET, Coding

Automatic AssemblyInfo creation on build!

I am currently developing a Sitecore module for a client, who needed a versioning based on builddate. They wanted the module to display an message to the user if it was out of date. We therefore needed to update the assembly files versions on each release. To ensure we remember to to update the version with every release, I looked into making this automatic. This can be usefull for Sitecore modules and all other applications.

To achieve this, I used AssemblyInfo for versioning. First we need to retrive the version form the assembly file:

var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion;

We use it to check against a webservice, where the client provided the newest version number in XML.

<ApplicationVersions>
   <SitecoreConnector>2017.05.31.1003</SitecoreConnector>
</ApplicationVersions>

There is a lot of modules or plugins which can auto increment the version in assemblyinfo. However we needed it to be based on date and time. Therefore I decided to use T4 TextTemplates, to create a AssemblyVersion.cs file:

<#@ template language="C#" #>
<#@ output extension=".cs" #>  
using System;
using System.Reflection;
 
[assembly: AssemblyVersion("<#=DateTime.Now.ToString("yyyy.MM.dd.HHmm")#>")]
[assembly: AssemblyFileVersion("<#=DateTime.Now.ToString("yyyy.MM.dd.HHmm")#>")]
[assembly: AssemblyCopyright("Copyright © Customer <#=DateTime.Now.Year#>")]

T4 is used by developers as part of an application or tool framework to automate the creation of text files with a variety of parameters. These text files can ultimately be any text format, such as code (for example C#), XML, HTML or XAML. T4 Text transformation (Wiki)

I use C# to get the date and time, and format it to my versioning needs. It can also be used to set the Copyright year dynamically. When this file is saved, Visual Studio automatically runs the template and create a AssemblyVersion.cs file:

using System;
using System.Reflection;
 
[assembly: AssemblyVersion("2017.05.31.1003")]
[assembly: AssemblyFileVersion("2017.05.31.1003")]
[assembly: AssemblyCopyright("Copyright © Customer 2017")]

To automate this creation, I have created a pre-build event for the project. This event uses TextTransform.exe to transform the T4 template before the build.

set textTemplatingPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
if %textTemplatingPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" set textTemplatingPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
%textTemplatingPath% "$(ProjectDir)AssemblyVersion.tt"

And thats it! Automatic created AssemblyVersion.cs which runs on every build.

Sitecore

Pasting ‘CLEAN’ text into Single line text fields

We come across a lot of customers complaining about pasting text into Sitecore fields. When pasting text from e.g Outlook or any other Microsoft applications, you usually get a lot of MSO styling with your text:

<p class="MsoNormal" style="background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;">
<span style="font-size:10.0pt;font-family:"Arial",sans-serif">
Copied text from Outlook
</span> 
<o:p></o:p> 
</p>

When using the Rich Text Editor, you get the option to “clean” the pasted text. However, this is not the case for Single Line Text fields in Sitecore. Getting styling saved have the potential to break Sitecore Experience editor or any other custom code depending on the usage of the text field.

To clean the pasted text, you can simply implement a javascript on your layout and it will be applied to your fields:

    function handlePaste(e) {
        var clipboardData, pastedData;
 
        // Stop data being pasted into div
        e.stopPropagation();
        e.preventDefault();
 
        // Get pasted data via clipboard API
        clipboardData = e.clipboardData || window.clipboardData;
        pastedContent = clipboardData.getData('Text');
 
        // Execute paste with clean data
        document.execCommand('insertText', false, pastedContent);
    }
 
    document.addEventListener('paste', handlePaste);

 

Sitecore

Editcontext inside editcontext == Bad things.

As a developer, you need to be able to edit a Sitecore item for different reasons. We had a issue were we updated users data on login. The process was a bit complex and we needed to calculate multiple fields and values for each user and set it on their profileitem.

When editing a Sitecore item, the developer uses a editcontext:

using (new EditContext(item))
{
    item["FieldName"] = "Value";
}

However we had multiple fields needed to be edited, so we combined the field edits in one editcontext. At some point a nested method created a new editcontext with the same item.

using (new EditContext(item))
{
    // Update item
    CalculateFieldValueAndSet(item)
    // Update item
    item["Field1"] = "FieldValue";
}
private void CalculateFieldValueAndSet(Item item)
{
     // Calculate value
     var value = "a" + "b";
     using (new EditContext(item))
     {
          item["FieldName"] == value;
     {
}

When the editcontext inside CalculateFieldValueAndSet is closed, it closes all editing of the item resulting in a “Item is not en editing mode” error:

Exception: Sitecore.Exceptions.EditingNotAllowedException
Message: Item '/sitecore/content/user/B/bd/bd' is not in editing mode. Item ID: {0}
Source: Sitecore.Kernel

So be careful when using editcontext for complex and editing multiple fields on a item. 🙂