I had put together an application to perform Load Test in both Microsoft Office SharePoint Server (MOSS) 2007 and Windows SharePoint Services (WSS) 3.0. The application as it is now is more “Load” than “Test”. The next step will be to save the results in a database and enable live queries. I am planning to use the Jet Database (Access). I am releasing this intermediate version because if used with other tools it can complete the cycle. This can be used to test capacity problems before publishing a SharePoint portal.
This is how the application works: you enter a SharePoint URL (Web Collection or Web) and the application enumerates all items (lists, list items, webs, sub-webs, documents, pages etc) and hit each once for every user you define. In the screen below we will simulate 5 users hitting the site for 30 minutes. You can run the application from any workstation. You don’t have to run it from the server. All operations used in this application are supported in production environment (i.e. it only uses webservices and browser requests) and it does not alter any configuration nor add any content. It access the information in a similar way of SharePoint Designer’s.
When you click the “Start” button you are asked to enter your credentials (no anonymous or FBA authentication since it will use the webservices to enumerate the content). Choose current user or enter the credentials if you don’t have the appropriate rights.
After logging in, the application will enumerate all items and hit them. In this fiddler except you can see how it silently works:
If there is any error (unauthorized user, resource not found or time out) it will show up in the error list view. Note that some errors from SharePoint will not show in this version as it does not return a status error. A tool like Fiddler can help in this gap.
Full Project including source code can be found here:
http://mossloadtest.codeplex.com/
I plan to continue this project and feedback is highly appreciated via this blog.
Notice that this is another private project and it is not endorsed or supported by my employer Microsoft. I will try my best to answer questions and fix bugs, but it is an application AS IS.
There are some occasions in the course of work as Support Escalation Engineer in Microsoft that we rely heavily in analyzing SharePoint metadata. Installing software in a production environment is a no-no for most companies (or at least it requires a change control request). I had put together a few features in an Explorer like SharePoint GUID that uses web-services and thus does not have to be installed in the server (any desktop will do). The way it is now, it is still very incipient but it enables you to verify most of internal metadata of lists definition and results sets. I had put it in codeplex a long time a go and planned to get back to work with it again soon. This time actually never came. Now, a friend in Australia volunteered to help me finish the application. I am also bringing this back to life because a colleague here at Microsoft, Yuri Diogenes, from the Security Team worked with me in a MOSS/ISA issue and found the application helpful to identify Alternate Access Mapping (AAM) and Host Headers issues. The application let's you choose your authentication mode with SharePoint (User/Password, Current User or Certificate).
After connecting, it lists all site collection lists and libraries:
Including the metadata internals:
You can download it here:
http://www.codeplex.com/MOSSSupportExplorer
The full source code is also included. Please noticed that this is a personal project I put in Codeplex. This is not a Microsoft Product.
There are scenarios on which you can access a unique site collection using different URLs even if these URLs are not listed in the Alternate Access Mapping (AAM) of the application. This normally does not cause any problem and even though you should not, you can live without adding DNS/Netbios aliases to AAM. If you plan to use blob cache, however, make sure that you list all URLs you may think of in the AAM.
One probable and common scenario is that on which you have multiples Web Front End (WFEs) and they are reached via Network Load Balancer (NLB). You did not add any host header when you created the web application and yet you can access the web application using both the local server name (eg. MOSS-WFE01) or the DNS entry (eg. http://myportal.contoso.com). However, when you add blob cache functionality to the site (see how to do this here), the cache does not seem to (and actually does not) work.
It happens because blob cache serving and caching happens in a level above SharePoint. It relies on the class HttpApplication rather than WebApplication. If necessary it goes to SharePoint to get the content and cache or just skip the file. If the file is already cached it retrieves from the file system rather than via SharePoint. At some point the blob cache trims the resource accordingly to user permission. At this point SharePoint has to be inquired and if part of the URL containing the web application is not in the AAM, SharePoint will assume the user has no rights and will not return anything at all.
How to verify if I am using the correct Alternate Access Mapping in my Network Load Balancer
Knowing the exact host header coming from the NLB is not so easy. Depending on the configuration, the NLB may use just an IP address or a FQDN. In order to find out do the following.
1. Enable IIS logging by selecting Web Sites, right-clicking and choosing properties.
2. In Properties/Advanced make sure Host (cs-host) is enabled.
3. Clear the cache in the web browser in any workstation (make sure the request will go through the NLB) and hit a couple of pages to populate the log
4. Navigate to the log file in the server you made the change (normally c:\windows\system32\LogFiles) and fin the latest file. You will something like this (in this example cs-host = team.learning.local):
5. Make sure the host header is listed in the Alternate Access Mapping (Central Administration | Operations | Alternate Access Mapping | Edit Public Url | Choose Web Application):

*** UPDATE: I submitted this blog entry as KB and it was published here:
http://support.microsoft.com/kb/2000283
Let's face it, updating Sharepoint is not an easy task. It is true for both Microsoft Office Sharepoint Server (MOSS) 2007 and Windows Sharepoint Services 3.0. Let's highlight some points:
- MOST IMPORTANT: MOSS is built on top of WSS and you have to apply WSS and MOSS updates on MOSS installation
- You have to install localized versions of the updates even if you only uses English
- If you have MOSS, you have to install localized version of the updates even if you use English only
- VERY IMPORTANT: A later update patch DOES NOT include previous patches, so you may have to install a series of patches to get to a specific version. For example, August CU requires you to install SP1, Infrastructure Updates and other patches to be installed before.
- The only reliable information about current and past update paths can be found at http://blogs.msdn.com/sharepoint
- Before December Cumulative Update (CU) Uber Package, you were supposed to download a patch for each additional language you have installed in your farm.
How to update:
First, know where you are now. The way to know your version is going to Central Administration | Operations | Servers in Farm. The version will be shown in the page. This version will not tell you all as you have other components besides the server version. Some people, including me, prefer to check the version in the files. To get the version of the files, we use SPSReports.
SPSReports will generate a large amount of captured files. The file version will be available at <SERVERNAME>_WSEVer.TXT. This is an excerpt of such file (showing SP1 files):
c:\program files\common files\microsoft shared\web server extensions\12\bin\*.*
--a-- W32i DLL ENU 6.6.7.5 shp 1,030,144 02-21-2007 dbghelp.dll
--a-- W32i APP - 12.0.4518.1016 shp 23,824 11-08-2006 hcinstal.exe
--a-- W32i DLL - 12.0.4518.1014 shp 123,232 10-26-2006 microsoft.office.irm.formprotector.dll
--a-- W32i DLL - 12.0.4518.1014 shp 38,736 10-26-2006 microsoft.office.irm.msoprotector.dll
--a-- W32i DLL - 12.0.4518.1014 shp 34,640 10-26-2006 microsoft.office.irm.ofcprotector.dll
--a-- W32i DLL - 12.0.6211.1000 shp 92,000 08-24-2007 msscntrs.dll
--a-- W32i APP - 12.0.6211.1000 shp 282,496 08-24-2007 mssdmn.exe
--a-- W32i APP - 12.0.6211.1000 shp 159,648 08-24-2007 mssearch.exe
--a-- W32i DLL - 12.0.6211.1000 shp 694,104 08-24-2007 mssph.dll
--a-- W32i DLL - 12.0.6211.1000 shp 2,059,104 08-24-2007 mssrch.dll
--a-- W32i DLL - 12.0.6212.1000 shp 1,079,200 09-02-2007 offparser.dll
--a-- W32i DLL - 12.0.6219.1000 shp 3,012,136 11-15-2007 owssvr.dll
--a-- W32i DLL - 12.0.6211.1000 shp 26,496 08-25-2007 oisimg.dll
--a-- W32i DLL - 12.0.4518.1016 shp 26,456 11-08-2006 oleparser.dll
--a-- W32i DLL - 12.0.6211.1000 shp 357,264 08-25-2007 onetnative.dll
--a-- W32i DLL - 12.0.6219.1000 shp 1,983,528 11-16-2007 onetutil.dll
--a-- W32i DLL - 12.0.4518.1014 shp 100,144 10-27-2006 osafehtm.dll
--a-- W32i APP - 12.0.6211.1000 shp 58,232 08-25-2007 owstimer.exe
--a-- W32i DLL - 12.0.4518.1014 shp 400,200 10-26-2006 microsoft.office.policy.dll
--a-- W32i DLL - 12.0.4518.1014 shp 52,040 10-26-2006 microsoft.office.workflow.tasks.dll
--a-- W32i DLL - 12.0.6211.1000 shp 6,407,584 08-24-2007 microsoft.sharepoint.portal.dll
(....)
Depending on the product, there will be files with more significance than others and be aware that not all files will be updated to the latest build. In the excerpt above oleparser.dll is still in version 12.0.4518 (RTM version) while owssrv.dll is in SP1 (12.0.6219).
Checking your version:
- For WSS Global: Check OWSSVR.dll
- For WSS Local: normally by checking the localized javascript version and comparing with the global javascript files and by mssmsg.dll (it may vary - see Knowledge Base method later in this post)
- For MOSS Local: normally by checking the localized javascript version and comparing withe the global javascript files and by Htmlchkr.dll (it may vary - see knowledge Base method later in this post)
- For MOSS Global: Check Microsoft.Sharepoint.Portal.dll (also make sure all other updates as WSS and DLC are compatible with the build, I will talk about it later)
- For Document Lifecycle Management (DLC) aka Workflow: highest version between microsoft.office.policy.dll and microsoft.office.workflow.tasks.dll
- For Infrastructure Updates (IU): It varies (see Knowledge Base method later in this post)
Checking what takes to get up-to-date
As I told you before, the Sharepoint Team blog is the most reliable source of update path. If you want to update to the latest recommended version or just fix a previous update not working so well because you did not know that WSS updates was also mandatory, you better check the blog. Other reason to fix a previous update is when your javascript yields an error every time you try to edit a rich edit field or it only yields an error when you are not editing in English.
One of the problems I see occasionally in my work at Microsoft is clients who were not aware they had to install the language patch update for every language they have in addition to the local English patch. One symptom of the problem is the Javascript errors I mentioned. Sometimes they prefer to wait for SP2 and avoid to update in all Cumulative Updates released. Before December CU, all other patches required a long list of prerequisites. My colleague Robert Gullick put together a list for all paths up to December CU.
The new updates make things easier. For example, October CU required at least 4 patches (if you don't have other languages installed) while December CU only requires two patches no matter how many languages you have installed.
Let's take a look at the requirements for having October CU in MOSS (i.e. update path):
- KB936988 - WSS SP1 + LanguagePack SP1 - 6219
- KB957109 - WSS August CU Local - 6327
- KB957691 - WSS October CU Global - 6332
- KB936984 - MOSS SP1 + Language Pack SP1 - 6219
- KB955586 - MOSS DLC Component Update Local - 6324
- KB955937 - MOSS Excel Services security update - 6324 *
- KB958569 - MOSS DLC Component Update Global - 6331
- KB958567 - MOSS October CU Local - 6331
- KB957693 -MOSS October CU Global - 6331
If you had installed any of the KBs in the path you don't have to install the KB again. And now we finally will talk about how to use the KB method.
How to identify if the KB is already installed (Knowledge Base method)
Unfortunately the KBs are not visible in Add/Remove Programs in Control Panel. But the information is in the registry. Let's say I want to check if KB958569 is installed or not.
- Open Registry Editor (Start | Run | Regedit.exe)
- Go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
- Press Ctrl+F to start a search
- Look for the KB number by entering only the number without the prefix (eg. 958569)
- If you find it under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall it is because it is installed.
- This method is not so reliable because it does not keep track of past KB installed but it can be a good start
- You can issue the command "reg export HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall uninst.txt" to dump the registry info in the text file.
How to download a specific KB hotfix (don't forget the local patches also)
Some hotfixes do not have an immediate public download place. You have to request. Please see the sample below for KB 958567 which is local update (and must be installed for each language you have in your environment).
The KB is located here:
http://support.microsoft.com/kb/958567
for other KBs, just change the number in the url.
The hotfix can be download here:
http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=958567
Notice that you will be offered only the English version initially. Click on “Show hotfixes for all platforms and languages” and it will show for all languages and architectures (x86 and x64).
Choose all languages and architectures that apply. You will receive an e-mail with instructions on how to download the patch.
Please follow these for all patches.
I had recently faced a problem which I believe other people will face at least once: how to get the context from a URL in a C# Console Application. I needed to use ProfileLoader which requires a context. You cannot get current context when you run an application outside a Sharepoint page (like in Windows or Console Application). The solution below gets a context from the topology (which happens to be deprecated but works) and get all properties from the profile. See the sample code below:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.Office.Server;
using Microsoft.SharePoint.Portal;
using Microsoft.SharePoint.Portal.Topology;
using Microsoft.SharePoint;
using System.Web;
namespace TestProperties
{
class Program
{
static void Main(string[] args)
{
//get portal site context from topology
string strUrl = "http://myserver";
TopologyManager tm = new TopologyManager();
PortalSite ps = tm.PortalSites[new Uri(strUrl)];
PortalContext pc = PortalApplication.GetContext(ps);
ServerContext sc = pc.ServerContext;
PropertyCollection props = ProfileLoader.GetProfileLoader(sc).GetUserProfileManager().Properties;
foreach (Property prop in props)
{
if (prop.Type != PropertyDataType.Binary && prop.DefaultPrivacy == Privacy.Public)
{
Console.WriteLine("{0} - {1}", prop.DisplayName, prop.Name);
}
}
}
}
}
WSS and MOSS enable outgoing e-mail configuration and most of the time this configuration is straightforward. As of matter of fact you do not have much to configure as shown below:
Figure 1 - Outgoing e-mail settings
This same configuration is used for alerts and notifications as it is for invitations. Most of the e-mail sending is accomplished by OWSTIMER.EXE (The Sharepoint Timer Service) which run as the Farm Administrator Account in all servers in the farm. When you have alerts sending e-mail most of the time and failing just occasionally, it is possible that one of the servers is not able to deliver the e-mail. Testing e-mail delivery in WFEs (Web Front Ends) is easier, since adding someone to a site and sending a welcome (see screenshot below) mail will force Sharepoint to send the e-mail from the WFE serving the request and the result is shown immediately when you press OK. It may bring some extra configuration to identify the WFE if you are using NLB (Network Load Balancer), but nothing compared to OWSTIMER.EXE identification problem.
Figure 2 - Sending Welcome mail is a fast to way to identify connection problems as the result is in the next page
However if you still have problems to identify why e-mails are not being delivered using the Welcome Page or the server you suspect is not serving pages you may need to run a few other tests. First, you have to check if the suspect server is reaching the SMTP server. You can use the basic ping and then try the telnet connection to see if you get the right response as specified in KB153119. If everything seems correct and you still have problem, try the following code:
(UPDATED)
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using System.Net.Mail;
namespace SendMail
{
public class MailServerInfo
{
protected string server;
public string Server
{
get { return server; }
}
protected string from;
public string From
{
get { return from; }
}
protected string replyTo;
public string ReplyTo
{
get { return replyTo; }
}
public MailServerInfo(SPSite spSite)
{
server = "(empty)";
from = "(empty)";
replyTo = "(empty)";
SPWebApplication spWebApplication = spSite.WebApplication;
if (spWebApplication.OutboundMailServiceInstance != null)
{
from = spWebApplication.OutboundMailSenderAddress;
replyTo = spWebApplication.OutboundMailReplyToAddress;
SPOutboundMailServiceInstance smtpServer = spWebApplication.OutboundMailServiceInstance;
server = smtpServer.Server.Address;
}
}
public string ServerInformation
{
get
{
return String.Format("Site Specific - server: {0}, From: {1}, ReplyTo: {2}",
server, from, replyTo);
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Sharepoint e-mail configuration tester");
Console.WriteLine("======================================");
Console.WriteLine("Written by Rodney Viana");
Console.WriteLine("More info at http://blogs.msdn.com/rodneyviana\n");
Console.WriteLine("This sample application is supplied \"AS IS\"");
Console.WriteLine("It is ONLY for demonstration/proof of concept purposes");
Console.WriteLine("If you DO NOT agree with the license at:\n\thttp://www.codeplex.com/rodneyviana/license\n\tPress Ctrl+C");
Console.Write("Press any other key to continue...");
Console.ReadKey();
Console.Write("\n\nPlease enter url (eg.: http://localhost):");
string siteUrl = Console.ReadLine();
try
{
using (SPSite spSite = new SPSite(siteUrl))
{
using (SPWeb web = spSite.OpenWeb())
{
if (web == null)
{
Console.WriteLine("Web could not be found (OpenWeb returned null). Aborting.");
return;
}
Console.WriteLine("The site has been found at " + web.Url);
Console.Write("Please enter recipient login name (eg. domain\\administrator): ");
string login = Console.ReadLine();
string displayName = "";
string emailAddress = "";
try
{
SPUtility.GetFullNameandEmailfromLogin(web, login, out displayName,
out emailAddress);
}
catch (Exception ex)
{
Console.WriteLine("Unable to get user information.");
Console.WriteLine("Error: "+ex.Message);
Console.Write("Press any key...");
Console.ReadKey();
return;
}
Console.WriteLine("Display Name: {0}\nE-mail: {1}", displayName,
emailAddress);
string subject = "Test message";
string body = "Testing app message";
MailServerInfo serverInfo = new MailServerInfo(spSite);
string server = serverInfo.Server;
string replyTo = serverInfo.ReplyTo;
string from = serverInfo.From;
Console.WriteLine(serverInfo.ServerInformation);
Console.WriteLine("Trying to send e-mail via Sharepoint...");
if (!SPUtility.SendEmail(web, true, true, emailAddress, subject, body))
{
Console.WriteLine("Error: Unable to send e-mail via Sharepoint");
Console.WriteLine("Trying via SMTP...");
SmtpClient client;
try
{
client = new SmtpClient(server);
}
catch (Exception ex)
{
Console.WriteLine("Could not contact server");
Console.WriteLine("Error: " + ex.Message);
Console.Write("Press any key...");
Console.ReadKey();
return;
}
try
{
client.Send(from, emailAddress, subject, body);
}
catch (Exception ex)
{
Console.WriteLine("Unable to send via SMTP client");
Console.WriteLine("Error: " + ex.Message);
Console.Write("Press any key...");
Console.ReadKey();
return;
}
Console.WriteLine("E-mail has been sent successfully via SMTP client");
}
else
{
Console.WriteLine("E-mail has been sent successfully via Sharepoint");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Unable to find url.");
Console.WriteLine("Error: " + ex.Message);
}
Console.Write("Press any key...");
Console.ReadKey();
}
}
}
You can download project and executable at http://www.codeplex.com/rodneyviana/Release/ProjectReleases.aspx?ReleaseId=19103.
If you are not much in software development you can extract only the .exe file (SendMail\SendMail\bin\Debug\SendMail.exe) and run it without installation. This application will send an e-mail using the farm's configuration. Enter the url of a valid site and a valid user login name which will be the recipient of the test message. The application will retrieve the e-mail information from the user login name. You must run the application from one of the servers in the farm.
Figure 3 - The application will resolve the recipient e-mail address as well as the farm's outgoing e-mail configuration
If the configuration is correct the intended recipient will receive the following e-mail:
Figure 4 - E-mail will be received if everything is configured correctly
If you can receive this e-mail and still are unable to receive e-mail from sharepoint check the Application Pool credential (in case of Welcome page) or web farm administrator account (in case of alert and other e-mails sent by OWSTIMER.EXE).
In this post we are going to explore User Profiles by extending the Organizational Hierarchy in MOSS. I've posted in Codeplex both installer and code for the Organizational Chart Hierarchy web part. This post will only discuss installation and configuration. I will discuss the code in posts to come.
What this webpart does:
This web part reads and analyzes the user profiles in MOSS and creates a full hierarchical organizational chart. See screenshot below:
What I need to make it work:
1. It requires MOSS (Microsoft Office Sharepoint Server) 2007.
2. It requires you have imported profiles from you AD.
3. In order to have the hierarchy displayed correctly you have to include managerial and department information for all users who have manager (see the example in the snapshot below).
4. You have to edit the departmentconfig.xml file if you want links to the department sites. After the install, this file normally is found at: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\OrgChartPart\. This file is only available after the install is complete.
5. Just edit, add or delete <Mapping Department="[Department Name As in Profile]">[url]</Mapping> entries as appropriate. The mapping must be exact. As the example below (this entry is case sensitive):
<Mapping Department="Operations">http://myweb/sites/operations</Mapping>
Installing Steps:
1. Download the latest release here: http://www.codeplex.com/orgchartpart/Release/ProjectReleases.aspx?ReleaseId=15590 and save in a folder you will remember later (eg. c:\downloads).
2. Go to MOSS Bin folder, commonly at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN
3. Run the following commands from the command prompt when logged as farm administrator (assuming the .wsp file is in C:\Downloads):
stsadm -o addsolution -filename c:\downloads\OrgChartPart.wsp
stsadm -o deploysolution -name c:\downloads\OrgChartPart.wsp -allowgacdeployment -immediate
stsadm -o execadmsvcjobs
4. Go to the shared services provider you use for personalization in Central Administration.
5. Choose Personalization services permission under User Profiles and My Sites.
6. Make sure NT AUTHORITY\Authenticated Users has "Personal Features" and "Personal Site" rights.
7. Go to the site collection you want to include the web part (eg. http:/myweb).
8. Go to Site Actions | Site Settings | Modify All Site Settings
9. Go to Site Collection Features under Site Collection Administration
10. Activate Org chart Web Part
11. Verify if OrgChartPart is available in the Web Part Gallery of the Site Collection (Site Actions | Site Settings | Galleries | WebParts)
12. Add the web part wherever you want in a page
13. Make sure you adjust the web part to render correctly:
Notes:
- The beautiful tree layout in Javascript was developed by the talented Emilio CL and can be found here: http://www.codeproject.com/KB/scripting/graphic_javascript_tree.aspx
- Don't try it in a large Enterprise with over 500 user profiles. It will work but will be slow. A few modifications are necessary to enable better performance.
- Next post will be about the source code.
Whomever tried to populate a MOSS (Microsoft Office Sharepoint Server) form field using Javascript noticed that it is far from being straightforward. The secret is in the undocumented core.js After a client's request I was able to come up with a not so-intrusive Javascript solution.
You have to open the site in Sharepoint Designer, navigate to the list you want (in my example I used a list called "Public Details"), look for a spot in the html code and copy the Javascript code below to both EditForm.aspx and NewForm.aspx.
For each list in your site there will be a folder. For instance, let’s say you have a list called Public Details. In Sharepoint Designer you should edit files under site\Lists\Public Details as shown below (be aware these pages will be unghosted after that):
To test the application, just add the query string ?BDC=<value> to the url (eg. http://portal/Lists/Public+Details.aspx?BDC=123). See code below:
|
<script type="text/javascript" language="javascript" for="window" event="onload">
var elems = document.getElementsByTagName("DIV"); var bdcString="";
var queryString = document.location.search.split("&");
for (var i=0;i<queryString.length;i++) { if ((queryString[i].split("=")[0]=="BDC") || (queryString[i].split("=")[0]=="?BDC")) bdcString=queryString[i].split("=")[1]; }
if(bdcString == "") return;
for (var i=0;i<elems.length;i++) {
if (elems[i].id.indexOf("_upLevelDiv") > 1) { var subs = elems[i].id.substring(0,elems[i].id.indexOf("_upLevelDiv")); var upLevel = document.getElementById(subs+"_upLevelDiv"); if(upLevel == null) { alert("error Up Level is invalid: "+subs+"\n"+upLevel); return; } var downlevel=document.getElementById(getSubControlID(subs, g_EntityEditorDownLevelId)); downlevel.value = bdcString; var editor=document.getElementById(subs) SetInnerText(upLevel, bdcString); copyUplevelToHidden(subs); } }
</script> |