/// <summary>
        /// Adds neighborhood actions that will announce adding or changing of a specific activity to all followers of the proximity server.
        /// </summary>
        /// <param name="ActionType">Type of action on the identity profile.</param>
        /// <param name="ActivityId">User defined activity ID.</param>
        /// <param name="OwnerIdentityId">Network identifier of the action owner's identity.</param>
        /// <param name="AdditionalData">Additional data to store with the action.</param>
        /// <returns>
        /// true if at least one new action was added to the database, false otherwise.
        /// <para>
        /// This function can throw database exception and the caller is expected to call it within try/catch block.
        /// </para>
        /// </returns>
        /// <remarks>The caller of this function is responsible starting a database transaction with FollowerLock and NeighborhoodActionLock locks.</remarks>
        public async Task <bool> AddActivityFollowerActionsAsync(NeighborhoodActionType ActionType, uint ActivityId, byte[] OwnerIdentityId, string AdditionalData = null)
        {
            log.Trace("(ActionType:{0},ActivityId:{1},OwnerIdentityId:'{2}')", ActionType, ActivityId, OwnerIdentityId.ToHex());

            bool            res       = false;
            List <Follower> followers = (await unitOfWork.FollowerRepository.GetAsync()).ToList();

            if (followers.Count > 0)
            {
                // Disable change tracking for faster multiple inserts.
                unitOfWork.Context.ChangeTracker.AutoDetectChangesEnabled = false;

                DateTime now = DateTime.UtcNow;
                foreach (Follower follower in followers)
                {
                    NeighborhoodAction neighborhoodAction = new NeighborhoodAction()
                    {
                        ServerId              = follower.NetworkId,
                        ExecuteAfter          = null,
                        TargetActivityId      = ActivityId,
                        TargetActivityOwnerId = OwnerIdentityId,
                        Timestamp             = now,
                        Type           = ActionType,
                        AdditionalData = AdditionalData
                    };
                    await InsertAsync(neighborhoodAction);

                    res = true;
                    log.Trace("Activity action with activity ID {0}, owner identity ID '{1}' added for follower ID '{2}'.", ActivityId, OwnerIdentityId.ToHex(), follower.NetworkId.ToHex());
                }
            }
            else
            {
                log.Trace("No followers found to propagate identity profile change to.");
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
예제 #2
0
        /// <summary>
        /// Updates identity profile and creates neighborhood action to propagate the change unless the client did not want the propagation.
        /// </summary>
        /// <param name="IdentityId">Network identifier of the identity to update.</param>
        /// <param name="SignedProfile">Signed profile information with updated values.</param>
        /// <param name="ProfileImageChanged">True if profile image is about to change.</param>
        /// <param name="ThumbnailImageChanged">True if thumbnail image is about to change.</param>
        /// <param name="NoPropagation">True if the client does not want this change of profile to be propagated to the neighborhood.</param>
        /// <param name="IdentityNotFound">If the function fails because the identity is not found, this referenced value is set to true.</param>
        /// <param name="ImagesToDelete">If the function succeeds and the profile images are altered, old image files has to be deleted, in which case their hashes
        /// are returned in this list, which has to be initialized by the caller.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> UpdateProfileAndPropagateAsync(byte[] IdentityId, SignedProfileInformation SignedProfile, bool ProfileImageChanged, bool ThumbnailImageChanged, bool NoPropagation, StrongBox <bool> IdentityNotFound, List <byte[]> ImagesToDelete)
        {
            log.Trace("()");
            bool res = false;

            bool signalNeighborhoodAction = false;
            bool success = false;

            List <byte[]> imagesToDelete = new List <byte[]>();

            ProfileInformation profile = SignedProfile.Profile;

            DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.HostedIdentityLock, UnitOfWork.FollowerLock, UnitOfWork.NeighborhoodActionLock };
            using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
            {
                try
                {
                    HostedIdentity identity = (await GetAsync(i => (i.IdentityId == IdentityId) && (i.Cancelled == false))).FirstOrDefault();
                    if (identity != null)
                    {
                        bool isProfileInitialization = !identity.Initialized;

                        identity.Initialized = true;
                        identity.Version     = profile.Version.ToByteArray();
                        identity.Name        = profile.Name;

                        GpsLocation location = new GpsLocation(profile.Latitude, profile.Longitude);
                        identity.SetInitialLocation(location);
                        identity.ExtraData = profile.ExtraData;
                        identity.Signature = SignedProfile.Signature.ToByteArray();

                        if (ProfileImageChanged)
                        {
                            if (identity.ProfileImage != null)
                            {
                                imagesToDelete.Add(identity.ProfileImage);
                            }
                            identity.ProfileImage = profile.ProfileImageHash.Length != 0 ? profile.ProfileImageHash.ToByteArray() : null;
                        }

                        if (ThumbnailImageChanged)
                        {
                            if (identity.ThumbnailImage != null)
                            {
                                imagesToDelete.Add(identity.ThumbnailImage);
                            }
                            identity.ThumbnailImage = profile.ThumbnailImageHash.Length != 0 ? profile.ThumbnailImageHash.ToByteArray() : null;
                        }


                        Update(identity);


                        if (!NoPropagation)
                        {
                            // The profile change has to be propagated to all our followers
                            // we create database actions that will be processed by dedicated thread.
                            NeighborhoodActionType actionType = isProfileInitialization ? NeighborhoodActionType.AddProfile : NeighborhoodActionType.ChangeProfile;
                            string extraInfo = identity.PublicKey.ToHex();
                            signalNeighborhoodAction = await unitOfWork.NeighborhoodActionRepository.AddIdentityProfileFollowerActionsAsync(actionType, identity.IdentityId, extraInfo);
                        }

                        await unitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        success = true;
                    }
                    else
                    {
                        IdentityNotFound.Value = true;
                    }
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!success)
                {
                    log.Warn("Rolling back transaction.");
                    unitOfWork.SafeTransactionRollback(transaction);
                }

                unitOfWork.ReleaseLock(lockObjects);
            }

            if (success)
            {
                // Only when the function succeeds the old images can be deleted.
                ImagesToDelete.AddRange(imagesToDelete);

                // Send signal to neighborhood action processor to process the new series of actions.
                if (signalNeighborhoodAction)
                {
                    Network.NeighborhoodActionProcessor neighborhoodActionProcessor = (Network.NeighborhoodActionProcessor)Base.ComponentDictionary[Network.NeighborhoodActionProcessor.ComponentName];
                    neighborhoodActionProcessor.Signal();
                }

                res = true;
            }


            log.Trace("(-):{0}", res);
            return(res);
        }