예제 #1
0
        // *************************************************************
        // Workaround to maintain cache integrity in authorization token revoke scenario
        public void RemoveAuthorizationTokenFromCache(ActivityDO activity)
        {
            lock (_loadedNodes)
            {
                foreach (var loadedPlan in _loadedPlans)
                {
                    PlanTreeHelper.Visit(loadedPlan.Root, x =>
                    {
                        var a = x as ActivityDO;

                        if (a != null && a.Id == activity.Id)
                        {
                            a.AuthorizationToken   = null;
                            a.AuthorizationTokenId = null;
                        }
                    });
                }
            }

            _planStorage.UpdateElement(activity.Id, x =>
            {
                var a = x as ActivityDO;

                if (a != null)
                {
                    a.AuthorizationToken   = null;
                    a.AuthorizationTokenId = null;
                }
            });
        }
예제 #2
0
        /**********************************************************************************/

        public TPlanNode GetById <TPlanNode>(Guid id)
            where TPlanNode : PlanNodeDO
        {
            lock (_loadedNodes)
            {
                LoadedPlan loadedPlan;

                // if we have loaded this node before?
                if (!_loadedNodes.TryGetValue(id, out loadedPlan))
                {
                    // try to load plan for this node
                    var plan = GetPlanByMemberId(id);

                    // non existent node or new node that has not been saved yet
                    if (plan == null)
                    {
                        return(null);
                    }

                    loadedPlan = new LoadedPlan(plan);
                    _loadedPlans.Add(loadedPlan);
                    // add all noded to the loaded nodes list
                    PlanTreeHelper.Visit(plan, x => _loadedNodes.Add(x.Id, loadedPlan));
                }

                // search for the node in the corresponding plans
                return((TPlanNode)loadedPlan.Find(id));
            }
        }
예제 #3
0
        /**********************************************************************************/

        private void AddToCache(PlanNodeDO root)
        {
            var expirOn    = _expirationStrategy.NewExpirationToken();
            var cachedPlan = new CachedPlan(root, expirOn);

            _plans.Add(root.Id, cachedPlan);

            PlanTreeHelper.Visit(root, x => _planNodesLookup.Add(x.Id, new CacheItem(x, cachedPlan)));
        }
예제 #4
0
        /**********************************************************************************/

        public PlanSnapshot(PlanNodeDO node, bool cloneNodes)
        {
            PlanTreeHelper.Visit(node, (x, y) =>
            {
                var clone = cloneNodes ? x.Clone() : x;

                _nodes[x.Id] = clone;

                clone.ParentPlanNodeId = y != null ? y.Id : (Guid?)null;
            });
        }
예제 #5
0
        /**********************************************************************************/

        private void DropCachedPlan(PlanNodeDO root)
        {
            CachedPlan cachedPlan;

            if (!_plans.TryGetValue(root.Id, out cachedPlan))
            {
                return;
            }

            PlanTreeHelper.Visit(root, x => _planNodesLookup.Remove(x.Id));

            _plans.Remove(root.Id);
        }
예제 #6
0
        /**********************************************************************************/

        private void RemoveExpiredPlans()
        {
            lock (_sync)
            {
                foreach (var planExpiration in _plans.ToArray())
                {
                    if (planExpiration.Value.Expiration.IsExpired())
                    {
                        _plans.Remove(planExpiration.Key);
                        PlanTreeHelper.Visit(planExpiration.Value.Root, x => _planNodesLookup.Remove(x.Id));
                    }
                }
            }
        }
예제 #7
0
        /**********************************************************************************/
        // this is just simplification for the first implementation.
        // We can only insert plans. If we want to edit plan, we need to get corresponding node and edit it's children
        public void Add(PlanDO plan)
        {
            lock (_loadedNodes)
            {
                var loadedPlan = new LoadedPlan(plan, true);

                PlanTreeHelper.Visit(plan, x =>
                {
                    if (x.Id == Guid.Empty)
                    {
                        x.Id = Guid.NewGuid();
                    }

                    _loadedNodes.Add(x.Id, loadedPlan);
                });
                _loadedPlans.Add(loadedPlan);
            }
        }
예제 #8
0
        /**********************************************************************************/

        public PlanNodeDO Get(Guid id, Func <Guid, PlanNodeDO> cacheMissCallback)
        {
            PlanNodeDO node;

            lock (_sync)
            {
                CacheItem cacheItem;

                if (!_planNodesLookup.TryGetValue(id, out cacheItem))
                {
                    node = cacheMissCallback(id);

                    if (node == null)
                    {
                        return(null);
                    }

                    // Get the root of PlanNode tree.
                    while (node.ParentPlanNode != null)
                    {
                        node = node.ParentPlanNode;
                    }

                    // Check cache integrity
                    if (PlanTreeHelper.Linearize(node).Any(x => _planNodesLookup.ContainsKey(x.Id)))
                    {
                        DropCachedPlan(node);
                    }

                    AddToCache(node);
                }
                else
                {
                    node = cacheItem.Plan.Root;
                    // update plan expiration
                    cacheItem.Plan.Expiration = _expirationStrategy.NewExpirationToken();
                }

                node = PlanTreeHelper.CloneWithStructure(node);
            }

            return(node);
        }
예제 #9
0
        /**********************************************************************************/

        public void SaveChanges()
        {
            lock (_loadedNodes)
            {
                foreach (var loadedPlan in _loadedPlans)
                {
                    var plan = loadedPlan;
                    plan.Root.LastUpdated = DateTimeOffset.UtcNow;
                    var parentPlan = loadedPlan.Root as PlanDO;

                    PlanTreeHelper.Visit(loadedPlan.Root, (x, y) =>
                    {
                        if (x.Id == Guid.Empty)
                        {
                            x.Id = Guid.NewGuid();
                        }

                        x.ParentPlanNode   = y;
                        x.ParentPlanNodeId = y != null ? y.Id : (Guid?)null;

                        if (parentPlan != null)
                        {
                            x.Fr8AccountId   = parentPlan.Fr8AccountId;
                            x.Fr8Account     = parentPlan.Fr8Account;
                            x.RootPlanNodeId = parentPlan.Id;

                            UpdateForeignKeys(x);
                        }
                        else
                        {
                            UpdateForeignKeys(x);
                        }

                        _loadedNodes[x.Id] = plan;
                    });

                    var previous = loadedPlan.RebuildSnapshot();
                    var changes  = loadedPlan.Snapshot.Compare(previous);

                    _planStorage.Update(loadedPlan.Root.Id, changes);
                }
            }
        }
예제 #10
0
        public PlanNodeDO Find(Guid nodeId)
        {
            if (IsDeleted)
            {
                return(null);
            }

            PlanNodeDO result = null;

            PlanTreeHelper.Visit(Root, x =>
            {
                if (x.Id == nodeId)
                {
                    result = x;
                    return(false);
                }

                return(true);
            });

            return(result);
        }
예제 #11
0
        /**********************************************************************************/

        public PlanSnapshot.Changes Update(Guid planId, PlanSnapshot.Changes changes)
        {
            var validChanges = new PlanSnapshot.Changes();

            lock (_sync)
            {
                CachedPlan plan;

                if (!_plans.TryGetValue(planId, out plan))
                {
                    foreach (var insert in changes.Insert)
                    {
                        var clone = insert.Clone();

                        if (insert is PlanDO)
                        {
                            plan = new CachedPlan((PlanDO)clone, _expirationStrategy.NewExpirationToken());
                            _plans.Add(planId, plan);
                            _planNodesLookup.Add(planId, new CacheItem(clone, plan));
                            clone.RootPlanNode = clone;
                            break;
                        }
                    }
                }

                foreach (var insert in changes.Insert)
                {
                    if (!_planNodesLookup.ContainsKey(insert.Id))
                    {
                        if (insert.ParentPlanNodeId == null || _planNodesLookup.ContainsKey(insert.ParentPlanNodeId.Value))
                        {
                            _planNodesLookup.Add(insert.Id, new CacheItem(insert.Clone(), plan));
                        }
                    }
                }

                foreach (var insert in changes.Insert)
                {
                    CacheItem nodeCacheItem;

                    if (!_planNodesLookup.TryGetValue(insert.Id, out nodeCacheItem))
                    {
                        continue;
                    }

                    validChanges.Insert.Add(insert);

                    var node = nodeCacheItem.Node;

                    if (insert.ParentPlanNodeId != null)
                    {
                        var parent = _planNodesLookup[insert.ParentPlanNodeId.Value].Node;

                        parent.ChildNodes.Add(node);
                        node.ParentPlanNode = parent;
                        node.RootPlanNode   = plan.Root;
                    }
                }

                foreach (var deleted in changes.Delete)
                {
                    CachedPlan cachedPlan;

                    if (_plans.TryGetValue(deleted.Id, out cachedPlan))
                    {
                        PlanTreeHelper.Visit(plan.Root, x =>
                        {
                            _planNodesLookup.Remove(x.Id);
                            validChanges.Delete.Add(x);
                        });

                        _plans.Remove(plan.Root.Id);

                        return(validChanges);
                    }

                    CacheItem node;
                    if (_planNodesLookup.TryGetValue(deleted.Id, out node))
                    {
                        validChanges.Delete.Add(deleted);
                        _planNodesLookup.Remove(deleted.Id);
                        node.Node.RemoveFromParent();
                    }
                }

                foreach (var update in changes.Update)
                {
                    bool approveUpdate = false;

                    foreach (var changedProperty in update.ChangedProperties)
                    {
                        CacheItem originalCacheItem;

                        if (!_planNodesLookup.TryGetValue(update.Node.Id, out originalCacheItem))
                        {
                            continue;
                        }

                        var original = originalCacheItem.Node;

                        // structure was changed
                        if (changedProperty.Name == "ParentPlanNodeId")
                        {
                            CacheItem parentCacheItem;

                            if (update.Node.ParentPlanNodeId == null || !_planNodesLookup.TryGetValue(update.Node.ParentPlanNodeId.Value, out parentCacheItem))
                            {
                                continue;
                            }

                            var parent = parentCacheItem.Node;

                            original.RemoveFromParent();
                            parent.ChildNodes.Add(original);
                            original.ParentPlanNode   = parent;
                            original.ParentPlanNodeId = parent.Id;

                            approveUpdate = true;
                        }
                        else
                        {
                            approveUpdate = true;
                            changedProperty.SetValue(original, changedProperty.GetValue(update.Node));
                        }
                    }

                    if (approveUpdate)
                    {
                        validChanges.Update.Add(update);
                    }
                }
            }

            return(validChanges);
        }
예제 #12
0
        /**********************************************************************************/
        // This method updates locally cached elements from global cache or DB
        // This method will overwrite all local changes
        public TPlanNode Reload <TPlanNode>(Guid id)
            where TPlanNode : PlanNodeDO
        {
            var planFromDb = GetPlanByMemberId(id);

            lock (_loadedNodes)
            {
                // have we already loaded this plan?
                var loadedPlan = _loadedPlans.FirstOrDefault(x => x.Root.Id == planFromDb.Id);

                if (loadedPlan == null)
                {
                    //if no, then just get this plan by id
                    return(GetById <TPlanNode>(id));
                }

                // get list of currently loaded items
                var currentNodes = PlanTreeHelper.Linearize(loadedPlan.Root).ToDictionary(x => x.Id, x => x);
                var dbNodes      = PlanTreeHelper.Linearize(planFromDb).ToDictionary(x => x.Id, x => x);

                foreach (var planNodeDo in dbNodes)
                {
                    PlanNodeDO currentNode;

                    // sync structure
                    var originalChildren = planNodeDo.Value.ChildNodes;
                    planNodeDo.Value.ChildNodes = new List <PlanNodeDO>(originalChildren.Count);

                    foreach (var childNode in originalChildren)
                    {
                        planNodeDo.Value.ChildNodes.Add(GetNewNodeOrGetExising(currentNodes, childNode));
                    }

                    planNodeDo.Value.ParentPlanNode = GetNewNodeOrGetExising(currentNodes, planNodeDo.Value.ParentPlanNode);
                    planNodeDo.Value.RootPlanNode   = GetNewNodeOrGetExising(currentNodes, planNodeDo.Value.RootPlanNode);

                    if (currentNodes.TryGetValue(planNodeDo.Key, out currentNode))
                    {
                        //sync local cached properties with db one
                        currentNode.SyncPropertiesWith(planNodeDo.Value);
                        currentNode.ChildNodes     = planNodeDo.Value.ChildNodes;
                        currentNode.ParentPlanNode = planNodeDo.Value.ParentPlanNode;
                        currentNode.RootPlanNode   = planNodeDo.Value.RootPlanNode;
                    }
                    else // we don't have this node in our local cache.
                    {
                        _loadedNodes[planNodeDo.Key] = loadedPlan;
                    }
                }

                // remove nodes, that we deleted in the DB version
                foreach (var planNodeDo in currentNodes)
                {
                    if (!dbNodes.ContainsKey(planNodeDo.Key))
                    {
                        _loadedNodes.Remove(planNodeDo.Key);
                    }
                }

                loadedPlan.RebuildSnapshot();
                return((TPlanNode)loadedPlan.Find(id));
            }
        }