1

One of the problems that seems to repeat itself over and over again in MOSS deployments / pushes, is that eventually you have trouble with Timer Jobs. The way to know if something is working or not is to keep refreshing the Jobs list in Central Administration, which gets to be a really clunky task, especially with more than a couple servers in the farm.

Soren Nielsen again has written an excellent post about how to fix them.

So yesterday I put together a little utility in Windows Forms that is designed to just give a little better visualization over what's happening across the MOSS farm with Timer Jobs and SharePoint Services.

Timer Job Monitoring/Dashboard Utility

This utility simply connects to the local farm, categorizes all the running timer jobs (from SPFarm.Local.TimerService.RunningJobs) and puts them into one of 3 lists - Running / Incomplete / Idle. Also, I've tried to take an additional step out of problem diagnosis by showing the status of all SharePoint services across all servers in the farm. This is done through WMI by querying the Win32_Service class.

Everything is updated in 2-second intervals (since the point of this is to detect problems quickly in a deployment process).

If you'd like to try this out, you can download it here. (Just make sure you have the .NET Framework installed - which you should, if you're running MOSS - and run from one of the farm servers.)

7

This comes from the "Captain Obvious" department, but took me a few minutes nonetheless. When running a utility application (which will probably be the next post), SPFarm.Local didn't throw any exceptions but simply returned as null.

A quick Google for "SPFarm null" brings you to the conclusion that this is a 64-bit problem. And indeed, I was trying to use a .NET application compiled in a 32-bit environment (under "Any CPU" target) on a 64-bit server. However, changing the target to 64-bit and recompiling did not fix the problem.

As it turns out, the account I was running it under didn't have permissions to access the farm. I would have expected SharePoint to throw some kind of UnauthorizedAccess exception, however in this scenario SPFarm.Local simply returns null. A quick "Run as..." and it came up fine, regardless of the target CPU.

Lesson learned - if you start seeing "Object reference not set to an instance of an object" exceptions with the SPFarm object, check the account permissions that you're running under.

7

First, a short intro - I'm starting this blog for 2 reasons - 1, I'm learning a lot of stuff right now that if not used regularly, I'll forget - and need a quick way to document some of it, and 2, if it weren't for the other people out there doing the same, I wouldn't have learned a lot of what I have. So hopefully my experiences will help someone else out there, too.

Now, on to the good stuff. The problem I've been tackling this week is in MOSS 2007 - specifically, the deployment and propagation of Content Types in an enterprise environment, using the Features & Solutions Framework. Here's the background:

  1. MOSS is being used as a publishing platform for a very large project. (Hundreds of thousands of webs).
  2. Following best practices for enterprise MOSS architecture, Content Types are being deployed through Features, and are inherited by pages (list items) in subwebs.

The challenge is that MOSS really doesn't support updates to content types through the Features & Solutions framework (i.e., you are using XML Based Content Types). Once you've deployed your feature, it's going to be a little bit of a fight to update a content type by upgrading the solution. On the other hand, MOSS's editing capabilities of content types through the UI works extremely well. So, my goal has been to come up with a way to replicate the "UI" behavior within the confines of the Features & Solutions framework.

The first thing I discovered is that if you Deactivate your feature, Retract it, Delete it, Install it, Deploy it, and Activate your feature again, new fields that you've added to the Content Type in XML do, in fact, appear. The only problem is, everything that inherits from that Content Type (let's say, oh, a few million list items?) does not see any of the changes. This is because a new "list" Content Type is created any time you assign a content type to a list, and the new List Content Type inherits from the "real" one that you deployed through your feature. Soren Nielsen describes this briefly in his blog post.

So, at this point, the problem that still remains is that we need a way to propagate changes from a Parent Content Type down to its Child Content Types (in this case, pages in a publishing web that inherit from it). Microsoft's MSDN article on Updating Child Content Types pretty much spells this out for us:

"You cannot add columns to an existing site content type declaratively, in other words, by updating the Feature XML files."

Unfortunately, we simply can't create a business requirement in an enterprise MOSS installation that says "once we deploy content types, they can never change."

There are a few blogs out there that have proposed the same solution, as well as a Microsoft engineer that worked with me on this issue - use the MOSS Object Model to recurse individually through each subweb, check each list, compare fields, and programmatically add the field links that don't exist. Some have even integrated this into a STSADM command. I gave this a shot, and it takes between 0.2 - 0.4 seconds to check and compare fields on a list item. This may not sound like a lot (and will work fine for a smaller MOSS install), but for this particular environment, this technique would take at least 66 hours (2.7 days) to propagate Content Type fields.

You may notice, though, that MOSS 2007 out of the box has a special little radio button in the UI where you add a new column to a content type - "Update all content types that inherit from this content type" (yes/no). Propagating new columns/fields this way works dramatically faster - about 0.04 seconds per WEB. This is much more acceptable.

Kicking this around with a team member, we eventually understood what MOSS was actually doing. MOSS, out of the box, during a Content Type update, creates a list of changes to a content type, and then will propagate those changes via the SPContentType.Update() method. The cool thing, is that .Update() takes an optional boolean parameter - "UpdateChildren". This will propagate the current set of changes to child content types (which in our case are lists). Tested it, and it works! Essentially, there's no way to make the Features & Solutions framework call .Update(true) - if we can find a way to do this, we have our solution.

At the end of the day, we need to update the Content Type XML files, but programmatically add or make changes to the fields ourselves in the Object Model. So I've modified the steps to deploy a solution with a small utility to go along with it. Here is the procedure:

  1. Upgrade the revised solution in-place (no deactivating features). At this point you will NOT see any new fields/columns in the content type.
  2. Compare the fields in your revised solution (WSP) file with the fields and field links that currently exist in your "Live" Content Type as it currently exists in the MOSS farm. I wrote a utility to accomplish this by extracting and parsing the contents of the WSP package and make a class model of the fields.
  3. For any fields that don't exist, create the field (SPField) and add a field link (SPFieldLink) to the Content Type.
  4. Call SPContentType.Update(true).

As long as you can make field/column changes programmatically and .Update(true) them in one shot, MOSS does the rest (and much faster than enumerating through every list).