May 13, 2008

Unity vs Castle Windsor

Recently I did a post on breaking dependencies which used the Castle Windsor container as the Inversion of Control container.  I thought I'd also have a look at doing the same thing with the Unity container from Microsoft's Patterns & Practices team, which was version 1.0'ed last month.

The good news?  It's pretty much just some slight syntax changes and that's about it.  The only problem I found was that after installing the Unity help file for VS2008 my Visual Studio help is now broken and now I have to go and repair my Visual Studio installation.  I'd suggest sticking to the CHM file instead.

Here's the only differences between the two applications:

1. References (duh!)

Castle Windsor
using Castle.Windsor;
using Castle.Core.Resource;
using Castle.Windsor.Configuration.Interpreters;

Unity
using System.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

2. Instantiating the Container


Castle Windsor
            IWindsorContainer container = new WindsorContainer(new XmlInterpreter());

Unity
            IUnityContainer container = new UnityContainer();

UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);

3. The .config files


Castle Windsor
<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
<
configSections>
<
section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</
configSections>

<
castle>
<
components>
<
component id="prompt.input" service="SalesPrompter.IInputControl, SalesPrompter"
type="SalesPrompter.PromptInputControl, SalesPrompter" />
<
component id="console.display" service="SalesTax.IMessageDisplay, SalesTax"
type="SalesTax.ConsoleMessageDisplay, SalesTax" />
<
component id="input.parser" service="SalesTax.IInputParser, SalesTax"
type="SalesTax.InputParser, SalesTax" />
<
component id="salelinefactory" service="SalesTax.ISaleLineFactory, SalesTax"
type="SalesTax.SaleLineFactory, SalesTax" />
</
components>
</
castle>

</
configuration>

Unity
<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
<
configSections>
<
section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</
configSections>

<
unity>
<
typeAliases></typeAliases>
<
containers>
<
container>
<
types>
<
type type="SalesPrompter.IInputControl, SalesPrompter"
mapTo="SalesPrompter.PromptInputControl, SalesPrompter"/>
<
type type="SalesTax.IMessageDisplay, SalesTax"
mapTo="SalesTax.ConsoleMessageDisplay, SalesTax"/>
<
type type="SalesTax.IInputParser, SalesTax"
mapTo="SalesTax.InputParser, SalesTax"/>
<
type type="SalesTax.ISaleLineFactory, SalesTax"
mapTo="SalesTax.SaleLineFactory, SalesTax"/>
</
types>
</
container>
</
containers>
</
unity>

</
configuration>

 


Note that there was nothing different in the resolving of references.  Both containers have a Resolve<T>() method that does exactly the same thing.


So, how easy is that? :-)  The P&P team have done a pretty good job with this container.  Admittedly I haven't really looked into some of the more advanced options, but it looks quite good and being from the P&P team it's likely to get more traction that the Castle Windsor container in the corporate development world.

May 7, 2008

Breaking Dependencies using Dependency Injection

Let's assume you've done some reading recently and heard talk of this Loosely Coupled Architecture thing and how it's meant to help keep an application maintainable, make it more readily changeable and will assist in the creation of unit tests .   You know that it's the right way to go and you can see all the benefits of it. You even think "I should do this on that application at work", but every time you look at the code at work and wonder what space monkeys were hired to write the code in the first place and you think about how tightly bound everything is it all starts to look too hard, so you give up and get back to working with things as they because the problem seems insurmountable.

Now a loosely coupled architecture is at it's heart just another way of saying object oriented design.  I'm sure you'll remember from when you learnt all that Object Oriented Programming stuff that there are some basic principle for OO Programming, namely:

Abstraction, Encapsulation, Polymorphism and Inheritance

And there are also some OO Design principles that sit alongside the OOP principles:

Encapsulate what changes

Favour composition over inheritance

Program to interfaces not implementations

Depend on abstractions, not concrete classes (dependency inversion)

A class should have only one reason to change (single responsibility)

Your work code and the "this problem is too big" feeling doesn't have to be the case.  It's just an indication that you've got legacy code and that it isn't well designed.  The good news is that by following some basic steps you can refactor a legacy, tightly-bound application into one with a loosely coupled architecture, though it will take some time (think many months, not weeks).

There are two basic methods for doing this.  The first is Extract & Override (see Roy Osherove's blog entry) and the other is Dependency Injection.  This is what I'll focus on here.

First, let's have a look at some code from a simple sales application:

    public class Sale
{
private List<SaleLine> saleLines = new List<SaleLine>();
private decimal totalTax;
private decimal totalValue;
...
public bool Add(string inputLine)
{
SaleLine saleLine;

saleLine = InputParser.ProcessInput(inputLine);
if (saleLine == null)
return false;
saleLines.Add(saleLine);
...

Here we see that a sale has multiple sale lines associated with it.  Pretty normal, and something you'll see in many code bases the world over, but we're breaking some of the OOD rules - we're programming to implementations not interfaces and we're using a static method (on the InputParser) which means we're tightly coupled between the Sale object and the SaleLine object.


We're also doing something else that's a no-no.  The Add method is coordinating a whole lot of work to convert an input string into a SaleLine object before adding it to the saleLines collection, whichmeans the class does more than one thing. 


Let's have a quick look at the ProcessInput method on the InputParser class as well

        public static SaleLine ProcessInput(string input)
{
SaleLine saleLine;
...
// create the sale line
saleLine = new SaleLine(quantity, name, price);
return saleLine;
}

What we have here is another tight coupling.  We're creating saleline objects from within the method (the new SaleLine() call) which means we have a tight dependency between the saleline class and the InputParser class.  If we wanted to change the SaleLine to some other class, we'd have some refactoring to do.


 


So let's make some changes...


Lets start by creating interfaces for all of our concrete classes, for example:

    public class Sale : ISale
{
private List<ISaleLine> saleLines = new List<ISaleLine>();

Now, if our definition of what a sale line is changes we don't have to make any changes in the Sale object.  Nice.


Let's also change the input parser so it doesn't create Sale objects directly.  We should do that through the use of a factory pattern implementation, such as the following:

    public class SaleLineFactory : ISaleLineFactory
{
public ISaleLine GetNewSaleLine(int lineQuantity, string name, decimal unitPrice)
{
return new SaleLine(lineQuantity, name, unitPrice);
}
}

And in our InputParser we change the code as follows:

            saleLine = saleLineFactory.GetNewSaleLine(quantity, productName, price);

Good. So now we're not actually creating the SaleLine object directly in our code.  This is a good thing as it breaks the tight coupling between the classes, but doesn't it just move the dependency from the SaleLine class to the SaleLineFactory class?


It would if we just added saleLineFactory = new SaleLineFactory() to the InputParser, but not if we pass an instance of the saleLineFactory class to the InputParser.  This is the Dependency Injection pattern at work.


Now, while we're working in the InputParser class we also have the opportunity to fix another tight coupling.  That between the Sale class and the InputParser.  Let's change the input parser so that we're no longer a static class, but rather a normal class (in a real world situation this could be complex) and let's pass to the ProcessInput method the Sale we want the new line to be added to.  We can then change the sale object so that it's Add method just adds ISaleLine objects instead of being passed a string and then calling to the parser.  Something like this:

    public class Sale : ISale
{
private List<ISaleLine> saleLines = new List<ISaleLine>();
...
public bool Add(ISaleLine saleLine)
{
if (saleLine == null)
return false;
saleLines.Add(saleLine);

public class InputParser : IInputParser
{
private ISaleLineFactory saleLineFactory;
public InputParser(ISaleLineFactory saleLineFactory)
{
this.saleLineFactory = saleLineFactory;
}

public void ProcessInput(string input, ISale sale)
{
ISaleLine saleLine;
...
saleLine = saleLineFactory.GetNewSaleLine(quantity, name, price);
sale.Add(saleLine);
return;
}

Much cleaner.  We now have no hard dependencies between our classes and we are programming to interfaces not concrete classes. 


But we're not done yet.  We still have to create a saleLineFactory class, an InputParser class, a Sale object, etc for us to be able to do any real work.  Where/when/how do we do all of this?


This is where an Inversion of Control (IoC) container can come in handy.  We could just create these objects manually at application startup, but using an IoC container is much easier.  An IoC container lets you map interfaces to classes, i.e. to say that InterfaceX is implemented by ClassY and the container will resolve these dependencies for you.  Most IoC containers let you do a whole lot more as well (Aspects for example).  For this example we'll use the Castle Windsor container to control our object creation for us:

        static void Main(string[] args)
{
IWindsorContainer container = new WindsorContainer(new XmlInterpreter());

IInputControl inputter = container.Resolve<IInputControl>();
IInputParser parser = container.Resolve<IInputParser>();

ISale sale;
string input;

sale = new Sale();
input = inputter.GetInput();
while (!inputter.EOF())
{
parser.ProcessInput(input, sale);
input = inputter.GetInput();
}

And here's the configuration file:

    <configSections>
<
section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</
configSections>

<
castle>
<
components>
<
component id="prompt.input" service="Sales.IInputControl, Sales"
type="Sales.PromptInputControl, Sales" />
<component id="input.parser" service="Sales.IInputParser, Sales"
type="Sales.InputParser, Sales" />
<
component id="salelinefactory" service="Sales.ISaleLineFactory, Sales"
type="Sales.SaleLineFactory, Sales" />
</
components>
</
castle>

Now the astute among you will immediately ask why I didn't put the Sale or SaleLine objects into the container?  The answer is fairly simple - they're not good candidates for objects to place in the container.  You want to put things in the container that are service classes, not data classes.  The definition of a sale and saleline will be fairly static, plus the use of the ISaleLineFactory lets us control which ISaleLine implementation we want to use. it's also why the factory is one of the classes the IoC container is aware of - a factory class is a service that the application uses.


You might also ask, why am I using sale = new Sale() in my sample code?  Well, I could have written an ISaleFactory and used that instead (and in a real world system it's what I'd recommend) but this is a sample app, so I beg your forgiveness :-)


Finally, the really astute amongst you will notice that I never call Resolve for the ISaleLineFactory interface and I don't provide parameters for the IInputParser class when it's resolved.  So how does it get into the input parser?  Well, the cool thing about the Windsor container is that it will automatically scan the constructors for classes it's aware of, and if that constructor has a dependency (i.e. the ISaleLineFactory interface) then the container will automatically instantiate an object for that interface and pass it through.


This makes managing dependencies and instantiation of classes so much simpler, and in large applications this is invaluable.


 


Now there is one other side effect to these changes.  By having loosely coupled objects writing unit tests is a lot easier now, and they are genuine unit tests, in other words they test one thing and one thing only.  Previously I would have had an input parser test as follows:

        [Test]
public void ParseACorrectLine()
{
saleLine = InputParser.ProcessInput("1 book at 12.49");
Assert.IsNotNull(saleLine);
Assert.AreEqual("book",saleLine.ProductName);
Assert.AreEqual(1, saleLine.Quantity);
Assert.AreEqual(12.49m, saleLine.Price);
}

 


But this is effectively an integration test.  It's testing that the parser and the saleline classes are both working and working together properly.  Not an ideal situation.


But with a loosely coupled design I can now use mocking and write a test as follows:

        [TestMethod]
public void ParseACorrectLine()
{
mocksRepository = new MockRepository();
saleLineFactory = mocksRepository.DynamicMock<ISaleLineFactory>();
sale = mocksRepository.DynamicMock<ISale>();
parser = new InputParser(display, saleLineFactory);
Expect.Call(saleLineFactory.GetNewSaleLine(1,"book",12.49m)).Return(new SaleLine(1,"book",12.49m));
mocksRepository.ReplayAll();
parser.ProcessInput("1 book at 12.49", sale);
mocksRepository.VerifyAll();
}

Now my test is checking that the interactions between the InputParser and the other classes are correct, not that the other classes are functioning correctly as well (which is what the first test did).  Note that the return of a new SaleLine object is just there to provide a return value for the call to the saleline factory.  Not having it would cause the test to fail.


 


Hopefully this is enough to get you started on improving that crappy code you have to work with every day and making it a more loosely coupled application, and as always I'd love some feedback :-)

Agile and Remuneration

A thread has sprung up on the scrumdevelopment list about seniority and scrum, and how some people don't like scrum because if it's all about the team then how do individuals get recognized and get treated as more senior than others.

Well firstly, there's a misunderstanding that needs to be overcome here.  The agile manifesto states that one of the principles is "Individuals and interactions over processes and tools." Individuals!  If you're a member of a scrum team you won't suddenly be seen the same as everyone else yet at the same time having a job title like "super senior developer uber architect" doesn't mean the team will respect you either.  As in all team situations individuals need to prove themselves to their team mates through valuable contributions.  Respect and leadership will come naturally as a result.

This is all well and good, but how do you then tie this to remuneration, salaries, bonuses, etc.  How do you ensure that a team gets rewarded but that individuals who contribute more to the team get rewarded further?

In Rugby Union (the sport when the scrum name came from) they have the concept of a man-of-the-match.  After each match a team will succeed or fail, but regardless of the result an individual is voted for as the player who contributed most to the performance of their team.  At the end of the season those votes are tallied and a "best and fairest" award is given to the person who contributed most to the overall performance of the team.  For software development why not apply the exact same principle to work out who has contributed the most?

The process is straightforward:

  1. At the start of the sprint retrospective (you are doing one, aren't you?) get the team to vote using a 3-2-1 system for the 3 people who contributed most to the performance of the team.  Obviously alter this slightly if you have a smaller team.  This also has the handy side-effect of getting the team thinking about what happened over the duration of the sprint, which is useful for the retrospective itself.
  2. Don't work out the results straight away.  Conduct the sprint retrospective as per usual.
  3. After the retrospective is done, work out the results and tell the team who the MVP or person-of-the-sprint is.  If you can, give that person a prize or some other form of recognition on the spot in front of their peers.
  4. At the end of a release cycle, or some other time period of your choosing (maybe based on pay review cycles) tally up the votes over the sprints in that period and you'll have a clear understanding of who the main contributors are.  Being peer voted there's no "teachers pet" syndrome to worry about either.

Once you know who the best people on the team are you can provide rewards, recognition, bonuses and/or pay increases accordingly.

Be aware that individuals who do not rate highly in the voting may feel excluded which is why it's important to reward the team as a whole as well.  In general try and ensure that team rewards are better than individual rewards otherwise you'll end up with a fractured team.

Apr 29, 2008

An Agile Agile Presentation for SBTUG Tomorrow Night

Unfortunately Mitch has come down ill (get better, mate!) and won't be able to present at SBTUG - The Sydney Business & Technology User Group - tomorrow night.  Craig has kindly let me step in and cover for him and I'll be doing my Agile Agile presentation so feel free to come along and be a part of the action.

I hope to see you there, and bring your questions!

P.S. Thanks Craig for the kind words.

Apr 26, 2008

The Whitest Beach In Australia

Another quick holiday update - Wednesday we spent the day in the Cape Le Grande National Park near Esperance in Western Australia, which is apparently home to the whitest beach in Australia at Lucky Bay.  After visiting it I can understand why it's so highly rated; and the other beaches in the park aren't that far behind it.

The water is crystal clear, the ocean is a brilliant blue and the sand is so soft and clean it's almost unbelievable. It actually crunches and squeaks underfoot and clumps up like icing sugar, just fantastic! Plus, if you have a 4WD you can drive out along Luck Bay's 3km of shoreline. Oh, and there's almost no-one around - being 750km from Perth certainly helps. Check out these shots for an idea of what I'm talking about.

Here's my foot seen through thigh deep water - you can't do that at a city beach!

image

And here's a view towards the ocean.  Check out the range of blues - simple amazing!

image

And finally a shot along the shore - as you can see the waves are small enough for me to take the camera into the water without fear of getting wet, and being in a national park there's no development along the coastline.  Just Australia at it's best!

image

Apr 14, 2008

In Scarborough Beach

Holidays are great when you're in a fantastic location.  The family and I are currently in Scarborough Beach, Western Australia have a great, relaxing time.

image 

Check out this wonderful sunset.  You don't see sunsets like this on the east coast (for obvious reasons)

image

Apr 11, 2008

Away for a Few Weeks

Just a quick note to let you all know I'll be on leave for a few weeks in Western Australia driving around the southwest corner of this great country of mine.  I _might_ do an occasional blog post while I'm away but it's more likely that I'll be relaxing and sleeping instead - you can always keep up with what I'm doing via twitter.

How To Fix Problems With Locked Files During a Web Application Build

I had a client recently that was having a big problem with builds on dev machines failing due to files being locked and unable to be deleted. The problem irregular and it was causing the developers to get frustrated and waste a lot of time in stopping and restarting Visual Studio to try and release the locks.

The errors the team were seeing were all similar to this:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets : warning MSB3061: Unable to delete file "C:\WebApplication\bin\Web.dll". Access to the path 'C:\WebApplication\bin\Web.dll' is denied.


The application itself was a web application, build on XP machines running VS2005 SP1 and set to use IIS as the default web server (i.e. the application virtual directory was pointed at the source code folder).


The team had already checked that the indexing service was off and that antivirus wasn't locking files.  When using the sysinternals utilities to see what other processes were locking those files nothing appeared apart from devenv.exe itself (i.e. visual studio).


The problem as it turned out was in the settings of the virtual directory in IIS6.  The virtual directory still had the "index this location" flag turned on.  It turned out that IIS was placing a temporary lock on these files, even though the web application wasn't being started (ie it was just a compile, not a debug run).  Why this didn't appear in the sysinternals utilities I'm not sure.


Anyway, after turning the "index this location" setting off the file locking problem disappeared and the devs started smiling again.  Happy days :-)

Apr 7, 2008

Deploying Web Sites using TFS Deployer, PowerShell and FTP

I had a situation recently where I needed to deploy a web site into a production environment from TFS where the only way we could transfer files was via FTP.  In case it's not obvious I didn't want to deploy every time the build ran, but rather, only when the build was declared good.

To do this, I used TFS Deployer (developed by Mitch, Chris, Darren and Geoff of Readify) available from CodePlex. TFS Deployer is a small utility that monitors changes in the build quality of a TFS Team Build and initiates a PowerShell script based on what the change in build quality was.  It runs something like this:

deploy1

A TeamBuild is executed and after completion the build has a quality of "Unexamined"

1. Someone runs some tests, checks the build is OK and decides the build quality can be progressed

2. TFS updates the build quality and raises an alert that the build quality has changed

3. On startup TFS Deployer registers itself as a listener for build quality events and picks up the alert.  It examines the old/new qualities and determines if a PowerShell script should be run. If a script needs to be run then PowerShell is started and the script executed.

4. Results from the script are emailed to a specified email address of distribution list.

clip_image002

 

TFS Deployer Setup

I'll assume that TFS Deployer is installed and set up as per the instructions.  To initiate the PowerShell script we set up a deployment mapping as follows:

    <Mapping xmlns=""
Computer="MyBuildServer"
OriginalQuality="Unexamined"
NewQuality="Released to Production"
Script="DeployToProd.ps1"
NotificationAddress="you.are@here.com" />

That's about it.  Pretty tough :-) Now for something a little harder.


Getting Ready for Deployment via FTP


OK, this is where it could get painful.  PowerShell doesn't have any native FTP support. So the first option you have would be to use the native .NET FTP classes from but that's a real pain because you effectively have to implement your own FTP client in PowerShell.  No thanks!


But don't despair - the open source Indy Project is a project that provides a wrapper and helper functions around for all the FTP calls you might want to make (plus a whole bunch of other low level networking goodness) and they have a .NET version of their library.  Grab a copy of that (and the Mono.Security DLL that goes with it) and you'll have all you really need to get going.


I should also mention that because I'm deploying to a production box I'm assuming that IIS is already set up - after all it's very unlikely that we'd be dropping the IIS virtual directory on every deployment.  We'll just be dropping our files in over the top of whatever is already there.


The PowerShell Script


Time for the script itself - I'll just take this section at a time.  First up, the script is called from TFS Deployer which means we have access to the TFS build data for the build that just had it's quality changed. So we're going to set two variables for the folders where things live.  $loc is the root of the build drop location, and $sourcefiles is where the published web site live (ie the stuff we need to deploy).

Set-Location $TfsDeployerBuildData.DropLocation;
$loc = get-location;
set-location "Mixed Platforms\Release\_PublishedWebSites\";
$sourceFiles = get-location;

Opening and Closing FTP Connections


Next we're going to create a few PowerShell script functions to support the opening and closing of our FTP connections.


When we open the connection we're first going to use the .NET assembly loader to bring in the Indy.Sockets library then use the methods in that to make the connection and then return the FTP connection object from the function.  The close-method is a bit simpler in that we simply wrap the call to the FTP-disconnect. (yes, it's not really required as a function, but you never know when you might want to add logging, error handling, etc).

function Open-FTPConnection($ftphost, $username, $password) {

[void][Reflection.Assembly]::LoadFrom("C:\path\to\Indy.Sockets.dll")
$ftp = new-object Indy.Sockets.FTP
$ftp.Disconnect()
$ftp.Host = $ftphost
$ftp.Username = $username
$ftp.Password = $password
$ftp.Connect()
$ftp.Passive=$true;
return $ftp
}

function Close-FTPConnection($ftp) {
$ftp.Disconnect();
}

Oh - For those not familiar with PowerShell syntax the :: operator lets us call static methods on a type and the [] wrap a type identifier. 


FTP Miscellaneous Functions


Next we define a few more functions just to wrap up some of the basic FTP calls.  Note: Download-FTPFile isn't used - it's just there for your reference.

function Get-FTPCurrentLocation($ftp) {
return $ftp.RetrieveCurrentDir();
}

function Download-FTPFile($ftp, $sourceFileName, $targetDir) {
$ftp.Get($sourceFileName, ($targetDir + $sourceFileName), $true, $false);
}

function Upload-FTPFile($ftp, $sourceFileName, $targetFileName) {
$ftp.Put($sourceFileName, $targetFileName, $false);
}

Nothing overly complex in that.


Getting the Contents of an FTP Folder


Now we get to something a bit more interesting.  Here we're creating a function that iterates over the contents of the FTP location and optionally deleting files as it goes.  It will return a list of sub-folders in the folder for later use.


The function works as follows:


1. Get the directory listing for the current FTP location.


2. Checks if the current directory is the root folder or not - appends a trailing slash if it isn't.


3. For each item in the folder...


3a. Parses the string to get the file name


3b. Checks if the item is a directory or a file (directories have a "d" in their attributes)


3c. For folders we return the full path to the sub-folder.


3d. For files we check the delete flag and nuke the file if it is set.


You'll notice that the $result.add($name) call is cast to a [void].  If we don't do this then when we return from the function we get 2 sets of files.  One from the $result object and one for each file name that was written to the output stream by the $result.add() method call.

function Get-FTPFolders($ftp, [bool]$removeFiles) {
$ls = new-object System.Collections.Specialized.StringCollection;
$result = new-object System.Collections.Specialized.StringCollection;
$ftp.List($ls, "", $true);
$currdir = Get-FTPCurrentLocation($ftp);
if($currdir -ne "/") {
$currdir = $currdir + "/";
}
foreach ($item in $ls)
{
[string[]]$fields=[Regex]::Split($item, " +");
$startField=8; #the file/directory name starts after 8 fields
[string]$name=$currdir;

#make sure we join up file names that were split (ie ones with spaces)
for ($field=$startField; $field -lt $fields.Length; $field++)
{
if ($field -eq $startField)
{
$temp = ""
} else
{
$temp = " "
}
$name += $temp + $fields[$field];
}

if ($item.StartsWith("d"))
{ #directory
[void]$result.Add($name);
}
else
{
if ($item.StartsWith("-")) { #files have '-' as first character
if ($removeFiles)
{
$ftp.Delete($name);
}
}
}
}
return $result
}

Iterating/Deleting the FTP Folder Structure


Next I have two more methods.  One to read through the contents of the FTP folder hierarchy, and one to clear it out.  Both methods are roughly the same with only variations for the call to Get-FTPFolders.

function Get-FTPTree($ftp)
{
$thisdir = Get-FTPCurrentLocation($ftp);
$thisdir;
$subfolders = (Get-FTPFolders $ftp $false);
if ($subfolders -ne $null) {
foreach ($xitem in $subfolders)
{
$ftp.ChangeDir($xitem);
Get-FTPTree($ftp);
}
}
$ftp.ChangeDir($thisdir);
return;
}

function Clean-FTPTree($ftp)
{
$thisdir = Get-FTPCurrentLocation($ftp);
$thisdir;
$subfolders = (Get-FTPFolders $ftp $true);
if ($subfolders -ne $null) {
foreach ($xitem in $subfolders)
{
$ftp.ChangeDir($xitem);
Clean-FTPTree($ftp);
$foldername = $xitem.split("/");
$ftp.ChangeDir($thisdir);
$ftp.RemoveDir($foldername[$foldername.Count - 1]);
}
}
$ftp.ChangeDir($thisdir);
return;
}

Note that in the Clean method we delete folders by stripping the folder name off the end of the full path, stepping back up a level and then calling the removedir method.  It's not pretty, but it works.


Putting it all Together


Now we have everything in place let's actually do what we need to do. Here's what happens:


1. We make the connection to the FTP server.


2. We clean out the existing FTP tree


3. We get a list of all the files we're going to upload.


4. We copy each file individually to the ftp server, creating folders where required (note the filename processing).


5. Just to make sure things are right - we get the contents of the site so we can check the upload worked.


6. Close the connection - we're done!

#Make a connection

$f = Open-FTPConnection "my.ftp.server.com" "ftp_user_account" "ftp_account_password";

write-output "----CLEANING----";
Clean-FTPTree($f);

write-output "----UPLOADING----";

$localfiles = (get-childitem $sourcefiles -r)
foreach ($localfile in $localfiles) {
$remfilename = $localfile.FullName.Replace($sourcefiles.ProviderPath, "");
$remfilename = $remfilename.Replace("\", "/");
if ($localfile.Attributes -eq "Directory") {
Write-Output ("Creating " + $localfile.FullName + ":" + $remfilename);
$f.MakeDir($remfilename);
}
else {
Write-Output (" Uploading " + $localfile.FullName + ":" + $remfilename);
upload-ftpfile $f $localfile.FullName $remfilename
$remfilename;
}
}

write-output "----VERIFYING----";
Get-FTPTree($f);

Close-FTPConnection $f

One thing you might notice is that we use the ProviderPath property of the $sourcefiles variable.  This is because $sourcefiles is a PathInfo object and when TFS Deployer runs the PathInfo will contain a UNC path pointing to a network share - if we just use the Path property then we will get the PowerShell Provider identifier in the string causing the FTP upload to fail.


 


Hopefully this is a good starting point for you if you are trying to do the same thing.