Second Life of a Hungarian SharePoint Geek

August 8, 2011

Client Object Model Internals – Theory of the Server Side

Filed under: Managed Client OM, SP 2010 — Tags: , — Peter Holpar @ 20:07

Since May – when I published some of the results of my investigation about the client object model (see that posts here and here) – I’m planning to continue that series. BTW, if you haven’t read that posts yet, I really encourage you to do so before reading further as these posts contain some basic information I won’t repeat here and now.

As I wrote in the former parts, the client object model is an exciting new feature of SharePoint 2010, but unfortunately it is still rather undocumented. If someone would like to understand its internals the best thing is to browse the methods of the building classes using Reflector and to experiment with the classes.

In this part I try to give you a general overview of the main server- side components of the architecture then continue with the client-side classes in the following post. Introducing these components hopefully will help you understand the next, more practical part.

Let’s start with the server-side components that is mainly implemented in the Microsoft.SharePoint.Client.ServerRuntime assembly.

The entry point of the client OM server runtime is a WCF service,  Client.svc (14\ISAPI folder) with service class Microsoft.SharePoint.Client.ClientRequestService. This class implements the IClientRequestService interface that has a single method: ProcessQuery that has a single Stream parameter and its return type is also a Stream. These streams represents the request and response streams sent by and sent back to the client. The host factory class for the service is the Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, although this fact has really no significance in this post.

The ProcessQuery method of ClientRequestService simply creates a new instance of ClientRequestServiceImpl, and call its ProcessQuery method.

What is much more important for us is the abstract ClientServiceHost class that is created in the ProcessQuery method mentioned above. This class servers as a base class for two additional classes: SimpleClientServiceHost and SPClientServiceHost (this one can be found in the Microsoft.SharePoint.Client assembly).

The static CreateServiceHost method creates the ClientServiceHost instance used in ProcessQuery. This process is a bit tricky, namely it first iterates through the available service host types (more about them later), then it creates a chain from that client host type instances using the Initialize method of the ClientServiceHost class. This method has a parameter of  a ClientServiceHost type, and the method is virtual on the base class, but overridden in the subclasses.

Now back to the point where CreateServiceHost knows from which service host type should it work with. It turns out if we check the GetServiceHostTypes method. It checks the web.config of the SharePoint application and looks for the ClientServiceHostElement instances in the HostTypes  property (ClientServiceHostElementColection)  in a configuration section at microsoft.sharepoint.client/serverRuntime of type ClientServiceServerRuntimeSection and create a list of them.

This section look like this by default:

  1. <microsoft.sharepoint.client>
  2.   <serverRuntime>
  3.     <hostTypes>
  4.       <add type="Microsoft.SharePoint.Client.SPClientServiceHost, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  5.     </hostTypes>
  6.   </serverRuntime>
  7. </microsoft.sharepoint.client>

Although there is only a single item by default, you should know that later types in the config sections are inserted to the beginning of the list by the GetServiceHostTypes so they will be at the beginning of the chain created by CreateServiceHost.

The ClientServiceServerRuntimeSection has another property ProxyAssemblies (AssemblyCollection) that will be described next.

Another abstract method of the ClientServiceHost class is the ProxyAssemblyNames. As SPClientServiceHost implements it, it first aggregates all proxy assembly names of the other client service hosts instances from the service host chain, then checks for SPClientCallableProxyLibrary instances of the current web application (SPWebApplication) through its ProxyLibraries property, and adds the value in their AssemblyName property to the proxy list.

Remark: The SimpleClientServiceHost class contains a similar but simpler code for ProxyAssemblyNames, as it collects the proxy assembly list from the ProxyAssemblies  property of the ClientServiceServerRuntimeSection. The class itself has not really much additional value and as far as I saw not used by the system. However it will be great importance when we try to inject our custom extensions in the forthcoming part.

Near the end of the SPWebApplication constructor class (Microsoft.SharePoint.Administration.SPWebApplication) we find the following lines:

public SPWebApplication(string name, SPWebService service, SPApplicationPool applicationPool);

this.m_ClientCallableSettings = new SPClientCallableSettings();
this.m_ClientCallableSettings.AddDefaultProxyLibraries();

The AddDefaultProxyLibraries method adds a SPClientCallableProxyLibrary referring to the Microsoft.SharePoint.Proxy assembly to the list of proxy libraries. It means this assembly will be automatically part of the proxy assembly list.

The Microsoft.SharePoint.Proxy assembly can be found at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\CONFIG\BIN in the case of a typical SharePoint installation. It worth checking the classes in this assembly since we can learn a lot about the working of the client OM.

The assembly contains two main types of classes: proxies and converters, with class names suffixed _Proxy and _Converter accordingly. The classes are ordered into namespaces that correspond to the namespace of the “original” classes. The proxies are subclasses of the _ServerProxy class and decorated with the ServerProxyAttribute, the converters are subclasses of _DataConverter class and decorated with the DataConverter attribute (both of these base classes can be found in Microsoft.SharePoint.Client.ServerRuntime assembly). Both of these attributes have two parameter, the first one (TargetType) is the type of the “original” server class, the second one (TargetTypeId) is a string representing a Guid that matches the same-named property of the ClientCallableTypeAttribute of the corresponding server class.

The proxy classes contains code to route static / instance level property get / set requests and method calls from the client to server objects, the converters are responsible to convert XML serialized object description received as part of the client request to server objects, and vice versa, JSON serialize the response objects to the client using the Microsoft.SharePoint.Client.JsonWriter class (Microsoft.SharePoint.Client.ServerRuntime assembly again).

Methods of proxies and converters have a common parameter, it is the ProxyContext object (Microsoft.SharePoint.Client.ServerRuntime assembly).

Where of that ProxyContent comes from? That is really the missing piece of the puzzle we need to understand the basics of the server side of the client OM.

The ProcessQuery method of ClientRequestServiceImpl we discussed earlier creates a ClientMethodsProcessor and calls its Process method. The constructor of the ClientMethodsProcessor class creates a private instance of ProxyContext and uses that instance to route client requests to the server runtime.

The Process meClientCallableObjectFactory(ServerTypeId="740c6a0b-85e2-48a0-a494-e0f1759d4aa7")]thod iterates through the subnodes of the ObjectPaths node in the request to populate the object map. Then calls the ProcessStatements method with the Actions node as a parameter. This method processes statements one-by-one using the ProcessOne method. The ProcessOne method is really a large if-else if block with several branches for different action types based on the Name of the Action subnodes (like ObjectPath, SetProperty or StaticMethod).

There are separate methods for each action types, however the general working is quite similar. For example, SetProperty action is handled by the ProcessSetProperty method. In this method we first get the object the property setting should operate on from the object map by passing the value of the ObjectPathId attribute of the current node to the GetObjectFromObjectPathId method. Next, we get the correct proxy for this object using the GetServerProxy method, and calls its SetProperty method passing a reference to the object to be modified, the name of the property and the Parameter node (that contains in this case the new value of the property) and the proxy context. The GetServerProxy method calls the static GetServerMethod class of the ProxyMap class. This method has two “versions”, one locates the proxy class based on the .NET Type of the server class, the other does the same based on the Guid value set for the server and proxy class (see the ServerProxyAttribute and ClientCallableTypeAttribute attributes discussed earlier). BTW, ProxyMap has two overloads for the GetDataConverter method as well that operate the same way to return the right _DataConverter instance. All of these methods receives a ClientServiceHost parameter as well.

Where the ProxyMap object knows which converter / proxy it should return? In its Init method (called by its EnsureInited method, called by both GetServerProxy and GetDataConverter methods) the ClientServiceHost instance and it iterates through all of the proxy assemblies looking for types decorated with either ServerProxyAttribute or DataConverterAttribute. Two dictionaries created for both proxies and converters, one is Type-based, the other is – I assume you already guessed it – Guid based.

The Init method also looks for the ClientCallableObjectFactoryMapAttribute attribute on the proxy assemblies, and if one found, the GuidType pair defined in the IdType properties of the attribute is added to a fifth dictionary. For example, the Microsoft.SharePoint.Proxy assembly is decorated with this attribute:

ClientCallableObjectFactoryMap("740c6a0b-85e2-48a0-a494-e0f1759d4aa7", typeof(SPObjectFactory)

Microsoft.SharePoint.SPObjectFactory is a class inherited from ClientCallableObjectFactory and defined in the Microsoft.SharePoint assembly and decorated as:

ClientCallableObjectFactory(ServerTypeId="740c6a0b-85e2-48a0-a494-e0f1759d4aa7")

If you check its GetObjectById method you can see how a SharePoint server object is returned based on a kind of “path” constructed from short names and IDs of objects. If you read my former posts carefully, you are probably already familiar with this kind of notation (see the ObjectIdentity in the former posts).

Methods of the ClientMethodsProcessor class (like the ProcessSetProperty method) can access the factory object through the following chain of calls:

GetObjectFromObjectPathId – GetObjectFromObjectPath – GetObjectFactory (ClientMethodsProcessor) – CreateObjectFactory (ProxyMap)

The results are written back to the client again a JsonWriter class in the Process method of ClientMethodsProcessor.

Although the server side internals of the client OM would definitely deserve more space, I hope this short introduction will help you to better understand how things work together to proxy server side functionality for the client side code. In the next part of the series I plan to show you how the client side invokes this infrastructure and how it interprets the response of the server.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: