Saturday, May 18, 2013

Problems with uninstalling external dlls from the GAC with wsp retract in Sharepoint and ways to solve it

In this post there won’t be technical solutions. Instead it will be kind of analysis of the problems which we currently have in regular wsp deployment process and how they can be solved. So what is the actual problem?

On the same Sharepoint farm there may be many web applications implemented by single or different vendors. These web applications may have own set of wsp packages, single web app may have several packages deployed to it or it may use functionality defined in wsp packages deployed globally:

image

In the example above there are 2 web apps, both use wsp 1 and wsp 2, which install the same dll (with the same fully strong name) into the GAC (this dll is located inside wsp and defined in their manifest.xml files).

Once we retract e.g. wsp 2, dll is also uninstalled from GAC, and functionality defined in wsp 1 will be most probably broken (wsp 1 and wsp 2 may be implemented by completely different vendors and don’t know about each other):

image

How this situation can be improved? The first idea which comes to the mind is to implement reference counter for external dlls. Note, that we are talking about external dlls, which are installed into the GAC with wsp. Each wsp may have own dll with code compiled for its artifacts. This is different story: such dlls should be always uninstalled from the GAC when parent wsp is retracted. During retracting of wsp the process should check this counter and uninstall dll only when it becomes 0 (similar to the reference counters in old COM technology):

image

When wsp is retracted reference count is decremented:

image

When wsp 2 is deployed again, it may update existing dll or leave it as is. 1st way is preferrable from my point of view. It will override existing dll only if it contains the same fully qualified strong name, which only should happen when dlls are the same. So it won’t break wsp 1, but with updating we will ensure that new dll will always be installed in GAC during wsp installation.

Looks easy, but what is the cost which we should pay for this? The problem is that in real life wsp deployment may fail as well as wsp retracting. And the main problem which I see in these kind of solutions is reliability of wsp deployments. E.g. suppose that retracting of wsp 2 failed:

image

What we can say about reference count in this case? Nothing definite. Ref count depends on further actions of administrator. He may remove failed wsp from solution store, add it back and deploy with –force key. In this case if we would decrement ref during retracting error, we need to increment it back, so at the end we would have ref count = 2. However administrator may always deploy wsp package with –force attribute, and if he will do it e.g. with wsp 1 which is correctly deployed already, ref count should not be incremented. As you can see it is almost impossible to design reliable wsp deployment process with reference counts.

The simpler solution in this case from my point of view would be adding one more switch parameter to the retractsolution stsadm command and Uninstall-SPSolution powershell cmdlet (e.g. LeaveInGAC), which when set will mean to leave external dlls in GAC. I.e. by default these commands will still uninstall dlls from GAC in order to keep backward compatibility for the deployment process, but also it will be possible to leave dlls in GAC if needed. It should be clearly noticed that this parameter only affects external dlls, not own wsp dlls which should be retracted always regardless of this parameters. What will you say about such feature?

Update 2013-05-20: this post become interesting for developers. Here are some ideas of how you may try to handle this problem without requesting new switch param in Uninstall-SPSolution powershell cmdlet:

  1. From me: Include external components as a source code to your wsp and use own copy of this component. As other web applications will continue to use original component when you will retract your wsp, it won’t affect them. The problem with this approach is that source code is not often available, not all components which we use are opensource.
  2. From avishnyakov: Include external components as dll into separate wsp, which will contain only these dlls. Advantage of this approach is that such wsp is most probably won’t be retracted so often as regular wsps with Sharepoint artifacts. So it is safer than including assemblies into regular wsp. However at some moment administrator may deploy wsp from other supplier, which may have the same dll as in your common wsp. After retracting of their wsp your functionality will be broken.
  3. From Jarno Leikas: instead of full trust assemblies which are installed into the GAC, install 3rd party assemblies to the local bin folders of the application virtual directory. It will also require code access security section in manifest.xml (see Deploy DLL to Local app folder (Bin folder) in sharepoint 2010). This is the MS recommended way btw now. However be ready for additional work and problems with this approach. Personally I try to avoid changing content inside virtual folders (custom http application in global.asax, custom web.config modifications, even App_GlobalResources if it require additional actions from our side), because in most cases it caused some problems afterwards.

If you have other ideas, you can share them in comments.

5 comments:

  1. Hi, Alex!
    I was thinking about this problem and ILMerge (http://research.microsoft.com/en-us/people/mbarnett/ILMerge.aspx) come to my mind.

    What about combing all external assemblies into your SharePoint assembly (or custom assembly, which will be deployed as part of .wsp) using above tool?

    ReplyDelete
  2. Hello Kai,
    interesting, but not sure that it is possible. According to their information: "if the primary assembly has a strong name, and a .snk file is provided, then the target assembly is re-signed with the specified key so that it also has a strong name". I.e. you may use it for further referencing in other projects, but not for combing them to your wsp (if you compiled your assembly against real NHibernate for example, it won't work if afterwards you will merge NHibernate to your dll, because strong name of its assemblies will be changed).

    ReplyDelete
    Replies
    1. ILMerge re-sign assemblies (if /keyfile provided) because new assembly is treated as single assembly. But it take care of resolving references.
      I used it for merging Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll into single exe file and all work fine on machine where sharepoint is not installed. Consider reflector screenshots before and after using ILMerge:
      http://6g6.eu/sih0-11087.jpg
      http://6g6.eu/sih0-2705.jpg

      I think you should try it.

      Delete
    2. hm, that's interesting. Probably I will really try it at some point. Thank you for idea.

      Delete
  3. I have to agree with the second approach: Having an "external DLLs WSP". Of course you will need to make sure that all vendors use this WSP to deploy external DLLs and hence don't retract existing DLLs - but for that you have rules and guidelines. Another option I sometimes see is one WSP per external DLL (e.g. Telerik.UI.wsp). You will always have the problem that some vendor may delete stuff from the GAC which he is not supposed to, he has full trust on the server so that mistake is easy to make.

    ReplyDelete