I am on a team developing a "portal" for SharePoint Foundation 2010. I am responsible for making the installer which sets up a new instance of SharePoint. It handles more than just the installation of the solution files and configuration of SharePoint. The installer handles deploying files to the GAC and creating our federated security client (used for our SharePoint claims based auth).
During the install of an instance I complete a few tasks that are specifically related to SharePoint and use the SharePoint API in .NET 3.5:
- Create/Configure Trust
- Create a web application.
- Deploy solutions to farm and web application
- Create default site collection
- Activate default features on the site collection
- Update the web application "properties"
Up to know I have been able to have an install work properly on a single SharePoint server, ie one server hosts the farm database and another is the full stack of SharePoint services. I have begun testing with a setup that will mimic the real world setup of SharePoint in our hosting environment. There we have a database server and at least three SharePoint servers, only one of of which has Central Administration.
The problem I've run into is that in this case I can correctly and without any errors do all the steps above except the activation of features. I've removed the code which does this step and verified that the web application, site collection, trust, etc (except for the "properties") have been created correctly by launching the site, authenticating, and seeing the default "team" theme. However, once I activate the default features, either via API or through the site collection settings, I can no longer access the site and receive an error.
This comes to the crux of my problem and the issue I face when explaining it here. Our solution is highly complex in that it involves a lot configuration from aspects of security and reliance on other services outside of SharePoint. I don't know if some of our settings are not propagating across the servers causing an internal error in our solution to cause the error page. I also do not know if when I'm setting the "properties" if they're sticking in the farm situation. I had an issue where I was not setting these right when I first started out on this task and it caused an issue similar to what I'm seeing now.
I also am unsure if the solutions are correctly being deployed therefore corrupting the features. I've setup ULS viewer on each of the SharePoint servers yet I cannot find anything relating to the correlation ID for the error I receive on the error page. I actually get no events in the ULS viewer on "master" SharePoint server. I can see the deploying and retracting of solutions in the viewer on the other two servers.
I must be missing something that happens in a farm with multiple servers. I am hoping that someone can take a look at the bit of code I use to deploy solutions and set properties to help me out. I would also really like to figure out why I cannot see the error that I'm getting in any of the ULS Viewers. Can someone explain this for me?
Also I'm looking for more definitive examples of how to configure and setup SharePoint via the C# API. Up to this point I've been decompiling the code for the commandlets and replicating what I can in code. I still do not have a "correct" way to activate a feature via C# and am just creating a process running stsadm.exe to activate the features.
Here is my code for adding and deploying a solution:
SPSolution newSolution = SPFarm.Local.Solutions.Add(Path.Combine(this.SharePointPointRootFolder, solutionDefinition.filename));
if (!solutionDefinition.webApplicationSpecific)
{
Console.WriteLine("Adding a farm specific solution.");
newSolution.Deploy(DateTime.Now, true, true);
this.WaitForSolutionJobsToComplete(newSolution.Id);
newSolution.Synchronize();
}
else //specific to a web application. Load the new solution.
{
Console.WriteLine("Adding web application specific solution.");
Collection<SPWebApplication> webAppCol = new Collection<SPWebApplication>();
webAppCol.Add(this.WebApplication);
newSolution.Deploy(DateTime.Now, true, webAppCol, true);
this.WaitForSolutionJobsToComplete(newSolution.Id);
newSolution = SPFarm.Local.GetObject(newSolution.Id) as SPSolution;
newSolution.Synchronize();
this.WaitForSolutionJobsToComplete(newSolution.Id);
newSolution.Update();
}
Here is my code for activating features. It just uses a process to use STSADM.EXE:
void UpdateFeatures(SPSite site)
{
Console.WriteLine("Updating features.");
SPSolutionDefinitionCollection collection = SolutionConfigurer.LoadSolutionDefinitions();
foreach (SPSolutionDefinition solutionDefinition in collection.spSolutionDefinition)
{
Guid id = new Guid(solutionDefinition.ID);
if (solutionDefinition.spFeatureDefinition != null && solutionDefinition.spFeatureDefinition.Length > 0)
{
foreach (Models.SPFeatureDefinition fDef in solutionDefinition.spFeatureDefinition)
{
Guid fDefId = new Guid(fDef.ID);
//this.Activate(fDefId, site);
Process stsadmP = WebApplicationConfigurer.GetSTSADMProcess(this.STSADMPath, "-o activatefeature -id " + fDef.ID + " -url " + this.SiteCollectionUrl + " -force");
Console.WriteLine(string.Format("Invoking STSADM.EXE for feature deployment: {0} with arguments:{1}", stsadmP.StartInfo.FileName, stsadmP.StartInfo.Arguments));
stsadmP.Start();
stsadmP.WaitForExit();
if (stsadmP.ExitCode == 0)
{
Console.WriteLine(string.Format("STSADM.EXE successfully ran for feature id {0} and produced this output: {1}", fDefId, stsadmP.StandardOutput.ReadToEnd()));
}
else
{
Console.WriteLine(string.Format("STSADM.EXE did not produce a successful exit code for feature id {0} and produced this output: {1} + {2}", fDefId,
stsadmP.StandardOutput.ReadToEnd(), stsadmP.StandardError.ReadToEnd()));
}
}
}
}
}
Here's how I update a property. I do this last because I was doing it after deploying the solutions, but it seems that activating features was resetting the values.
public void UpdatePropertiesWebApplication()
{
string dnsName = this.SigningCertificate.GetNameInfo(X509NameType.DnsName, false);
SPWebApplication webApplication = SPFarm.Local.GetObject(this.WebApplicationId) as SPWebApplication;
if (webApplication != null)
{
Console.WriteLine("Updating property information for the web application.");
if (!webApplication.Properties.ContainsKey(Constants.PropertyBagKeys.ESB_ROOT))
{
webApplication.Properties.Add(Constants.PropertyBagKeys.ESB_ROOT, this.ESBUrl);
}
else
{
Console.WriteLine("Updating ESB Root URL. " + this.ESBUrl);
webApplication.Properties[Constants.PropertyBagKeys.ESB_ROOT] = this.ESBUrl;
}
Console.WriteLine("ESB Root Url: " + webApplication.Properties[Constants.PropertyBagKeys.ESB_ROOT]);
if (!webApplication.Properties.ContainsKey(Constants.PropertyBagKeys.REALM))
{
webApplication.Properties.Add(Constants.PropertyBagKeys.REALM, this.SharePointTrustPoint);
}
else
{
Console.WriteLine("Updating realm. " + this.SharePointTrustPoint);
webApplication.Properties[Constants.PropertyBagKeys.REALM] = this.SharePointTrustPoint;
}
Console.WriteLine("Realm: " + webApplication.Properties[Constants.PropertyBagKeys.REALM]);
if (!webApplication.Properties.ContainsKey(Constants.PropertyBagKeys.SSO_ROOT))
{
webApplication.Properties.Add(Constants.PropertyBagKeys.SSO_ROOT, this.SSOUrl);
}
else
{
Console.WriteLine("Updating the broker url. " + this.SSOUrl);
webApplication.Properties[Constants.PropertyBagKeys.SSO_ROOT] = this.SSOUrl;
}
Console.WriteLine("Broker Url: " + webApplication.Properties[Constants.PropertyBagKeys.SSO_ROOT]);
if (!webApplication.Properties.ContainsKey(Constants.PropertyBagKeys.TRUSTED_CERT_DNS_IDENT))
{
webApplication.Properties.Add(Constants.PropertyBagKeys.TRUSTED_CERT_DNS_IDENT, dnsName);
}
else
{
Console.WriteLine("Updating trusted cert dns identity. " + dnsName);
webApplication.Properties[Constants.PropertyBagKeys.TRUSTED_CERT_DNS_IDENT] = dnsName;
}
Console.WriteLine("Trusted Certificate Identity: " + webApplication.Properties[Constants.PropertyBagKeys.TRUSTED_CERT_DNS_IDENT]);
webApplication.Update(true);
}
}