Working with a feature branch in Subversion (TortoiseSVN on Windows)

Tuesday, March 09, 2010

Branching and merging is one of the most nerve-racking activities for people working with Subversion.  Unlike Mercurial and other DVCS where branching and merging is commonplace, typical workflows in Subversion do not include this activity.  As a result, the terminology is typically confusing, the massive number of changes can be scary, and a lot of people are so concerned about "getting it right" that they typically just avoid the practice altogether and work completely outside of source control.  That, of course, is the exact opposite of what we need when working on an important design spike or critical new feature.

 Conceptually, what we want with a design spike or large new feature is to:

  • Create a new branch for the change
  • Work in the new branch, committing early and often
  • Periodically pull in (merge) the changes from the project trunk
  • When complete, reintegrate the branch into the trunk
For these instructions, I assume that you have a subversion repository checked out and you're actively working on the trunk.

Creating a new branch:
  1.  Right-click on the directory you want to branch.  Pick the Branch/Tag operation.
  2. On the branch/tag screen, enter a new branch (typically the repository has a /branches folder as well as a /trunk folder).  The new branch is typically /branches/mynewbranch, where the branch name itself is a folder that does not exist in the repository.
  3. In this scenario, you will also want to choose "switch working copy to new branch/tag".  We'll be doing work on the new feature or design change immediately, so this tells subversion that any new commits will be on that other branch.
 At this point you'll want to take note of the revision number that had the last changes.


Work in the new branch, committing early and often

This is standard subversion behavior.

Periodically pull in (merge) the changes from the project trunk

You'll want to keep track of the last revision you've pulled changes from.  In the beginning, this revision number is the revision that represented the branch itself.

  1. Pick the Merge tool off the TortoiseSVN menu.
  2. Choose the "Merge a range of revisions" from the first screen of the wizard.
  3. In the "URL to merge from", choose the trunk.
  4. In the Revision range to merge, the best approach is to be precise.  Put in the last revision number that was merged (e.g. if you branched when the repository was at revision 10, put in 11-HEAD).  You can leave this blank, but we want to avoid merging a change twice.  You may need to look at the log to help find the right revisions.
Hit next and do a test merge just to be sure before performing the actual merge.  All other settings can be left at their defaults.  Note that the merge is done in your working directory, so you'll have a bunch of local changes to test and commit on your branch.

Reintegrate the branch into the trunk
  1. Commit all branch changes
  2. Switch the working directory back to the trunk.  This is done via the TortoiseSVN switch command.
  3. Pick the Merge command off the TortoiseSVN menu as before.  This time, however, we'll pick the "Reintegrate a branch" option.
  4. In the "From URL" field, put the URL to the branch you've finished working on.
  5. Test the merge and if happy, perform the merge, test the changes, and commit to the trunk.


Spring.Net-enabled WCF Services available from Microsoft Ajax

Thursday, February 25, 2010

Implementing Spring.NET WCF services is fairly straightforward.

Implementing MS Ajax WCF services is also straightforward, if you pick the right New Item to add from Visual Studio.

The complication comes in when you want a Spring.NET WCF service that handles calls from Microsoft Ajax controls.  This method will let you add them. 

Step 1. Add new "Ajax-Enabled WCF Service"





Step 2. Create your methods, test and make sure all base functionality is working.  This sample is for a CascadingDropDown control from the Ajax Control Toolkit, but any control will work.





Step 3. Introduce an interface to use for the methods.  Spring.Net requires an interface, and this is the crux of the problem.  Here I've created IMyAjaxService.


Step 4. Move the [ServiceContract(Namespace = "")] from the class to the interface


Step 5. Move the [OperationContract] tags from the methods on the class to the interface method definitions

 

Step 6. Change web.config endpoint contract (xpath = /configuration/system.serviceModel/services/service/endpoint) to reference the interface



Step 7: Test. The test should work, without dependency injection from Spring.Net.  Now we have a WCF service that responds to Ajax, but has the interface definitions just the way Spring likes them.

Wiring in Spring

For Spring.NET to handle WCF, you need .NET 3.0 or higher, and you need Spring.Net 1.3.0 or higher.  You'll need the following DLLs available for binding (either your bin directory or the GAC):

  • Spring.Core.dll from the 2.0 folder
  • Spring.Web.dll from the 2.0 folder
  • Spring.Services.dll from the 3.0 folder
In the Service you've created, edit the markup and add Factory="Spring.ServiceModel.Activation.ServiceHostFactory" onto the end, like so:

This will get Spring.Net into the activation pipeline for the service.

Lastly, you'll add the new object to spring configuration.  It is CRITICAL that the OBJECT ID MATCH THE SERVICE NAME FROM WEB.CONFIG.  The Spring.Net documentation mentions this, but I don't think they mention it very loudly.  The type information is exactly as you'd expect: the type for the WCF service class.


Also CRITICAL:  Add singleton="false" to the object definition for Spring for your new object


Intermittent Operation Aborted Errors in IE when using MS Ajax

Wednesday, July 08, 2009

This issue has been plaguing our project: operation aborted errors, intermittent in nature, occurring sometimes as little as once/week. Even in IE 8 there were issues, although thankfully not the crazy dialog box we see in IE 6 and 7.

It turns out that the problem is due to a bug in Ajax itself. I won't go into all the details, since they're covered very well in these two blog posts:

I didn't particularly care for the recommended way of packaging the fix, so instead I've used built-in ASP.NET Ajax functionality to override the default script delivery. I also updated the fixed functions to work for all versions of Ajax (at the time of this writing - currently Ajax bundled with .NET 3.5 SP1 and below). First, here are the updated functions:


  • Sys$_Application$initialize:

    function Sys$_Application$initialize() {
    if(!this._initialized && !this._initializing) {
    this._initializing = true;
    var u = window.navigator.userAgent.toLowerCase(),
    v = parseFloat(u.match(/.+(?:rv|it|ml|ra|ie)[\/: ]([\d.]+)/)[1]);

    var initializeDelegate = Function.createDelegate(this, this._doInitialize);

    if (/WebKit/i.test(u) && v < 525.13)
    {
    this._load_timer = window.setInterval(function()
    {
    if (/loaded|complete/.test(document.readyState))
    {
    initializeDelegate();
    }
    }, 10);
    }
    else if (/msie/.test(u) && !window.opera)
    {
    document.attachEvent('onreadystatechange',
    function (e) {
    if (e && arguments.callee && document.readyState == 'complete') {
    document.detachEvent('on'+e.type, arguments.callee);
    initializeDelegate();
    }
    }
    );
    if (window == top) {
    (function () {
    try {
    document.documentElement.doScroll('left');
    } catch (e) {
    setTimeout(arguments.callee, 10);
    return;
    }
    initializeDelegate();
    })();
    }

    }
    else if (document.addEventListener
    && ((/opera\//.test(u) && v > 9) ||
    (/gecko\//.test(u) && v >= 1.8) ||
    (/khtml\//.test(u) && v >= 4.0) ||
    (/webkit\//.test(u) && v >= 525.13))) {
    document.addEventListener("DOMContentLoaded", initializeDelegate, false);
    }
    else
    {
    $addHandler(window, "load", initializeDelegate);
    }
    }
    }


  • Sys$_Application$_doInitialize():

    function Sys$_Application$_doInitialize() {
    if (this._initialized) {
    return;
    }
    Sys._Application.callBaseMethod(this, 'initialize');
    if (this._load_timer !== null)
    {
    clearInterval(this._load_timer);
    this._load_timer = null;
    }
    var handler = this.get_events().getHandler("init");
    if (handler) {
    this.beginCreateComponents();
    handler(this, Sys.EventArgs.Empty);
    this.endCreateComponents();
    }
    if (Sys.WebForms) {
    if (this._onPageRequestManagerBeginRequest) this._beginRequestHandler = Function.createDelegate(this, this._onPageRequestManagerBeginRequest);
    if (this._beginRequestHandler) Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(this._beginRequestHandler);
    if (this._onPageRequestManagerEndRequest) this._endRequestHandler = Function.createDelegate(this, this._onPageRequestManagerEndRequest);
    if (this._endRequestHandler) Sys.WebForms.PageRequestManager.getInstance().add_endRequest(this._endRequestHandler);
    }

    if (this.get_stateString){
    var loadedEntry = this.get_stateString();
    if (loadedEntry !== this._currentEntry) {
    this._navigate(loadedEntry);
    }
    }
    this.raiseLoad();
    this._initializing = false;
    }


  • Sys$_Application$_loadHandler():

    function Sys$_Application$_loadHandler() {
    if(this._loadHandlerDelegate) {
    Sys.UI.DomEvent.removeHandler(window, "load", this._loadHandlerDelegate);
    this._loadHandlerDelegate = null;
    }
    this._initializing = true;
    this._doInitialize();
    }




Next, here is how I overrode how the framework delivers the script:


<ajaxtoolkit:toolkitscriptmanager runat="server" enablepartialrendering="true" id="ScriptManager">
<scripts>
<asp:scriptreference name="MicrosoftAjax.js" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" path="~/js/MicrosoftAjax-withFix.js">
</scripts>
</ajaxToolkit:ToolkitScriptManager>

You do not need to use the ToolKitScriptManager - you can also do it this way:


<asp:scriptmanager runat="server" enablepartialrendering="true" id="ScriptManager">
<scripts>
<asp:scriptreference name="MicrosoftAjax.js" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" path="~/js/MicrosoftAjax-withFix.js">
</scripts>
</asp:ScriptManager>

Note that you can replace ~/js with the directory of your choosing.

Be aware that when the framework loads the file, it automatically adds .debug or .release onto the end of the file name, so the final file reference provided will be either MicrosoftAjax-withFix.debug.js or MicrosoftAjax-withFix.release.js.

If you don't want to bother incorporating these fixes into .NET 3.5 SP1 Ajax files, feel free to download them from here:


Javascript Hacks: Using XHR to load binary data

Tuesday, July 07, 2009

I recently needed to get image data from a server using Javascript, base64 encode it, and post that data back to an application. While the details of why I needed to do this are a bit complex, I believe that getting image data through an XMLHttpRequest object and base 64 enconding it will become more valuable in terms of client-side image manipulation using the data URI scheme for image tags.

This would allow a Javascript developer, for instance, to load an existing image (say, a photo), without base64 encoding it on the server, load it into an image tag with a data URI, and make direct manipulations on that image.

Unfortunately, this area is relatively new and browsers have a lot of differences. Data URI support is still very new, inconsistent, and limited. In the meantime, here is how you get that base64 encoded image in the first place:

Internet Explorer:


IE has a property of XMLHttpRequest object for binary data ResponseBody. This contains exactly what we need, but unfortunately the property is not visible to Javascript. Since the string returned to Javascript by ResponseText will be terminated at the first null value, we must use ResponseBody. This requires a bit of VBScript, which can do one of the following things:
  1. Get the numeric value of each unsigned byte and turn that into a number in a string of comma delimited numbers. This is less efficient, but gets you in and out of VBScript as quickly as possible, allowing a generic base64 encoding routine. This is the route I followed (it may be less efficient, but it pales in comparison with the XHR request just made):

    Function BinaryArrayToAscCSV( aBytes )
    Dim j, sOutput
    sOutput = "BinaryArrayToAscCSV"
    For j = 1 to LenB(aBytes)
    sOutput= sOutput & AscB( MidB(aBytes,j,1) )
    sOutput= sOutput & ","
    Next
    BinaryArrayToAscCSV = sOutput
    End Function

  2. Base 64 encode it directly in VBScript.

Once this is done, we can then base64 encode it using a fairly generic function in Javascript:

Base64 = {

// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

encodeBinaryArrayAsString : function(input){
var ascArr;
var output = "";
var bytebuffer;
var encodedCharIndexes = new Array(4);

var inx = 0;
ascArr = input.substring("BinaryArrayToAscCSV".length, input.length - 1).split(',');
while(inx < ascArr.length){
// Fill byte buffer array
bytebuffer = new Array(3);
for(jnx = 0; jnx < bytebuffer.length; jnx++)
if(inx < ascArr.length)
bytebuffer[jnx] = parseInt(ascArr[inx++]);
else
bytebuffer[jnx] = 0;

// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;

// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (ascArr.length - 1);
switch(paddingBytes){
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for(jnx = 0; jnx < encodedCharIndexes.length; jnx++)
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
return output;
}
};



Firefox:

Firefox works a little differently, as there is no RequestBody property. In this case, RequestText is not truncated as long as you override the mime type coming from the server, forcing Firefox to pass the data unaltered. All we need to do is compensate for binary data coming back and being placed in a Unicode Javascript string. To compensate, we can AND each character with 0xFF to throw away the high-order byte (see https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data). The resulting encoding function looks like this:

Base64 = {

// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

encodeBinary : function(input){
var output = "";
var bytebuffer;
var encodedCharIndexes = new Array(4);
var inx = 0;
var paddingBytes = 0;

while(inx < input.length){
// Fill byte buffer array
bytebuffer = new Array(3);
for(jnx = 0; jnx < bytebuffer.length; jnx++)
if(inx < input.length)
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
else
bytebuffer[jnx] = 0;

// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;

// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (input.length - 1);
switch(paddingBytes){
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for(jnx = 0; jnx < encodedCharIndexes.length; jnx++)
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
return output;
};


Ideally we'd combine these two functions into a single encoding function, but I've left them separate for clarity. Note also that these techniques do not appear to work for Safari, Chrome or Opera. It should work for IE6 if using the correct ActiveX XHR object, but I was not supporting IE6. I did a spot check on Safari/Chrome/Opera and they were not working, but I did not investigate as they were not supported browsers for my implementation. The actual XHR function I used was:

LoadBinaryResource = function(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);

if (req.overrideMimeType)
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
if (typeof(req.responseBody) !== 'undefined') return BinaryArrayToAscCSV(req.responseBody);
return req.responseText;
}

LoadBinaryResourceAsBase64 = function(url) {
var data = LoadBinaryResource(url);

if (data.indexOf("BinaryArrayToAscCSV") !== -1)
return Base64.encodeBinaryArrayAsString(data);
else
return Base64.encodeBinary(data);
}



A little surprised no one caught this

Tuesday, October 28, 2008

So I just watched last week's SNL Weekend Update Thursdays on NBC.com, and decided to play around with their viewer. I haven't watched shows there before, but the Olympics were Silverlight and I wanted to know if they used Silverlight (no, they use Flash), and what features they had. I hit the info button (the thing with the arrow pointing to it), and was greeted with the interface you see below...the red box is mine (added for emphasis). Clearly they had some of the original mockup "Lorem Ipsum" in there and no one bothered to update it or correct it.


It doesn't get much better than this

Thursday, September 25, 2008

How's this for a setup message? Kinda hard to comply with...








One more technical analysis link

Tuesday, May 27, 2008

I bumped across Stoxline during my recent foray into the technical analysis world. It provides a good quick summary of some key price points.


Emil's Wicked Cool Blog