August 11, 2016

Sitecore Config Transforms - Use Replace instead of Remove

August 11, 2016

Sitecore Config Transforms - Use Replace instead of Remove

I love config transforms.  I love being able to have a single file that can be transformed for any number of my build destinations.  It's much, much better than trying to maintain separate config files for each destination.  Many times, I have config files that should *only* exist on either a CM or CD server.  Originally, I used the "Remove" method to remove the entire contents of a config file like so:


While it doesn't technically break anything, it leads to errors in the logs on Sitecore startup that complain about "Reader is in incorrect state":


The fix is actually really easy.  Instead of removing the entire "sitecore" node in the file, just replace it:

Now Sitecore be all happy.


June 28, 2016

Using Config Transforms to Manage Sitecore Indexes

June 28, 2016

Using Config Transforms to Manage Sitecore Indexes

In my previous post, I talked about setting up your Solr indexes to use the "SwitchOnRebuildSolrSearchIndex" index type to avoid index downtime.  In that post, I mentioned that I was using Config Transforms in order to manage the configuration settings across my different environments.  Here's a quick example of how to set up an index for the different environments:

The 'Default' or 'Standard' Index configuration that I have in my solution:

What's going on?:

  • I have separate configurations for "Web" vs "Master" databases. This configuration is for the "Web index. 
  •  I'm using the 'onPublishEndAsync' indexing strategy since this is my "Web" database.  This tells Solt to update the index when a publish is complete.

I then "Add Transform" in Visual Studio to create the specific versions for the other environments.

Note: In order to add the tranformations, you have to have separate solution configurations configured for each environment.

I'll skip all my local/dev/qa/stage environments and just show what I've got for production, but note that each environment can and usually *should* have its own transformation.

Production CM server configuration:

What's going on?:

  • I tell the transform to look for all indexes in the parent config file and change the index type to "SwitchOnRebuildSolrSearchIndex."  I like to keep only one index per configuration file, but if you had multiple indexes defined, this will change all of them.
  • I'm inserting the "rebuildcore" parameter into the index configuration.  This is required so that Sitecore knows which Solr core to swap to after the rebuild is complete.  It's also very important that it goes in in the third position as Sitecore expects it there.

When the transformation is applied, it looks like this:

For the Production CD server, I have the following transform:

What's going on?:

  • As with the CM server, I'm changing the index type to "SwitchOnRebuildSolrSearchIndex", but as you will see with the next point, it doesn't really matter.
  • We are setting the index strategy to "manual."  Our Solr indexes are centrally located on another server and we want the indexing to only happen once.  The rebuilding of the indexes are controlled by the actions of the CM server.
Setting up different configurations based on environment is easily accomplished using config transforms.  It keeps your configuration files clean and easy to manage.


March 26, 2016

Solr in Production for Sitecore - SwitchOnRebuildSolrSearchIndex

March 26, 2016

Solr in Production for Sitecore - SwitchOnRebuildSolrSearchIndex

The configuration for running Solr in your local dev environment is likely going to be different than running it in the production environment.  Oftentimes, your local indexes will be configured as a type of "Sitecore.ContentSearch.SolrProvider.SolrSearchIndex" while using the "syncMaster" indexing strategy.  This strategy rebuilds the index after data changes are saved in the master database.  It works well locally if you want to avoid having to publish to the Web database to see your indexes update, but this doesn't work in a production environment where your Content Delivery (CD) servers don't have access to the master database.  Not to mention, you don't want to be indexing content that hasn't been published yet.

In a production environment, it is much more common to use the "SwitchOnRebuildSolrSearchIndex" index type while using the "onPublishEndAsync" indexing strategy.  This type keeps your indexes up and running even when they are rebuilding.  This is accomplished by building to a secondary index and when the rebuild is complete, Sitecore uses that index as the primary index.  The rebuild is triggered when items are published to the 'Web' database.

Note: I should also note here that the CD servers should actually have their indexing strategies set to "manual" due to the Content Management (CM) server controlling when the central Solr indexes are being rebuilt .  More on this below.

Since I have different strategies defined for the different environments and have different build configurations in my Visual Studio solution, I utilized config transforms to allow the correct type and strategy to be used in the different environments.  I'll detail these transforms in a future blog post.

For this example, my local development instance has a custom index used for indexing products defined as follows:

          <index id="product_search_index" type="Sitecore.ContentSearch.SolrProvider.SolrSearchIndex, Sitecore.ContentSearch.SolrProvider">
            <param desc="name">$(id)</param>
            <param desc="core">$(id)</param>
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
            <configuration ref="contentSearch/indexConfigurations/productSearchIndexConfiguration" />
            <strategies hint="list:AddStrategy">
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/syncMaster" />
            </strategies>
            <commitPolicyExecutor type="Sitecore.ContentSearch.CommitPolicyExecutor, Sitecore.ContentSearch">
              <policies hint="list:AddCommitPolicy">
                <policy type="Sitecore.ContentSearch.TimeIntervalCommitPolicy, Sitecore.ContentSearch" />
              </policies>
            </commitPolicyExecutor>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
                <Database>web</Database>
                <Root>/sitecore/Content/Products</Root>
              </crawler>
            </locations>
          </index>

My local dev instance is a CM/CD combined instance and as you can see in my config, it uses the "Sitecore.ContentSearch.SolrProvider.SolrSearchIndex" type, with the "syncMaster" strategy on the "master" database.  I can now update items and have them immediately available in my index without having to go through the trouble of publishing.

When deploying to a dev, stage or prod CM server, I want to ensure that my products index is always available and only contains data that has been published.  We can accomplish this with the "SwitchOnRebuildSolrSearchIndex" index type and the "onPublishEndAsync" indexing strategy.  I have the following defined for a dedicated CM server:

          <index id="product_search_index" type="Sitecore.ContentSearch.SolrProvider.SwitchOnRebuildSorlSearchIndex, Sitecore.ContentSearch">
            <param desc="name">$(id)</param>
            <param desc="core">$(id)</param>
            <param desc="rebuildcore">$(id)_sec</param>
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
            <configuration ref="contentSearch/indexConfigurations/productSearchIndexConfiguration" />
            <strategies hint="list:AddStrategy">
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsync" />
            </strategies>
            <commitPolicyExecutor type="Sitecore.ContentSearch.CommitPolicyExecutor, Sitecore.ContentSearch">
              <policies hint="list:AddCommitPolicy">
                <policy type="Sitecore.ContentSearch.TimeIntervalCommitPolicy, Sitecore.ContentSearch" />
              </policies>
            </commitPolicyExecutor>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
                <Database>web</Database>
                <Root>/sitecore/Content/Products</Root>
              </crawler>
            </locations>
          </index>

Note: These two index configurations are shown to illustrate the process for both switching and utilizing the onPublishEndAsync strategy.  In an actual production environment, you should separate your indexes so that you have one for the master database and one for the web database.

The changes of note here are:
  1. The index type was changed to "Sitecore.ContentSearch.SolrProvider.SwitchOnRebuildSorlSearchIndex"
  2. We added a new parameter named "rebuildcore".  This tells Sitecore the name of the secondary core to utilize while building.  Note that you need to have an additional core created to facilitate this.
  3. The strategy has been changed to "onPublishEndAsync"
  4. The "Database" we are indexing is the "Web" database.
This is a configuration for a CM server.  In order to keep the CD servers from kicking off a rebuild of the indexes, you should set their index strategy type to "contentSearch/indexConfigurations/indexUpdateStrategies/manual".

Now, each time you publish an item, the index will be automatically updated and you don't have to worry about the rebuild time taking it offline as Sitecore will be using the secondary core you defined.

Additional Reading

December 23, 2015

Could not create instance of type: Sitecore.ContentSearch.LuceneProvider.SwitchOnRebuildLuceneIndex. No matching constructor was found.

December 23, 2015

Could not create instance of type: Sitecore.ContentSearch.LuceneProvider.SwitchOnRebuildLuceneIndex. No matching constructor was found.

Okay, that's a long post title.

While configuring a new Sitecore 8.1-Update 1 site that is using Lucene for indexing, I ran into an issue while changing all the indexes to utilize SwitchOnRebuildLuceneIndex.  We do this to allow the system to switch indexes while the new index is rebuilding so that we don't temporarily lose access to an index while it is being rebuilt.  Configuration-wise, we do this by utilizing config transforms.

After building and deploying the solution locally, I was seeing the following nested exception:

Exception: Sitecore.Exceptions.ConfigurationException
Message: Could not create instance of type: Sitecore.ContentSearch.LuceneProvider.SwitchOnRebuildLuceneIndex. No matching constructor was found.
Source: Sitecore.Kernel
   at Sitecore.Configuration.Factory.CreateFromTypeName(XmlNode configNode, String[] parameters, Boolean assert)
   at Sitecore.Configuration.Factory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert, IFactoryHelper helper)
   at Sitecore.Configuration.Factory.GetInnerObject(XmlNode paramNode, String[] parameters, Boolean assert)
   at Sitecore.Configuration.Factory.AssignProperties(XmlNode configNode, String[] parameters, Object obj, Boolean assert, Boolean deferred, IFactoryHelper helper)
   at Sitecore.Configuration.Factory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert, IFactoryHelper helper)
   at Sitecore.Configuration.Factory.CreateObject(String configPath, String[] parameters, Boolean assert)
   at Sitecore.ContentSearch.ContentSearchManager.get_SearchConfiguration()
   at Sitecore.ContentSearch.ContentSearchManager.GetIndex(String name)
   at Sitecore.Marketing.Search.CampaignDefinitionSearchProvider..ctor(String indexName)

So I had actually recently seen a very similar error while configuring some Solr indexes for a different client.  From that troubleshooting experience, I knew I could crack open the Sitecore.ContentSearch.LuceneProvider assembly to look for the constructor that was having issues.

I could see that the SwitchOnRebuildLuceneIndex constructor accepts three parameters: name, folder and propertyStore.

Upon inspecting my transforms, I came across the Sitecore.ContentSearch.Lucene.Index.Analytics.config file.  This config file specifies the sitecore_analytics_index index, and has four parameters: name, folder, propertyStore and group.  This is more than the SwitchOnRebuildLuceneIndex constructor is expecting.

Workaround/Fix

Don't use SwitchOnRebuildLuceneIndex on the sitecore_analytics_index!  Just leave it as the 'standard' type of Sitecore.ContentSearch.LuceneProvider.LuceneIndex.

As to why this index is different, Patrick notes that the crawlers indexing the analytics data are observers and there is no need to have a secondary index.  Read more in his blog post.

October 22, 2015

EXM install fails when Admin user is disabled

October 22, 2015

EXM Install Fails when Admin user is disabled

Wow, been heads-down with client work for a while and haven't had a chance to write to the 'ol blog.  Glad to have some breathing room before the next project starts to get a few posts out.

While installing EXM 3.0 rev. 150429 on a Sitecore 8.0 Update 3 instance for a client, we ran into an interesting issue.  What is normally a pretty simple install was failing for us.

We were seeing the following error:

ERROR Installation failed: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> Sitecore.Exceptions.AccessDeniedException: Only administrators can create new domains
   at Sitecore.SecurityModel.DomainManager.AddDomain(String domainName, Boolean locallyManaged)
   at Sitecore.Modules.EmailCampaign.Core.InstallationPostAction.AddCommonDomain()
   at Sitecore.Modules.EmailCampaign.Core.InstallationPostAction.RunPostStep()
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Sitecore.Install.Installer.ExecutePostStep(String action, IProcessingContext context)
   at Sitecore.Shell.Applications.Install.Dialogs.InstallPackage.InstallPackageForm.AsyncHelper.CatchExceptions(ThreadStart start)
The "Sitecore.Exceptions.AccessDeniedException: Only administrators can create new domains" line really caught my eye and after poking around for a bit I discovered the issue: Sitecore was expecting the "sitecore/admin" user and we had disabled this account.  Now this was done intentionally for security reasons and we created a separate account with "admin" privileges.  So it appears as though the install requires that the "sitecore/admin" context is used when installing EXM.  (Good thing we only disabled the user and didn't delete it!)

Steps to Reproduce

  1. Use SIM (you *are* using SIM, right?) to spool up a fresh instance of 8.0 update 3.
  2. Create new account and give it administrator privileges.
  3. Log out of "sitecore\admin" and log in as the new admin user.
  4. Disable 'old' admin account.
  5. Upload and install EXM package on the CM server.
  6. Note failure message.

Workaround/Fix

Pretty straight-forward here, but install EXM under the context of the "sitecore/admin" user.

I reported my error and findings/workaround to Sitecore Support and they were able to confirm that this is a bug that will be resolved in a future version of EXM.

June 26, 2015

Run Multiple Solr Instances for Sitecore

June 26, 2015

Run Multiple Solr Instances for Sitecore

At Arke, we have recently made the switch to recommending Solr as the 'default' indexing solution for Sitecore rather than Lucene.  If you're wondering which to use on your project, please see Sitecore's document on when to use Solr over Lucene.

Switching to Solr

There was a little pain involved in switching from Lucene to Solr for an active project, but luckily, my co-worker, Patrick Perrone had already been down the path of getting Solr running on his Windows machine and provided an excellent step-by-step guide on how to make it work.  If you haven't already, see his 3-part series on Making Sitecore 8 and Solr Work Together.

Thanks to Patrick, getting Solr running was a breeze.  This worked great for my active project, but now I'm starting up new projects where Solr will be the indexing mechanism from day 1.  How will I separate client Solr cores from one another?

Installing Multiple Instances of Solr on your (Windows) Machine

Patrick's install guide works great when you're only going to be running a single instance of Solr, but in order to get a second instance of Solr running on my machine, I had to make some minor modifications.

Note: These steps assume you already have a solr instance up and running with the proper Sitecore-generated schemas.  If you haven't followed all the steps to get Solr running with Sitecore, please do those first to save a bit of configuration time on your cores later.

  1. Stop your Tomcat service.

  2. I installed my Solr directory at C:\Solr and all my standard Sitecore and custom cores were then in directories under there.  In order to clean things up, I created a new folder named ClientName1 that would now contain the cores for that client.  Go ahead and create a folder named ClientName2 while you're here for the project you are starting up.

  3. Once created, pull all the content from the root of C:\Solr into this new ClientName1 folder.

  4. Since the original cores were already configured to work with Sitecore, it's a simple matter of copy/pasting all the cores into a second client folder.  Copy all the files from the root of ClientName1 and paste them into ClientName2.  At this time, you should remove any custom cores that were defined specifically for client 1.  You can leave all the Sitecore cores as we're going to rebuild them as a final step anyways to get client 2 data into them.  Your Solr root should now just have client folders in it, nice and clean-like.

  5. Go into your 'Monitor Tomcat' tool and remove the Java Options you set as part of the initial configuration of Solr.  In my case, I removed Dsolr.solr.home=C:\Solr  This option tells Tomcat where the home Solr directory is, but it assumes there is only one instance running.  Our next steps will define the multiple home directories.

  6. Move the solr.war file out of the root of your ...\Tomcat 8.0\webapps\ directory.  In my case, I moved the solr.war file up one directory from C:\Program Files\Apache Software Foundation\Tomcat 8.0\webapps to C:\Program Files\Apache Software Foundation\Tomcat 8.0\  Having the war file in the webapps directory is another indicator to Tomcat that it's only running a single instance and it will ignore the configuration files we are going to set in the next step.

  7. Go to your \Tomcat 8.0\conf\Catalina\localhost directory and create new XML config files for each client.  I named mine solr-<clientNameX>.xml  Note that this filename will be how you access your Solr cores in the browser.  In this case, I'll access my two clients by http://tomcat:8081/solr-clientName1 and http://tomcat:8081/solr-clientName2.  You can name them whatever you like, but the contents should look like this:

    <Context docBase="C:\Program Files\Apache Software Foundation\Tomcat 8.0\solr.war" debug="0" crossContext="true" >
        <Environment name="solr/home" type="java.lang.String" value="C:\solr\ClientName1" override="true" />
    </Context>
    

    Here, you are referencing the path to the solr.war file that we moved and are setting the Solr home directory to where all your cores are for this client.

  8. Copy the XML file you just created and paste it to create the XML file for ClientName2. Edit the file and change the path to from the C:\solr\ClientName1 directory to the ClientName2 directory.

  9. Start your Tomcat service back up.

  10. Navigate to http://tomcat:8081/solr-clientname1 and http://tomcat:8081/solr-clientname2 and verify that both instances are running. (using the port number used when configuring Solr and the file names for the XML configs you defined)

  11. Edit your ContentSearch.Solr.ServiceBaseAddress setting in the Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config file to point to the correct new instance of Solr.

  12. Once running, go back to your Sitecore instances and rebuild your indexes!
You can now safely run Solr on multiple projects and have confidence that each client's cores will not be mixed up with one another.

June 18, 2015

Sitecore 8 Bug: Link Sets Anchor Target to Active Browser

June 18, 2015

Links be broke
We recently ran into an issue while trying to render links set in Sitecore 8 (Update 3, to be specific).  When using the "General Link" field to insert a link, we discovered that the "target" options for the link were incorrect.  The options provided out of the box were "Active Browser", "Custom" and "New Browser":

Original Link Target Options


One would assume that "Active Browser" would set the target of the anchor tag to "_self" or "_top" so that it opens in the same window.  Unfortunately, what happens is that Sitecore literally puts "Active Browser" in the target attribute, which is not valid and ends up actually opening a new window/tab.

Workaround/Fix

I opened a support ticket with Sitecore and they acknowledged this as a bug and it should be fixed in a future release.

In the meantime, you can adjust the settings of the "Link Details" box to display new target options as follows:

  1. In the "Core" database, navigate to the "/sitecore/client/Applications/Dialogs/InsertLinkViaTreeDialog/PageSettings/TargetsSearchPanelConfig" item.
  2. In the "Root" field of the "Filters" section, specify the "sitecore/client/Business Component Library/System/Texts/Targets" item. (default is "sitecore/client/Applications/Dialogs/InsertLinkViaTreeDialog/PageSettings/Targets")
Now, when you insert a link, you see target options that will work when rendered in the browser: "_blank", "_parent" and "_top":

Updated Link Target Options
Note: If you already had links set to "Active Browser", leaving the default of "SelectTarget" will still render the target as "Active Browser."  You need to open the "Insert Link" option, select "Insert" and then save the item for it to use the proper value.