//--- Methods ---
        protected override Yield Start(XDoc config, Result result)
        {
            yield return(Coroutine.Invoke(base.Start, config, new Result()));

            _varnish            = Plug.New(Config["uri.varnish"].AsUri);
            _deki               = Plug.New(Config["uri.deki"].AsUri);
            _apikey             = Config["apikey"].AsText;
            _delayPurgeTimespan = TimeSpan.FromSeconds(config["varnish-purge-delay"].AsInt ?? 10);
            var dispatcher = new UpdateRecordDispatcher(OnQueueExpire);

            _updateDelayQueue = new UpdateDelayQueue(_delayPurgeTimespan, dispatcher);

            // set up subscription for pubsub
            XDoc subscriptionSet = new XDoc("subscription-set")
                                   .Elem("uri.owner", Self.Uri)
                                   .Start("subscription")
                                   .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument)
                                   .Elem("channel", "event://*/deki/pages/create")
                                   .Elem("channel", "event://*/deki/pages/move")
                                   .Elem("channel", "event://*/deki/pages/update")
                                   .Elem("channel", "event://*/deki/pages/delete")
                                   .Elem("channel", "event://*/deki/pages/revert")
                                   .Elem("channel", "event://*/deki/pages/createalias")
                                   .Elem("channel", "event://*/deki/pages/tags/update")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/comments/create")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/comments/update")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/comments/delete")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/files/create")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/files/update")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/files/delete")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/files/move")
                                   .Elem("channel", "event://*/deki/pages/dependentschanged/files/restore")
                                   .Elem("channel", "event://*/deki/files/create")
                                   .Elem("channel", "event://*/deki/files/update")
                                   .Elem("channel", "event://*/deki/files/delete")
                                   .Elem("channel", "event://*/deki/files/move")
                                   .Elem("channel", "event://*/deki/files/restore")
                                   .Start("recipient")
                                   .Attr("authtoken", _apikey)
                                   .Elem("uri", Self.Uri.At("queue"))
                                   .End()
                                   .End();
            Result <DreamMessage> subscriptionResult;

            yield return(subscriptionResult = PubSub.At("subscribers").PostAsync(subscriptionSet));

            string accessKey = subscriptionResult.Value.ToDocument()["access-key"].AsText;
            XUri   location  = subscriptionResult.Value.Headers.Location;

            Cookies.Update(DreamCookie.NewSetCookie("access-key", accessKey, location), null);
            _subscriptionLocation = location.AsLocalUri().WithoutQuery();
            _log.DebugFormat("subscribed VarnishPurgeService for events at {0}", _subscriptionLocation);
            result.Return();
        }
Esempio n. 2
0
        //--- Methods ---
        protected override Yield Start(XDoc config, Result result)
        {
            yield return(Coroutine.Invoke(base.Start, config, new Result()));

            // set up cache reaper
            _ttl = TimeSpan.FromSeconds(Config["news-ttl"].AsDouble ?? 60 * 60);
            double?checkInterval = Config["check-interval"].AsDouble;

            if (checkInterval.HasValue)
            {
                _checkInterval = TimeSpan.FromSeconds(checkInterval.Value);
            }
            else
            {
                double checkInterval2 = _ttl.TotalSeconds / 10;
                if (_ttl.TotalSeconds < 30 || checkInterval2 < 30)
                {
                    checkInterval2 = 30;
                }
                _checkInterval = TimeSpan.FromSeconds(checkInterval2);
            }
            TaskTimer.New(_ttl, delegate(TaskTimer timer) {
                lock (_pageViews) {
                    View next;
                    do
                    {
                        if (_pageViews.Count == 0)
                        {
                            break;
                        }
                        next = _pageViews.Peek();
                        if (next != null)
                        {
                            if (next.Time.Add(_ttl) < DateTime.UtcNow)
                            {
                                _pageViews.Dequeue();
                            }
                            else
                            {
                                break;
                            }
                        }
                    } while(next != null);
                }
                timer.Change(_checkInterval, TaskEnv.None);
            }, null, TaskEnv.None);

            // get the apikey, which we will need as a subscription auth token for subscriptions not done on behalf of a user
            _apikey = config["apikey"].AsText;

            // build ignore list
            string ignore = config["ignore"].AsText;

            if (!string.IsNullOrEmpty(ignore))
            {
                foreach (string page in ignore.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    _ignore[page] = null;
                }
            }
            // set up subscription for page views
            XDoc subscription = new XDoc("subscription-set")
                                .Elem("uri.owner", Self.Uri.AsServerUri().ToString())
                                .Start("subscription")
                                .Elem("channel", "event://*/deki/pages/view")
                                .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument)
                                .Start("recipient")
                                .Attr("authtoken", _apikey)
                                .Elem("uri", Self.Uri.AsServerUri().At("notify", "view").ToString())
                                .End()
                                .End();
            Result <DreamMessage> subscribe;

            yield return(subscribe = PubSub.At("subscribers").PostAsync(subscription));

            string accessKey = subscribe.Value.ToDocument()["access-key"].AsText;
            XUri   location  = subscribe.Value.Headers.Location;

            Cookies.Update(DreamCookie.NewSetCookie("access-key", accessKey, location), null);
            _subscriptionLocation = Plug.New(location.AsLocalUri().WithoutQuery());
            _log.DebugFormat("set up initial subscription location at {0}", _subscriptionLocation.Uri);
            result.Return();
        }
Esempio n. 3
0
        protected override Yield Start(XDoc config, IContainer container, Result result)
        {
            yield return(Coroutine.Invoke(base.Start, config, new Result()));

            // ensure imagemagick is setup correctly.
            if (string.IsNullOrEmpty(ImageMagickConvertPath))
            {
                throw new NotImplementedException("Please set 'imagemagick-convert-path' in config to path of ImageMagick's 'convert'");
            }
            if (!File.Exists(ImageMagickIdentifyPath))
            {
                throw new FileNotFoundException("Cannot find ImagicMagick 'identify' binary: ", ImageMagickIdentifyPath);
            }
            if (string.IsNullOrEmpty(ImageMagickIdentifyPath))
            {
                throw new NotImplementedException("Please set 'imagemagick-identify-path' in config to path of ImageMagick's 'identify'");
            }
            if (!File.Exists(ImageMagickConvertPath))
            {
                throw new FileNotFoundException("Cannot find ImagicMagick 'convert' binary: ", ImageMagickConvertPath);
            }

            // check for 'apikey'
            _apikey = Config["api-key"].AsText ?? Config["apikey"].AsText;
            if (string.IsNullOrEmpty(_apikey))
            {
                throw new ArgumentNullException("apikey", "The global apikey is not defined. Please ensure that you have a global <apikey> defined in the MindTouch Core service settings xml file.");
            }
            InitializeContainer(container);

            // intialize instance manager
            _instanceManager = InstanceManager.New(this, this.TimerFactory);

            // setup resource manager
            lock (SyncRoot) {
                if (ResourceManager == null)
                {
                    ResourceManager = new PlainTextResourceManager(ResourcesPath);
                    ScreenFont      = new DekiFont(Plug.New("resource://mindtouch.deki/MindTouch.Deki.Resources.Arial.mtdf").Get().AsBytes());
                }
            }

            // initialize scripting engine
            XDoc scripting = Config["scripting"];

            DekiScriptLibrary.InsertTextLimit = scripting["max-web-response-length"].AsLong ?? DekiScriptLibrary.InsertTextLimit;
            DekiScriptLibrary.MinCacheTtl     = scripting["min-web-cache-ttl"].AsDouble ?? DekiScriptLibrary.MinCacheTtl;

            // set up deki pub sub (by default we override uri.publish with our own service, unless @must-use=true is specified)
            if (!(Config["uri.publish/@must-use"].AsBool ?? false))
            {
                Result <Plug> pubsubResult;
                XDoc          pubsubConfig = new XDoc("config")
                                             .Elem("uri.deki", Self.Uri.With("apikey", MasterApiKey))
                                             .Start("downstream")
                                             .Elem("uri", PubSub.At("publish").Uri.WithoutLastSegment().At("subscribers"))
                                             .End()
                                             .Start("components")
                                             .Start("component")
                                             .Attr("type", typeof(IPubSubDispatcher).AssemblyQualifiedName)
                                             .Attr("implementation", typeof(DekiDispatcher).AssemblyQualifiedName)
                                             .End()
                                             .End()
                                             .Elem("authtoken", MasterApiKey);
                foreach (var cookie in Cookies.Fetch(PubSub.Uri))
                {
                    pubsubConfig.Add(cookie.AsSetCookieDocument);
                }
                var messageQueuePath = config["publish/queue-path"].AsText;
                if (!string.IsNullOrEmpty(messageQueuePath))
                {
                    pubsubConfig.Elem("queue-path", messageQueuePath);
                }
                yield return(pubsubResult = CreateService(
                                 "pubsub",
                                 "sid://mindtouch.com/dream/2008/10/pubsub",
                                 pubsubConfig,
                                 new Result <Plug>()));

                PubSub = pubsubResult.Value;
            }

            // set up package updater service (unless it was passed in)
            XUri packageUpdater;

            if (config["packageupdater/@uri"].IsEmpty)
            {
                var packageConfig = config["packageupdater"];
                packageConfig = packageConfig.IsEmpty ? new XDoc("config") : packageConfig.Clone();
                if (packageConfig["package-path"].IsEmpty)
                {
                    packageConfig.Elem("package-path", Path.Combine(Path.Combine(config["deki-path"].AsText, "packages"), "default"));
                }
                yield return(CreateService(
                                 "packageupdater",
                                 "sid://mindtouch.com/2010/04/packageupdater",
                                 new XDoc("config")
                                 .Elem("apikey", MasterApiKey)
                                 .AddNodes(packageConfig),
                                 new Result <Plug>()
                                 ));

                packageUpdater = Self.Uri.At("packageupdater");
            }
            else
            {
                packageUpdater = config["packageupdater/@uri"].AsUri;
            }
            _packageUpdater = Plug.New(packageUpdater);

            // set up emailer service (unless it was passed in)
            XUri mailerUri;

            if (config["uri.mailer"].IsEmpty)
            {
                yield return(CreateService(
                                 "mailer",
                                 "sid://mindtouch.com/2009/01/dream/email",
                                 new XDoc("config")
                                 .Elem("apikey", MasterApiKey)
                                 .AddAll(Config["smtp/*"]),
                                 new Result <Plug>()
                                 ));

                mailerUri = Self.Uri.At("mailer");
            }
            else
            {
                mailerUri = config["uri.mailer"].AsUri;
            }
            _mailer = Plug.New(mailerUri);

            // set up the email subscription service (unless it was passed in)
            XUri pageSubscription;

            if (config["uri.page-subscription"].IsEmpty)
            {
                XDoc pagesubserviceConfig = new XDoc("config")
                                            .Elem("uri.deki", Self.Uri)
                                            .Elem("uri.emailer", mailerUri.At("message"))
                                            .Elem("resources-path", ResourcesPath)
                                            .Elem("apikey", MasterApiKey)
                                            .Start("components")
                                            .Start("component")
                                            .Attr("scope", "factory")
                                            .Attr("type", typeof(IPageSubscriptionDataSessionFactory).AssemblyQualifiedName)
                                            .Attr("implementation", "MindTouch.Deki.Data.MySql.UserSubscription.MySqlPageSubscriptionSessionFactory, mindtouch.deki.data.mysql")
                                            .End()
                                            .End()
                                            .AddAll(Config["page-subscription/*"]);
                foreach (var cookie in Cookies.Fetch(mailerUri))
                {
                    pagesubserviceConfig.Add(cookie.AsSetCookieDocument);
                }
                yield return(CreateService(
                                 "pagesubservice",
                                 "sid://mindtouch.com/deki/2008/11/changesubscription",
                                 pagesubserviceConfig,
                                 new Result <Plug>()
                                 ));

                pageSubscription = Self.Uri.At("pagesubservice");
                config.Elem("uri.page-subscription", pageSubscription);
            }
            else
            {
                pageSubscription = config["uri.page-subscription"].AsUri;
            }
            _pageSubscription = Plug.New(pageSubscription);

            // set up package importer, if not provided
            if (Config["uri.package"].IsEmpty)
            {
                yield return(CreateService(
                                 "package",
                                 "sid://mindtouch.com/2009/07/package",
                                 new XDoc("config").Elem("uri.deki", Self.Uri),
                                 new Result <Plug>()));

                Config.Elem("uri.package", Self.Uri.At("package"));
            }

            // set up lucene
            _luceneIndex = Plug.New(Config["indexer/@src"].AsUri);
            if (_luceneIndex == null)
            {
                // create the indexer service
                XDoc luceneIndexConfig = new XDoc("config")
                                         .AddNodes(Config["indexer"])
                                         .Start("apikey").Attr("hidden", true).Value(MasterApiKey).End();
                if (luceneIndexConfig["path.store"].IsEmpty)
                {
                    luceneIndexConfig.Elem("path.store", Path.Combine(Path.Combine(config["deki-path"].AsText, "luceneindex"), "$1"));
                }
                yield return(CreateService("luceneindex", SID_FOR_LUCENE_INDEX, luceneIndexConfig, new Result <Plug>()).Set(v => _luceneIndex = v));

                _isLocalLuceneService = true;
            }
            else
            {
                // push our host's pubsub service to lucene, to keep it up to date on our changes
                var pubsub = new XDoc("pubsub").Attr("href", PubSub);
                foreach (var cookie in PubSub.CookieJar.Fetch(PubSub.Uri))
                {
                    pubsub.Add(cookie.AsSetCookieDocument);
                }
                yield return(_luceneIndex.At("subscriptions").PostAsync(pubsub));
            }

            // configure indexing whitelist
            _indexNamespaceWhitelist = new[] { NS.MAIN, NS.PROJECT, NS.USER, NS.TEMPLATE, NS.HELP, NS.MAIN_TALK, NS.PROJECT_TALK, NS.USER_TALK, NS.TEMPLATE_TALK, NS.HELP_TALK, NS.SPECIAL, NS.SPECIAL_TALK };
            if (!string.IsNullOrEmpty(Config["indexer/namespace-whitelist"].AsText))
            {
                List <NS> customWhitelist = new List <NS>();
                foreach (string item in Config["indexer/namespace-whitelist"].AsText.Split(','))
                {
                    NS ns;
                    if (SysUtil.TryParseEnum(item, out ns))
                    {
                        customWhitelist.Add(ns);
                    }
                }
                _indexNamespaceWhitelist = customWhitelist.ToArray();
            }

            if (!Config["wikis/globalconfig/cache/varnish"].IsEmpty)
            {
                // create the varnish service

                // TODO (petee): getting the varnish config from wikis/globalconfig/cache is a hack
                // The frontend needs to get the max-age to send out the cache headers but we currently have no way
                // of getting the DekiWikiService config so we'll hack it so it comes back in GET:site/settings.
                XDoc varnishConfig = new XDoc("config")
                                     .Elem("uri.deki", Self.Uri.With("apikey", MasterApiKey))
                                     .Elem("uri.varnish", Config["wikis/globalconfig/cache/varnish"].AsUri)
                                     .Elem("varnish-purge-delay", Config["wikis/globalconfig/cache/varnish-purge-delay"].AsInt ?? 10)
                                     .Elem("varnish-max-age", Config["wikis/globalconfig/cache/varnish-max-age"].AsInt ?? 300)
                                     .Start("apikey").Attr("hidden", true).Value(MasterApiKey).End();
                yield return(CreateService("varnish", SID_FOR_VARNISH_SERVICE, varnishConfig, new Result <Plug>()));
            }
            _isInitialized = true;
            result.Return();
        }
Esempio n. 4
0
        //--- Methods ---
        protected override Yield Start(XDoc config, Result result)
        {
            yield return(Coroutine.Invoke(base.Start, config, new Result()));

            // get the apikey, which we will need as a subscription auth token for subscriptions not done on behalf of a user
            _apikey = config["apikey"].AsText;

            // capture the wikiId of the wiki starting us
            string wikiId = config["wikiid"].AsText ?? "default";

            // set up plug deki, so we can validate users
            XUri dekiUri = config["uri.deki"].AsUri ?? new XUri("http://localhost:8081/deki");

            _deki = Plug.New(dekiUri).With("apikey", _apikey).WithHeader("X-Deki-Site", "id=" + wikiId);

            // get ajax polling interval
            _pollInterval = TimeSpan.FromSeconds(config["poll-interval"].AsDouble ?? 60);

            // set up subscription reaper
            TaskTimer.New(TimeSpan.FromSeconds(60), timer => {
                lock (_subscriptions) {
                    var staleSubs = new List <uint>();
                    foreach (KeyValuePair <uint, Subscription> pair in _subscriptions)
                    {
                        if (pair.Value.LastTouched.Add(_pollInterval).Add(TimeSpan.FromSeconds(10)) < DateTime.UtcNow)
                        {
                            staleSubs.Add(pair.Key);
                        }
                    }
                    foreach (uint pageId in staleSubs)
                    {
                        _log.DebugFormat("removing subscription for {0}", pageId);
                        _subscriptions.Remove(pageId);
                    }
                }
                timer.Change(TimeSpan.FromSeconds(60), TaskEnv.None);
            }, null, TaskEnv.None);


            // set up subscription for pubsub
            XDoc subscription = new XDoc("subscription-set")
                                .Elem("uri.owner", Self.Uri.AsServerUri().ToString())
                                .Start("subscription")
                                .Elem("channel", string.Format("event://{0}/deki/pages/update", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/revert", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/tags/update", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/comments/create", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/comments/update", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/comments/delete", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/files/create", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/files/update", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/files/delete", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/files/properties/*", wikiId))
                                .Elem("channel", string.Format("event://{0}/deki/pages/dependentschanged/files/restore", wikiId))
                                .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument)
                                .Start("recipient")
                                .Attr("authtoken", _apikey)
                                .Elem("uri", Self.Uri.AsServerUri().At("notify").ToString())
                                .End()
                                .End();
            Result <DreamMessage> subscribe;

            yield return(subscribe = PubSub.At("subscribers").PostAsync(subscription));

            string accessKey = subscribe.Value.ToDocument()["access-key"].AsText;
            XUri   location  = subscribe.Value.Headers.Location;

            Cookies.Update(DreamCookie.NewSetCookie("access-key", accessKey, location), null);
            _subscriptionLocation = Plug.New(location.AsLocalUri().WithoutQuery());
            _log.DebugFormat("set up initial subscription location at {0}", _subscriptionLocation.Uri);
            result.Return();
        }
        //--- Methods ---
        protected override Yield Start(XDoc config, IContainer container, Result result)
        {
            yield return(Coroutine.Invoke(base.Start, config, new Result()));

            // set up plug for phpscript that will handle the notifications
            _emailer = Plug.New(config["uri.emailer"].AsUri);

            // set up plug deki, so we can validate users
            _deki = Plug.New(config["uri.deki"].AsUri);

            // get the apikey, which we will need as a subscription auth token for subscriptions not done on behalf of a user
            _apikey = config["apikey"].AsText;
            _cache  = new PageChangeCache(_deki.With("apikey", _apikey), TimeSpan.FromSeconds(config["page-cache-ttl"].AsInt ?? 2));

            if (!container.IsRegistered <IPageSubscriptionInstance>())
            {
                var builder = new ContainerBuilder();
                builder.Register <PageSubscriptionInstance>().As <IPageSubscriptionInstance>().FactoryScoped();
                builder.Build(container);
            }

            // TODO (arnec): this should be hitting the API to retrieve resources

            // resource manager for email template
            var resourcePath = Config["resources-path"].AsText;

            if (!string.IsNullOrEmpty(resourcePath))
            {
                _resourceManager = new PlainTextResourceManager(Environment.ExpandEnvironmentVariables(resourcePath));
            }
            else
            {
                // creating a test resource manager
                _log.WarnFormat("'resource-path' was not defined in Config, using a test resource manager for email templating");
                var testSet = new TestResourceSet {
                    { "Notification.Page.email-subject", "Page Modified" },
                    { "Notification.Page.email-header", "The following pages have changed:" }
                };
                _resourceManager = new PlainTextResourceManager(testSet);
            }

            // set up subscription for pubsub
            var subscriptionSet = new XDoc("subscription-set")
                                  .Elem("uri.owner", Self.Uri.AsServerUri().ToString())
                                  .Start("subscription")
                                  .Elem("channel", "event://*/deki/users/*")
                                  .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument)
                                  .Start("recipient")
                                  .Attr("authtoken", _apikey)
                                  .Elem("uri", Self.Uri.AsServerUri().At("updateuser").ToString())
                                  .End()
                                  .End()
                                  .Start("subscription")
                                  .Elem("channel", "event://*/deki/pages/create")
                                  .Elem("channel", "event://*/deki/pages/update")
                                  .Elem("channel", "event://*/deki/pages/delete")
                                  .Elem("channel", "event://*/deki/pages/revert")
                                  .Elem("channel", "event://*/deki/pages/move")
                                  .Elem("channel", "event://*/deki/pages/tags/update")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/comments/create")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/comments/update")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/comments/delete")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/files/create")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/files/update")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/files/delete")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/files/properties/*")
                                  .Elem("channel", "event://*/deki/pages/dependentschanged/files/restore")
                                  .Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument)
                                  .Start("recipient")
                                  .Attr("authtoken", _apikey)
                                  .Elem("uri", Self.Uri.AsServerUri().At("notify").ToString())
                                  .End()
                                  .End();
            Result <DreamMessage> subscribe;

            yield return(subscribe = PubSub.At("subscribers").PostAsync(subscriptionSet));

            string accessKey = subscribe.Value.ToDocument()["access-key"].AsText;
            XUri   location  = subscribe.Value.Headers.Location;

            Cookies.Update(DreamCookie.NewSetCookie("access-key", accessKey, location), null);
            _subscriptionLocation = Plug.New(location.AsLocalUri().WithoutQuery());
            _log.DebugFormat("set up initial subscription location at {0}", _subscriptionLocation.Uri);

            // set up notification accumulator queue
            TimeSpan accumulationMinutes = TimeSpan.FromSeconds(config["accumulation-time"].AsInt ?? 10 * 60);

            _log.DebugFormat("Initializing queue with {0:0.00} minute accumulation", accumulationMinutes.TotalMinutes);
            _notificationQueue = new NotificationDelayQueue(accumulationMinutes, SendEmail);
            result.Return();
        }