Back to Cleverworkarounds mainpage
 

Sep 05 2009

Missing metadata with Office 2003 – yet another “duh” moment…

I had a problem this week that got resolved with something quite obvious, but I learned a lot in the process, so I am detailing it here.

Symptoms: Using MOSS 2007 SP2 and Office 2003 SP3, users complained that metadata on documents went missing. Consider the scenario below where we have a sample document library.

image

Our user opens the “Memo to Council re Convenor” document into Word 2003, makes a change and saves it. Note the difference. Where’s my metadata?

image

I asked about this on the ozmoss mailing list, and others noted the same issue. Some had applied the June 2009 cumulative update for WSS3 and the problem was solved for them. I applied this update, and it improved the situation, but did not cure the problem totally.

Upon further investigation, we were able to ascertain that the problem would only happen on certain PCs. The same user could update the same document on a different PC and it would work fine. A fiddler trace of the process allowed me to narrow down the issue and understand the sequence of events. The root cause of the issue was incorrect handling of HTML/JavaScript by Office 2003 and/or Internet Explorer. This manifests itself in an inconsistency in the display of properties in the Office “Web File Properties” dialog box (file->properties in an office 2003 app). Consider the example below.

clip_image002  clip_image002[5]

Note that the first dialog shows that values for the metadata columns are blank or default. Now consider the same document opened on a different PC. Well what do you know – we have metadata!

Fiddler confirmed that when saving a document to SharePoint from an Office 2003 application, the same HTTP request is made to SharePoint as is done when displaying the document properties in the above example.

In both cases, A HTTP GET request is made to the owssvr.dll  (WSS RPC)  and the dialogview method is called. Therefore, the root of my problem has something to do with the fact that the data returned by this call was not being parsed properly by MSWord on one PC (as illustrated by the first of the above dialog box screenshots). When a user than saved their edits, blank or default values are being written back to SharePoint, in effect “losing” the metadata.

So let’s take a look at the HTTP call and the data returned. The call to owssvr.dll  looks like this.

GET /somesite/_vti_bin/owssvr.dll?location=My%20Committee/Agenda%20attachment%20July%2009.doc&dialogview=SaveForm HTTP/1.1

The output returned by SharePoint is a bunch of HTML and JavaScript that MSWord then renders by calling the Internet Explorers rendering framework programmatically. (I’ve included sample output at the end of the post for the hardcore nerds).

So the question now becomes, what was preventing the correct HTML rendering on one PC and not the other? This was a difficult question to answer because I was unable to find a way to debug JavaScript when the output was rendered in Office 2003 apps.

When you think about it, there can be many potential causes here. Given that Internet Explorer is effectively doing the work (WinINet), this whole process could be adversely affected by add-ins, zone settings, AD policies, virus scanning, etc. On one affected PC I disabled all all-ins to Internet Explorer and retried without success. After around half an hour of frustration, I decided to reset IE’s configuration as shown below.

image

After accepting the various warnings, I retried the test and it all worked! Metadata was now being properly saved. “Awesome”, I thought. I’m not quite sure what the true root cause is, but at least I know how to cure it.

Then it suddenly dawned on me that I’d been stupid. For all of my low level examination of the office 2003/RPC interaction with SharePoint, picking the brain of gurus like MCM Spence Harbar, I’d never thought to clear the temporary internet files cache. Doh!

On the next affected PC I did so, and the problem also went away instantly.

*Blush*

In my defence of my dumbness however, I had never examined the behind the scenes integration with Office 2003 and SharePoint, and it did not even twig that Internet Explorer would be involved in the picture. Given that this problem manifests itself within Office 2003 applications, it is not immediately obvious that your temporary internet files would cause an issue here – but now I know better :-) .

And now I get it…

Now Office 2007 is not affected by this because it does not use this method at all. In 2007, the document information panel uses InfoPath to render metadata. At the time I naively thought that was a dumb idea because the document information panel cannot display custom column types. For example, let’s say you created a custom column type to hold a phone number with the correct formatting for country code and the like. In a SharePoint site you can display it fine, but in the Office 2007 document information panel, you will never see it. InfoPath only will work with the out of the box in column types.

My rationale was that if Office 2007 instead used JavaScript and HTML, then it should be able to display these sorts of custom columns. That may actually be true, but it is irrelevant. When you think of the sheer number of ways that the rendering of the code could be interfered with (Temporary Internet Files being one precedent), you can see why the Office team might have opted for the safety of InfoPath instead.

Now that I understand that this is how Office 2003 does it, I can plainly see that it leaves too much room for error.

So even though my problem was solved by a simple clearing of the cache, it still remains that things like a bad add-in or a bad AD policy setting could interfere with the Office 2003/SharePoint integration. So if you ever have this issue crop up, try the temporary internet files thing or reset IE’s configuration completely. It may save you a lot of time and pain troubleshooting.

Thanks for reading

Paul Culmsee

www.sevensigma.com.au

 

 

 

<html dir="ltr">
<HEAD>
    <META Name="GENERATOR" Content="Microsoft SharePoint">
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
    <META HTTP-EQUIV="Expires" content="0">
    <!-- _locID="id_PageTitle" _locComment="{StringCategory=TTL}" -->
    <Title ID=onetidTitle>File Properties</Title>
<Link REL="stylesheet" Type="text/css" HREF="/_layouts/1033/styles/core.css">
    <META Name="Microsoft Theme" Content="default">
    <META Name="Microsoft Border" Content="none">
<script src="/_layouts/1033/init.js"></script>
<script src="/_layouts/1033/core.js"></script>
 
<script src="/_layouts/1033/bform.js"></script>
 
</HEAD>
<Script ID="Form_Validate">
function Form_Validate(fVisible)
{
    if (frm.FValidate(fVisible))
        document.OWSForm.IsFormValid.value="true";
    else
        document.OWSForm.IsFormValid.value="false";
}
</Script>
<Script ID="Update_UI_From_Values">
var bFormFieldsInited = false;
function Update_UI_From_Values()
{
    frm.SetFirstFocus(bFormFieldsInited);
    bFormFieldsInited = true;
    frm.DataBind();
    DefaultControls();
}
</Script>
<Script ID="Update_Values_From_UI">
function Update_Values_From_UI()
{
    frm.SetFirstFocus(bFormFieldsInited);
    bFormFieldsInited = true;
    frm.FValidate(false);
}
</Script>
<script language="JavaScript">
L_tooltipfile_Text = "";
L_tooltipfolder_Text = "";
selectedElement = null
inChangeSelection = false
slElem = null;
oldSelection = "";
bIsFileDialogView = true;
function selectrow()
{
    if (slElem) {
        slElem.className=oldSelection;
        slElem.title="";
        }
    selectedElement = window.event.srcElement;
    while (selectedElement.tagName!="TR") {
           selectedElement=selectedElement.parentElement;
           }
    slElem = selectedElement;
    oldSelection=slElem.className;        
    slElem.className="ms-selected";
    if (slElem.getAttribute("fileattribute") == "file")
        slElem.title=L_tooltipfile_Text;
    else
        slElem.title=L_tooltipfolder_Text;
    document.selection.empty();
}
function checkScroll()
{
    if (document.body.scrollHeight > document.body.offsetHeight ||
        document.body.scrollWidth > document.body.offsetWidth)
       document.body.scroll="yes";
}
</script>
<BODY marginwidth=0 marginheight=0 onload="checkScroll()" onresize="checkScroll()" scroll=no>
<!-- Banner -->
<TABLE  CELLPADDING=0 CELLSPACING=0 BORDER=0 WIDTH="100%" >
  <TR>
   <TD COLSPAN=3 STYLE="width:100%">
    <TABLE class=ms-bannerframe CELLPADDING=0 CELLSPACING=0 BORDER=0 STYLE="width:100%">
     <TR>
      <TD VALIGN=BOTTOM WITDH=25>
      <font size=1>&nbsp;</font>
      </TD>
     </TR>
    </TABLE>
   </TD>
  </TR>
</table>
<table width=100% cellpadding=4 cellspacing=0>
 <tr>
  <td>
   <TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0 WIDTH="100%" >
    <tr>
     <td>
      <table cellpadding=0 cellspacing=0 style="margin-left: 3px;margin-bottom:2px">
       <tr>
        <td nowrap class="ms-titlearea">Some User</td>
       </tr>
       <tr>
        <td ID=onetidPageTitle nowrap class="ms-pagetitle">My Committee</td>
       </tr>
      </table>
     </td>
    </tr>
    <tr>
     <td>
      <table border=0 width=100% cellpadding=0 cellspacing=0 style="margin-left: 3px;margin-bottom: 3px">
       <tr>
        <td class="ms-sectionline" height=1><img src="/_layouts/images/blank.gif"></td>
       </tr>
      </table>
     </td>
    </tr>
        <!-- Item Form -->
        
<SCRIPT>
var frm = new OWSForm("OWSForm", false, "http:\u002f\u002fintranetdev\u002fsomesite\u002f_layouts\u002f");
</SCRIPT>
 
<SCRIPT> frm.dopt.chDateSep = "\u002f"; frm.dopt.chTimeSep = ":"; frm.dopt.SetTimeFormat(0); frm.dopt.SetDateOrder(1); frm.dopt.SetDOW(0); frm.dopt.stAM = "AM"; frm.dopt.stPM = "PM"; frm.dopt.TimeMarkPosn = 0; frm.dopt.webTZOffsetMin = -480;  frm.nopt.chDigSep = ","; frm.nopt.chDecimal = "."; frm.nopt.chMinus = "-"; frm.nopt.iNegNumber = 1; frm.nopt.SetGrouping("3;0"); 
frm.stFieldPrefix = "urn:schemas-microsoft-com:office:office#";
frm.stImagesPath = "\u002f_layouts\u002fimages\u002f";
frm.wBaseType = 1;
</SCRIPT><Form Name="OWSForm" id=OWSForm EncType="multipart/form-data" Action="http://intranetdev/somesite/_vti_bin/owssvr.dll?CS=65001" Method=POST onSubmit="return false;">
<INPUT Type=Hidden Name="_charset_" Value="utf-8">
<INPUT ID=onetidCmd Type=Hidden Name="Cmd" Value="Save">
<INPUT ID=onetidIsFormValid type=hidden name="IsFormValid">
<INPUT ID=onetidFormWasPosted type=hidden name="FormWasPosted">
<INPUT ID="MustUpdateForm" type=hidden name="MustUpdateForm" value="true">
<INPUT type=hidden name="NextID" id="NextID" value="-1">
<INPUT type=hidden name="NextUsing" id="NextUsing" value="">
<SPAN id='part1'><INPUT ID=onetidIOHidden TYPE=HIDDEN NAME="List" VALUE="{EF494645-1D24-4761-A874-0CB866FA494C}">
<INPUT ID=onetidIOHidden TYPE=HIDDEN NAME="ID" VALUE="New">
<TABLE border=0 cellpadding=2>
<SCRIPT>var _g_tp_fNewForm = true;</SCRIPT>
<TR style="display:none"><TH nowrap valign=top class="ms-formlabel"><nobr>Comment<font color=red></font></nobr></TH><TD class="ms-formbody"><SCRIPT>fld = new NoteField(frm,"Comment1","Comment","");fld.stNumLines = "20";fld.IMEMode="";fld.BuildUI();</SCRIPT>&nbsp;<br><SPAN class="ms-formdescription"></SPAN></TD></TR><TR style="display:none"><TH nowrap valign=top class="ms-formlabel"><nobr>Document&nbsp;Status<font color=red></font></nobr></TH><TD class="ms-formbody"><SCRIPT>fld = new ChoiceField(frm,"Corro_x0020_Status","Document Status","Not Started"); fld.format = "Dropdown"; fld.AddChoice("Not Started", "");fld.AddChoice("No Action Required", "");fld.AddChoice("In Progress", "");fld.AddChoice("Completed", "");fld.AddChoice("Deferred", "");fld.AddChoice("Waiting on someone else", "");fld.IMEMode="";fld.BuildUI();</SCRIPT><SPAN class="ms-formdescription"></SPAN></TD></TR><TR style="display:none"><TH nowrap valign=top class="ms-formlabel"><nobr>Content&nbsp;Type<font color=red></font></nobr></TH><TD class="ms-formbody"><SCRIPT>fld = new ChoiceField(frm,"ContentType","Content Type","LSWA Document"); fld.format = "Dropdown"; fld.AddChoice("LSWA Document", "");fld.AddChoice("Folder", "");fld.IMEMode="";fld.BuildUI();</SCRIPT><SPAN class="ms-formdescription"></SPAN></TD></TR><TR style="display:none"><TH nowrap valign=top class="ms-formlabel"><nobr>Committee&nbsp;Document<font color=red></font></nobr></TH><TD class="ms-formbody"><SCRIPT>fld = new ChoiceField(frm,"Committee_x0020_Document","Committee Document",""); fld.format = "Dropdown"; fld.AddChoice("Agendas", "");fld.AddChoice("Minutes", "");fld.AddChoice("Actions", "");fld.AddChoice("Administration", "");fld.IMEMode="";fld.BuildUI();</SCRIPT><SPAN class="ms-formdescription"></SPAN></TD></TR><TR style="display:none"><TH nowrap valign=top class="ms-formlabel"><nobr>Document&nbsp;Type<font color=red></font></nobr></TH><TD class="ms-formbody"><SCRIPT>fld = new ChoiceField(frm,"Document_x0020_Types","Document Type",""); fld.format = "Dropdown"; fld.AddChoice("Incoming Correspondence", "");fld.AddChoice("Outgoing Correspondence", "");fld.AddChoice("Internal Document", "");fld.AddChoice("Fax", "");fld.AddChoice("E-mail", "");fld.AddChoice("Memo", "");fld.IMEMode="";fld.BuildUI();</SCRIPT><SPAN class="ms-formdescription"></SPAN></TD></TR><TR style="display:none"><TH nowrap valign=top class="ms-formlabel"><nobr>Year<font color=red></font></nobr></TH><TD class="ms-formbody"><SCRIPT>fld = new ChoiceField(frm,"Year","Year",""); fld.format = "Dropdown"; fld.AddChoice("2000", "");fld.AddChoice("2001", "");fld.AddChoice("2002", "");fld.AddChoice("2003", "");fld.AddChoice("2004", "");fld.AddChoice("2005", "");fld.AddChoice("2006", "");fld.AddChoice("2007", "");fld.AddChoice("2008", "");fld.AddChoice("2009", "");fld.AddChoice("2010", "");fld.IMEMode="";fld.BuildUI();</SCRIPT><SPAN class="ms-formdescription"></SPAN></TD></TR></TABLE>
<script type="text/javascript">
_g_tp_rgctNames = new Array;
</script><SCRIPT>var _tp_rgctfld = null;var _tp_ctfld = null;var _g_tp_rgcts = new Array;</SCRIPT>
<script type="text/javascript">
_g_tp_rgctNames.push("LSWA Document");
</script>
 
            <SCRIPT>
            _tp_rgctfld = new Array;
            
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="ContentType";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="SelectFilename";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="FileLeafRef";
                _tp_ctfld.fRequired = BoolFromString("TRUE");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Created";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("TRUE");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Title";
                _tp_ctfld.fRequired = BoolFromString("FALSE");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("FALSE", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("TRUE", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Modified";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("TRUE");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Modified_x0020_By";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("FALSE");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Created_x0020_By";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("FALSE");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Comment1";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Corro_x0020_Status";
                _tp_ctfld.fRequired = BoolFromString("");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Committee_x0020_Document";
                _tp_ctfld.fRequired = BoolFromString("FALSE");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Document_x0020_Types";
                _tp_ctfld.fRequired = BoolFromString("FALSE");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="Year";
                _tp_ctfld.fRequired = BoolFromString("FALSE");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("");
                _tp_ctfld.stDisplay ="";
                    _tp_rgctfld.push(_tp_ctfld);
                
                _tp_ctfld = new Object(null);
                _tp_ctfld.stName="MoveDocu";
                _tp_ctfld.fRequired = BoolFromString("FALSE");
                _tp_ctfld.fHidden = BoolFromString("");
                _tp_ctfld.fShowInNewForm = BoolFromString2("", true);
                _tp_ctfld.fShowInEditForm = BoolFromString2("", true);
                _tp_ctfld.fReadOnly = BoolFromString("TRUE");
                _tp_ctfld.stDisplay ="Move Document";
                    _tp_rgctfld.push(_tp_ctfld);
                
            _g_tp_rgcts.push(_tp_rgctfld);
            </SCRIPT>
            
<script type="text/javascript">
_g_tp_rgctNames.push("Folder");
</script>
 
            <SCRIPT>
            _tp_rgctfld = new Array;
            _g_tp_rgcts.push(_tp_rgctfld);
            </SCRIPT>
            
<script type="text/javascript">
function _FixMpCt2Flds()
{
    var frm = frmCurrent;
    var rgn1 = frm.rgctNames;
    var rgn2 = _g_tp_rgctNames;
    var rgctflds = _g_tp_rgcts;
    if (rgctflds.length < rgn1.length)
    {
        var rgnew = new Array;
        var i;
        var j = 0;
        for (i = 0; i < rgn1.length; i++)
        {
            var n1 = rgn1[i];
            var n2 = rgn2[j];
            if (n1 != n2)
            {
                rgnew.push(new Array);
            }
            else
            {
                rgnew.push(rgctflds[j]);
                j++;
            }
        }
        _g_tp_rgcts = rgnew;
    }
}
_FixMpCt2Flds();
</script>
 
</SPAN></form>
<SCRIPT>
</SCRIPT>
 
        <!-- FooterBanner closes the TD, TR, TABLE, BODY, And HTML regions opened above -->
&nbsp;
</td></tr></table></PlaceHolder></TD></TR>
</TABLE>
</BODY>
</HTML>
 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Jun 28 2009

SPD workflows: “ERROR: request not found in the TrackedRequests”

I’ve written this post to document a dumbass thing that I did and the error that it caused. Hopefully it might help someone googling in desperation sometime…

The popularity of the “Tribute to the humble leave form” series over at SharePoint Magazine, has meant that we actually get a few gigs implementing – surprise, surprise – InfoPath leave forms with SharePoint Designer workflows! This was actually quite unexpected, as I wrote that series as a joke and for end-user training purposes.

Now, I fully realise that many people don’t like tools like SharePoint Designer in the hands of mere mortals, but I personally think it is a terrific means to encourage buy-in and user evangelism and I encourage its sustainable use. Although “real developers” may disagree with me here, SPD workflows enable quick results without the embarrassing aftermath of premature code compilation (which always leads to frustration, right girls? :-D ).

Anyhow enough sexual innuendo – here is my error with my lessons learned

The symptoms

Recently, a happily running leave form workflow ground to a halt with an error as shown below. What the screenshot below shows is that the workflow died right after a “Pause for duration” workflow action. (Note the “pausing for 2 minutes” line, just before the actual workflow error).

image

Now, the interesting thing here is that the pause action never happened. The workflow bombed out straight away and there was no pause at all. Yet, the fact that the pause action logged to the history list meant that it attempted to do so. It seemed that the pause action ran for long enough to log that it was about to pause, but died when actually trying to.

The pause action occurred in step 2 of my workflow, but it is worthwhile showing you the first workflow step first.

Below is a screenshot of step 1 of the workflow. This first step is called “Modify Item Permissions” and we are using the codeplex custom workflow actions to modify permissions of the submitted leave form, ensuring that only the requestor of the leave (and their boss) can see or modify the form.

image

The step in the workflow, called “Manager Approval”, shows where we pause for 2 minutes. The reason we pause is to get around another workflow error that I will explain at the end of the post. After the pause action was completed and the workflow recommenced, it performed a “Collect data from user” task as shown below. This created a task for the requestors manager to approve the leave request.

image

We know that the “Collect data from user” action never ran, because the “Employee Leave Approval” task never actually got created in the task list. Thus, we know the error occurred at the pause action. Or, did it?

This error occurred consistently whether the workflow was manually started or auto-triggered. Checked all the usual suspects – timber jobs ok, permissions unchanged and working well, etc. Scanning the ULS logs at this time showed an interesting error with a much less interesting stack trace (that I have pasted at the end of the post for readability).

“Unexpected    ERROR: request not found in the TrackedRequests. We might be creating and closing webs on different threads”

Hmm, what is TrackedRequests and why is a request not found? Googling that error message wasn’t much help at all. Although it is quite common, nothing matched my particular context. But then I received a lucky break. When I cancelled the running workflow that was in this error state, I received a new error that made it quite easy to work out what was going on.

WinWF Internal Error, terminating workflow Id# 02334a17-3211-4d30-902f-bf34e20354c6    
Unexpected    System.Workflow.Runtime.Hosting.PersistenceException: Object reference not set to an instance of an object. —> System.NullReferenceException: Object reference not set to an instance of an object.     at DP.Sharepoint.Workflow.Common.RemoveListItemPermissionEntry(SPListItem item, String principalName, Boolean breakRoleInheritance)    

It was this error that gave it away. For a start, the method being called was DP.Sharepoint.Workflow.Common.RemoveListItemPermissionEntry and the exception was a null reference. RemoveListItemPermissionEntry sounded suspiciously like the permission based workflow actions of the “Modify Item Permissions” step as highlighted below.

image

But wait… the workflow never failed at this line at all. It actually failed at the first action of the next step (“Manager Approval”)  with the “request not found in the TrackedRequests” error. Interesting, eh?

Let’s put aside the issue of what step and what action the workflow failed on for a moment, and examine the second error message more closely. If you examine the “Modify Item Permissions” action highlighted above, can you think of any reason that I would get a null reference exception?

Ah! What happens if the previous action called “Set Variable”, set a blank or null value? I suspect that the “Grant permissions on an item” workflow action will attempt to change permissions of the item to a blank or null user. The result? That workflow action will die with a null reference exception – precisely what the error states.

When I checked the source of the variable “LineManagerAccount”, it was looking up a contact list for the manager of the requestor. Sure enough, it did turn out that that column was blank for some users. Thus, a simple exercise in input validation combined with a small correction to that list item and the problem was solved.

Further questions and further information

I could stop this post there I suppose and perhaps someone, sometime might find this post and think “Wohoo!”. But this problem raised a couple of issues, as well as made it clear to me that I had been stupid in how I designed this one. Let’s tackle them one by one.

1. When did it die again?

Clearly the error that caused all of this, occurred at the last action of the “Modify Item Permissions” step. We know this because the step before was where the lookup to the contact list happened, and this is where the null value came from that caused the exception. But then why did the workflow not stop at this point? Why did it continue onward and attempt to move to the “Manager Approval” steps and die on the “Pause for duration” action?

Whether this is default behaviour of SPD workflows or a logic fault in the exception handling code in the codeplex-based “Grant Permission on an Item” action, this makes life hard to troubleshoot because the error message never made sense. I only found out the true root cause when I terminated the workflow manually and got lucky because the offending exception suddenly appeared.

So, the conclusion to draw? If your SPD workflow dies with “ERROR: request not found in the TrackedRequests” in the logs, it may be that the real cause of the problem is a previous workflow step and not the step where the workflow actually stopped.

2. Paul is a dumbass

Okay, so let’s talk about lessons learnt. Some people reading this may wonder why I never used Active Directory or the user profile store to store manager details for a user and to do the lookup from there. The answer is that this contact list approach simply made sense for this client and this is actually not the dumb thing that I did. The dumb thing that I did was to forget that all of the necessary business logic and data validation steps could have been performed in the InfoPath form itself.

Remember that InfoPath forms can have data connections to all sorts of sources such as SharePoint lists, web services and relational databases. (I am not going into detail on how to do that here because I have already covered it in quite a lot in part 5 of the “Leave Form Tribute” series). We can have a published InfoPath form connect to any SharePoint list or library across the entire farm to look up business logic details, such as an approver. This is something that SharePoint Designer workflows cannot do out of the box – it can only lookup from the site where the workflow has been created.

But, more important than that, we can easily use InfoPath rules and data validation functionality to ensure that the values are not null before submitting.

What this all means is that your SPD workflows end up being simpler and easier to maintain because InfoPath is providing all of the data to the workflow as well as the validation of the data. Therefore, the workflow now doesn’t have to do the lookup work and have unnecessary steps and actions.

For these reasons I think that, if you can, grab all of the data you need for a business process using the InfoPath form using hidden fields. Then publish the form and ensure these hidden fields are published as site columns.

For this particular purpose, I think that InfoPath is a lesser evil than SharePoint Designer workflows.

3. What are the pause actions there for anyway?

Finally, I promised I’d explain why I had a pause action in the workflow. This is to get around a race condition with workflows that produces an intermittent error message:

“This task is currently locked by a running workflow and cannot be edited”

I’ve seen this occur in several situations and it pretty much boils down to some workflow actions being synchronous and subsequent actions starting before the previous action properly completed. Consider this example.

One workflow creates or updates an item in a list, say a task list. But the task list also has a workflow attached to it. This means that the first workflow creates the task item and then moves onto the next step. Meanwhile, a new workflow has been triggered for that new task list item. This secondary workflow refers to information stored in the main item which causes the race condition.  If the information in the main item is not committed before this workflow attempts to use it, you’ll get null values when the main item is a new item.  When working with SPD workflows, these null values will show up as ????. 

To resolve the race condition, I had to ensure that the main item was committed before the secondary workflow was started by pausing the workflow.  Why does pausing the workflow cause the changes to be committed?  According to the MSDN article “How Windows SharePoint Services Processes Workflow Activities” http://msdn.microsoft.com/en-us/library/ms442249.aspx

Windows SharePoint Services runs the workflow until it reaches a point where it cannot proceed because it is waiting for some event to occur: for example, a user must designate a task as completed. Only at this "commit point" does Windows SharePoint Services commit the changes made in the previous Windows SharePoint Services-specific workflow activities…

The not-so-clever workaround is to pause the workflow in between actions. When you add a pause, the workflow “cannot proceed because it is waiting for some event to occur” and therefore makes it a commit point. Not pretty – but works.

I hope that you find this article of use in your SharePoint Designer workflow troubleshooting endeavours. Below, I have pasted the complete stack traces (for the search engines).

Thanks for reading

Paul Culmsee

06/25/2009 12:25:20.46 w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected ERROR: request not found in the TrackedRequests. We might be creating and closing webs on different threads. ThreadId = 1, Free call stack = at Microsoft.SharePoint.SPRequestManager.Release(SPRequest request) at Microsoft.SharePoint.SPWeb.Invalidate() at Microsoft.SharePoint.SPWeb.Close() at Microsoft.SharePoint.SPSite.Close() at Microsoft.SharePoint.SPSite.Dispose() at Microsoft.SharePoint.Workflow.SPWorkflowManager.<>c__DisplayClass1.<StartWorkflow>b__0() at Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper(Object state) at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2() at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at Microsoft.SharePoint.SPSecurity.RunWithEl…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …evatedPrivileges(WaitCallback secureCode, Object param) at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at Microsoft.SharePoint.Workflow.SPWorkflowManager.StartWorkflow(SPListItem item, SPWorkflowAssociation association, SPWorkflowEvent startEvent, Boolean bAutoStart, Boolean bCreateOnly) at Microsoft.SharePoint.Workflow.SPWorkflowManager.StartWorkflow(SPListItem item, SPWorkflowAssociation association, String eventData, Boolean isAutoStart) at Microsoft.SharePoint.Workflow.SPWorkflowManager.StartWorkflow(SPListItem item, SPWorkflowAssociation association, String eventData) at Microsoft.SharePoint.WebControls.SPWorkflowDataSourceView.Insert(IDictionary values) at Microsoft.SharePoint.WebControls.SPWorkflowDataSourceView.Ins…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …ert(IDictionary values, DataSourceViewOperationCallback callback) at Microsoft.SharePoint.WebPartPages.DataFormWebPart.FlatCommit() at Microsoft.SharePoint.WebPartPages.DataFormWebPart.PerformCommit() at Microsoft.SharePoint.WebPartPages.DataFormWebPart.HandleOnSave(Object sender, EventArgs e) at Microsoft.SharePoint.WebPartPages.DataFormWebPart.RaisePostBackEvent(String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includ…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …eStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest() at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) at System.Web.UI.Page.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) at System.Web.HttpApplication.ApplicationStepManager.ResumeSteps(Exception error) at System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) at System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) at System.Web.HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr) at Sys…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …tem.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType) , Allocation call stack (if present) at Microsoft.SharePoint.Library.SPRequest..ctor() at Microsoft.SharePoint.SPGlobal.CreateSPRequestAndSetIdentity(Boolean bNotGlobalAdminCode, String strUrl, Boolean bNotAddToContext, Byte[] UserToken, String userName, Boolean bIgnoreTokenTimeout, Boolean bAsAnonymous) at Microsoft.SharePoint.SPWeb.InitializeSPRequest() at Microsoft.SharePoint.SPWeb.EnsureSPRequest() at Microsoft.SharePoint.SPWeb.get_Request() at Microsoft.SharePoint.SPWeb.InitWebPublic() at Microsoft.SharePoint.SPWeb.get_ServerRelativeUrl() at Microsoft.SharePoint.SPWeb.get_Url() at Microsoft.SharePoint.SPUser.InitMember() at Microsoft.SharePoint.SPUser..ctor(SPWeb web, ISecura…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …bleObject scope, String strIdentifier, Object[,] arrUsersData, UInt32 index, Int32 iByParamId, String strByParamSID, String strByParamEmail, SPUserCollectionType userCollectionType, Boolean isSiteAuditor) at Microsoft.SharePoint.SPUser..ctor(SPWeb web, ISecurableObject scope, String strIdentifier, Object[,] arrUsersData, UInt32 index, Int32 iByParamId, String strByParamSID, String strByParamEmail, SPUserCollectionType userCollectionType) at Microsoft.SharePoint.SPUserCollection.GetByIDNoThrow(Int32 id) at Microsoft.SharePoint.SPSite.get_SystemAccount() at Microsoft.SharePoint.WorkflowActions.Helper.ResolveUserField(WorkflowContext context, Object fvalue) at Microsoft.SharePoint.WorkflowActions.Helper.LookupUser(WorkflowContext context, Guid listId, Int32 listItem, Strin…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …g fieldName) at Microsoft.SharePoint.WorkflowActions.Helper.LookupUser(WorkflowContext context, String listIdOrName, Int32 listItem, String fieldName) at Microsoft.SharePoint.WorkflowActions.LookupActivity.Execute(ActivityExecutionContext provider) at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(Activity activity, ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutorOperation.Run(IWorkflowCoreRuntime workflowCoreRuntime) at System.Workflow.Runtime.Scheduler.Run() at System.Workflow.Runtime.WorkflowExecutor.RunScheduler() at System.Workflow.Runtime.WorkflowExecutor.RunSome(Object ignored) …

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …at System.Workflow.Runtime.Hosting.DefaultWorkflowSchedulerService.WorkItem.Invoke(WorkflowSchedulerService service) at System.Workflow.Runtime.Hosting.DefaultWorkflowSchedulerService.QueueWorkerProcess(Object state) at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.runTryCode(Object userData) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading._ThreadPoolW…

06/25/2009 12:25:20.46* w3wp.exe (0x0BA8) 0x16F8 Windows SharePoint Services General 0 Unexpected …aitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack) at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)

 

06/25/2009 12:27:16.49 w3wp.exe (0x0BA8) 0x1CB4 Windows SharePoint Services Workflow Infrastructure 88xr Unexpected WinWF Internal Error, terminating workflow Id# 02334a17-3211-4d30-902f-bf34e20354c6

06/25/2009 12:27:16.49 w3wp.exe (0x0BA8) 0x1CB4 Windows SharePoint Services Workflow Infrastructure 98d4 Unexpected System.Workflow.Runtime.Hosting.PersistenceException: Object reference not set to an instance of an object. —> System.NullReferenceException: Object reference not set to an instance of an object. at DP.Sharepoint.Workflow.Common.RemoveListItemPermissionEntry(SPListItem item, String principalName, Boolean breakRoleInheritance) at DP.Sharepoint.Workflow.PermissionsService.<>c__DisplayClass1.<ProcessGrantRequest>b__0() at Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper(Object state) at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2() at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param)…

06/25/2009 12:27:16.49* w3wp.exe (0x0BA8) 0x1CB4 Windows SharePoint Services Workflow Infrastructure 98d4 Unexpected … at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at DP.Sharepoint.Workflow.PermissionsService.ProcessGrantRequest(PermissionRequest pr) at DP.Sharepoint.Workflow.PermissionsService.Commit(Transaction transaction, ICollection items) at System.Workflow.Runtime.WorkBatch.PendingWorkCollection.Commit(Transaction transaction) at System.Workflow.Runtime.WorkBatch.Commit(Transaction transaction) at System.Workflow.Runtime.VolatileResourceManager.Commit() at System.Workflow.Runtime.WorkflowExecutor.DoResourceManagerCommit() at System.Workflow.Runtime.Hosting.WorkflowCommitWorkBatchService.CommitWorkBatch(CommitWorkBatchCallback commitWorkBatchCallback) at System.Workflow.Runtime.Hosting.DefaultWorkflowCommitWorkBatchSer…

06/25/2009 12:27:16.49* w3wp.exe (0x0BA8) 0x1CB4 Windows SharePoint Services Workflow Infrastructure 98d4 Unexpected …vice.CommitWorkBatch(CommitWorkBatchCallback commitWorkBatchCallback) at System.Workflow.Runtime.WorkflowExecutor.CommitTransaction(Activity activityContext) at System.Workflow.Runtime.WorkflowExecutor.Persist(Activity dynamicActivity, Boolean unlock, Boolean needsCompensation) — End of inner exception stack trace — at System.Workflow.Runtime.WorkflowExecutor.Persist(Activity dynamicActivity, Boolean unlock, Boolean needsCompensation) at System.Workflow.Runtime.WorkflowExecutor.ProtectedPersist(Boolean unlock)

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Apr 14 2009

Cannot restore from recycle bin – nested folders

Oh my god – I have written a short, useful post instead of a bloated theory fest! What is the world coming to?

I was all set to go and catch up with SharePoint Sezai today, when the phone rang and the IT guy from one of my clients had a wee problem. Someone had been doing some serious file rearrangement surgery on a large document library with folders (yeah, yeah, I know – “folders are evil”  blah, blah). In one of those typical right mouse click spasms that we sometimes have, a large folder was blown away.

“No problem”, thinks the IT guy. “I’ll use my trusty SharePoint recycle bin to restore it.”

Sure enough, there is the folder listed, so they select it and choose to restore.

Uh-oh – error!

image

The IT guy took the above screenshot and mailed it through to me. From looking at the <sacrasm>incredibly useful, non threatening and clear error message above</sacrasm>, it was the presence of the LoadRecursive() function that gave me a hunch as to what was going on.

Turns out that it wasn’t just this particular folder that was accidentally deleted. The parent folder had been deleted also. But the problem was that during this major file rearrangement surgery that I mentioned at the start of this article, that parent folder had been replaced with a new one with the same name.

The recycler cannot handle this. It seems to make two assumptions

  1. It assumes that you always want to restore something back to the original location that it was deleted from
  2. It expects that if something has been deleted from a list of library, it sure as hell should not exist when you attempt to restore it back to the site.

I asked the IT guy to rename the second, identically named folder to something different and then reattempt the restore.

Bingo! My client is happy, their IT guy lives to fight another day and I now have learned the meaning behind yet another barely decipherable SharePoint stack trace :-) .

(Turns out I shouldn’t have cancelled on Sezai after all!)

Thanks for reading

Paul Culmsee

 

 

P.S: Now for the Google crawler, the text version of the error in teensy font :-)

The item cannot be restored. at Microsoft.SharePoint.SPRecycleBinUtility.ThrowAppropriateRestoreException(SPException ex, SPRecycleBinItem lastItem)

at Microsoft.SharePoint.SPRecycleBinItemCollection.RestoreCore(Guid[] ids)

at Microsoft.SharePoint.SPRecycleBinItemCollection.Restore(Guid[] ids)

at Microsoft.SharePoint.ApplicationPages.RecycleBinPage.ProcessAction(String action, String guidString)

at Microsoft.SharePoint.ApplicationPages.RecycleBinPage.OnLoad(EventArgs e)

at System.Web.UI.Control.LoadRecursive()

at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Sep 16 2008

Sometimes "Microsoft bashing" is justified

Microsoft bashing is a favourite pastime of many a nerd. Whether it is justified or not in many cases is debatable since M$ will never please everyone. But the point is, it is cathartic and in actual fact, good therapy because venting your frustrations at Bill Gates is much healthier than at your colleagues or family.

To my Microsoft employee friends reading this. Don’t feel all defensive – some of the very best Microsoft bashing I have ever heard comes from you guys anyway :-)

So although sometimes the M$ bashing is completely unjustified, long shall it continue to preserve the sanity of IT professionals around the globe.

Having said that, on occasion you will hit some Microsoft induced pain that is legitimately and frustratingly dumb. By "legitimately", I mean that you cannot say "although in hindsight it was dumb, I can actually understand why they decided to do that". Instead you get caught out and experience pain and frustration simply because of a silly Microsoft oversight.

In this case, the oversight is with the SharePoint Configuration Wizard

Continue reading “Sometimes "Microsoft bashing" is justified”

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Sep 01 2008

Not Good Enough Stories…

Tags: Risk,Security,Troubleshooting @ 7:17 pm

Bill, a former colleague of mine who is very technically savvy, has a little corner of the blogspace called www.notgoodenough.net.

Here he posts about the everyday dumb issues that he comes across that make his working life that little bit harder. By day, he manages some very complex infrastructure for a company of over 1000 staff, and we often catch up for a coffee to compare notes on the latest head-shaking piece of dumbness that we have recently encountered. Now doing "not good enough" stories for Microsoft is simply too easy, so that’s why I like the fact that Bill goes after the likes of IBM and Packeteer as well :-)

The thing that is really scary when you read his stories, is the sheer unbridled freakin *lameness* of some of the things that he has encountered. My own stores are very similar, and it reinforces to me that usually when something goes pear shaped enough to cost time and money, the underlying cause is rarely clever.

Continue reading “Not Good Enough Stories…”

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

Technorati Tags: , ,



Jul 20 2008

A neat trick with the publishing feature

As I mentioned in my last post, I always security trim SharePoint. A very common issue with a security trimmed SharePoint is the "access denied" message that you receive when trying to activate the "Office SharePoint Server Publishing Infrastructure" site collection feature.

image

There are lots of blog topics about this. Most refer to this one as the definitive source.

Activating Office SharePoint Server Publishing Infrastructure

The issue here is that the publishing feature actually breaks a security rule. When you think about it, activating a site collection feature should only ever modify settings related to that site collection. I previously blogged about how it was bad practice for a site collection feature to modify the web.config files for a given web application.

If you set the scope of this feature at the site collection level, you are basically allowing a site collection administrator to make a change that affects all other site collections in the web application. Uh – does the site collection administrator have access to other site collections? Probably not, so why the hell would you allow them to perform a task that impacts on site collections to which they have no access? You don’t – it breaks the security model.

Well, as it happens, the publishing feature needs to create some SharePoint timer jobs to deal with the scheduling of publishing pages (i.e. Setting the date/time of when a page should be published to the masses). But where are timer jobs edited and managed?

Site Collection Administration! This is a *farm* level operation. Should a site collection administrator have the access rights to add custom timer jobs to the SharePoint farm? Hell, no! that is a farm administrator’s job!

So, is it no surprise then, that the publishing feature barfs when activated by a site collection administrator who has no *farm* level access? (I’ve pasted the error message to the end of this article).

I could whine about how this *should* be done, but instead, I’ll simply tell you the two quickest tricks to fix this issue. Both of these methods do not require changing permissions as per the aforementioned blog.

The first method, is to use STSADM to activate the feature for the site collection. By definition, to use the STSADM command, you have to be logged into the SharePoint server and be a farm administrator. Therefore, running the following command should do the trick.

stsadm -o activatefeature -name PublishingSite -url http://sitecollectionurl

The second method is the one that I use (although it’s less clean). As per normal, I create a web application, and then create a site collection. (Usually the blank template for reasons that I will talk about some other time). But rather than log into the site and activate the publishing feature via site settings, I immediately create a *second*  site collection (e.g /sites/boo).

Remember that I am performing this task via SharePoint central administration, so I have farm level permissions.

When I create this second site collection, I choose the publishing site template. As the name suggests, the publishing site template uses the publishing site feature by default. So in creating this site collection, the SharePoint timer jobs get added to the farm.

I then immediately *delete* this site collection, as it is no longer needed.

Now I can load the originally created site collection and activate the publishing feature. It will work fine, because the timer jobs have already been created, and all of the other goodies that the publishing feature gives you, are scoped at the *site collection* level. Therefore, no more "access denied" messages.

Regards

Paul Culmsee

www.cleverworkarounds.com

 

Error message: (stop reading now – this is for google :-)

"Feature Activation: Threw an exception, attempting to roll back.  Feature ‘PublishingResources’ (ID: ‘aebc918d-b20f-4a11-a1db-9ed84d79c87e’).  Exception: Microsoft.SharePoint.SPException: Provisioning did not succeed. Details: Failed to provision the scheduling job definitions.  Page scheduling will not succeed. OriginalException: Access denied. —> System.Security.SecurityException: Access denied.     at Microsoft.SharePoint.Administration.SPPersistedObject.Update()     at Microsoft.SharePoint.Administration.SPJobDefinition.Update()     at Microsoft.SharePoint.Administration.SPWorkItemJobDefinition.Update()     at Microsoft.SharePoint.Publishing.Internal.RootProvisioner.<>c__DisplayClass5.<AddSchedulingJobDefinitions>b__4()     at Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Jul 19 2008

Good advice hidden in the Infrastructure Update

I guess the entire SharePoint world now is aware of the post SP1 "Infrastructure updates" put out by Microsoft recently. Probably the best thing about them are that the flaky "content deployment" feature has had some serious work done on it. (My advice has always been use with extreme caution or avoid it, but now I will have to reassess).

Anyway, that is not what I am writing about. Being a former IT Security consultant, I have always installed SharePoint in a "least privilege" configuration. I use different service accounts for search, farm, SSP, reporting services and web applications. The farm account in particular, is one that needs to be very carefully managed given its control over the heart of SharePoint – the configuration database.

Specifically, the farm account never should have any significant rights on any SharePoint farm server, over and above what it minimally needs (which is some SQL Server rights and some DCOM permission stuff). For what it’s worth, I do not use the Network Service account either, as it is actually more risky than a low privileged domain or local user account.

Developers on the other hand tend to run their boxes as full admin. I have no problem with this, so long as there is a QA or test server that is running least privilege.

However, as always with tightening the screws, there are some potential side effects in doing this. There are occasions when the farm account actually needs to perform a task that requires higher privileges, over and above what it has by default. Upgrading SharePoint from a standard to enterprise license is one such example, and it seems that performing the infrastructure update may be another.

If you take the time to read the installation instructions for the infrastructure update, there is this gem of advice:

To ensure that you have the correct permissions to install the software update and run the SharePoint Products and Technologies Configuration Wizard, we recommend that you add the account for the SharePoint Central Administration v3 application pool identity to the Administrators group on each of the local Web servers and application servers and then log on by using that account

You may wonder why this even matters? After all, it is exceedingly likely that you logged into the server as an administrator or domain administrator anyway. Surely you have all the permission that you need, right?

The answer is that not all update tasks are run by your logged-in account. Although you start the installation using your account, at a certain point during the install, many tasks are performed by the SharePoint Central Administration application. Thus, despite you having administrative permissions, SharePoint (using the farm account) will not have.

So the advice above essentially says, temporarily add the SharePoint farm account to the local administrators group and then log into the server as that user account. Now perform the action as instructed. That way the account that starts the installation is the same account that runs SharePoint and thus we know that we are using the correct account with the correct server privileges.

Once the installation has been completed, you can log out of the farm account and then revoke its administrator access, and we are back to the original locked down configuration.

So keep this in mind when performing farm tasks that are likely to require some privileges at the operating system level. It is a good habit to get into (provided you remember to lock down permissions afterwards :-) ).

Cheers

Paul

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Jul 09 2008

Office Server Search memory leak and stuck on "crawling"

Tags: SQL Server,Troubleshooting @ 11:18 pm

It is the typical scenario isn’t it. Site works fine for a week and then is officially launched on a Monday morning and the site breaks after an hour complaining that it cannot connect to the configuration database. Whoa?

The SQL Server was checked and confirmed to be running fine, and in checking the SharePoint web front end server I I noticed was MSSEARCH.EXE was chewing memory at a rapid rate of knots. Checking the event logs showed up a steady sequence of event ID 7888, 10036 and 3355 messages. Later I noticed that the search crawl was stuck on "Crawling".

If you search on this topic, many people recommend recreating your Shared Service Provider. In this case, this is unnecessary (not to mention drastic).

It turned out to be an unfortunate combination of factors.

  • The client was using SQL Server 2005 with SP1
  • The client had a SQL Server maintenance plan with a "rebuild index" task.

imageNon SQL reader? Maintenance plans are a "good thing". Think of it as a fitness regime for your SQL Server. You can regularly perform integrity checks, tasks to optimise performance, run backups and the like. A screenshot of a basic SQL2005 maintenance plan is to the right.

As it turns out, my client ran a maintenance plan each Sunday (hence why this broke on the Monday of go-live). It also turns out that the "rebuild index" maintenance plan task in SQL Server prior to SP2 has a teeny weenie problem. (By "rebuilding" an index, we mean that it is in effect, deleting and recreating it again).

When rebuilt, various options set on the indexes are not recreated the same way as they were originally created. As you may have guessed, this is extremely uncool, since SharePoint set various indexes in a certain way, it expects things to remain consistent.

There is a KB that you can read all about it here.

http://support.microsoft.com/kb/930887/en-us

Also, another great blog post about the issue is here:

http://blogs.vertigo.com/personal/michael/Blog/Lists/Posts/Post.aspx?ID=4

Some of effects of this dodgily recreated index are listed in the KB article, including:

  1. In Shared Services, the "indexing status" remains stuck in the "Crawling" state
  2. The number of handles that are opened by the MSSearch.exe process increases
  3. The number of TCP connections to the computer that is running SQL Server increases
  4. Several error are recorded in the SharePoint logfiles.
  • "SqlCrawl::ExecuteCommand fails Error 0x80040e2f"
  • CGathererQueueManager::FlushQueue failed with recoverable error 0x80040e2f
  • CGathererFilterSink::CommitLinks : pGatherAddLink->AddLinkComplete error=0x80040e2f

Additionally, in my case, MSSEARCH.EXE leaked memory very rapidly when viewed in Task Manager and I received the event logs as shown at the end of this post.

To resolve it, follow the instructions at Michael’s blog as the KB article is poorly written, but make sure that you do it for all tables as listed in the KB article! Michael’s post only covers 2 indexes and there actually are more in the search database as shown below.

Table Index
MSSAlertDocHistory IX_AlertDocHistory
MSSAnchorChangeLog IX_MSSAnchorChangeLog
MSSAnchorPendingChangeLog IX_MSSAnchorPendingChangeLog
MSSCrawlChangedSourceDocs IX_MSSCrawlChangedSourceDocs
MSSCrawlChangedTargetDocs IX_MSSCrawlChangedTargetDocs
MSSCrawledPropSamples IX_MSSCrawledPropSamplesByDocid
MSSCrawlErrorList IX_MSSCrawlErrorList_hrResult
MSSCrawlHostList IX_MSSCrawlHostList_Name
MSSCrawlQueue IX_MSSCrawlQueue
MSSDocSdids IX_MSSDocSdids

Hope this helps someone

Paul

Error log entries below for the google crawler. You can stop reading now! :-)

  • Event Type:      Error
  • Event Source:   Office SharePoint Server
  • Event Category:            Office Server General
  • Event ID:          7888
  • Date:                1/07/2008
  • Time:                12:03:03 PM
  • User:                N/A
  • Computer:         MyServer

Description:

Message: A connection was successfully established with the server, but then an error occurred during the pre-login handshake.  When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: Named Pipes Provider, error: 0 – No process is on the other end of the pipe.)

 

  • Event Type:      Error
  • Event Source:   Office Server Search
  • Event Category:            Gatherer
  • Event ID:          10036
  • Date:                1/07/2008
  • Time:                12:03:38 PM
  • User:                N/A
  • Computer:         MyServer

Description:

A database error occurred.

Source: Microsoft OLE DB Provider for SQL Server

Code: 17 occurred 1 time(s)

Description: [DBNETLIB][ConnectionOpen (Connect()).]SQL Server does not exist or access denied.

 

  • Event Type:      Error
  • Event Source:   Windows SharePoint Services 3
  • Event Category:            Database
  • Event ID:          3355
  • Date:                1/07/2008
  • Time:                12:04:08 PM
  • User:                N/A
  • Computer:         MyServer

Description:

Cannot connect to SQL Server. MyServer not found. Additional error information from SQL Server is included below.

[DBNETLIB][ConnectionOpen (Connect()).]SQL Server does not exist or access denied.

  • Event Type:      Error
  • Event Source:   Office SharePoint Server
  • Event Category:            Office Server Shared Services
  • Event ID:          6482
  • Date:                1/07/2008
  • Time:                12:05:12 PM
  • User:                N/A
  • Computer:         MyServer

Description:

Application Server Administration job failed for service instance Microsoft.Office.Server.Search.Administration.SearchServiceInstance (009e9001-5c8f-4705-9b4d-ee514c442fc7).

Reason: Exception from HRESULT: 0x80040D1B

Techinal Support Details:

System.Runtime.InteropServices.COMException (0x80040D1B): Exception from HRESULT: 0x80040D1B

   at Microsoft.Office.Server.Search.Administration.SearchServiceInstance.SynchronizeDefaultContentSource(IDictionary applications)

   at Microsoft.Office.Server.Search.Administration.SearchServiceInstance.Synchronize()

   at Microsoft.Office.Server.Administration.ApplicationServerJob.ProvisionLocalSharedServiceInstances(Boolean isAdministrationServiceJob)

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

07/01/2008 12:25:01.85 mssearch.exe (0x0E20)                          0x0E38 Search Server Common             GathererSql                               0          Monitorable       CSqlCrawl::ExecuteCommand fails Error 0x80040e2f – File:d:\office\source\search\search\gather\server\gathersql.cxx Line:407 

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



May 28 2008

SharePoint development/deployment governance…

My home-town compatriot Jeremy Thake has hit onto a governance topic that I think will turn into a very popular series once he is done with it (in committing to writing it, he will be a busy boy indeed for a while I suspect :-) ).

He has written a post on current methods and common issues of deployment of SharePoint customisations from dev to staging to prod, with particular reference to the current pain associated with iterative development and deployment.

The state of play in this area out in SharePoint land is not the best, due to a wide variety of factors and Jeremy sums it up very well.

Certainly, there are some extremely annoying architectural quirks. Back when I did the branding series I encountered some, but Jeremy has hit some nastier ones than what I found.

Anyway, take a look at his post – its a good read.

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



Jan 15 2008

SQL Logins and AD Accounts can bite

This one had me going for a while.

I was minding my own business doing a MOSS install. I successfully created the Office Server Search Service and onto the creation of the Shared Service Provider. Created the SSP and MySite Web Applications and then at the final step of creating the SSP, it bombed out after a long period of time with an error.

image

Reason: Windows NT user or group ‘MyDomain\MOSS_Search.service’ not found. Check the name again.

Continue reading “SQL Logins and AD Accounts can bite”

 Digg  Facebook  StumbleUpon  Technorati  Deli.cio.us  Slashdot  Twitter  Sphinn  Mixx  Google  DZone 

No Tags



« Previous PageNext Page »

Today is: Thursday 17 May 2012 |