Thursday, December 18, 2008

Tortoise SVN - tag & merge tutorial

Since it's quite confusing how to branch/tag and then merge a branch with the production code - here's a short tutorial on the issue. Note that the strategy of tagging and merging presented below is probably not the only possible, however it works in practice.

I assume that a SVN repository is created, the SVN server works and Tortoise is installed on the client machine.

Step 1 - Import local files into the repository

Start from creating a local directory with application files in it.

  

You need to "put" the local directory into the SVN server. In SVN language this is called "import". Click the Application directory with the right mouse button and choose TortoiseSVN/Import from the shell context menu.

After you click "OK", the local directory is imported into the SVN. Note, that I use /trunk subdirectory to actually store files there. This is because we will create other subdirectories in the same repository and these subdirectories will contain tags of the code.

Step 2 - Checkout managed repository from SVN back to your local machine

Now you need a new, fresh location on your local filesystem dedicated to work as a local image of the repository. Create a fresh directory, let's call it ApplicationCode, right click on it and choose "SVN Checkout" from the context menu.

The Checkout operation retrieves the contents of the repository and creates a local image of it which can be then synchronized with the repository using the "Update" and "Commit" operations.

Local filesystem after the checkout operation looks like this:

Delete the /Application folder, it's no longer needed.

Step 3 - Continue working on the /trunk line of the code

This is what people find confusing.

Should I continue to develop application using the /trunk line? Or should I rather create a new /development line?

Well, there's no single answer to this question, however the advice is as follows: use the /trunk line to make any changes you want and whenever you make a release to clients - create a tag.

Let's then continue to work on the /trunk line for a while - create a new document "c.txt" in the /ApplicationCode directory and commit changes into the repository (right click on the /ApplicationCode and select "Commit" option).

Step 4 - Tag whenever you release your application to clients

A great day comes and you release your application to your clients. Click the /ApplicationCode with the right mouse button and select TortoiseSVN/Branch-tag option from the context menu. This invokes the "Copy (Branch/Tag)" window where you should give a name to the tag's directory.

Let's assume that the tag's name is "tag1" (this is where it turns out that putting files into /trunk subdirectory is important - both /trunk and /tag1 will be subdirectories of the same repository directory on the server).

Click "OK" and SVN will happily inform you that "your working copy remains on the previous path. If you want your next changes to be in the just created copy then you need to switch over to that copy path. Use the Switch command to do that."

Step 5 - Oops, a bug's been found. Go back to the Tag and fix the bug

As you continue your work on the /ApplicationCode directory and Update/Commit changes into the /trunk subdirectory in the SVN, a day comes when a bug is found in the code released to your clients. This is where you need to go back to the code, make changes and synchronize changes with the /trunk line.

As the message from the previous step says, you could use context menu's "TortoiseSVN/Switch" command to switch the code in the local /Application directory. However, this is not required!

Instead, I prefer to restore the image of the SVN's /tag1 into just another, fresh directory in the local filesystem. Let's then create /ApplicationCode_Tag1 directory in the local filesystem and checkout the /tag1 into it (just like we did in Step 2).

Now, let's fix the bug in the code retrieved from the /tag1: we'll modify the b.txt and create d.txt.

Commit changes from the /ApplicationCode_Tag1 local directory. Note that changes are stored only in the /tag1/ subdirectory in the SVN.

Step 6 - Merge the patched code with the /trunk line

This is the most fun part - we'll merge changes applied to the /tag1/ line of the code with the /trunk line of the code.

First, invoke the TortoiseSVN/Merge function from the context menu on the /ApplicationCode directory.

Note that the text in the lower groupbox correctly informs you that the result of the merge will be stored in the /ApplicationCode folder which contains an image of the /trunk folder. However, what you are supposed to do is to specify the From: and To: sources correctly.

Let's leave the To: untouched and click the "Show log" beside the "Revision" in the From: section.

Here you see that one of the revisions is printed with blue color - this is the number of revision which had been active when the tag has been made. Remember the number (1101 in my case), cancel the log window and put that number in the Revision textbox in the Merge window:

Do a "Dry run" where you can inspect changes which are about to be made to the /trunk subdirectory. In our example you'll learn that the b.txt is going to be updated and d.txt is going to be added. Click "Merge" and inspect the contents of the /ApplicationCode folder:

Happy SVNing!

Tuesday, December 16, 2008

Application Architecture Guide 2.0 from Patterns & Practices

The Microsoft's Patterns and Practices group continue their great work on the Application Architecture Guide. You should definitely check out the newly released Beta 2 version.

http://www.codeplex.com/AppArchGuide

Wednesday, November 26, 2008

log4net and appenders for different levels

The log4net uses different logging levels, ALL, DEBUG, INFO, WARN, ERROR and FATAL.

What I wanted to have is to log debugging, information and warnings into an appender and errors and fatal errors to another appender.

Here's how to define two appenders for different levels:

   1: <?xml version="1.0" standalone="yes"?>
   2: <log4net>
   3:     <appender name="RSLogFileAppenderInfo" type="log4net.Appender.RollingFileAppender">
   4:         <file value="PATHTOFILE\info.log" />
   5:         <appendToFile value="true" />
   6:         <rollingStyle value="Size" />
   7:         <filter type="log4net.Filter.LevelRangeFilter">
   8:             <acceptOnMatch value="true" />
   9:             <levelMin value="DEBUG" />
  10:             <levelMax value="WARN" />
  11:         </filter>
  12:         <maxSizeRollBackups value="10" />
  13:         <maximumFileSize value="10MB" />
  14:         <staticLogFileName value="true" />
  15:         <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  16:         <layout type="log4net.Layout.PatternLayout">
  17:             <conversionPattern value="%newline%date [%thread] %-5level - %message" />
  18:         </layout>
  19:     </appender>
  20:     <appender name="RSLogFileAppenderFatal" type="log4net.Appender.RollingFileAppender">
  21:         <file value="PATHTOFILE\error.log" />
  22:         <appendToFile value="true" />
  23:         <rollingStyle value="Size" />
  24:         <filter type="log4net.Filter.LevelRangeFilter">
  25:             <acceptOnMatch value="true" />
  26:             <levelMin value="ERROR" />
  27:             <levelMax value="FATAL" />
  28:         </filter>
  29:         <maxSizeRollBackups value="10" />
  30:         <maximumFileSize value="10MB" />
  31:         <staticLogFileName value="true" />
  32:         <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  33:         <layout type="log4net.Layout.PatternLayout">
  34:             <conversionPattern value="%newline%date [%thread] %-5level - %message" />
  35:         </layout>
  36:     </appender>
  37:     <root>
  38:         <level value="ALL" />
  39:         <appender-ref ref="RSLogFileAppenderInfo" />
  40:         <appender-ref ref="RSLogFileAppenderFatal" />
  41:     </root>
  42: </log4net>

Note that there are two appenders with different filters, the former raning from DEBUG to WARN and the latter ranging from ERROR to FATAL.


The simple helper class I've found somewhere and use to simplify the logging:



   1: public class Log
   2: {
   3:     public static ILog For( object LoggedObject )
   4:     {
   5:         if ( LoggedObject != null )
   6:             return For( LoggedObject.GetType() );
   7:         else
   8:             return For( null );
   9:     }
  10:  
  11:     public static ILog For( Type ObjectType )
  12:     {
  13:         if ( ObjectType != null )
  14:             return LogManager.GetLogger( ObjectType );
  15:         else
  16:             return LogManager.GetLogger( string.Empty );
  17:     }
  18: }

To test different levels defined for these two appenders just call:



   1: class LogTest
   2: {
   3:   public void Test()
   4:   {
   5:       Log.For( this ).Fatal( "fatal" );
   6:       Log.For( this ).Error( "error" );
   7:       Log.For( this ).Warn ( "warn" );
   8:       Log.For( this ).Info ( "info" );
   9:       Log.For( this ).Debug( "debug" );
  10:   }
  11: }
  12: ...
  13: LogTest test = new LogTest();
  14: test.Test();

Note that logs with different levels are correctly directed to appropriate appenders.

Thursday, September 18, 2008

ASPNET user account folder and files

Did you ever thought about the folder and files created by Windows XP for the ASPNET user account?

"The ASPNET user account is an internal account and no one is able to log using this identity"

This is the most common answer for my question. And yes, the answer is true. At least it touches the most important part. But there's more.

You see, since Windows XP invokes ASP.NET processes using ASPNET account, it also automatically creates a user folder with typical structure (Application Data, Cookies, My Documents etc.) for this account.

And here comes the strange part: the folder structure for the ASPNET user account is created under "Documents and Settings" but instead of username as a folder name, the machinename/username is used.

Suppose now that your machine's name is XYZ and you use a user account named XYZ. Guess what - you'll see ASPNET user local folder inside your own user folder.

Something tells me that this is not quite right. I should not be able to peek into other user's files but since the folder sits inside my own local folder, I am able to browse it with no restrictions.

I would say that this is rather insecure, no matter if it can be easily misused or not.

Tuesday, September 9, 2008

The "cookie" property of mshtml.IHtmlDocument2 does not work anymore

In one of our applications we host the Internet Explorer ActiveX control in a window and manually create parameters for navigation (including the body and http headers). To correctly inject the ASP.NET session id cookie into such requests (the page we navigate to comes from the ASP.NET server) we have to be able to somehow inject the ASP.NET_SessionId cookie into the navigation context.

The application was written two (or three) years ago, and we've been injecting the cookie using the cookie property of the mshtml.IHtmlDocument2 interface.

/* we've retrieving the reference to the document */
mshtml.IHtmlDocument2 doc = ...; 
 
/* and we've been setting the aspnet session id cookie */
doc.cookie = ...;

This worked like a charm, up to this year - the application is heavily used but only in september (it's responsible for gathering and processing some data available in september and then the data is passed to another appliaction) and this year we've been surprised to see that the cookie property does not work (at least in Internet Explorer 7). Both accessors (set and get) seem to have no effect and you always get the null value from the getter.


Pretty annoying. I guess it's somehow related to security issues but surely it broke the backward compatibility, at least for us.


The solution is to use another method which can inject cookies into the http processing chain:



/* injects cookies into the http processing for
   current process */
[DllImport("wininet.dll")]
public static extern bool InternetSetCookie( 
    [MarshalAs(UnmanagedType.LPStr)]
    string Url,
    [MarshalAs(UnmanagedType.LPStr)]
    string CookieName,
    [MarshalAs(UnmanagedType.LPStr)]
    string CookieData
    );
 
...
    /* first inject the cookie */
    InternetSetCookie(
        "url",
        "ASP.NET_SessionId",
        "theaspnetcookievalue_nomatterhowyougetit" );
 
    /* then navigate */
    webBrowser1.Navigate( "url" );
  
    /* the cookie is correctly set for the navigation.
       although you STILL cannot see it using mshtml.IHtmlDocument2.cookie,
       the http debugger reveals that it's really there 
    */

Wednesday, September 3, 2008

SmtpClient exception - An invalid character was found in the mail header

This hit me today as I tried to move a configuration section to another configuration file. It seems that the SmtpClient class does not like non-english letters in email header.

MailMessage mail = new MailMessage();
 
mail.From = new MailAddress( 
    "noreply@noreply.com", 
    "ąęłóżśńć", // <- the cause of the exception
    Encoding.UTF8 ); 

What's really scary is that the code above actually works - the exception occured only when the subject name was read from the configuration file!


After an intense struggle, I've found the solution. Just add



mail.SubjectEncoding = Encoding.UTF8;
somewhere around. It works, at least for me.

Wednesday, August 27, 2008

Remote desktop to another OS and back again

As you surely know, XP is a single-user OS which means that there can be only a single user logged-in at one time. If the administrator logs in from a remote machine using the remote desktop tool, the current user is logged off and a message shows up saying "this machine is currently in use [...]"

If you have a spare 3 minutes, try to reproduce following steps:

  1. Log into a non-virtual Windows XP
  2. Use Virtual PC to boot a virtual Windows XP and log into it
  3. Use remote desktop in the virtual Windows XP to log back to the non-virtual Windows XP (which hosts the virtual one)

What happens to your non-virtual Windows session after you log into it from the virtual machine? Will the "this machine is currently in use" message show up?

Well, if you do it using two XPs, the host screen goes black and the machine stops responding. The non-virtual XP is controlled "from the inside".

What's great is that the non-virtual machine still runs - you can access all it's services, the application server, the database server and others run perfectly. You just cannot control the machine anymore using the keyboard.

I belive that there are few other interesting options to continue this experiment.

For example, can you regain the control of the OS by accessing it with the remote desktop from yet another machine? What happens when the Windows Server 2003 is used instead Windows XP?

Thursday, August 14, 2008

Lightweight Service Bus frameworks

The application environment I work on needs an architectural fundament capable of a loose-coupled integration. There's a reference handbook on integration patterns, the "Enterprise Integration Patterns" by Hohpe/Woolf (also take a look at the "Integration Patterns" book from Microsoft Patterns & Practices group).

 

Anyway, there are four major integration patterns:

  • File Sharing - where application share data by exchaning files
  • Shared Database - where applications are build around a single database
  • Remote Procedure Calls - where applications expose their data through remote interfaces
  • Messaging - where applications exchange messages with the help of a centralized component (called a "Service Broker" when the messages are processed synchronously or the "Enterprise Service Bus" when a messaging component is used to store and handle messages) which acts as a backbone of the application environment


[http://msdn.microsoft.com/en-us/library/aa475433.aspx]

One of the most interesting aspects of an ESB implementation is handling various MEPs (Message Exchange Patterns: synchronous, asynchronous, send-and-forget, publish-subscribe). The latter, publish/subscribe seems the most attractive option to integrate an environment consisting of different components.

There are plenty of fully fledged ESB implementations but the core of a service bus can be built using much a simple environment based solely on a messaging component like MSMQ or IBM Webshpere MQ. And since the "low level" integration with a messaging component could be painful, we need lightweight service bus frameworks just to wrap unnecessary calls into friendly APIs.

After some research, I've found three such lightweight service bus frameworks for the .NET platform:

  • nServiceBus - uses MSMQ as messaging component and Spring.NET as the IoC container. Supports so called "sagas" which are just long-running workflows as well as the scalability over a large enterprise system.
  • Simple Service Bus - built on top of the nServiceBus. At first look seems like the porting of few nServiceBus features is not completed at the moment. Uses the Castle.Microkernel as the IoC container.
  • MassTransit  - seems to take a more universal approach, supports MSMQ / ActiveMQ as messaging components and  different IoC containers. There's not much to evaluate since the useful versions cannot be downloaded at the moment.

I hope to write more on the topic soon since this is not only the top-priority task I work on at the moment but also, in the same time, it's just just quite fun to play with.

Friday, July 18, 2008

Generating random sequences with LINQ

A concise random number generator

Inspired by The LINQ Enumerable Class article from the lastest MSDN Magazine issue, I started to play a little bit with the random sequence generator:

/* generate the sequence */
Random rnd = new Random();
var sequence = Enumerable.Range( 1, 10 ).OrderBy( n => rnd.Next() );
 
/* write down the sequence */
sequence.ToList().ForEach( n => Console.WriteLine( n ) );

As you can see, the sequence is generated with two statements, the first one initializes a random number generator and the second one sorts the list using the results from the generator.


My immediate thought was: "Is there a way to genearte such random sequence using a single statement?"


The obvious approach



var sequence = Enumerable.Range( 1, 10 ).OrderBy( n => (new Random()).Next() );

does not work because a new random number generator is created for each value from the list and the same random value is returned from all these newly created generators.




Random number generator testing

Let's write a test of the random number generator - we treat consecutive values from the array as coordinates and draw an image of the generator:



static void RandomSequenceGeneratorTest( IEnumerable<int> Sequence )
{
    int Max = Sequence.Max();
 
    /* create image */
    using ( Bitmap bitmap = new Bitmap( Max, Max ) )
    using ( Graphics graphics = Graphics.FromImage( bitmap ) )
    {
        graphics.Clear( Color.Black );
 
        int prev = Sequence.First();
        foreach ( var current in Sequence.Skip(1) )
        {
            graphics.DrawRectangle( 
                Pens.White, 
                new Rectangle( 
                    /* draw a point 
                       using current and previous values
                       as coordinates
                    */
                    new Point( prev, current ), 
                    new Size( 1, 1 ) ) );
 
            prev = current;
        }
 
        bitmap.Save( "test.png", ImageFormat.Png );
    }
}

Testing the generator (version 1)

Let's also test our generator:



var sequence = Enumerable.Range( 1, 500 ).OrderBy( n => ( new Random() ).Next() );
RandomSequenceGeneratorTest( sequence );


An improved generator (version 2)

However, I can modify the generator slightly to get much better results!



var sequence = Enumerable.Range( 1, 500 ).OrderBy( n => n * ( new Random() ).Next() );
RandomSequenceGeneratorTest( sequence );


Hey! It seems that the multiplication makes the generator much less predictive, however as the clear pattern reveals, the resulting sequence is still not random!


What exacly happens is the arithmetic overflow caused by



n * (new Random()).Next()

which "distrubutes" multiplied values in a more "random" way.


Do we really need (new Random())?

After I've realized that the "randomness" of the improved generator is caused by arithmetic overflows, I immediately thought of getting rid of the (new Random()). Maybe the overflow itself is enough to get random sequence?



var sequence = Enumerable.Range( 1, 500 ).OrderBy( n => n * 1234567890 );
RandomSequenceGeneratorTest( sequence );


Well, it is not. Altough the sequence printed on the console looks quite random at first sight, the image reveals that it's not random at all. It seems that the value produced by the (new Random()).Next() is then important, even though the first test (version 1) revealed that the value itself is not enough!


Even more improved generator (version 3)

Let's go back to our improved generator and try to improve it even more:



var sequence = Enumerable.Range( 1, 500 ).OrderBy( n => n * n * ( new Random() ).Next() );
RandomSequenceGeneratorTest( sequence );


I guess, I am satisfied. However, I do not think I am curious enough to dig for a exhaustive explanation. Does really the arithmetic overflow causes this generator to produce random sequence? What's the exact role of (new Random()).Next() here? It seems that it's not important itself (version 1), however removing it completely also does not work.


I belive that the deferred nature of LINQ enumeration is the one of keys to explain these obervations. I would also like to see some deeper and more throughout tests of my "yet-improved" generator.


On the other hand, could it be possible that incidentally, by making a square function (n*n), I've built a chaotic function with random distribution over its domain?

Friday, July 11, 2008

C# Puzzle No. 11 (intermediate)

Generic list type needs an item type to be initialized:

List<int>    listInt;
List<string> listString;
...

On the other hand, C# 3.0 allows anonymous types to be used in the code. An anonymous type is never explicitely named:



var item = new { Field1 = "The value", Field2 = 5 };
Console.WriteLine( item.Field1 );

How it is then possible to declare a generic list of anonymous type?



var item = new { Field1 = "The value", Field2 = 5; };
 
List<?> theList = 
    /* how do I make a generic list with item in it 
       so that I can add other items of the same anonymous type?
     */