The DevHuddle

thehuddle The DevHuddle. This is a term I started using to identify what my team does to plan development work for an iteration.  Basically this is where we sync up and get everyone on  an even playing field for consistency, map out our plans and design and coordinate who is going to do what. Why a huddle? In football the team gets the orders (prioritized stories) from the coach (project manager) and then the quarterback (team lead) explains the plan and how the players (the developers) are going to carry it out.  And to illustrate the quarterback draws the plans out sometimes on the ground or in his/her hands (the whiteboard). That's why we call it the DevHuddle.

 

Photo courtesy of Jonathan Starr.

The Real Reason to Use IoC/DI (Inversion of Control/Dependency Injection) and a Container Like Windsor

Can you imagine maintenance where you would only have to change a configuration? How about not having to touch existing production code but be able to completely change the behavior of an existing application?!

The real reason to use a dependency injection container is Maintenance. When considering that after you write your first line of code, everything from that point on is maintenance, maintenance is a pretty big deal.  Containers make changes seamless. Actually, interface-based development makes changes easier. Being able to push an implementation of that interface into the dependent objects when constructing them (dependency injection) makes changes seamless. And being able to push that all to the container configuration: awesome!

Case in point, yesterday I was asked to look into changing a notification system.  I added a new implementation of the interface the application was using, changed the configuration, and I was done. I didn't have to go into the application's code and make any changes at all! That is powerful. I changed the behavior of the application without touching a line of existing code!!!

Another way to do it? In my recent presentation at KC Day of Dot Net, a question came up about using Windsor to make changes to existing code in production without actually changing any code or redeploying existing DLLs. The question was "Can you have Windsor use an assembly the application doesn't have a reference to?" The idea is that you need to change functionality of a certain piece of the application, but you can't change the existing code at all.  This means you cannot add any references to new code or put new code into the existing program.  How would you do this?

Implementing new Behaviors Without Changing a Line of Existing Code

It's actually very simple when you are using inversion of control containers.  Basically you could use Windsor and a new DLL that implements new behaviors on existing interfaces that the application uses.  Then you would put that new DLL next to the rest of the application code and change the information in the container configuration to point to the new code.

1. So we have our employee.system implementation of a simple HR system.  Now we create a project call employee.system.additional and reference the code that has the interface we need to implement.

image

2. Then we create our implementation of the interface. This is a new implementation of ICommand.  It's very simple, it just logs a message.

image

3. After we are done, we build the new assembly and take that that DLL and place it in the same folder as the existing code.

image

4. Then we crack open the Configuration file and make some changes to allow us to use the new DLL's code (the existing code doesn't even know of this new assembly).

image

5. Then we just run the application. We just completely changed the behavior of the application and changed ZERO lines of code in the existing app!!!!

image

Could you imagine maintenance where you would only have to change a configuration?  And not having to touch production code but be able to influence change!

Download the sample here using SVN: https://robz.homedns.org/svn/Presentations/trunk/IoCPresentation/

Caveat to Adding the Everyone Account in VisualSVN Server

You may experience issues after allowing the Everyone special account in VisualSVN Server, such as not being able to commit code.

VisualSVN Server is a great product to manage Subversion for you!  Recently I posted on how to allow anonymous access to your repository and set up the special "Everyone" account, but I recently think I found a bug in it.  It may be expected behavior for what the Everyone account represents, but for me, it's quite unexpected.

imageOnce I add the Everyone account , it will override any other settings I have for other groups or users (if they inherit their permissions from the parent).  I recently allowed Everyone in a repository so that people could  download source code.  Then I made updates to the code today and went to check it in and found out I no longer had permissions!  Adding Everyone with Read Only access invalidated my committers group!  The group was still there (shown on the right), but for some reason I was being rejected. Below is the from the error log:

[Wed Dec 10 20:01:54 2008] [error] [client _REMOVED_] Access denied: '_REMOVED_' CHECKOUT Presentations:/trunk/IoCPresentation/product/EmployeeSystem/infrastructure/logging/custom/Log4NetLogFactory.cs

This isn't the first time I have noticed strange behavior since adding the Everyone account, but this is the first time I realized what was going on.

So the workaround is to change your other groups AFTER you add the Everyone account. For me this means clicking on each group and changing the permissions level from [Inherit from parent] to explicitly what it should be, and clicking {Apply}. In the case in the picture on the right, that means to explicitly click on [Read / Write] and click {Apply}.

The bug I see here is that when adding the Everyone account explicitly to a repository, you can not have your other groups inherit their permissions from the parent.  I have seen strange behavior other ways with the Everyone account.  Please let me know if this is expected behavior. :D

By the way, I am on VisualSVN Server 1.6.2, which is the latest release at the time of writing this.

Kansas City Day of Dot Net : Inversion Of Control With Windsor Container

Today I gave a presentation on Windsor Container at KC's Day of Dot Net.  The presentation went pretty smoothly, other than accidentally unplugging the projector link. :D

I introduced fluent interfaces into my presentation code this time around and it seemed to be somewhat well received. I also added Mass Transit's startable facility to the windsor_rawlogo Windsor configuration and showed how easy it is to use Windsor to leverage production level code. 

Thanks to all that came! I appreciate the interest and the great questions. Hopefully I answered them somewhat well. :D

Point subversion here to get the full presentation: https://robz.homedns.org/svn/Presentations/trunk/IoCPresentation/ 

Here is the file that has just the code :

 

Nothin but .NET Developer Boot Camp - Wrap up

Over the past few days I have been honored to be at the Nothin but .NET Developer Boot Camp in Philadelphia, PA. I have also been posting updates every day.

A few people have asked me if this course is worth it.  I would say if you want to be passionate about what you do (or if you already are), then the answer is a resounding YES!!

The sheer amount of benefit you get from knowledge and participation, you may feel like you are a bandit making out with only having had to pay the extremely reasonable price of the course! To say that this course is great belies the fact that it can forever change your life!  There are certain times in our lives when people of great influence come into focus and we have a chance to learn from them. This course is one of those chances.  JP Boodhoo is one of those people. He is not only a great influence, but also tremendously motivational! I am humbled by the fact that he was humble and learned from people at the course in addition to his approach to teaching.

JP also mentioned that some people come to this course and realize that software development is not for them.  They go out and find what makes them happy or passionate and start doing that after taking this course. I was a little bit fearful of taking this course because of that and because I knew it would get me out of my comfort zone. I had a choice to go to the DevConnections conference where I would be able to sit back and learn at a different pace, OR come to Nothin but .NET and really stretch my mind and physical stamina and actually get better at what I do.  And I have to tell you, for a moment, I actually thought about going to DevConnections.  The more I thought about it, I knew I wanted to get out of my comfort zone and have my world rocked again. 

If you are faced with the same dilemma, my hope is that you will make the decision to come to JP's training.  There is really nothing else like it out there so I can't compare it to anything. The days sound like they are long, with over 80 hours of training, but the time will seem short. You will form bonds with those around you and you will learn so much, and not just from JP either. You will be a better programmer by leaps and bounds at the end of the week if you apply yourself and take the training to heart! You will be having so much fun that you won't want the week to end!

Coming away from this course, I find that you learn the most if you do what I did. No matter how much you know, be prepared to learn (and check your ego at the door as well). I found out (once again) how little I really know in the realm of software development. I am forever a student in the art, thanks in part to people like Dru and JP, who challenge me to think and find new alternative solutions to problems.

Nothin but .NET Developer Boot Camp - Day 5

Friday was the last day of the training course and today we went over a couple of patterns and had a discussion on Domain Driven Design. After the discussion, we were released to continue working on our end to end solution. The team I was on (Go Conquistadors!) got all the way down to the database second out of four teams. We were first to have all parts vertical though.  Then we started to refine our design even more.  And that is where we left the solution on Friday night.

People Bowing Out

As the day progressed, I started noticing people leaving to get back home. It could be due to the holidays that they were not able to stay until the close of training on Friday night.  I think most everyone was mentally tired and some of us were physically tired as well by this time. We lost the most people by 10PM. 

The training lasted from 9AM to 3:30AM tonight. I was pretty much done by about 1:30AM, but I hung out until we were done with the training.  I did notice that the focus and intensity seemed to lighten up today. It could be that much of what we were learning was finally taking hold for most people. Or it could be that I was really tired. :D

Our Solution

So I mentioned that we finished a vertical solution from the UI down to the database.  That felt pretty good to get there with the fluent interfaces. Of course, now that we had it one way, we could refine it or find another way to implement the same thing.  You may have noticed that I posted last about some of the directions we were going with our solution.  I worked on the repository and mapper's fluent interfaces.  I wasn't quite happy with the solution for the repository that I had come up with because it required the client of this interface to have knowledge of the parameters real names and it only did ANDing (this AND this).

Repository.get().a_list_of<Product>(with_parameters)

So Friday evening, I put in some work to get it closer to this:

Repository.get().a_list(Where<Product>.has_a(x => x.department_id).equal_to(department_id)).and_a().or_a()

It is not complete yet, but I imagine in some of my free time at home I will continue to develop the solution.  Now the client caller only has to have knowledge of domain object properties, which it already does have the need to know.  It gives you an elegant solution that looks like this:

image

I could show something like this to my wife and she would be able to read it. For each product in the repository list where product has a department id equal to the department browser dto's department id, return a map from product to a product browser dto.

The End of the Night

The night wrapped up with a picture of everyone in the boot camp survival T-Shirts and watching a video on Randy Pausch's Last Lecture named "Really Achieving your Childhood Dreams".

Randy Pausch was an inspiration to millions. He was diagnosed with terminal cancer at some point before this presentation. He will make you smile, laugh and possibly cry. I would suggest to watch it with those you care about as it will warm your heart.

Nothin but .NET Developer Boot Camp - Day 4

So you may notice this post is a little late.  The course moved to an open format today and the last people left for the night at 4:30 AM. That's 9 AM to 4:30 AM.

Basically today we got direction from JP and then formed groups that we worked with all day to implement an end to end solution from the point that we were already at.  One of the things you will push yourself for today is understanding fluent interfaces to whatever extent that you can. The other part is how to build them.  They are much harder to build and grokk than a normal interface.

Fluent Interfaces

We have been working with fluent interfaces all week and JP has kept pushing us to find new ways to build them.  Basically a fluent interface is one that reads almost like a sentence when making calls to the code (almost as if discoverable).

So let's talk about the old way first.  Imagine building an interface with a method Add() on it.  So you would call something like routes = new Routes(); and then routes.Add(something); right?

Routes routes = new Routes()

routes.Add(interface1);
routes.Add(interface2);


return routes;

Now try to imagine that as a fluent interface.  You could call something to the effect of:

Route.To<Something>().when(Route.contains_certain_path(RoutePath.something)

So what do you suppose it is that makes it fluent?  It reads like a sentence. Route to something when route contains certain path "certain path".  It makes it very easy for consumers and clients of this code and even users to understand the intent of the code. It reads like a sentence.

So let's take a look at two things I wrote today (in addition to other things :D). Both use Static Gateway.

foreach (var product in Repository.get().a_list_of<Product>(with_parameters))
{
    yield return Map.from(product).to(a_product_browser_dto());
}

What you notice here is again, it reads like a sentence. "Repository get a list of product with parameters." "Map from product to a product browser dto."

Map and Repository are gateways. This is what Map looks like.

public class Map { public static MapBuilder<From> from<From>(From from_object) { return new MapBuilder<From>(from_object); } } public class MapBuilder<From> { private readonly From from_object; public MapBuilder(From from_object) { this.from_object = from_object; } public To to<To>(To to_object) { return Container.get_me_an_implementation_of_an<IMapper<From, To>>().map_from(from_object); } }

 

public interface IMapper<From, To>
{
    To map_from(From from_object);
}

Behind the scenes it actually uses the same IMapper that we have been using for awhile. The fluent interface is quite awesome, but it can be an advanced topic.

So day 4 was long, but the time FLEW by so you don't really notice how much time has gone by. This whole week seems to fly by because you are having so much fun and learning so much!

Nothin but .NET Developer Boot Camp - Day 3

Today the most important thing is Coffee!  When you take this course (not if *grin*) you definitely need to get coffee after lunch. 

Some things of note today:

Group Interaction

There is a lot more group interaction with tasks and the tasks are getting to a point where they take quite a bit lot longer to complete.  Group dynamics has definitely picked up and come into play a lot more because by the third day you are much more comfortable with everyone. I am seeing personalities coming out as we get out of the honeymoon stage and that is pretty awesome!

Focus

Another thing is focus. Sometime this afternoon I found myself just tired.  I started losing focus.  So that is why I mention coffee.  Coffee right after lunch will help keep your focus up (unless you don't like coffee, then I suggest jumping jacks!).  And if you like espresso, I suggest a little more of that to give you a little extra. :D

Front Controller

Over the last couple of days we have been creating a Front Controller. The concept of a front controller is the architecture behind ASP.NET MVC.  Basically, it means that all requests go through one controller that is responsible for hooking those requests up to commands to run (views to display).

Containers

We built a container from scratch.  So what is a container? Think something to the effect of Windsor Container or Structure Map. To do that, first we need to understand a container's concerns.  So what are a container's concern? Construction, Life cycle and Aspects.

Construction is how it builds objects for you when you ask for them. Life cycle is how those objects live (whether or not they are singletons, transients, singleton per session, etc). Aspects are implemented through dynamic composition (a little more complicated, done through dynamic methods or reflection.emit).

Dynamic composition is basically wrapping an object with the object that implements the interface.  Then it intercepts the methods, does something, and then calls the actual methods to do what they are doing.  This is known as method interception. Slightly complicated to think about (not sure on implementation yet).

We also went over a whole bunch of concepts and manifestations of those concepts and completed specifications by first check-in from everyone in the class.  Oh yeah, and class today was from 9:00 AM - 1:30 AM.  Tomorrow it's projected to be at least 1-2 hours longer.

Nothin but .NET Developer Boot Camp - Day Two

Today has been awesome! More of what we learned yesterday, plus more.  We got more in depth into certain patterns and have had a huge helping of BDD all day long. The format has been that JP has written the specs and we get the tests to pass. The format looks like it will start to shift to JP doing less and less code and us doing more and more of it.

Three Project Solution

So one thing I immediately picked up on is the 3 project solution.  One of the projects holds all of the automation and automated build stuff. So it's really kind of a two project solution. The website and the code.  That's a pretty compelling change.  So I know you are asking, where are the tests at if you only have a website project and code project? That tests (aka Specifications) live right next to their implementation and are separated out by the automated build into two separate assemblies (one for the tests and one for the production code). 

 

image

Naming Conventions

The naming inside of not only the specs, but the production code is very verbose.  I am guessing the naming of methods is based towards DDD (although I haven't got into that yet and JP hasn't really talked about it yet).

So let's take a look at the tests:

image

And a look at an implementation:

image

So far pretty sweet. Lots of pairing as well!

Nothin but .NET Developer Boot Camp - Day One EOD Update

Today I learned I have a lot to learn! Patterns, Delegates, Lambdas, PSake, Extension Methods, etc, etc, etc.

Photo credit to Hikako.

headSo far we have went through patterns and principles.  Ones that come to mind are the Strategy Pattern, Static Gateway Pattern, Decorators, and Specification Pattern. We also got into SRP (single responsibility principle), OCP (open closed principle), Composition over Inheritance, BDD (behavior driven design), DDD (domain driven design), and then we talked a little about MVP (Model View Presenter). I feel like a sponge!

We also got a sink or swim introduction to Lambdas (UPDATE: I got a sink or swim introduction. The course states strong C# skills already which would include .NET 3.5 knowledge and experience.) which brings a pretty good knowledge of anonymous delegates.  I haven't fully grokked Lambdas yet, but I am getting there.  We also got right into Extension methods which I am comfortable with. And we have been to the walls with Generics. :D

Another awesome thing we got right into today was PSake. It is an automated build tool that uses PowerShell.  It's a compelling alternative to NAnt, but can be used hand in hand with NAnt.

If you come to training, doing the homework helps! I did the homework, and I still felt overwhelmed today. It's quite a bit of hands on work with a lot of open-ended time to complete assigned tasks. :D

So far, definitely worth it!

Oh yeah, and class was from 8:30AM to 11:20PM.

Nothin but .NET Developer Boot Camp - Day One Midday Update

My brain hurts. That is all.

Delegates FTW!!!!

Going Back to Boot Camp

In 1998 I went through Basic Combat Training for the United States Army. It was 9 weeks of grueling, awesome and exhausting training. It was training that definitely pushed the limits of your body and mind. I learned quite a lot and the training forever shaped the person I am today.

Next week, I will have the opportunity to go to Boot Camp again, but this time it will be for .NET development. JP Boodhoo, a great guy who I met at Alt.NET, conducts a course known as Nothin' But .NET Developer Boot Camp.  This training is expected to physically and mentally wear you out, stretching your mind (and the CLR) to the limits. JP even mentioned as much himself:

My use of the term bootcamp is not a marketing pitch. You WILL:

  • be exhausted at the end of the day
  • spend no less that 14 hours in a course day
  • have your longest days on Thursday and Friday (historically, 20 – 24 hours is not uncommon)

Why would I want to go back to boot camp? The benefits of this kind of training speak for themselves on the course overview.

Have a look. I'll wait. Just looking at the sheer amount of topics covered blows my mind.  Many of these topics I have a good understanding on already, but getting the fundamentals down is an important way to move forward.  Plus there are many things I hope to learn next week!

And when you have finished the course you should have a grasp on these concepts:

  • Interface Based Programming
  • Design By Contract
  • Behaviour Driven Development
  • Layered Architecture
  • Object Oriented Programming
  • Design Patterns
  • Unit Testing
  • Interaction vs. State Based Testing
  • Dependency Injection
  • Object Relational Mapping
  • Domain Driven Design
  • Build Automation

Judging what I have heard from others, this is one of the best courses you can take for .NET development out there. I will definitely let you know AFTER next week how it goes (I will most likely be too tired during the training).

HTC Touch Pro

htcPro

Photo originally from Engadget.

 

Today I went to Best Buy to pick up a new phone. I've had the HTC Apache (aka the PPC6700 or VX6700) for nearly three years now.  I have kept the Apache up to date by putting WM6 on it, otherwise I probably would have switched sooner.  I was going to get the HTC Touch Diamond, then I learned of the HTC Touch Pro (HTC 6850).  This isn't an iPhone, but it's close enough for me.

http://www.htc.com/www/product/touchpro/overview.html

Call For Input: Branch Development Syncing Issue

All of the code we write must be tested and accepted by users before it can go into production.  Which brings about a unique possibility of a syncing issue when we are doing so in a branch development scenario.

The image below is a great reference.  Say we have two imaginary branches, one for features and two for production fixes. In reality there would probably be a branch for every production fix and multiple branches for on-going development.  The trunk is code that has been released to production.

We are doing our development in branch one. Every once in awhile we integrate changes from the trunk because it reflects production code (and we want to make sure we have that integrated in our code).We hit a code lock down period before production where all changes need to be tested.  In a branching development scenario, we would need to merge all changes from the mainline BEFORE we start our regression testing (integrate and test). 

This testing before a release to production could take awhile, and by awhile, anywhere from a couple of days to a couple of weeks.  What if after we do our final merge up from the trunk to the branch and begin testing, there is an emergency change that goes into production? Do we pull that back up (integrate the new changes that went into prod) and start our testing over? If there are several issues (that keep requiring emergency fixes), would we ever get out of testing?

BranchDevelopmentSync

From Zero to Continuous Integration Presentation: Tulsa TechFest 2008

Thanks to all who made it through my talk on Continuous Integration, as it was pretty dry with lots of page refreshing to show changes as we were building.  I am thinking this presentation isn't geared towards a fast format or I wasn't quite ready for it.  Either way, CI is an important tool to leverage anywhere code is being written, even if it is a dry talk. :D

Here are the files and the configuration that show how to connect up to SVN, VSS, TFS, and Vault. Also there is a triggered build in here to deploy after a successful build.

Enhancing VisualSVN Server to Allow Anonymous Access

Rick Strahl had a great recent post on Running VisualSVN Server for Subversion Source Control. I have been running VisualSVN Server for my repositories for a little while now and especially love how painless the setup is. Not to mention that VisualSVN Server is 100% free!

One part that I especially liked was the part I have been missing and that is anonymous access to repositories.  I have been thinking about having this for awhile and with Rick's post I was able to allow it:

Anonymous Repository Access

Speaking about authentication and repository access permissions, one thing that took me a while to get though is how to configure anonymous access. By default Visual SVN Server requires users to log in to access the repository, even when the Everyone account is set up with ReadOnly access. Unfortunately VisualSVN Server doesn't appear to provide an option to enable this functionality and it turned out to be a bit frustrating to set up the Everyone account with ReadOnly access and still be prompted for a required username and password. By default the Everyone account still needs to be an authenticated account.

As I mentioned I'm a dork when it comes to Apache configuration, but the fix for this is easy (thanks to Matthieu who commented on a previous post) by using Satisfy Any in the virtual directory configuration:

<Location /svn/>
  DAV svn

  SVNListParentPath on
  SVNParentPath "c:/SvnWestWind/"
  SVNIndexXSLT "/svnindex.xsl"
  SVNPathAuthz on

  AuthName "Subversion Repositories"
  AuthType Basic
  AuthBasicProvider file
  AuthUserFile "c:/SvnWestWind//htpasswd"
  AuthzSVNAccessFile "c:/SvnWestWind//authz"

  Satisfy Any
  Require valid-user
</Location>

 

This is sweet! But in the comments on user asked how to do get the everyone set up and where to find the virtual directory configuration file.  I had the same questions, but after about 3 minutes I figured it all out.

How to Get the Anonymous Access Up and Running on VisualSVN Server


1. Edit the file as in Rick's Post. The file can be found here:

%VisualSVN Server Root Install Directory%\conf\httpd.conf (most likely C:\Program Files\VisualSVN Server\conf\httpd.conf).

2. After you edit the file, restart the service for good measure (may not be required).
3. Open VisualSVN Server MMC snap in and right click on Repositories. Select {Properties...}. To do this for a single repository (instead of all, just select the properties of that repository instead).

image image

 
4. Click {Add..} (on the Security tab).

image 


5. Select Everyone. Click {OK}.

image 


6. Change access to {Read Only}. Click {Apply}. Click {OK}.

image 


7. Reload your web page or try to connect to the SVN repository with no username/password. You should be good to go! :D

SharpZipLib Versus 7-Zip for Large File Sets

Recently I was asked to help out when we had a need to reduce the size of a directory of rolling backups. And when I say size, I am talking around 12GB a day backup sizes.  This was already a compressed directory, but I found that if we zip up the folders, we can get even better compression (to the tune of 60+% more room).

First note: Windows built-in zipping utility is not so good, it won't even try to compress anything that size. Instead it throws an error. Nice.

Basically I started working with two options, #ZipLib (SharpZipLib) or 7-Zip.  #ZipLib is written entirely in C#, which lends nicely to logging and reporting purposes, but in my experience it may not be the answer for large file sets (NOTE: I benchmarked with #Zip's ZipFile, not with FastZip, which might be faster with the same compression results). 7-Zip is a fast utility, but it is an external application, so you have to call it from within your code and wait for the exit code.  I realize this already gives 7-Zip an unfair advantage, but I needed the best tool for the job, so the comparison really is about which you should use when you are working with large file sets.

Note: 7-Zip has a Stand Alone version (7za.exe) that I am talking about from here on out when I mention 7-Zip. They are on version 4.57.

Both tools compressed a 2GB directory (500MB on a compressed drive) down to a size of 185MB in ZIP format.  #ZipLib took nearly an hour, so I didn't let it finish with the other directories. 7-Zip took a little over an hour and a half to compress all 12GB.  I am presenting the code for both, so if anyone sees a faster way to use #ZipLib, I am definitely open to suggestions because I can get better logging about what is going on while it is running than I can with 7-Zip.

The IFileZipUtility Interface

The first thing we need is an interface IFileZipUtility.

namespace Tools.Compression
{
    /// <summary>
    /// Zips files up
    /// </summary>
    public interface IFileZipUtility
    {
        /// <summary>
        /// Zips files from a directory and subdirectories
        /// </summary>
        /// <param name="zipFilePath">The name of the zipped file</param>
        /// <param name="directory">directory to zip files up in</param>
        void GenerateZipFile(string zipFilePath, string directory);

        /// <summary>
        /// Zips files from a directory and subdirectories if recursive is true
        /// </summary>
        /// <param name="zipFilePath">The name of the zipped file</param>
        /// <param name="directory">directory to zip files up in</param>
        /// <param name="recursive">Would you like to zip up subdirectories as well?</param>
        void GenerateZipFile(string zipFilePath, string directory,bool recursive);
    }
}

The FileZipUtility Implementation of IFileZipUtility

Now we create our FileZipUtility that will call out to an IZip Application.

using log4net;

namespace Tools.Compression
{
    /// <summary>
    /// Zips Files up
    /// </summary>
    public sealed class FileZipUtility : IFileZipUtility
    {
        //todo: implement zipping filters

        private readonly ILog _logger = LogManager.GetLogger(typeof(FileZipUtility));
        private readonly IZip _zipApplication;

        /// <summary>
        /// Calls other constructor with default of 7-Zip for speedy compression
        /// </summary>
        public FileZipUtility() : this(new SevenZip())
        {
        }

        /// <summary>
        /// Creates a new zip utility that can zip files up
        /// </summary>
        /// <param name="zipApplication">Zip application to use on the back end</param>
        public FileZipUtility(IZip zipApplication)
        {
            _logger.DebugFormat("Initializing {0}.", GetType());
            _zipApplication = zipApplication;
        }

        /// <summary>
        /// Zips files from a directory and subdirectories
        /// </summary>
        /// <param name="zipFilePath">The name of the zipped file</param>
        /// <param name="directory">directory to zip files up in</param>
        public void GenerateZipFile(string zipFilePath, string directory)
        {
            GenerateZipFile(zipFilePath, directory, true);
        }

        /// <summary>
        /// Zips files from a directory and subdirectories
        /// </summary>
        /// <param name="zipFilePath">The name of the zipped file</param>
        /// <param name="directory">directory to zip files up in</param>
        /// <param name="recursive">Would you like to zip up subdirectories as well?</param>
        public void GenerateZipFile(string zipFilePath, string directory, bool recursive)
        {
            _logger.InfoFormat("Attempting to zip up directory \"{0}\" to \"{1}\".", directory,zipFilePath);
            _zipApplication.GenerateZipFile(zipFilePath, directory, recursive);
        }
    }
}

The IZip Interface

This is very simple. One method.

namespace Tools.Compression
{
    /// <summary>
    /// Zips up files and folders
    /// </summary>
    public interface IZip
    {
        /// <summary>
        /// Generates zip files
        /// </summary>
        /// <param name="zipFilePath">Path of zip file to create</param>
        /// <param name="directoryToZip">The top level directory to zip up</param>
        /// <param name="recursive">Would you like to zip up subdirectories as well?</param>
        void GenerateZipFile(string zipFilePath, string directoryToZip,bool recursive);
    }
}

The #ZipLib Implementation of IZip

using System.IO;
using Tools.FileSystem;
using log4net;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Core;

namespace Tools.Compression
{
    /// <summary>
    /// Zips up files and directories using the #ZipLibrary (SharpZipLibrary), which is native to C#.  Do not use if you are zipping up huge amounts of data.
    /// </summary>
    public sealed class SharpZip : IZip
    {
        private readonly ILog _logger = LogManager.GetLogger(typeof(SharpZip));
        private ZipFile _currentZipFile;
        private readonly IFileSystemAccess _fileSystem;

        /// <summary>
        /// Default constructor that calls the other with a windows file system
        /// </summary>
        public SharpZip() :this(new WindowsFileSystemAccess())
        {
        }

        /// <summary>
        /// Creates a new #zip item that can zip up files natively.
        /// </summary>
        /// <param name="fileSystem">Type of file system you are using.</param>
        public SharpZip(IFileSystemAccess fileSystem)
        {
            _logger.DebugFormat("Initializing {0}.", GetType());
            _fileSystem = fileSystem;
        }

        /// <summary>
        /// Generates zip files using the #ZipLib library
        /// </summary>
        /// <param name="zipFilePath">Path of zip file to create</param>
        /// <param name="directoryToZip">The top level directory to zip up</param>
        /// <param name="recursive">Would you like to zip up subdirectories as well?</param>
        public void GenerateZipFile(string zipFilePath, string directoryToZip,bool recursive)
        {
            if (string.IsNullOrEmpty(zipFilePath) || string.IsNullOrEmpty(directoryToZip))
            {
                _logger.WarnFormat("Either you have left out a path to zip files to or the directory that you want to zip up. You specified {0} and {1} respectively. No files will be zipped."
                    , zipFilePath ?? ""
                    , directoryToZip ?? ""
                    );
                return;
            }

            if (_fileSystem.DirectoryExists(directoryToZip))
            {
                _logger.InfoFormat("Using #ZipLib to zip up directory {0}.",directoryToZip);
                DirectoryInfo di = _fileSystem.GetDirectoryInfo(directoryToZip);
                ZipFile zf = ZipFile.Create(zipFilePath);

                _currentZipFile = zf;

                zf.BeginUpdate();
                zf.NameTransform = new ZipNameTransform(di.Parent.FullName);

                FileSystemScanner scanner = new FileSystemScanner(".*")
                                                {
                                                    ProcessFile = ProcessZipFile,
                                                    ProcessDirectory = ProcessZipDirectory
                                                };
                scanner.Scan(directoryToZip,recursive);

                zf.CommitUpdate();
                zf.Close();
            }
        }

        /// <summary>
        /// Callback for adding a new file.
        /// </summary>
        /// <param name="sender">The scanner calling this delegate.</param>
        /// <param name="args">The event arguments.</param>
        private void ProcessZipFile(object sender, ScanEventArgs args)
        {
            _logger.DebugFormat("Adding file \"{0}\" to zip.", args.Name);
            _currentZipFile.Add(args.Name);
        }

        /// <summary>
        /// Callback for adding a new directory.
        /// </summary>
        /// <param name="sender">The scanner calling this delegate.</param>
        /// <param name="args">The event arguments.</param>
        /// <remarks>Directories are only added if they are empty and
        /// the user has specified that empty directories are to be added.</remarks>
        private void ProcessZipDirectory(object sender, DirectoryEventArgs args)
        {
            if (!args.HasMatchingFiles)
            {
                _logger.DebugFormat("Adding folder \"{0}\" to zip.", args.Name);
                _currentZipFile.AddDirectory(args.Name);
            }
        }
    }
}

The 7-Zip Implementation of IZip

using System;
using Tools.External;
using Tools.FileSystem;
using log4net;

namespace Tools.Compression
{
    /// <summary>
    /// Zips up files and folders using 7-Zip, a command line tool with speed
    /// </summary>
    public sealed class SevenZip : IZip
    {
        private readonly ILog _logger = LogManager.GetLogger(typeof(SevenZip));
        private readonly IFileSystemAccess _fileSystem;
        private readonly string _sevenZipExecutablePath = ".\\7Zip\\7za.exe";

        /// <summary>
        /// Default constructor that calls the other with a windows file system
        /// </summary>
        public SevenZip()
            : this(new WindowsFileSystemAccess())
        {
        }

        /// <summary>
        /// Creates a new 7-Zip ready to zip files
        /// </summary>
        /// <param name="fileSystem">Type of file system you are using.</param>
        public SevenZip(IFileSystemAccess fileSystem)
            : this(fileSystem, ".\\7Zip\\7za.exe")
        {
        }

        /// <summary>
        /// Creates a new 7-Zip ready to zip files
        /// </summary>
        /// <param name="fileSystem">Type of file system you are using.</param>
        /// <param name="sevenZipExecutablePath">Path to the 7-Zip stand alone file</param>
        public SevenZip(IFileSystemAccess fileSystem, string sevenZipExecutablePath)
        {
            _logger.DebugFormat("Initializing {0}.", GetType());
            _fileSystem = fileSystem;
            _sevenZipExecutablePath = sevenZipExecutablePath;
        }

        /// <summary>
        /// Generates zip files using the 7-Zip on the command line. Blindingly fast
        /// </summary>
        /// <param name="zipFilePath">Path of zip file to create</param>
        /// <param name="directoryToZip">The top level directory to zip up</param>
        /// <param name="recursive">Would you like to zip up subdirectories as well?</param>
        public void GenerateZipFile(string zipFilePath, string directoryToZip, bool recursive)
        {
            if (string.IsNullOrEmpty(zipFilePath) || string.IsNullOrEmpty(directoryToZip))
            {
                _logger.WarnFormat("Either you have left out a path to zip files to or the directory that you want to zip up. You specified {0} and {1} respectively. No files will be zipped."
                    , zipFilePath ?? ""
                    , directoryToZip ?? ""
                    );
                return;
            }

            if (_fileSystem.DirectoryExists(directoryToZip))
            {
                _logger.InfoFormat("Using 7-Zip to zip up directory {0}.", directoryToZip);
                string externalAppArgs = string.Format("a -tzip \"{0}\" \"{1}\\*.*\" -r", zipFilePath, directoryToZip);
                if (!recursive)
                {
                    externalAppArgs = string.Format("a -tzip \"{0}\" \"{1}\\*.*\"", zipFilePath, directoryToZip);
                }
                _logger.InfoFormat("Calling external process {0} with arguments {1}.", _sevenZipExecutablePath, externalAppArgs);
                SevenZipExitCodeType exitCode = (SevenZipExitCodeType)ExternalApplication.RunCommandLine(
                        _sevenZipExecutablePath
                        , externalAppArgs
                        , true
                        , false
                        );

                if (exitCode != SevenZipExitCodeType.Success)
                {
                    string error = string.Format("7-Zip Utility had an error zipping up files. The reported error was {0}.",
                                      Enum.GetName(typeof(SevenZipExitCodeType), exitCode));
                    _logger.ErrorFormat(error);
                    throw new ApplicationException(error);
                }
            }
        }
    }
}

Our Best Friend Forever 7-Zip's Exit Codes

namespace Tools.Compression
{
    /// <summary>
    /// Exit codes associated with 7-zip
    /// </summary>
    public enum SevenZipExitCodeType
    {
        Success = 0,
        Warning = 1,
        FatalError = 2,
        CommandLineError = 7,
        NotEnoughMemoryError = 8,
        UserStoppedProcessingError = 255
    }
}

The External Application Utility

With 7-Zip we are calling an external application so we need a static class that will make calls out to the application and give us an integer exit code that we will convert to the 7-Zip Exit Codes above.

using System.IO;
using System.Diagnostics;
using log4net;

namespace Tools.External
{
    /// <summary>
    /// Allows access to external processes and applications through the command line
    /// </summary>
    public static class ExternalApplication
    {
        private static readonly ILog _logger = LogManager.GetLogger(typeof(ExternalApplication));

        /// <summary>
        /// Runs an external process on the command line
        /// </summary>
        /// <param name="filePath">Path to the executable</param>
        /// <param name="args">Any arguments you want passed to the executable</param>
        /// <returns>Returns the exit code of the executable</returns>
        public static int RunCommandLine(string filePath, string args)
        {
            return RunCommandLine(filePath, args, true, true);
        }

        /// <summary>
        /// Runs an external process on the command line
        /// </summary>
        /// <param name="filePath">Path to the executable</param>
        /// <param name="args">Any arguments you want passed to the executable</param>
        /// <param name="waitForExit">Whether to wait for external application to complete before returning</param>
        /// <param name="setWorkingDirectoryToPath">Whether to change the working directory of the executable to the executable's directory</param>
        /// <returns>Will return -1 every time if you do not wait for the executable to complete. Returns the exit code of the executable</returns>
        public static int RunCommandLine(string filePath, string args, bool waitForExit, bool setWorkingDirectoryToPath)
        {
            ProcessStartInfo process = new ProcessStartInfo(filePath, string.Format(" {0}", args))
                                           {
                                               UseShellExecute = false,
                                               RedirectStandardOutput = false,
                                               CreateNoWindow = false
                                           };

            if (setWorkingDirectoryToPath)
            {
                _logger.Debug("Setting working directory to the path of the executable.");
                process.WorkingDirectory = Path.GetDirectoryName(filePath);
            }

            _logger.InfoFormat("Starting process \"{0}\" with arguments \"{1}\" in working directory \"{2}\". Waiting for exit? {3}", 
                            process.FileName, 
                            process.Arguments, 
                            process.WorkingDirectory, 
                            waitForExit.ToString()
                            );

            return RunProcess(process, waitForExit);
        }

        private static int RunProcess(ProcessStartInfo processInfo, bool waitForExit)
        {
            using (Process process = new Process())
            {
                int exitCode = -1;

                process.StartInfo = processInfo;

                process.Start();
                if (waitForExit)
                {
                    process.WaitForExit();
                    exitCode = process.ExitCode;
                }

                return exitCode;
            }
        }
    }
}
 

The As_A_FileZipUtility Test

And at the very least you need to verify your Zip Utility Works. I definitely need some more tests on this including mocks.

using System.IO;
using Tools.FileSystem;
using MbUnit.Framework;
using Tools.Compression;

namespace Tools.Tests.Integration.Compression
{
    [TestFixture]
    public class As_A_FileZipUtility
    {
        private FileZipUtility _utility;
        private const string _sevenZipPath = "7Zip\\7Za.exe";

        [SetUp]
        public void I_want_to()
        {
            _utility = new FileZipUtility(new SevenZip(new WindowsFileSystemAccess(), _sevenZipPath));
        }

        [Test]
        public void Validate_FileZipUtility_is_created_successfully()
        {
            Assert.IsNotNull(_utility);
        }

        [Test]
        public void Validate_GenerateZipFile_creates_a_zipped_file()
        {
            const string zipFileName = @".\TestZippedFile.zip";
            const string directory = ".\\7Zip";

            _utility.GenerateZipFile(zipFileName, directory, true);

            Assert.IsTrue(File.Exists(zipFileName));
        }
    }
}

One thing you may have noticed is an interface IFileSystemAccess. This is my step between the code and the file system to be orthogonal.

This will quickly introduce zipping into your application with logging (using one of my biggest loves: log4net).  I am serious about feedback for #ZipLib. If you have found FastZip to be extremely fast, I would love to hear about it (and may explore it then).

Late on Your Credit Card Bill? You May Be Able to Avoid Default Rate

You just realized that you forgot to pay your bill and it was due yesterday. What do you do?

Pay it and call customer service immediately.  They may be able to help you out, especially if you have been a good customer and are cordial on the phone.

There is probably a grace period on your account. Understand that this has nothing to do with how late your payment can be without adverse action. Grace period is how long before the credit company starts charging you interest on new purchases (typically 25 days).

With most credit cards, if you are late on one bill, you are charged a late fee and in a lot of cases you go into default status (which among other things means a higher interest rate). The late payment is also reported to your credit reporting agencies (TransUnion, Experian, and Equifax).  Sometimes things just get forgotten and credit companies realize this. But remember, it is also how they make the most money!

Credit companies make money in at least two ways. The first way credit companies make money is to charge merchants a percentage of the sale to accept credit (although I am not sure if this is split from VISA, MasterCard, etc., down to the credit companies or not but I imagine they get a slice). The second, and more important to you, way they make money is from what they get from you above your purchases (interest, fees, etc). They make money when you OWE money to them.

That gives you the consumer some bargaining power. They have a product and you can choose to use a product with another company if they won't work with you. Your bargaining chip here is to tactfully tell them that if they default your account, you will just transfer the balance and they will not make any more interest off of you.

There are at least four rules when dealing with customer service

  1. Be cordial and always say "Please" and "Thank you." You will find a little goes a LONG way.
  2. Be direct about what you would like to achieve when the exchange is complete. Sometimes asking a simple "Is there anything that you can do to help me out?" goes a long way.
  3. Be persistent. If you find you are not getting help from the person on the other end, thank the representative, hang up, and call right back.  The representatives are human and getting a yes in most cases is very subjective to how friendly the rep is feeling and how you connect with them.
  4. Follow up. Always follow up to see that you received the benefits you hoped you would receive. If you didn't, remember rule number 3.

Defining MSBuild And NAnt Tasks Together in the SAME Class

I haven't seen ANYONE talk about putting MSBuild together with NAnt.  To me, writing two separate classes (one for MSBuild and one for NAnt) is repetition. There may be a reason to keep them separate (and that might have to do with keeping other required assemblies around, i.e. NAnt.Core.dll when just wanting MSBuild), but I have not found a really good reason yet to separate them. If someone does, please let me know.

I just made the two play nice together in my last post.

How to Programmatically Install A Windows Service (.NET) on a Remote Machine (with or without Dependencies) - Part Two

Last time we created WmiService.  Now we are going to create the Tasks so that we can call it from the build script.  This time we are going to get our Create Service and Delete Service into our Automated Deployment Scripts of MS Build and/or NAnt.

Let's define our tasks.

The MS Build Tasks

<CreateService
    MachineName="serverName"
    ServiceName="aService"
    ServiceDisplayName="A Service"
    PhysicalLocation="C:\Location\dude.exe"
    StartMode="Automatic"
    UserName="user"
    Password="password"
    Dependencies="MSMQ,hlpsvc"
    />

<DeleteService
    MachineName="serverName"
    ServiceName="aService"
    />

The NAnt Tasks

<createService
    machineName="serverName"
    name="aService"
    displayName="A Service"
    physicalLocation="C:\Location\dude.exe"
    startMode="Automatic"
    userName="user"
    password="password"
    dependencies="MSMQ,hlpsvc"
    />

<deleteService
    machineName="serverName"
    name="aService"
    />

To be honest, I haven't written a custom NAnt Task before. I looked to Jake Opines' article to learn how to do it.  There are a few steps that you have to take to be up and running with NAnt and MSBuild.

Here is what Jake says you need to do get up and running with NAnt:

Before we get started writing any code, we need to reference NAnt.Core.dll<