示例#1
0
        public void TaskTimer_Shutdown()
        {
            var shouldFire = new ManualResetEvent(false);
            var neverFires = new ManualResetEvent(false);

            TaskTimer.New(
                TimeSpan.FromSeconds(2),
                delegate {
                _log.DebugFormat("this task timer should never have fired");
                neverFires.Set();
            },
                null,
                TaskEnv.None);
            TaskTimer.New(
                TimeSpan.FromSeconds(1),
                delegate {
                _log.DebugFormat("this task timer should fire before we try shutdown");
                shouldFire.Set();
            },
                null,
                TaskEnv.None);
            _log.DebugFormat("waiting for first task");
            Assert.IsTrue(shouldFire.WaitOne(2000, false));
            _log.DebugFormat("starting shutdown");
            TaskTimerFactory.Current.Dispose();
            _log.DebugFormat("shutdown complete");
            Assert.IsFalse(neverFires.WaitOne(2000, false));
        }
        private XDoc FetchSchema(Plug adoNetPlug)
        {
            XDoc   ret = null;
            string key = adoNetPlug.At(METADATA_PATH).ToString();

            lock (_cache) {
                _cache.TryGetValue(key, out ret);
            }

            if (ret == null)
            {
                string temp = adoNetPlug.At(METADATA_PATH).Get().AsTextReader().ReadToEnd();

                //HACKHACKHACK to workaround ns issue
                temp = temp.Replace("xmlns=\"http://schemas.microsoft.com/ado/2006/04/edm\"", "");
                ret  = XDocFactory.From(temp, MimeType.XML);

                // add result to cache and start a clean-up timer
                lock (_cache) {
                    _cache[key] = ret;
                }

                TaskTimer.New(TimeSpan.FromSeconds(CACHE_TTL), RemoveCachedEntry, key, TaskEnv.None);
            }

            //TODO: throw exception if schema is invalid somehow (or if the schema changed)
            return(ret);
        }
        private static string CachedWebGet(XUri uri, double?ttl, bool?nilIfMissing)
        {
            // fetch message from cache or from the web
            string result;

            lock (_webTextCache) {
                if (_webTextCache.TryGetValue(uri, out result))
                {
                    return(result);
                }
            }

            // do the web request
            Result <DreamMessage> response = new Result <DreamMessage>();

            Plug.New(uri).WithTimeout(DEFAULT_WEB_TIMEOUT).InvokeEx("GET", DreamMessage.Ok(), response);
            DreamMessage message = response.Wait();

            try {
                // check message status
                if (!message.IsSuccessful)
                {
                    if (nilIfMissing.GetValueOrDefault())
                    {
                        return(null);
                    }
                    return(message.Status == DreamStatus.UnableToConnect
                        ? string.Format("(unable to fetch text document from uri [status: {0} ({1}), message: \"{2}\"])", (int)message.Status, message.Status, message.ToDocument()["message"].AsText)
                        : string.Format("(unable to fetch text document from uri [status: {0} ({1})])", (int)message.Status, message.Status));
                }

                // check message size
                Result resMemorize = message.Memorize(InsertTextLimit, new Result()).Block();
                if (resMemorize.HasException)
                {
                    return(nilIfMissing.GetValueOrDefault() ? null : "(text document is too large)");
                }

                // detect encoding and decode response
                var stream   = message.AsStream();
                var encoding = stream.DetectEncoding() ?? message.ContentType.CharSet;
                result = encoding.GetString(stream.ReadBytes(-1));
            } finally {
                message.Close();
            }

            // start timer to clean-up cached result
            lock (_webTextCache) {
                _webTextCache[uri] = result;
            }
            double timeout = Math.Min(60 * 60 * 24, Math.Max(ttl ?? MinCacheTtl, 60));

            TaskEnv.ExecuteNew(() => TaskTimer.New(TimeSpan.FromSeconds(timeout), timer => {
                lock (_webTextCache) {
                    _webTextCache.Remove((XUri)timer.State);
                }
            }, uri, TaskEnv.None));
            return(result);
        }
示例#4
0
        private XDoc GetFeed(XUri uri, string format, Result <DreamMessage> result, int?max)
        {
            if (result.HasException)
            {
                return(new XDoc("html").Start("body").Value(string.Format("An error occurred while retrieving the feed ({0}).", result.Exception.Message)).End());
            }
            XDoc rss;

            if (result.Value.ContentType.IsXml)
            {
                rss = result.Value.ToDocument();
            }
            else
            {
                // NOTE (steveb): some servers return the wrong content-type (e.g. text/html or text/plain); ignore the content-type then and attempt to parse the document
                try {
                    rss = XDocFactory.From(result.Value.AsTextReader(), MimeType.XML);
                } catch (Exception e) {
                    return(new XDoc("html").Start("body").Value("An error occurred while retrieving the feed (invalid xml).").End());
                }
            }
            if (rss.IsEmpty)
            {
                return(new XDoc("html").Start("body").Value("An error occurred while retrieving the feed (no results returned).").End());
            }

            // check if result needs to be cached
            if (_cached > 0)
            {
                lock (_feeds) {
                    if (!_feeds.ContainsKey(uri))
                    {
                        _feeds[uri] = rss;
                        TaskTimer.New(TimeSpan.FromSeconds(_cached), OnTimeout, uri, TaskEnv.None);
                    }
                }
            }

            // Create format style parameter
            XsltArgumentList xslArg = new XsltArgumentList();

            xslArg.AddParam("format", String.Empty, format);
            if (max.HasValue)
            {
                xslArg.AddParam("max", String.Empty, max.ToString());
            }
            StringWriter writer = new StringWriter();

            _xslt.Transform(rss.AsXmlNode, xslArg, writer);
            return(AddTableStyles(XDocFactory.From("<html><body>" + writer.ToString() + "</body></html>", MimeType.HTML)));
        }
示例#5
0
        //--- Methods ---
        private Yield FetchResult(string name, XUri input, Hashtable args, Result <XDoc> response)
        {
            // build uri
            XUri uri = new XUri("http://www.dapper.net/RunDapp?v=1").With("dappName", name);

            if (input != null)
            {
                uri = uri.With("applyToUrl", input.ToString());
            }
            if (args != null)
            {
                foreach (DictionaryEntry entry in args)
                {
                    uri = uri.With(VARIABLE_PREFIX + SysUtil.ChangeType <string>(entry.Key), SysUtil.ChangeType <string>(entry.Value));
                }
            }

            // check if we have a cached result
            XDoc   result;
            string key = uri.ToString();

            lock (_cache) {
                if (_cache.TryGetValue(key, out result))
                {
                    response.Return(result);
                    yield break;
                }
            }

            // fetch result
            Result <DreamMessage> res;

            yield return(res = Plug.New(uri).GetAsync());

            if (!res.Value.IsSuccessful)
            {
                throw new DreamInternalErrorException(string.Format("Unable to process Dapp: ", input));
            }
            if (!res.Value.HasDocument)
            {
                throw new DreamInternalErrorException(string.Format("Dapp response is not XML: ", input));
            }

            // add result to cache and start a clean-up timer
            lock (_cache) {
                _cache[key] = res.Value.ToDocument();
            }
            TaskTimer.New(TimeSpan.FromSeconds(CACHE_TTL), RemoveCachedEntry, key, TaskEnv.None);
            response.Return(res.Value.ToDocument());
            yield break;
        }
 private void SetupCacheTimer(CacheEntry cacheEntry)
 {
     TaskTimer.New(cacheEntry.Expires, timer => {
         var entry = (CacheEntry)timer.State;
         _log.DebugFormat("removing '{0}' from cache", entry.Id);
         lock (entry) {
             // removing from lookup first, since a false return value on Remove indicates that we shouldn't
             // try to delete the file from disk
             if (_cacheLookup.Remove(entry.Id))
             {
                 DeleteCacheEntry(entry.Guid);
             }
         }
     }, cacheEntry, TaskEnv.None);
 }
示例#7
0
 //--- Constructors ---
 public PageChangeCache(Plug deki, TimeSpan ttl) :
     this(deki, (key, clearAction) => TaskTimer.New(ttl, timer => clearAction(), null, TaskEnv.None))
 {
 }
示例#8
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();
        }
        //--- Methods ---

        private XDoc PerformQuery(XUri dataservice, string resource, string expand, string filter, string orderby, int?skip, int?top, bool fetchSchema, out XDoc schema)
        {
            Plug p = _adoNetPlug;

            if (dataservice != null)
            {
                p = Plug.New(dataservice);
            }
            else if (p == null)
            {
                throw new ArgumentException("Missing field", "dataservice");
            }

            if (fetchSchema)
            {
                schema = FetchSchema(p);
            }
            else
            {
                schema = null;
            }

            if (!string.IsNullOrEmpty(resource))
            {
                //HACKHACKHACK: +'s aren't treated the same way as '%20' when uri is decoded on the server side
                string s = XUri.Encode(resource).Replace("+", "%20");
                p = p.At(s);
            }

            if (!string.IsNullOrEmpty(expand))
            {
                p = p.With("$expand", expand);
            }
            if (!string.IsNullOrEmpty(filter))
            {
                p = p.With("$filter", filter);
            }

            if (!string.IsNullOrEmpty(orderby))
            {
                p = p.With("$orderby", orderby);
            }

            if (skip != null)
            {
                p = p.With("$skip", skip.Value);
            }

            if (top != null)
            {
                p = p.With("$top", top.Value);
            }

            XDoc   ret = null;
            string key = p.ToString();

            lock (_cache) {
                _cache.TryGetValue(key, out ret);
            }

            if (ret == null)
            {
                ret = p.Get().ToDocument();

                // add result to cache and start a clean-up timer
                lock (_cache) {
                    _cache[key] = ret;
                }

                TaskTimer.New(TimeSpan.FromSeconds(CACHE_TTL), RemoveCachedEntry, key, TaskEnv.None);
            }
            return(ret);
        }
示例#10
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();
        }