Hacking Sitecore and Life one pipeline at a time!

Dynamic Sites Manager 1.9 Released

dynamic-sites

Version 1.9 Released

Today, Sitecore officially released Sitecore 8.2 Initial Release (Revision 160729). As such, I spent some time updating Dynamic Sites Manager to support Sitecore 8.2 in the most recent release.  In addition, earlier releases support Sitecore 8.1, 8.0, 7.5, 7.2, and 7.0.  With the release, the following versions will continue to be supported by future releases:

  • Version 1.7 – Sitecore 7.5
  • Version 1.8 – Sitecore 8.0 & Sitecore 8.1
  • Version 1.9 – Sitecore 8.2

Changes in Version 1.9

  • Compiled for Sitecore 8.2 Initial Release – 160729
  • Updated SiteCache to ensure compatibility with Cache refactor.
  • Removed Preview Button Hot Fix (has been corrected by Sitecore)
  • Removed Unused Code
  • Updated for .NET 4.5.2

Get Dynamic Sites Now

What Is This Thing?

I’m glad you asked! I realized tonight I’ve never blogged about this module.  So figure now is a good time as any! About two years I got really frustrated with the way that the community decided to implement multi-site solutions and ad-hoc sites.  What I realized was that most developers were overriding the SiteResolver in the httpBeginRequest pipeline, among other really bad habits.  I saw all kinds of things wrong with this:

  • Shared caches settings
  • Sitecore wasn’t truly aware of multiple sites
  • Preview and Page Editor (now Experience Editor) functionality was severely hampered.
  • New sites, if done by config, needed an IIS reset.

So I decided to do something about it.  The end result is Dynamic Sites Manager.

Module Walkthrough

To start off the walkthrough, I want to set the landscape. The magic to Sitecore is understanding the many different design patterns that have been implemented. With this in mind, I asked myself, how would Sitecore do multi-site management better than just the barebones support provided through configs?  I started with a review of the compiled Sitecore config. I found the following section:

site manager config orig

I realized suddenly that the design pattern here was the Provider pattern of having multiple providers with a default provider. As of Sitecore 8.0, Sitecore introduced the sitecore site provider, shown above. This was ground breaking because I took as Sitecore’s nod that they understand having multiple providers being managed might actually be something good.  That is why you see a list of providers under the sitecore provider.

However, that didn’t provide me with the EXACT functionality that I was looking for completely. Plus, I created this module before Sitecore created the Sitecore Site Provider. What I needed, was a provider that would provide my Dynamic Sites, as well as all of the other important sites that sitecore has. I developed the SwitcherSiteProvider which takes into consideration ALL of the other defined Site Providers, compiles a list, orders it (because site order actually matters). The result is what you see here:

site manager config

Note that defaultProvider has been set to switcher. This is where the actual implementation of the module exists. Past this, everything that Sitecore does with respect to sites is done using basic API functionality. No other customizations, or pipelines were affected to support this change.

Making It Dynamic

The only business requirement that I gave myself was that I wanted to control the sites through Sitecore, within the content editor.  So, I created a series of templates in Sitecore to support his functionality. I also didn’t want to hamper the vision other developers may have for how to control sites. So I made it as flexible as possible.

sites listHere we have the home nodes for the three sites that Sitecore is managing. I know… I’m real creative!. But each of these sites, have a different domain/hostname. So i need to have an area where I can define these site settings.

Dynamic Sites Manager creates a home in the System/Modules folder. that you see below here.

site config itemsThe Dynamic Site Settings item is the main configuration item where you can modify a few various settings about the module as a whole.

The Site definitions are where you define as many sites as you’d like. I know of one implementation using this module that is supporting over 600+ multi-sites! (Maybe I should bucket this!)

 

 

 

 

So let’s take a look at what one of these definitions look like:

site definition

  1. Hostname – Here you can define your hostname.  Leaving it blank acts as a default site. This is where order matters.  You can also use wildcard domains and subdomains, ordering appropriately to provide a fallback site mechanism (if you wanted to).
  2. This is the Home Item of where your content item for the root home item of the site lives. This can live ANYWHERE in the /sitecore/content node.
  3. Language – You can set a default language to your site if you’re running multilingual sites.
  4. Database – Select the database that you want your visitors to see.  Typically, “web” is what you’ll use, but for very large implementations, you may have a different production database for publishing.
  5. Checkboxs – Here are a few check mark boxes to turn on or off commonly used settings.
    1. The Inherit allows you to inherit settings from another site. This can help modify a large number of sites with similar settings.
  6. Last but not least, Custom Properties. One of the cool things that Sitecore did was that the Site object for Sitecore is actually just a dictionary object. You can add as many custom properties to a site as you’d like, and reference Sitecore API’s for accessing the values.  Helpful for API keys, encryption strings, etc.

Looking Under the Covers

So now that we’ve covered the basic implementation pattern and covered how I collect my site data, how do I make this work? The first thing on my mind was that I wanted the sites to be quick and efficient.  So I came up with the following technical requirements:

  1. If Sitecore had to compile the list of sites on every request, that’s a lot of overhead.  I wanted to make sure I cached the compiled list of sites.
  2. In addition, I wanted action on Sitecore’s knowledge of sites not to occur on visits, but when Site definitions are modified, created, or deleted.
  3. Lastly, I wanted sites to be immediately available, and not rely on a IIS reset.

Caching

Caching in Sitecore is actually pretty simple. Here I am using the Cache API of CustomCache to create my own cache structure. This cache is storing a complex type object of SiteCacheItem.

using System.Collections;
using Sitecore.Caching;
using Sitecore.SharedSource.DynamicSites.Utilities;
using Sitecore.Sites;

namespace Sitecore.SharedSource.DynamicSites.Caching
{
    internal class SiteCache : CustomCache
    {
        public SiteCache(long maxSize) : base(DynamicSiteSettings.CacheKey, maxSize)
        {
        }

        //AddSite
        public void AddSite(Site siteItem)
        {
            if (ContainsSite(siteItem))
            {
                RemoveSite(siteItem);
            }

            var cacheItem = new SiteCacheItem(siteItem);
            InnerCache.Add(siteItem.Name, cacheItem);
        }

        //GetSite
        public Site GetSite(string name)
        {
            if (!ContainsSite(name)) return null;
            return (Site)InnerCache.GetValue(name);
        }

        private void RemoveSite(Site siteItem)
        {
            if (ContainsSite(siteItem))
            {
                //Refresh Information
                InnerCache.Remove(siteItem.Name);
            }
        }

        //GetAllSites
        public SiteCollection GetAllSites()
        {
            return GetAllSites(InnerCache.GetCacheKeys());
        }

        //ContainsSite
        public bool ContainsSite(Site siteItem)
        {
            return InnerCache.ContainsKey(siteItem.Name);
        }

        public bool ContainsSite(string name)
        {
            return InnerCache.ContainsKey(name);
        }

        // Count
        public int Count()
        {
            return InnerCache.Count;
        }

        public SiteCollection GetAllSites([NotNull] IEnumerable orderedList)
        {
            var collection = new SiteCollection();

            foreach (string siteName in orderedList)
            {
                collection.Add(GetSite(siteName));
            }

            return collection;

        }
    }
}

The end result is that all of the sites that Sitecore manages are being kept in a Sitecore Cache, and you can see that cache on the Cache.aspx page.

site cache running

In the image above, Dynamic Sites is storing a total of 13 sites. This includes all of the system sites that Sitecore needs to run the Content Editor and Dashboard.

Updating Sitecore On Site Definition Changes

This is actually pretty easy. Basically, I created a series of Item Event Handlers that are configured monitoring the Site Definitions. Any changes to those items will recompile the site definition and update the Dynamic Sites cache.

event handlers

There’s a bunch of code underlying here that does a lot of the work. Feel free to look at the source. (DISCLAIMER: The code is not perfect. I could probably restructure the Manager class a bit. It ended up being a catch all class.)

No IIS Resets

It took me a while to figure out how to do this. The Sitecore SiteContextFactory stores a static variable that under normal circumstances, would only reset during IIS resets, config resets, etc.

namespace Sitecore.Sites
{
  public static class SiteContextFactory
  {
<strong>    private static readonly Lazy<BaseSiteContextFactory> Instance = new Lazy<BaseSiteContextFactory>((Func<BaseSiteContextFactory>) (() => (BaseSiteContextFactory) ServiceProviderServiceExtensions.GetRequiredService<BaseSiteContextFactory>(ServiceLocator.ServiceProvider)));
</strong>
    public static List<SiteInfo> Sites
    {
      get
      {
        return SiteContextFactory.Instance.Value.GetSites();
      }
    }

    public static SiteContext GetSiteContext(string name)
    {
      Assert.ArgumentNotNullOrEmpty(name, "name");
      return SiteContextFactory.Instance.Value.GetSiteContext(name);
    }

    public static SiteContext GetSiteContext(string hostName, string fullPath)
    {
      Assert.ArgumentNotNull((object) hostName, "hostName");
      Assert.ArgumentNotNull((object) fullPath, "fullPath");
      return SiteContextFactory.Instance.Value.GetSiteContext(hostName, fullPath);
    }

    public static SiteContext GetSiteContext(string hostName, string fullPath, int portNumber)
    {
      Assert.ArgumentNotNull((object) hostName, "hostName");
      Assert.ArgumentNotNull((object) fullPath, "fullPath");
      return SiteContextFactory.Instance.Value.GetSiteContext(hostName, fullPath, portNumber);
    }

    public static SiteContext GetSiteContextFromFile(string filename)
    {
      Assert.ArgumentNotNullOrEmpty(filename, "filename");
      return SiteContextFactory.Instance.Value.GetSiteContextFromFile(filename);
    }

    public static SiteInfo GetSiteInfo(string name)
    {
      Assert.ArgumentNotNullOrEmpty(name, "name");
      return SiteContextFactory.Instance.Value.GetSiteInfo(name);
    }

    public static string[] GetSiteNames()
    {
      return SiteContextFactory.Instance.Value.GetSiteNames();
    }

<strong>    public static void Reset()
    {
      SiteContextFactory.Instance.Value.Reset();
    }
</strong>  }
}

The Instance the variable at the top is what Sitecore uses to store information related to all of the sites running on sitecore.  But at the bottom, they added in a public Reset() method that actually resets this static variable.

Utilizing this method, is how I’m able to manipulate site configurations on-the-fly without have to reset IIS.

But Wait! There’s More!

As you can tell, there’s actually quite a bit of stuff going on under the covers with this module, then I am able to fit into one blog post.  But don’t take my word for it. Feel free to browse through the code repo, make comments, and help me make this an even better product.

If you need a multi-site management module for your Sitecore implementation, please consider using my module. And if you do, and need support, please do not hesitate to drop me a line!

Thanks for reading!

Tagged as: , , ,

Categorised in: Sitecore, Technology, Uncategorized

1 Response »

Trackbacks

  1. Dynamic Sites Manager 1.9.1 – Update | Sitecore Hacker

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 1,237 other subscribers

Blog Stats

  • 138,855 hits
Follow Sitecore Hacker on WordPress.com
Sitecore® and Own the Experience® are registered trademarks of Sitecore Corporation A/S in the U.S. and other countries.  This website is independent of Sitecore Corporation, and is not affiliated with or sponsored by Sitecore Corporation.