Where is the Shared Resource Provider? 

Tags:

First off though, what is a Shared Resource Provider and why would you want to retrieve it?

 

The shared resource provider (Microsoft.Office.Server.Administration.SharedResourceProvider) is the internal class within Microsoft.Office.Server.dll which instantiates the Shared Service Provider.

 

I need the SharedResourceProvider instance so that I can associate configuration data for a new shared service I am developing (Widgets Service). Storing and retrieving objects in association with a SharedResourceProvider is made easier thanks to SPPersistedObject and SPPersistedChildCollection (I'll cover that in another blog post).

 

You can see the SharedResourceProvider in the SharePoint_Config database with the following SQL.

 

select

o.Name, o.Properties

from

objects o

inner join

Classes c

on

o.ClassId=c.id

where fullname = 'Microsoft.Office.Server.Administration.SharedResourceProvider, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'

 

The results will show your SharedResourceProvider instances. The Properties column is not just the properties bag. It is all of the private and public fields of the object serialized as XML. You can cut/paste the properties into an XML document and then view in Internet Explorer to see the details (below).

Notice how the configuration details for My Sites are stored within the SharedResourceProvider. Also included is the Shared Service Database. Each Shared Service associates configuration data with the Shared Resource Provider in different ways. For example, My Site configuration is stored in the SharedResourceProvider object, but BDC configuration data is stored within the database specified by m_SharedServiceDatabase. Project Server 2007 associates configuration data with a SharedResourceProvider by setting the Parent property of their configuration entities (Project Sites) to the Shared Resource Provider.

 

As for retrieving the Shared Resource Provider, I have seen some solutions suggested which are based on the snip below (retrieved using .net reflector against the ServerFarm class in the Microsoft.Office.Server assembly).

Fire up .net reflector and you'll see that this piece of code is not exactly straightforward. Obviously the code is accessing the "Microsoft.Office.Server.SharedResourceProvider" property of a WebApplication, but there are conditions covered for if this property is null and also another condition around the IsParentSharedResourceProvider property. To avoid fully understanding or fully copying code out of reflector, I would rather just access those internal objects and call the public methods in similar fashion as other code within MOSS assemblies.

 

My approach is to first obtain one of those internal objects which can lead me to the SharedResourceProvider which is associated with a Web Application. The ServerFarm will suffice. Once I have a ServerFarm, I can access public methods from there to determine the SharedResourceProvider which is associated with a given web application.

 

static SPPersistedObject GetSharedResourceProvider(ServerContext context, SPSite site)
{


//our first step is to get a ServerFarm object, to do this, we must first obtain the ServerFarm type
Type serverType =Assembly.GetAssembly(context.GetType()).GetType("Microsoft.Office.Server.Administration.ServerFarm");

 

//now that we have a ServerFarm type, we can obtain an instance from the database
object serverFarm = site.WebApplication.Farm.GetObject(string.Empty, site.WebApplication.Farm.Id, serverType);
//the ServerFarm will give us a collection of SharedResourceProviders, we must reflect upon it to retrieve the property
PropertyInfo sharedResourceProvidersProperty = serverFarm.GetType().GetProperty("SharedResourceProviders");
IEnumerable sharedResourceProviders = (IEnumerable)sharedResourceProvidersProperty.GetValue(serverFarm, null);

//now that we have a SharedResourceProvider, we can ask it for its associated WebApplications
Type sharedResourceProviderType=Assembly.GetAssembly
(context.GetType()).GetType("Microsoft.Office.Server.Administration.SharedResourceProvider");

PropertyInfo webApplicationsProperty = sharedResourceProviderType.GetProperty("WebApplications");

//now the easy part, iterate through each SharedResourceProvider and check to see if our web application

//is in its list of WebApplications

foreach (SPPersistedObject sharedResourceProvider in sharedResourceProviders)
{

IEnumerable webApplications = (IEnumerable)webApplicationsProperty.GetValue(sharedResourceProvider, null);

foreach (SPWebApplication webApp in webApplications)
{

if (webApp.Id == site.WebApplication.Id)
{

return sharedResourceProvider;

}

}

}

return null;


}

 
Posted by Eric Bowden on 13-May-08
6 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
 

Links to this post

Comments


Kirk Liemohn commented on Thursday, 15-May-2008
Eric, great post! I have a few questions: 1. It looks like your method could just have easily had an overload that takes in a SPWebApplication instead of a SPSite. Should you show an overload that does this and have your other overload call this new one? 1a. The same could probably go for the ServerContext object. I’m not familiar with how easy it is to get one of these (I’m used to SPContext, though). 2. Does the fact that you must compare the web application ID in your for each loop indicate that there is a SRP for every web application and that they could be different? A discussion (or link) on this would be useful. 2a. Would it be possible to find the “master” SRP for the farm (if such a thing exists) in which you don’t need to look at any SPWebApplication ID for comparison? Or maybe you could return an array of SRPs instead of a single one? 3. It seems that there are some pros/cons of the approach of using the simple code (the Reflector snippet) vs. your code. It seems that your code covers some corner cases but it could break in the future since you are accessing internal methods. I guess the Reflector snippet could break in the future as well. Do you have thoughts around this? 4. Your code returns a type of SPPersistedObject. I know you want to go into that further on another blog post (I have put this as a blog idea, BTW), but it seems that you should discuss how one might use this once they get it back. 5. Using your code can you get the SSP? You mention that the SRP instantiates the SSP. Maybe demonstrating this would answer my #4 immediately above.


Eric Bowden commented on Thursday, 15-May-2008
1. Your suggestions lead to good optimization of the method. The only purpose of the ServerContext is so I can get the correct Assembly which contains the other classes/properties I obtain through reflection such as the ServerFarm. I believe there are other ways to accomplish the same thing; I happened to have had a ServerContext handy so took that approach. Also I could have just sent in the SPWebApplication instead of SPSite. 2. There could be a different SRP (Shared Resource Provider) for each web application. SRPs are associated with one or more web applications and of course it can be useful to have more than one SRP. There is a Deafult SRP but wether or not a web application is associated with the default SRP is an implementation detail which is not exposed in a public manner (as best I'm aware). The method I'm using is the only way I could find to call public methods within the Microsoft.Office.Server.dll to retrieve the SRP for a Web Application. The next best thing is the ServerContext has a private SharedResourceProvider property. But this being private, I did not want to use it. 2a. There is no "master" SRP but there is a default SRP. Retrieving the default SRP would require more reflection calls against classes in the Microsoft.Office.Server assembly but it could be done. 2a (part 2). The code is returning the array of SRPs. And then for each SRP it is iterating through the associated web applications. I'm sure there is a more efficient way to do this. The very most efficient would have been to use the SharedResourceProvider property on the ServerContext (it is marked private). 3. The code is accessing internal classes, public methods. Even though the classes are marked as internal, they are being accessed by other assemblies; namely, the Microsfot Project Server 2007 assemblies. I have read that there exists a way to access internal classes (using a CLR trick). I assume this is how Project Server 2007 assemblies accomplish this. But yes, I think there is risk that implementation details may change over time and break this code. We will all have to adapt to those changes (we being us and the Project Server 2007 team). Lifting the reflector snippet is a possibility; you just have to be sure to lift all of it which includes logic for handling a null SRP (in which case the Default SRP is used) and also handles the "IsParentSharedResourceProvider" case. The logic for the Default SRP btw is handled by the method which calls the code snippet shown. So, you have to back out one level to get it all. The IsParentSharedResourceProvider case appears to handle a multi-farm scenario where the web application is associated with a SRP from a parent farm. I cannot be sure by reading code and cannot experiment in order to write/lift and test code to handle this case. 4. The SharedResourceProvider is a SPPersistedObject. We cannot actually play much with the SRP as a fully typed (qualified) object because it is internal. The best we can do is have an SPPersisted object and use reflection to obtain more propeties and methods or use it as the parent of SPPersisted objects. Another use of the SRP (in the form of a SPPersistedObject) is we can set it as the parent of other SPPersistedObject(s). This is useful for associating configuration data in my Widgets Service with a specific SRP (subject matter for my next blog post). 5. Shared Service Provider, as best I can tell, is the SharePoint site which is designated as the administrative site for the SRP. The m_AdministrativeWebApplicationId property looks promising except for the fact that its an empty GUID (see screen snap above). I'll look at this and see if I can help there. Thanks for the comment Kirk!


Eric Bowden commented on Tuesday, 20-May-2008
Sorry Kirk for the formatting of the following comment. The code below will make more sense if you copy/paste it in to dev studio and reformat. Your last question which I didn't answer: How do I retrieve the Shared Service Provider (SSP) ? There really is no Shared Service Provider within the Moss assmeblies that I'm aware of. Generally I think folks will be thinking of two things when they say they want to retrieve the SSP: the administrative site used to configure shared services or the specific location of a shared service such as a shared web service. So I'll provide both in code. The first code snip shows how to retrieve the Administrative Site. You must first retrieve the Shared Resource Provider using code from the original blog post and then use that with code snip below. PropertyInfo administrativeSiteIdProperty = srp.GetType().GetProperty("AdministrationSiteId"); Guid administrativeSiteId = (Guid)administrativeSiteIdProperty.GetValue(srp, null); SPSite adminSite = new SPSite(administrativeSiteId); Console.WriteLine("Administrative site: " + adminSite.Url); The code for retrieving the URL of a shared service such as a shared web service is a little more complex. Here is snip... static int GetSharedServicePort(ServerContext context, SPFarm farm) { //our first step is to get a ServerFarm object, to do this, we must first obtain the ServerFarm type Type serverType = System.Reflection.Assembly.GetAssembly(context.GetType()).GetType("Microsoft.Office.Server.Administration.ServerFarm"); //now that we have a ServerFarm type, we can obtain an instance from the database object serverFarm = farm.GetObject(string.Empty, farm.Id, serverType); PropertyInfo sharedWebServiceSecureBindingProperty = serverFarm.GetType().GetProperty("SharedWebServiceSecureBinding"); object sharedWebServiceSecureBinding = sharedWebServiceSecureBindingProperty.GetValue(serverFarm, null); PropertyInfo portProperty = sharedWebServiceSecureBinding.GetType().GetProperty("Port"); int port = (int)portProperty.GetValue(sharedWebServiceSecureBinding, null); return (port); } int sharedServicePort = GetSharedServicePort(context, site.WebApplication.Farm); PropertyInfo nameProperty = srp.GetType().GetProperty("Name"); string sspName = (string)nameProperty.GetValue(srp, null); foreach (SPService service in site.WebApplication.Farm.Services) { if (service.DisplayName == "Excel Calculation Services") { foreach (SPServiceInstance instance in service.Instances) { UriBuilder blder = new UriBuilder(); blder.Port = sharedServicePort; blder.Host = instance.Server.Address; blder.Path = "/" + sspName + "/ExcelCalculationServer/"; Console.WriteLine("Excel web services: " + blder.Uri.ToString()); } } }


wow commented on Thursday, 30-Oct-2008
lc Welcome to wow gold. We are a world class wow powerleveling store online. We supply cheap wow gold to our loyal and World of Warcraft goldreliable customers. you canwow power leveling buy really cheapwow gold wow gold here. We havewow power leveling mass available stock ofwow power leveling wow gold onwow power leveling most of the servers, sowow power leveling that we can do aRolex really instant way of wow power levelingwow gold wow golddelivery. We canwow powerleveling deliver yourwow powerleveling wow gold on powerlevelingthe order powerlevelingin a short cheap wow goldwhile. We have beencheap wow gold an ebay powerpower leveling seller and paypalpower leveling confirmed seller rolex replicaof wow gold for replica rolexyears.So it ischeap wow gold securest and safestRunescape Gold to buy RuneScape Money wow gold from Watches Rolexus. Don’t be Rolex Watchesirresolute! We arewow gold hoping to servegold wow you and helpingWatch Rolex you to have a wonderfulRolex Watch wow life! We arers gold ready now, how about you?


car wash commented on Monday, 29-Dec-2008
Name:
URL:
Email:
Comments:

CAPTCHA Image Validation