JSON web service

Planning poker web consists of PlanningPokerController, that manages list of all scrum teams, and PlanningPokerService, that implements WCF JSON service.

PlanningPokerController

PlanningPokerController class is responsible to create and manage scrum teams in thread-safe manner. It has following methods:
  • ScrumTeamNames property returns collection of all scrum team names.
  • CreateScrumTeam creates new scrum team with specified team name and scrum master name. If team with the same name already exists, exception is thrown.
  • GetScrumTeam gets scrum team with specified name.
  • AttachScrumTeam adds existing scrum team to the controller. This method is used only in Azure version.
  • GetMessagesAsync sets callback handler that is called when a message is received by observer. If there is already a message in queue then callback is called immediately.
  • DisconnectInactiveObservers removes observers, which have not checked for messages for specified period of time. These observers probably closed web browser without disconnecting from team or lost internet connection.

PlanningPokerService

PlanningPokerService provides operations used by JavaScript client. Operations are very simple like CreateTeam, JoinTeam, SubmitEstimation. The only interesting operation is GetMessages. This is asynchronous operation implemented by 2 methods:
  • BeginGetMessages starts the process of waiting for message.
  • EndGetMessages called by callback when message is received and actually returns received messages.

For this purpose TaskCompletionSource is used. This object controls Task object that represents asynchronous operation of getting messages.

Start method creates new task completion source and start the get messages operation asynchronously.

public void Start()
{
    this.taskCompletionSource = new TaskCompletionSource<List<Message>>(this.AsyncState);
    Task.Factory.StartNew(this.SetProcessMessagesHandler);
}


SetProcessMessagesHandler finds scrum team and observer. Then it removes messages from queue, which were already read. And at last handler to process receiving messages is set on observer.

private void SetProcessMessagesHandler()
{
    try
    {
        using (var teamLock = this.PlanningPoker.GetScrumTeam(this.TeamName))
        {
            teamLock.Lock();
            var team = teamLock.Team;
            var member = team.FindMemberOrObserver(this.MemberName);

            // Updates last activity on member to record time, when member checked for new messages.
            member.UpdateActivity();

            // Removes old messages, which the member has already read, from the member's message queue.
            while (member.HasMessage && member.Messages.First().Id <= this.LastMessageId)
            {
                member.PopMessage();
            }

            this.PlanningPoker.GetMessagesAsync(member, this.ProcessMessages);
        }
    }
    catch (Exception ex)
    {
        this.ThrowException(ex);
    }
}


ProcessMessages is called by controller, when a message is received. It simply sets the result of TaskCompletionSource, so it notifies that asynchronous operation is finished and WCF callback is executed.

private void ProcessMessages(bool hasMessages, D.Observer member)
{
    try
    {
        List<Message> result;
        if (hasMessages)
        {
            result = member.Messages.Select(m => ServiceEntityMapper.Map<D.Message, Message>(m)).ToList();
        }
        else
        {
            result = new List<Message>();
        }

        this.ReturnResult(result);
    }
    catch (Exception ex)
    {
        this.ThrowException(ex);
    }
}


At last ReturnResult method sets result on TaskCompletionSource and ThrowException sets error on TaskCompletionSource.

private void ReturnResult(List<Message> result)
{
    Task.Factory.StartNew(() =>
    {
        this.taskCompletionSource.SetResult(result);
        if (this.Callback != null)
        {
            this.Callback(this.ProcessMessagesTask);
        }
    });
}

private void ThrowException(Exception ex)
{
    Task.Factory.StartNew(() =>
    {
        this.taskCompletionSource.SetException(ex);
        if (this.Callback != null)
        {
            this.Callback(this.ProcessMessagesTask);
        }
    });
}

Last edited Nov 2, 2012 at 10:12 AM by Duracellko, version 1

Comments

No comments yet.