Monday, June 25, 2012

Creating a real-time, collaborative, responsive retrospective app for agile teams in 30 minutes


Responsive web design term is related to the concept of developing a website design in a manner, that helps the lay out to get changed according to the user’s computer screen resolution. More precisely, the concept allows for an advanced 4 column layout 1292 pixels wide, on a 1025 pixel  width screen, that auto-simplifies into 2 columns. Also it suitably fixes on the smartphone and computer tablet screen.

SignalR is an Async library for .NET to help build real-time, multi-user interactive web applications
LESS extends CSS with dynamic behavior such as variables, mixins, operations and functions. LESS runs on both the client-side (Chrome, Safari, Firefox) and server-side, with Node.js and Rhino.
In this article, we’ll create a simple retrospective app using Responsive web design techniques, CSS3, LESS and SignalR that will allow multiple collaborators to review the same article in real-time.
1. Creating the html page.
Use the Less stylesheet language for creating the CSS for the website. you can download the less.js files from http://lesscss.org/ that is used in this article. You can define variables and classes using Less and then use them in the CSS files as:
@backgroundColor: #E6E6E6;
body {
       background-color: @backgroundColor;
}
.shadow(@borderColor)
{
       -moz-box-shadow: 3px 3px 4px @borderColor;
       -webkit-box-shadow: 3px 3px 4px @borderColor;
       box-shadow: 3px 3px 4px @borderColor;
}

.goodDataContent
{
       .dataContent;
       .shadow(@goodBorderColor);
       background-color: @goodDataBackgroundColor;
       margin-left: 50px;
}

2. Using media queries to render CSS based on screen preferences
Media queries are an excellent way to deliver different styles to different devices, providing the best experience for each type of user. A part of the CSS3 specification, media queries expand the role of the media attribute that controls how your styles are applied. For .e.g. in our app I have defined the image size and size of the contents by using media-queries as given below.
@media all and (max-width: 700), (max-height: 500)
{
       .goodImageContent
       {
              .imageContent(72);
              background-image:url(Images/good_78x78.png);
       }

       .badImageContent
       {
              .imageContent(72);
              background-image:url(Images/bad_72x72.png);
       }

       .betterImageContent
       {
              .imageContent(72);
              background-image:url(Images/idea_72x72.png);
       }

}

3. Creating the Retrospective manger Hub.
Hubs provide a higher level RPC framework over a PersistentConnection. If you have different types of messages that you want to send between server and client then hubs is recommended so you don't have to do your own dispatching.
To get started using Hubs, create a class that derives from Hub.
[HubName("retrospectiveManager")]
public class RetrospectiveManager : Hub
{
    [HubMethodName("newCharEntry")]
    public void RegisterNewChar(string data, string content, string teamId, string browserIp)
    {
        switch (content)
        {
            case "Good":
                Clients.addGood(data, teamId, browserIp);
                break;
            case "Bad":
                Clients.addBad(data, teamId, browserIp);
                break;
            case "Better":
                Clients.addBetter(data, teamId, browserIp);
                break;
            default:
                Clients.addMessage(data, teamId, browserIp);
                break;
        }
    }
}

Our hub receives messages via the RegisterNewChar method and broadcasts the data to addGood, addBad methods.
4. Using connections
Create a connection between the client and the server. When the connection has been started, we want to call the join method on the server.
retro = $.connection.retrospectiveManager;

$.connection.hub.start({ transport: 'auto' }, null);

retro.addGood = function (data, team, id) {
    if (browserId != id && teamId == team) {
        $('#goodDataContentDiv').html(data);
    }
};

5. HTML for the page
<div>
    <div class="sectionContent">
        <div class="goodImageContent">
        div>
        <div class="goodDataContent" contenteditable="true" accesskey="G" id="goodDataContentDiv"
            onkeyup="sendGoodChar()">
            What went well in this sprint?
            <br />
            1. 
        div>
    div>
    <div class="sectionContent">
        <div class="badImageContent">
        div>
        <div class="badDataContent" contenteditable="true" accesskey="B" id="badDataContentDiv"
            onkeyup="sendBadChar()">
            What didn't go well in this sprint?
            <br />
            1. 
        div>
    div>
    <div class="sectionContent">
        <div class="betterImageContent">
        div>
        <div class="betterDataContent" contenteditable="true" accesskey="I" id="betterDataContentDiv"
            onkeyup="sendBetterChar()">
            What can we do better next time?
            <br />
            1. 
        div>
    div>
div>

I have provided the app as free for distributed teams to use. You can use the app from the URL http://freeapps.agilecockpit.com/Retrospective.aspx. Use the teamId as the querystring for similar teams so that the teams with the same id can view data from other team members.
For e.g. you can use any number as a team id and share it with other team members and start using it like http://freeapps.agilecockpit.com/Retrospective.aspx?teamId=22

Screen shots:

Wednesday, June 20, 2012

SQLFire – setup a cluster of multiple servers


SQLFire is a memory-optimized, distributed database management system designed for applications that have demanding scalability and availability requirements. SQLFire offers some of the best features usually only seen in NoSQL databases, such as horizontal scalability, shared-nothing persistence and built in fault tolerance, but does it all while providing a real SQL interface. Applications can manage database tables entirely in memory, or they can persist tables to disk to reload the data after restarting the system. A SQLFire distributed system can be easily scaled out using commodity hardware.

In this post we’ll see how to setup and start a cluster of multiple SQLFire servers.

A SQLFire deployment consists of distributed member processes that connect to each other to form a peer-to-peer network, also known as a distributed system or SQLFire cluster. A server is a process that hosts data and is a member of a peer-to-peer distributed system.  

The connection management to the available servers is done using a locator. A locator maintains a list of available servers in the cluster, and updates that list as servers join and leave the cluster. Locators also load balance client connections across all available servers.

To create a locator you can use the sqlf locator command as given below.
sqlf locator start -dir=MyServerLocator -peer-discovery-address=localhost -peer-discovery-port=10101 -client-bind-address=localhost -client-port=1527
Where MyServerLocator is the folder created at the executing directory for the locator.

By starting the locator member first, the locator can manage cluster membership from the start as new servers join and leave the distributed system.
Use the sqlf server start command to start the servers and join them to the distributed system by specifying the locator as given below.
sqlf server start -dir=MyServer1 -locators=localhost[10101] -bind-address=localhost -client-port=1528
sqlf server start -dir=MyServer2 -locators=localhost[10101] -bind-address=localhost -client-port=1529

To connect to the SQLFire cluster using the JDBC thin client driver, use the connect client command and specify the host and port number of the SQLFire locator as
connect client 'localhost:1527';


Thursday, June 14, 2012

The canvas element – Basic usage


The HTML5 Canvas element has its contents rendered with JavaScript. The canvas element makes use of the "context" into which you can issue JavaScript commands to draw anything you want. The canvas is initially blank, and to display something a script first needs to access the rendering context and draw on it. You can obtain the rendering context and it's drawing function by using the getContext method.
var canvas = $('#basic-canvas')[0];
if (canvas.getContext) {
    var context = canvas.getContext('2d');   
}
Once the context is obtained you can use the beginPath function to a new path, and lineTo, arcTo, arc and similar methods are used to add segments to the path. The path can be closed using closePath. Once a path is created, you can use fill or stroke to render the path to the canvas.
context.beginPath();
context.moveTo(this.xpos + this.cornerRadius, this.ypos);
context.lineTo(this.xpos + this.width - this.cornerRadius, this.ypos);
context.quadraticCurveTo(this.xpos + this.width, this.ypos, this.xpos + this.width,
//more code here

if (this.isFill) {
    if (this.isGradient) {
        if (this.gradientLayout == 'Horizontal') {
            context.fillStyle = this.getHorizontalGradient(context);
        }
        else {
            context.fillStyle = this.getLinearGradient(context);
        }
    }
    else {
        context.fillStyle = this.color;
    }
    context.fill();
} else {
    context.strokeStyle = this.color;
    context.stroke();
}
context.closePath();
Here’s a simple example that draws two intersecting rounded corner rectangles. I have uploaded the js class content created for the sample below.

function canvasRectangle() {
    var options = {};
    if (arguments[0]) options = arguments[0];

    var default_values = {
        'xpos': 20,
        'ypos': 20,
        'width': 100,
        'height': 50,
        'color': '#000000',
        'isFill': false,
        'lineWidth': 1,
        'cornerRadius': 0,
        'isGradient': false,
        'stopColor': '#E1D6D3',
        'stopColor2': '#C7C7C7',
        'gradientLayout' : "Horizontal"
    };

    for (var index in default_values) {
        if (typeof options[index] == 'undefined') options[index] = default_values[index];
    }

    this.cornerRadius = options['cornerRadius'];
    this.xpos = options['xpos'];
    this.ypos = options['ypos'];
    this.width = options['width'];
    this.height = options['height'];

    this.color = options['color'];
    this.isFill = options['isFill'];

    this.isGradient = options['isGradient'];
    this.stopColor = options['stopColor'];
    this.stopColor2 = options['stopColor2'];

    this.usedDefaultStop = this.stopColor == '#E1D6D3';
    this.gradientLayout = options['gradientLayout'];

    this.lineWidth = options['lineWidth'];

    this.setParent = function (parent) {
        this.xpos = this.xpos + $(parent).position().left;
        this.ypos = this.ypos + $(parent).position().top;
    };

    if (this.cornerRadius == 0) {
        this.draw = function (context) {
            context.beginPath();
            context.moveTo(this.xpos, this.ypos);
            context.lineTo(this.xpos + this.width, this.ypos);
            context.lineTo(this.xpos + this.width, this.ypos + this.height);
            context.lineTo(this.xpos, this.ypos + this.height);
            context.lineTo(this.xpos, this.ypos);
            context.lineWidth = this.lineWidth;

            if (this.isFill) {
                if (this.isGradient) {
                    if (this.gradientLayout == 'Horizontal') {
                        context.fillStyle = this.getHorizontalGradient(context);
                    }
                    else {
                        context.fillStyle = this.getLinearGradient(context);
                    }
                }
                else {
                    context.fillStyle = this.color;
                }
                context.fill();
            } else {
                context.strokeStyle = this.color;
                context.stroke();
            }
            context.closePath();
        };
    } else {
        this.draw = function (context) {
        context.beginPath();
        context.moveTo(this.xpos + this.cornerRadius, this.ypos);
        context.lineTo(this.xpos + this.width - this.cornerRadius, this.ypos);
        context.quadraticCurveTo(this.xpos + this.width, this.ypos, this.xpos + this.width, this.ypos + this.cornerRadius);
        context.lineTo(this.xpos + this.width, this.ypos + this.height);
        context.quadraticCurveTo(this.xpos + this.width, this.ypos + this.height + this.cornerRadius, this.xpos + this.width - this.cornerRadius, this.ypos + this.height + this.cornerRadius);
        context.lineTo(this.xpos + this.cornerRadius, this.ypos + this.height + this.cornerRadius);
        context.quadraticCurveTo(this.xpos, this.ypos + this.height + this.cornerRadius, this.xpos, this.ypos + this.height);
        context.lineTo(this.xpos, this.ypos + this.cornerRadius);
        context.quadraticCurveTo(this.xpos, this.ypos, this.xpos + this.cornerRadius, this.ypos);
        context.lineWidth = this.lineWidth;

        if (this.isFill) {
            if (this.isGradient) {
                if (this.gradientLayout == 'Horizontal') {
                    context.fillStyle = this.getHorizontalGradient(context);
                }
                else {
                    context.fillStyle = this.getLinearGradient(context);
                }
            }else {
                context.fillStyle = this.color;
            }
            context.fill();
        } else {
            context.strokeStyle = this.color;
            context.stroke();
        }
        context.closePath();
        };
    }

    this.getLinearGradient = function (context) {
        var gradient = context.createLinearGradient(this.xpos, this.ypos, this.xpos, this.ypos + this.height);
        gradient.addColorStop(0, this.color);
        gradient.addColorStop(0.5, this.stopColor);
        gradient.addColorStop(1, this.color);
        return gradient;
    };

    this.getHorizontalGradient = function (context) {
        var gradient = context.createLinearGradient(this.xpos, this.ypos, this.xpos + this.width, this.ypos);
        gradient.addColorStop(0, this.color);
        if (this.usedDefaultStop) {
            gradient.addColorStop(0.2, this.color);
            gradient.addColorStop(0.3, '#EBDDDD');
            gradient.addColorStop(0.4, this.color);
        }
        else {
            gradient.addColorStop(0.1, this.stopColor);
            gradient.addColorStop(0.2, this.stopColor2);
            gradient.addColorStop(0.3, this.stopColor);
        }
        gradient.addColorStop(1, this.color);
        return gradient;
    };

    this.getRadialGradient = function (context) {
        var gradient = context.createRadialGradient(this.xpos, this.ypos, 5, this.xpos, this.ypos + this.height, 50);
        gradient.addColorStop(0, this.color);
        gradient.addColorStop(1, this.stopColor);
        return gradient;
    };
}

Usage:
var sampleCanvas = $('#sampleCanvas')[0];
if (sampleCanvas.getContext) {
    var context = sampleCanvas.getContext('2d');
    var rectangleGreen = new canvasRectangle({ 'xpos': 40, 'ypos': 40, 'width': 100, 'height': 40, 'lineWidth': 2, 'color': '#2AFF7F', 'isFill': true, 'cornerRadius': 20, 'isGradient': true, 'stopColor': '#7FFFAA', 'stopColor2': '#7FFFAA' });
    rectangleGreen.draw(context);
    var rectangleRed = new canvasRectangle({ 'xpos': 80, 'ypos': 60, 'width': 100, 'height': 40, 'lineWidth': 2, 'color': '#FFAA2A', 'isFill': true, 'cornerRadius': 20, 'isGradient': true, 'stopColor': '#FFD47F', 'stopColor2': '#FFD47F' });
    rectangleRed.draw(context);
}


Wednesday, June 13, 2012

Using Apache Cassandra with .NET - Part 2


One major difference between Cassandra and a traditional relational database is that Cassandra supports a variable amount of columns per rows in any given column family. In this post we will insert a column to the column family, "Tweets" The tweet column family is of contains one row with the text “sample tweet text1”. We will also see the test case for a null value insert exception.

[TestMethod]
public void InsertOnSubmitInsertsANewValueToTheColumnFamily()
{
    var key = "1".ToCassandraByte();
    const string columnName = "text";
    const string value = "sample tweet text 1";

    using (var context = new CassandraContext("localhost", 9160, "Twitter"))
    {
        context.Column.DeleteOnSubmit(x => x.ColumnFamily == "Tweets" && x.Key == key);
        context.SubmitChanges();

        var column = new Column().SetNameValue(columnName, value);
        context.InsertOnSubmit("Tweets", key, column);

        context.SubmitChanges();

        var tweet = (from x in context.ColumnList
                        .Where(x => x.ColumnFamily == "Tweets" && x.Key == key)
                    select x.ToObject<Tweet>()).FirstOrDefault();

        Assert.AreEqual(value, tweet.text);

        context.Column.DeleteOnSubmit(x => x.ColumnFamily == "Tweets" && x.Key == key);
        context.SubmitChanges();
    }
}

[TestMethod]
[ExpectedException(typeof(Thrift.TApplicationException))]
public void InsertNullValueTest()
{
    var key = "1".ToCassandraByte();
    const string columnName = "text";

    using (var context = new CassandraContext("localhost", 9160, "Twitter"))
    {
        var column = new Column()
            .SetNameValue(columnName, null);
        context.InsertOnSubmit("Tweet", key, column);

        context.SubmitChanges();
    }
}

Monday, June 11, 2012

Configuring data consistency options in Cassandra


Cassandra shows excellent performance in data distribution and availability. In Cassandra, consistency refers to how up-to-date and synchronized a row of data is on all of its replicas. Cassandra extends the concept of eventual consistency by offering tunable consistency. For any given read or write operation, the client application decides how consistent the requested data should be. In addition to tunable consistency, Cassandra has a number of built-in repair mechanisms to ensure that data remains consistent across replicas.

Cassandra supports the following write consistency levels.
  • ANY, having the lowest consistency with high availability. This mentions that a write must be written to at least one node. If all replica nodes for the given row key are down, the write can still succeed once a hinted handoff has been written.
  • ALL, having the highest consistency with lowest availability. A write must be written to the commit log and memory table on all replica nodes in the cluster for that row key.
  • QUORUM is a good middle-ground ensuring strong consistency, yet still tolerating some level of failure. A write must be written to the commit log and memory table on a quorum of replica nodes.
  • ONE: A write must be written to the commit log and memory table of at least one replica node.
  • LOCAL_QUORUM: A write must be written to the commit log and memory table on a quorum of replica nodes in the same data center as the coordinator node. Avoids latency of inter-data center communication.
  • EACH_QUORUM: A write must be written to the commit log and memory table on a quorum of replica nodes in all data centers.

For read consistency Cassandra supports the below given levels
  • ONE:  If latency is a top priority, consider a consistency level of ONE (only one replica node must successfully respond to the read or write request). There is a higher probability of stale data being read with this consistency level (as the replicas contacted for reads may not always have the most recent write).
  • ANY: If it is an absolute requirement that a write never fail, you may also consider a write consistency level of ANY. This consistency level has the highest probability of a read not returning the latest written values (see hinted handoff).
  • QUORUM: Returns the record with the most recent timestamp once a quorum of replicas has responded.

Creating a Cassandra connection configuration in .NET

[TestMethod]
public void CassandraConnectionConfigBuilderTest()
{
    var hosts = new[] { "a", "b", "c" };
    var builder = new CassandraConnectionConfigBuilder
    {
        Hosts = hosts,
        Port = 9160,
        ConsistencyLevel = ConsistencyLevel.QUORUM,
        Timeout = TimeSpan.FromSeconds(100),
        IsFramed = true,
    };


    var config = new CassandraConnectionConfig(builder);
    CollectionAssert.AreEqual(hosts, config.Hosts);
    Assert.AreEqual(9160, config.Port);
    Assert.AreEqual(ConsistencyLevel.QUORUM, config.ConsistencyLevel);
    Assert.AreEqual(TimeSpan.FromSeconds(100), config.Timeout);
    Assert.AreEqual(true, config.IsFramed);
           
}