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;
            }
        }
        /// <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;
        }