Integrate AKAMAI with Sitecore to deliver GeoIP personalisation

As probably not everybody knows AKAMAI offer as well GEOIP resolution capabilities based on MaxMind database.

OOTB Sitecore offer GeoIP personalisation based on their cloud service that consume MaxMind database, Sitecore charge customers for this service and in case you are not using Akamai, this could be an easy and effective way to have GeoIP in place without writing any code but just paying for Sitecore GeoIP service.

Therefore if you are already using AKAMAI and Sitecore GeoIP personalisation you should consider following this route…

In order to get AKAMAI to deliver personalisation you need to enable Content Targeting via property manager obviously you need to ensure that Content Targeting is included within your AKAMAI contract.

https://community.akamai.com/community/web-performance/blog/2016/03/16/content-targeting-a-basic-introduction

the-world-in-flags-570x293

Once that you have this in place, you will simply get GeoIp information injected within your http header that should look like:

X-Akamai-Edgescape: georegion=263,country_code=US,region_code=MA,city=CAMBRIDGE,dma=50 6,pmsa=1120,areacode=617,county=MIDDLESEX,fips=25017,lat=42.3933,l ong=-71.1333,timezone=EST,zip=02138-02142+02238-02239,continent=NA ,throughput=vhigh,asnum=21399

In order to configure Sitecore to read the following header instead than calling the original service, it is recommended to implement your custom lookup provider. Implementing your custom lookup provider is fairly simple since you need to inherit from Sitecore.Analytics.Lookups.LookupProviderBase and override WhoIsInformation to read your information from the HttpHeader and return the information as within the whoIsInformation object.

using Sitecore.Analytics.Lookups;
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using System;
using System.Web;
using System.Web.Hosting;

namespace xxx.Akamai.LookupProvider
{
    public class AkamaiGEoIpProvider : LookupProviderBase
    {
        public string ExtractValue(string keyName, string headerValue)
        {
            //ToDo: nice regex to Parse Value
        }

        public override WhoIsInformation GetInformationByIp(string ip)
        {

            WhoIsInformation information = new WhoIsInformation();
            var headerValue = Request.Headers["X-Akamai-Edgescape"];
            information.Country = ExtractValue("country_code", headerValue);
            information.PostalCode = ExtractValue("zip", headerValue);
            //etc etc to set all the value for the information object

            return information;
        }
    }
}

Once that you have implemented your lookup provider you would need to regiter within the web.config your own provider

 <lookupmanager>
        <patch:attribute name="defaultProvider">akamai</patch:attribute>
        <providers>
          <add name="akamai" patch:after="processor[@type='Sitecore.Analytics.Lookups.MaxMindProvider,Sitecore.Analytics']" type="xxx.Akamai.LookupProvider.AkamaiGEoIpProvider,xxx.AkamaiGeoIp">/>
</add></providers></lookupmanager>

Useful resources to consider when you implement a custom lookup provider are the following ones:

Sitecore GeoLiteProvider: https://github.com/craigtfromatl/Sitecore-GeoLite-Lookup-Provider

CircuitBreaker design pattern: http://www.redmoon.london/sitecore-ip-geolocation-service-resolving-geoip-information-on-the-first-request-using-circuit-breaker-design-pattern/

Akamai Edgescape Parser: https://github.com/madglory/edgescape-parser

Lucene Indexes in Sitecore 6.6 getting corrupted (ArgumentOutOfRangeException)

never seen this exception?

System.ArgumentOutOfRangeException 
Message: Non-negative number required. 
Parameter name: capacity Source: mscorlib 
  at System.Collections.Hashtable..ctor(Int32 capacity, Single loadFactor)
  at System.Collections.Hashtable.Clone() 
  at SupportClass.WeakHashTable.Clean() 
  at SupportClass.WeakHashTable.CleanIfNeeded() 
  at SupportClass.WeakHashTable.Add(Object key, Object value) 
  at Lucene.Net.Util.CloseableThreadLocal.Set(Object object) 
  at Lucene.Net.Index.TermInfosReader.GetThreadResources() 
  at Lucene.Net.Index.TermInfosReader.Get(Term term, Boolean useCache) 
  at Lucene.Net.Index.SegmentReader.DocFreq(Term t) 
  at Lucene.Net.Index.DirectoryReader.DocFreq(Term t) 
  at Lucene.Net.Search.Similarity.IdfExplain(Term term, Searcher searcher)
  at Lucene.Net.Search.TermQuery.CreateWeight(Searcher searcher) 
  at Lucene.Net.Search.BooleanQuery.BooleanWeight..ctor(BooleanQuery enclosingInstance, Searcher searcher) 
  at Lucene.Net.Search.BooleanQuery.CreateWeight(Searcher searcher) 
  at Lucene.Net.Search.Query.Weight(Searcher searcher) 
  at Lucene.Net.Search.Hits..ctor(Searcher s, Query q, Filter f, Sort o) 
  at Lucene.Net.Search.Searcher.Search(Query query, Sort sort) 
  at scSearchContrib.Searcher.QueryRunner.RunQuery(Query query, Boolean showAllVersions, String sortField, Boolean reverse, Int32 start, Int32 end) 
  at scSearchContrib.Searcher.QueryRunner.GetItems(IEnumerable`1 parameters, Boolean showAllVersions, String sortField, Boolean reverse, Int32 start, Int32 end) 

it means that your index is getting corrupted and need an app pool recycle…
to avoid it you can try with this trick:

&lt;setting name=&quot;MaxWorkerThreads&quot; value=&quot;100&quot;/&gt;