public void StartServices()
 {
     _runningEvent.Set();
     _status = DekiInstanceStatus.STARTING_SERVICES;
     ServiceBL.StartServices();
     _status = DekiInstanceStatus.RUNNING;
     _servicesRunningEvent.Set();
     _eventSink.InstanceStarted(DekiContext.Current.Now);
 }
 internal bool BeginShutdown()
 {
     // Note (arnec): This method must be callable without touching any internal state of the instance, since DekiContext may not exist
     if (_status != DekiInstanceStatus.RUNNING)
     {
         _log.DebugFormat("Cannot shutdown instance currently in state '{0}'", _status);
         return(false);
     }
     _log.DebugFormat("starting shutdown of instance '{0}'", Id);
     _status = DekiInstanceStatus.STOPPING;
     return(true);
 }
 internal void ShutdownAbandoned()
 {
     _log.Debug("transitioning abandoned instance to stopped for shutdown");
     _status = DekiInstanceStatus.STOPPED;
 }
        internal void EndShutdown()
        {
            if (_status != DekiInstanceStatus.STOPPING)
            {
                _log.WarnFormat("EndShutdown was attemped with instance in state {0}", _status);
                throw new InvalidOperationException("bad state");
            }

            // shut down task timer
            try {
                TimerFactory.Dispose();
            } catch (Exception e) {
                _log.Warn(string.Format("unable to shut down timerfactory for instance '{0}': {1}", Id, e.Message), e);
            }

            // shut down storage
            _log.DebugFormat("shutting down storage for '{0}'", Id);
            try {
                _storage.Dispose();
            } catch (Exception e) {
                _log.Warn(string.Format("unable to cleanly shut down storage for instance '{0}': {1}", Id, e.Message), e);
            }
            _storage = null;

            // shut down services
            _log.DebugFormat("shutting down services for '{0}'", Id);
            try {
                ServiceBL.StopServices();

                // reset instance fields
                RunningServices.Clear();
            } catch (Exception e) {
                _log.Warn(string.Format("unable to cleanly shut down services for instance '{0}': {1}", Id, e.Message), e);
            }

            // shutting down cache
            try {
                Cache.Dispose();
            } catch (Exception e) {
                _log.Warn(string.Format("unable to dispose the cache for instance '{0}': {1}", Id, e.Message), e);
            }

            // shutting down search cache
            try {
                SearchCache.Dispose();
            } catch (Exception e) {
                _log.Warn(string.Format("unable to dispose the search cache for instance '{0}': {1}", Id, e.Message), e);
            }

            // sending shut down event
            try {
                _eventSink.InstanceShutdown(DekiContext.Current.Now);
            } catch (Exception e) {
                _log.Warn(string.Format("unable to send the shutdown notification event for instance '{0}': {1}", Id, e.Message), e);
            }
            _eventSink = null;

            // unregister the instance to database mapping
            if (null != _sessionFactory)
            {
                try {
                    _sessionFactory.Dispose();
                } catch (Exception e) {
                    _log.Warn(string.Format("unable to cleanly shut down the session factory for instance '{0}': {1}", Id, e.Message), e);
                }
                _sessionFactory = null;
            }
            _log.DebugFormat("instance '{0}' stopped", Id);
            _status = DekiInstanceStatus.STOPPED;
        }
        //--- Constructor ---
        internal DekiInstance(DekiWikiService deki, string id, XDoc instanceConfig, ILicenseController licenseController)
        {
            if (deki == null)
            {
                throw new ArgumentNullException("deki");
            }
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentNullException("id");
            }
            this.Id = id;

            // Note (arnec): this is now the the third place we define a wikiid based logger repository, however constructors interdependencies
            // currently neccessitates this duplication, and fortunately it is mostly an aesthetic issue.
            _loggerRepository = new ContextLoggerRepository("[" + Id + "] ");
            _log = _loggerRepository.Get(GetType());
            this.TimerFactory = TaskTimerFactory.Create(this);
            this.Cache        = new DreamCache(TimerFactory);
            var cacheFactory     = new InMemoryKeyValueCacheFactory(TimerFactory);
            var searchSerializer = new SearchSerializer();

            cacheFactory.SetSerializer <SearchResult>(searchSerializer);
            cacheFactory.SetSerializer <SearchResultDetail>(searchSerializer);
            this.SearchCache   = cacheFactory.Create();
            this.Config        = instanceConfig;
            _licenseController = licenseController;
            this.Log           = LogManager.GetLogger(deki.GetType().Name + "_" + id);
            _deki   = deki;
            _status = DekiInstanceStatus.CREATED;
            _apiKey = Config[ConfigBL.SECURITY_APIKEY].AsText;
            foreach (XDoc hostDoc in Config["host"])
            {
                string host = hostDoc.Contents;
                if (!StringUtil.EqualsInvariantIgnoreCase(host, "*"))
                {
                    string port   = hostDoc["@port"].AsText;
                    string scheme = hostDoc["@https"].AsBool.GetValueOrDefault() ? "https://" : "http://";
                    string uri    = scheme + host + (string.IsNullOrEmpty(port) ? "" : ":" + port);
                    _canonicalUri = new XUri(uri);
                    _log.DebugFormat("divined canonical use from hosts as {0}", _canonicalUri);
                    break;
                }
            }
            if (_canonicalUri == null)
            {
                // Note (arnec): this is a best guess fallback. It will only work in these scenarios:
                // a) The host was set up with a uri.public that has ends in @api and with the @api points to the site uri, or
                // b) The api lives on the same machine as the site, so that deriving uri.public for the host from the machine
                // IP happens to point to the same machine
                // Either way it relies on the hard-coded assumption that the api is accessible via {site}/@api
                _canonicalUri = DreamContext.Current.ServerUri;
                if (_canonicalUri.LastSegment.EqualsInvariantIgnoreCase("@api"))
                {
                    _canonicalUri = _canonicalUri.WithoutLastSegment();
                }
                _log.DebugFormat("using server uri as canonical uri: {0}", _canonicalUri);
            }
            else
            {
                // Note (arnec): Propagating a much hard-coded assumption, i.e. that the Api for any Deki instance can be accessed
                // at the instances' canonical uri plus @api

                // register the api uri with the dream host so that requests originating from within Dream are guaranteed to be locally routed
                _deki.Env.At("status", "aliases").Post(new XDoc("aliases").Elem("uri.alias", _canonicalUri.At("@api")), new Result <DreamMessage>());
            }
        }
        /// <summary>
        /// Calls methods to perform initial init of a wiki instance including db updates, etc
        /// </summary>
        public void Startup(DekiContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (_status != DekiInstanceStatus.CREATED)
            {
                throw new InvalidOperationException("bad state");
            }
            _status = DekiInstanceStatus.INITIALIZING;

            // run startup code
            try {
                // have to initialize the event sink first since because license checking might trigger a license transition event
                _eventSink = new DekiChangeSink(Id, DekiContext.Current.ApiUri, DekiContext.Current.Deki.PubSub.At("publish").WithCookieJar(DekiContext.Current.Deki.Cookies));

                // create the IDekiDataSessionFactory for this instance
                Type typeMySql   = Type.GetType("MindTouch.Deki.Data.MySql.MySqlDekiDataSessionFactory, mindtouch.deki.data.mysql", true);
                Type typeCaching = null;
                try {
                    typeCaching = Type.GetType("MindTouch.Deki.Data.Caching.CachingDekiDataSessionFactory, mindtouch.data.caching", false);
                } catch (Exception x) {
                    Log.Warn("The caching library was found but could not be loaded. Check that its version matches the version of your MindTouch API", x);
                }

                // FUN FACT: DekiInstanceSettings is a proxy for the static calls in ConfigBL, which expects to access DbUtils.CurrentSession,
                // which has to be initialized by the session factory.. Yes, the very session factory that takes DekiInstanceSettings as part
                // of its initialization call. I call it the M.C.Escher Design Pattern.
                var sessionFactory = (IDekiDataSessionFactory)typeMySql.GetConstructor(Type.EmptyTypes).Invoke(null);
                sessionFactory = new LoggingDekiDataSessionFactory(sessionFactory);
                sessionFactory = new DekiDataSessionProfilerFactory(sessionFactory);
                if (typeCaching != null)
                {
                    sessionFactory = (IDekiDataSessionFactory)typeCaching.GetConstructor(new[] { typeof(IDekiDataSessionFactory) }).Invoke(new object[] { sessionFactory });
                }
                _sessionFactory = sessionFactory;
                _sessionFactory.Initialize(Config ?? _deki.Config, _licenseController.LicenseDoc, new DekiInstanceSettings());
                var state = context.LicenseManager.LicenseState;
                if (state == LicenseStateType.UNDEFINED)
                {
                    throw new MindTouchInvalidLicenseStateException();
                }

                // set deki license token
                _token = context.LicenseManager.LicenseDocument["licensee/deki"].AsText;
                if (string.IsNullOrEmpty(_token))
                {
                    // compute deki license token
                    var    tokenKey = _apiKey ?? _deki.MasterApiKey;
                    ulong  folded_productkey_md5 = 0;
                    byte[] md5_bytes             = StringUtil.ComputeHash(tokenKey, Encoding.UTF8);
                    for (int i = 0; i < md5_bytes.Length; ++i)
                    {
                        folded_productkey_md5 ^= (ulong)md5_bytes[i] << (i & 7) * 8;
                    }
                    _token = folded_productkey_md5.ToString("X");
                }

                // check if a storage config section was provided (default storage is filesystem provider)
                XDoc storageConfig;
                switch (ConfigBL.GetInstanceSettingsValue("storage/@type", ConfigBL.GetInstanceSettingsValue("storage/type", "default")))
                {
                case "default":
                    string defaultAttachPath = Path.Combine(_deki.DekiPath, "attachments");
                    storageConfig = new XDoc("config")
                                    .Elem("path", defaultAttachPath)
                                    .Elem("cache-path", Path.Combine(defaultAttachPath, ".cache"));
                    _storage = new FSStorage(storageConfig);
                    break;

                case "fs":
                    string fsPath = ConfigBL.GetInstanceSettingsValue("storage/fs/path", null);

                    //Replace a $1 with the wiki name
                    fsPath        = string.Format(PhpUtil.ConvertToFormatString(fsPath ?? string.Empty), Id);
                    storageConfig = new XDoc("config")
                                    .Elem("path", fsPath)
                                    .Elem("cache-path", ConfigBL.GetInstanceSettingsValue("storage/fs/cache-path", null));
                    _storage = new FSStorage(storageConfig);
                    break;

                case "s3":
                    storageConfig = new XDoc("config")
                                    .Elem("publickey", ConfigBL.GetInstanceSettingsValue("storage/s3/publickey", null))
                                    .Elem("privatekey", ConfigBL.GetInstanceSettingsValue("storage/s3/privatekey", null))
                                    .Elem("bucket", ConfigBL.GetInstanceSettingsValue("storage/s3/bucket", null))
                                    .Elem("prefix", string.Format(PhpUtil.ConvertToFormatString(ConfigBL.GetInstanceSettingsValue("storage/s3/prefix", string.Empty)), DekiContext.Current.Instance.Id))
                                    .Elem("timeout", ConfigBL.GetInstanceSettingsValue("storage/s3/timeout", null))
                                    .Elem("allowredirects", ConfigBL.GetInstanceSettingsValue("storage/s3/allowredirects", null))
                                    .Elem("redirecttimeout", ConfigBL.GetInstanceSettingsValue("storage/s3/redirecttimeout", null));
                    _storage = new S3Storage(storageConfig, _loggerRepository.Get <S3Storage>());
                    break;

                default:
                    throw new ArgumentException("Storage provider unknown or not defined (key: storage/type)", "storage/type");
                }

                HomePageId = DbUtils.CurrentSession.Pages_HomePageId;
                _eventSink.InstanceStarting(DekiContext.Current.Now);
            } catch {
                // we failed to initialize
                _status = DekiInstanceStatus.ABANDONED;
                throw;
            }

            // set state to initializing
            _status = DekiInstanceStatus.INITIALIZING;
        }
        public void Shutdown() {
            if(_status != DekiInstanceStatus.RUNNING) {
                throw new InvalidOperationException("bad state");
            }
            TimerFactory.Dispose();
            _storage.Shutdown();

            // run shudown code
            ServiceBL.StopServices();

            // reset instance fields
            _status = DekiInstanceStatus.STOPPED;
            RunningServices.Clear();
            _configCache = null;
            _storage = null;
            Cache.Dispose();
            SearchCache.Dispose();
            _eventSink.InstanceShutdown(DreamContext.Current.StartTime);
            _eventSink = null;

            // unregister the instance to database mapping
            if(null != _sessionFactory) {
                _sessionFactory.Dispose();
                _sessionFactory = null;
            }
        }
        //--- Constructor ---
        public DekiInstance(DekiWikiService deki, string id, XDoc instanceConfig) {
            if(deki == null) {
                throw new ArgumentNullException("deki");
            }
            if(string.IsNullOrEmpty(id)) {
                throw new ArgumentNullException("id");
            }

            this.Id = id;
            this.TimerFactory = TaskTimerFactory.Create(this);
            this.Cache = new DreamCache(TimerFactory);
            var cacheFactory = new InMemoryKeyValueCacheFactory(TimerFactory);
            var searchSerializer = new SearchSerializer();
            cacheFactory.SetSerializer<SearchResult>(searchSerializer);
            cacheFactory.SetSerializer<SearchResultDetail>(searchSerializer);
            this.SearchCache = cacheFactory.Create();
            this.Config = instanceConfig;
            this.Log = LogManager.GetLogger(deki.GetType().Name + "_" + id);
            _deki = deki;
            _status = DekiInstanceStatus.CREATED;
            foreach(XDoc hostDoc in Config["host"]) {
                string host = hostDoc.Contents;
                if(!StringUtil.EqualsInvariantIgnoreCase(host, "*")) {
                    string port = hostDoc["@port"].AsText;
                    string scheme = hostDoc["@https"].AsBool.GetValueOrDefault() ? "https://" : "http://";
                    string uri = scheme + host + (string.IsNullOrEmpty(port) ? "" : ":" + port);
                    _canonicalUri = new XUri(uri);
                    _log.DebugFormat("divined canonical use from hosts as {0}", _canonicalUri);
                    break;
                }
            }
            if(_canonicalUri == null) {

                // Note (arnec): this is a best guess fallback. It will only work in these scenarios:
                // a) The host was set up with a uri.public that has ends in @api and with the @api points to the site uri, or
                // b) The api lives on the same machine as the site, so that deriving uri.public for the host from the machine
                // IP happens to point to the same machine
                // Either way it relies on the hard-coded assumption that the api is accessible via {site}/@api
                _canonicalUri = DreamContext.Current.ServerUri;
                if(_canonicalUri.LastSegment.EqualsInvariantIgnoreCase("@api")) {
                    _canonicalUri = _canonicalUri.WithoutLastSegment();
                }
                _log.DebugFormat("using server uri as canonical uri: {0}", _canonicalUri);
            } else {

                // Note (arnec): Propagating a much hard-coded assumption, i.e. that the Api for any Deki instance can be accessed
                // at the instances' canonical uri plus @api

                // register the api uri with the dream host so that requests originating from within Dream are guaranteed to be locally routed
                _deki.Env.At("status", "aliases").Post(new XDoc("aliases").Elem("uri.alias", _canonicalUri.At("@api")), new Result<DreamMessage>());
            }
        }
        /// <summary>
        /// Calls methods to perform initial init of a wiki instance including db updates, etc
        /// </summary>
        public void Startup(DekiContext context) {
            if(context == null) {
                throw new ArgumentNullException("context");
            }
            if(_status != DekiInstanceStatus.CREATED) {
                throw new InvalidOperationException("bad state");
            }
            _status = DekiInstanceStatus.INITIALIZING;

            // run startup code
            try {

                // create the IDekiDataSessionFactory for this instance
                Type typeMySql = Type.GetType("MindTouch.Deki.Data.MySql.MySqlDekiDataSessionFactory, mindtouch.deki.data.mysql", true);
                Type typeCaching = null;
                try {
                    typeCaching = Type.GetType("MindTouch.Deki.Data.Caching.CachingDekiDataSessionFactory, mindtouch.data.caching", false);
                } catch(Exception x) {
                    Log.Warn("The caching library was found but could not be loaded. Check that its version matches the version of your MindTouch API", x);
                }

                IDekiDataSessionFactory factoryMySql = (IDekiDataSessionFactory)typeMySql.GetConstructor(Type.EmptyTypes).Invoke(null);
                IDekiDataSessionFactory factoryLogging = new LoggingDekiDataSessionFactory(factoryMySql);

                if(typeCaching != null) {
                    IDekiDataSessionFactory factoryCaching = (IDekiDataSessionFactory)typeCaching.GetConstructor(new[] { typeof(IDekiDataSessionFactory) }).Invoke(new object[] { factoryLogging });
                    _sessionFactory = factoryCaching;
                } else {
                    _sessionFactory = factoryLogging;
                }

                _sessionFactory.Initialize(Config ?? _deki.Config, new DekiInstanceSettings());

                try {
                    DbUtils.CurrentSession = _sessionFactory.CreateSession();

                    // check for 'api-key'
                    if(string.IsNullOrEmpty(ApiKey) && string.IsNullOrEmpty(_deki.MasterApiKey)) {
                        throw new ArgumentNullException("apikey", "Missing apikey for wiki instance. Please ensure that you have a global <apikey> defined (in the service settings xml file) or an instance specific key in the config table as 'security/api-key'.");
                    }

                    // check if a storage config section was provided (default storage is filesystem provider)
                    XDoc storageConfig;
                    switch(ConfigBL.GetInstanceSettingsValue("storage/@type", ConfigBL.GetInstanceSettingsValue("storage/type", "default"))) {
                    case "default":
                        string defaultAttachPath = Path.Combine(_deki.DekiPath, "attachments");
                        storageConfig = new XDoc("config")
                            .Elem("path", defaultAttachPath)
                            .Elem("cache-path", Path.Combine(defaultAttachPath, ".cache"));
                        _storage = new FSStorage(storageConfig);
                        break;
                    case "fs":
                        string fsPath = ConfigBL.GetInstanceSettingsValue("storage/fs/path", null);

                        //Replace a $1 with the wiki name                
                        fsPath = string.Format(Dream.PhpUtil.ConvertToFormatString(fsPath ?? string.Empty), Id);
                        storageConfig = new XDoc("config")
                            .Elem("path", fsPath)
                            .Elem("cache-path", ConfigBL.GetInstanceSettingsValue("storage/fs/cache-path", null));
                        _storage = new FSStorage(storageConfig);
                        break;
                    case "s3":
                        storageConfig = new XDoc("config")
                            .Elem("publickey", ConfigBL.GetInstanceSettingsValue("storage/s3/publickey", null))
                            .Elem("privatekey", ConfigBL.GetInstanceSettingsValue("storage/s3/privatekey", null))
                            .Elem("bucket", ConfigBL.GetInstanceSettingsValue("storage/s3/bucket", null))
                            .Elem("prefix", string.Format(Dream.PhpUtil.ConvertToFormatString(ConfigBL.GetInstanceSettingsValue("storage/s3/prefix", string.Empty)), DekiContext.Current.Instance.Id))
                            .Elem("timeout", ConfigBL.GetInstanceSettingsValue("storage/s3/timeout", null))
                            .Elem("allowredirects", ConfigBL.GetInstanceSettingsValue("storage/s3/allowredirects", null))
                            .Elem("redirecttimeout", ConfigBL.GetInstanceSettingsValue("storage/s3/redirecttimeout", null));
                        _storage = new S3Storage(storageConfig);
                        break;
                    default:
                        throw new ArgumentException("Storage provider unknown or not defined (key: storage/type)", "storage/type");
                    }

                    HomePageId = DbUtils.CurrentSession.Pages_HomePageId;
                } finally {
                    if(null != DbUtils.CurrentSession) {
                        DbUtils.CurrentSession.Dispose();
                        DbUtils.CurrentSession = null;
                    }
                }
                _eventSink = new DekiChangeSink(Id, DekiContext.Current.ApiUri, DekiContext.Current.Deki.PubSub.At("publish").WithCookieJar(DekiContext.Current.Deki.Cookies));
                _eventSink.InstanceStarting(DreamContext.Current.StartTime);
            } catch {

                // we failed to initialize
                _status = DekiInstanceStatus.ABANDONED;
                throw;
            }

            // set state to initializing
            _status = DekiInstanceStatus.INITIALIZING;
        }
 public void StartServices() {
     _runningEvent.Set();
     _status = DekiInstanceStatus.STARTING_SERVICES;
     DbUtils.CurrentSession = _sessionFactory.CreateSession();
     ServiceBL.StartServices();
     _status = DekiInstanceStatus.RUNNING;
     _eventSink.InstanceStarted(DreamContext.Current.StartTime);
 }
Beispiel #11
0
 internal void ShutdownAbandoned() {
     _log.Debug("transitioning abandoned instance to stopped for shutdown");
     _status = DekiInstanceStatus.STOPPED;
 }
Beispiel #12
0
        internal void EndShutdown() {
            if(_status != DekiInstanceStatus.STOPPING) {
                _log.WarnFormat("EndShutdown was attemped with instance in state {0}", _status);
                throw new InvalidOperationException("bad state");
            }

            // shut down task timer
            try {
                TimerFactory.Dispose();
            } catch(Exception e) {
                _log.Warn(string.Format("unable to shut down timerfactory for instance '{0}': {1}", Id, e.Message), e);
            }

            // shut down storage
            _log.DebugFormat("shutting down storage for '{0}'", Id);
            try {
                _storage.Dispose();
            } catch(Exception e) {
                _log.Warn(string.Format("unable to cleanly shut down storage for instance '{0}': {1}", Id, e.Message), e);
            }
            _storage = null;

            // shut down services
            _log.DebugFormat("shutting down services for '{0}'", Id);
            try {
                ServiceBL.StopServices();

                // reset instance fields
                RunningServices.Clear();
            } catch(Exception e) {
                _log.Warn(string.Format("unable to cleanly shut down services for instance '{0}': {1}", Id, e.Message), e);
            }

            // shutting down cache
            try {
                Cache.Dispose();
            } catch(Exception e) {
                _log.Warn(string.Format("unable to dispose the cache for instance '{0}': {1}", Id, e.Message), e);
            }

            // shutting down search cache
            try {
                SearchCache.Dispose();
            } catch(Exception e) {
                _log.Warn(string.Format("unable to dispose the search cache for instance '{0}': {1}", Id, e.Message), e);
            }

            // sending shut down event
            try {
                _eventSink.InstanceShutdown(DekiContext.Current.Now);
            } catch(Exception e) {
                _log.Warn(string.Format("unable to send the shutdown notification event for instance '{0}': {1}", Id, e.Message), e);
            }
            _eventSink = null;

            // unregister the instance to database mapping
            if(null != _sessionFactory) {
                try {
                    _sessionFactory.Dispose();
                } catch(Exception e) {
                    _log.Warn(string.Format("unable to cleanly shut down the session factory for instance '{0}': {1}", Id, e.Message), e);
                }
                _sessionFactory = null;
            }
            _log.DebugFormat("instance '{0}' stopped", Id);
            _status = DekiInstanceStatus.STOPPED;
        }
Beispiel #13
0
        internal bool BeginShutdown() {

            // Note (arnec): This method must be callable without touching any internal state of the instance, since DekiContext may not exist
            if(_status != DekiInstanceStatus.RUNNING) {
                _log.DebugFormat("Cannot shutdown instance currently in state '{0}'", _status);
                return false;
            }
            _log.DebugFormat("starting shutdown of instance '{0}'", Id);
            _status = DekiInstanceStatus.STOPPING;
            return true;
        }
Beispiel #14
0
 public void StartServices() {
     _runningEvent.Set();
     _status = DekiInstanceStatus.STARTING_SERVICES;
     ServiceBL.StartServices();
     _status = DekiInstanceStatus.RUNNING;
     _servicesRunningEvent.Set();
     _eventSink.InstanceStarted(DekiContext.Current.Now);
 }
Beispiel #15
0
        /// <summary>
        /// Calls methods to perform initial init of a wiki instance including db updates, etc
        /// </summary>
        public void Startup(DekiContext context) {
            if(context == null) {
                throw new ArgumentNullException("context");
            }
            if(_status != DekiInstanceStatus.CREATED) {
                throw new InvalidOperationException("bad state");
            }
            _status = DekiInstanceStatus.INITIALIZING;

            // run startup code
            try {

                // have to initialize the event sink first since because license checking might trigger a license transition event
                _eventSink = new DekiChangeSink(Id, DekiContext.Current.ApiUri, DekiContext.Current.Deki.PubSub.At("publish").WithCookieJar(DekiContext.Current.Deki.Cookies));

                // create the IDekiDataSessionFactory for this instance
                Type typeMySql = Type.GetType("MindTouch.Deki.Data.MySql.MySqlDekiDataSessionFactory, mindtouch.deki.data.mysql", true);
                Type typeCaching = null;
                try {
                    typeCaching = Type.GetType("MindTouch.Deki.Data.Caching.CachingDekiDataSessionFactory, mindtouch.data.caching", false);
                } catch(Exception x) {
                    Log.Warn("The caching library was found but could not be loaded. Check that its version matches the version of your MindTouch API", x);
                }

                // FUN FACT: DekiInstanceSettings is a proxy for the static calls in ConfigBL, which expects to access DbUtils.CurrentSession,
                // which has to be initialized by the session factory.. Yes, the very session factory that takes DekiInstanceSettings as part
                // of its initialization call. I call it the M.C.Escher Design Pattern.
                var sessionFactory = (IDekiDataSessionFactory)typeMySql.GetConstructor(Type.EmptyTypes).Invoke(null);
                sessionFactory = new LoggingDekiDataSessionFactory(sessionFactory);
                sessionFactory = new DekiDataSessionProfilerFactory(sessionFactory);
                if(typeCaching != null) {
                    sessionFactory = (IDekiDataSessionFactory)typeCaching.GetConstructor(new[] { typeof(IDekiDataSessionFactory) }).Invoke(new object[] { sessionFactory });
                }
                _sessionFactory = sessionFactory;
                _sessionFactory.Initialize(Config ?? _deki.Config, _licenseController.LicenseDoc, new DekiInstanceSettings());
                var state = context.LicenseManager.LicenseState;
                if(state == LicenseStateType.UNDEFINED) {
                    throw new MindTouchInvalidLicenseStateException();
                }

                // set deki license token
                _token = context.LicenseManager.LicenseDocument["licensee/deki"].AsText;
                if(string.IsNullOrEmpty(_token)) {

                    // compute deki license token
                    var tokenKey = _apiKey ?? _deki.MasterApiKey;
                    ulong folded_productkey_md5 = 0;
                    byte[] md5_bytes = StringUtil.ComputeHash(tokenKey, Encoding.UTF8);
                    for(int i = 0; i < md5_bytes.Length; ++i) {
                        folded_productkey_md5 ^= (ulong)md5_bytes[i] << (i & 7) * 8;
                    }
                    _token = folded_productkey_md5.ToString("X");
                }

                // check if a storage config section was provided (default storage is filesystem provider)
                XDoc storageConfig;
                switch(ConfigBL.GetInstanceSettingsValue("storage/@type", ConfigBL.GetInstanceSettingsValue("storage/type", "default"))) {
                case "default":
                    string defaultAttachPath = Path.Combine(_deki.DekiPath, "attachments");
                    storageConfig = new XDoc("config")
                        .Elem("path", defaultAttachPath)
                        .Elem("cache-path", Path.Combine(defaultAttachPath, ".cache"));
                    _storage = new FSStorage(storageConfig);
                    break;
                case "fs":
                    string fsPath = ConfigBL.GetInstanceSettingsValue("storage/fs/path", null);

                    //Replace a $1 with the wiki name                
                    fsPath = string.Format(PhpUtil.ConvertToFormatString(fsPath ?? string.Empty), Id);
                    storageConfig = new XDoc("config")
                        .Elem("path", fsPath)
                        .Elem("cache-path", ConfigBL.GetInstanceSettingsValue("storage/fs/cache-path", null));
                    _storage = new FSStorage(storageConfig);
                    break;
                case "s3":
                    storageConfig = new XDoc("config")
                        .Elem("publickey", ConfigBL.GetInstanceSettingsValue("storage/s3/publickey", null))
                        .Elem("privatekey", ConfigBL.GetInstanceSettingsValue("storage/s3/privatekey", null))
                        .Elem("bucket", ConfigBL.GetInstanceSettingsValue("storage/s3/bucket", null))
                        .Elem("prefix", string.Format(PhpUtil.ConvertToFormatString(ConfigBL.GetInstanceSettingsValue("storage/s3/prefix", string.Empty)), DekiContext.Current.Instance.Id))
                        .Elem("timeout", ConfigBL.GetInstanceSettingsValue("storage/s3/timeout", null))
                        .Elem("allowredirects", ConfigBL.GetInstanceSettingsValue("storage/s3/allowredirects", null))
                        .Elem("redirecttimeout", ConfigBL.GetInstanceSettingsValue("storage/s3/redirecttimeout", null));
                    _storage = new S3Storage(storageConfig, _loggerRepository.Get<S3Storage>());
                    break;
                default:
                    throw new ArgumentException("Storage provider unknown or not defined (key: storage/type)", "storage/type");
                }

                HomePageId = DbUtils.CurrentSession.Pages_HomePageId;
                _eventSink.InstanceStarting(DekiContext.Current.Now);
            } catch {

                // we failed to initialize
                _status = DekiInstanceStatus.ABANDONED;
                throw;
            }

            // set state to initializing
            _status = DekiInstanceStatus.INITIALIZING;
        }