Monday, June 22, 2009

Working with the 1.5 code base : part 2

In this part I'll cover how to give more detailed information of the task being executed. Not in the build stage view, but in the task detail view (F4 in CCTray).
As Craig pointed out, this is a rather easy update, thanks Craig.
On updating the Ftp task, I also did some smaller updates :
° make some settings not mandatory : useActiveConnectionMode, action
° make a better standard task description
° create the local folder if it does not exist

Now, in order to have the task show 'sub items'., all you have to do is call the following : Task.CurrentStatus.AddChild(new ThoughtWorks.CruiseControl.Remote.ItemStatus(""));
Off course, add something meaningful for the string value. I added the files being upload/downloaded to this overview, so you have an idea what the ftp task is doing.


Changes in the code : ftpLib

Add a new constructor, that takes in the calling task and add the addChild on the uploaded/downloaded events.
private Tasks.TaskBase CallingTask;

public FtpLib(Tasks.TaskBase callingTask)
{
CallingTask = callingTask;
this.FtpServer = new EnterpriseDT.Net.Ftp.FTPConnection();
this.FtpServer.ReplyReceived += HandleMessages;
this.FtpServer.CommandSent += HandleMessages;
this.FtpServer.Downloaded += new EnterpriseDT.Net.Ftp.FTPFileTransferEventHandler(FtpServer_Downloaded);
this.FtpServer.Uploaded += new EnterpriseDT.Net.Ftp.FTPFileTransferEventHandler(FtpServer_Uploaded);
}

void FtpServer_Uploaded(object sender, EnterpriseDT.Net.Ftp.FTPFileTransferEventArgs e)
{
string file;
if (!e.RemoteDirectory.EndsWith("/"))
file = string.Concat("Uploaded : ", e.RemoteDirectory, "/", e.RemoteFile);
else
file = string.Concat("Uploaded : ", e.RemoteDirectory, e.RemoteFile);

CallingTask.CurrentStatus.AddChild(new ThoughtWorks.CruiseControl.Remote.ItemStatus(file));
}

void FtpServer_Downloaded(object sender, EnterpriseDT.Net.Ftp.FTPFileTransferEventArgs e)
{
string file;
if (!e.RemoteDirectory.EndsWith("/"))
file = string.Concat("Downloaded : ", e.RemoteDirectory, "/", e.RemoteFile);
else
file = string.Concat("Downloaded : ", e.RemoteDirectory, e.RemoteFile);

CallingTask.CurrentStatus.AddChild(new ThoughtWorks.CruiseControl.Remote.ItemStatus(file));
}

Changes in the code : ftpTask

protected override bool Execute(IIntegrationResult result)
{
result.BuildProgressInformation.SignalStartRunTask(!string.IsNullOrEmpty(Description) ? Description : GetDescription());

string remoteFolder = FtpFolderName;
FtpLib ftp = new FtpLib(this);

...
}

private string GetDescription()
{
if (Action == FtpAction.DownloadFolder)
{
return string.Concat("Downloading ", FtpFolderName, " to ", LocalFolderName);
}

return string.Concat("Uploading ", LocalFolderName, " to ", FtpFolderName);
}


Full Code of ftpLib

namespace ThoughtWorks.CruiseControl.Core.Util
{
public class FtpLib : IFtpLib
{
private EnterpriseDT.Net.Ftp.FTPConnection FtpServer;
private Tasks.TaskBase CallingTask;

public FtpLib()
{
this.FtpServer = new EnterpriseDT.Net.Ftp.FTPConnection();

this.FtpServer.ReplyReceived += HandleMessages;

this.FtpServer.CommandSent += HandleMessages;

}

public FtpLib(Tasks.TaskBase callingTask)
{
CallingTask = callingTask;

this.FtpServer = new EnterpriseDT.Net.Ftp.FTPConnection();

this.FtpServer.ReplyReceived += HandleMessages;

this.FtpServer.CommandSent += HandleMessages;

this.FtpServer.Downloaded += new EnterpriseDT.Net.Ftp.FTPFileTransferEventHandler(FtpServer_Downloaded);

this.FtpServer.Uploaded += new EnterpriseDT.Net.Ftp.FTPFileTransferEventHandler(FtpServer_Uploaded);

}


public void LogIn(string serverName, string userName, string password, bool activeConnectionMode)
{

Log.Info("Connecting to {0} ...", serverName);

{
this.FtpServer.ServerAddress = serverName;
this.FtpServer.UserName = userName;
this.FtpServer.Password = password;
this.FtpServer.Connect();

if (activeConnectionMode)
{
Log.Debug("Active mode enabled");
this.FtpServer.ConnectMode = EnterpriseDT.Net.Ftp.FTPConnectMode.ACTIVE;
}
else
{
Log.Debug("Passive mode enabled");
this.FtpServer.ConnectMode = EnterpriseDT.Net.Ftp.FTPConnectMode.PASV;
}

this.FtpServer.TransferType = EnterpriseDT.Net.Ftp.FTPTransferType.BINARY;
}
}


public void DownloadFolder(string localFolder, string remoteFolder, bool recursive)
{

this.FtpServer.ChangeWorkingDirectory(remoteFolder);

EnterpriseDT.Net.Ftp.FTPFile[] FtpServerFileInfo = this.FtpServer.GetFileInfos();

string LocalTargetFolder = null;
string FtpTargetFolder = null;
bool DownloadFile = false;
string LocalFile = null;
System.IO.FileInfo fi = default(System.IO.FileInfo);

if (!System.IO.Directory.Exists(localFolder))
{
Log.Debug("creating {0}", localFolder);
System.IO.Directory.CreateDirectory(localFolder);
}

foreach (EnterpriseDT.Net.Ftp.FTPFile CurrentFileOrDirectory in FtpServerFileInfo)
{
if (recursive)
{
if (CurrentFileOrDirectory.Dir && CurrentFileOrDirectory.Name != "." && CurrentFileOrDirectory.Name != "..")
{

LocalTargetFolder = System.IO.Path.Combine(localFolder, CurrentFileOrDirectory.Name);
FtpTargetFolder = string.Format("{0}/{1}", remoteFolder, CurrentFileOrDirectory.Name);

if (!System.IO.Directory.Exists(LocalTargetFolder))
{
Log.Debug("creating {0}", LocalTargetFolder);
System.IO.Directory.CreateDirectory(LocalTargetFolder);
}

DownloadFolder(LocalTargetFolder, FtpTargetFolder, recursive);

//set the ftp working folder back to the correct value
this.FtpServer.ChangeWorkingDirectory(remoteFolder);
}
}

if (!CurrentFileOrDirectory.Dir)
{
DownloadFile = false;

LocalFile = System.IO.Path.Combine(localFolder, CurrentFileOrDirectory.Name);


// check file existence
if (!System.IO.File.Exists(LocalFile))
{
DownloadFile = true;
}
else
{
//check file size
fi = new System.IO.FileInfo(LocalFile);
if (CurrentFileOrDirectory.Size != fi.Length)
{
DownloadFile = true;
System.IO.File.Delete(LocalFile);
}
else
{
//check modification time
if (CurrentFileOrDirectory.LastModified != fi.CreationTime)
{
DownloadFile = true;
System.IO.File.Delete(LocalFile);

}
}
}


if (DownloadFile)
{
Log.Debug("Downloading {0}", CurrentFileOrDirectory.Name);
this.FtpServer.DownloadFile(localFolder, CurrentFileOrDirectory.Name);

fi = new System.IO.FileInfo(LocalFile);
fi.CreationTime = CurrentFileOrDirectory.LastModified;
fi.LastAccessTime = CurrentFileOrDirectory.LastModified;
fi.LastWriteTime = CurrentFileOrDirectory.LastModified;
}

}

}
}


public void UploadFolder(string remoteFolder, string localFolder, bool recursive)
{

string[] LocalFiles = null;

LocalFiles = System.IO.Directory.GetFiles(localFolder, "*.*");
this.FtpServer.ChangeWorkingDirectory(remoteFolder);


// remove the local folder value, so we can work relative
for (int i = 0; i <= LocalFiles.Length - 1; i++)
{
LocalFiles[i] = LocalFiles[i].Remove(0, localFolder.Length + 1);
}


//upload files
//FtpServer.Exists throws an error, so we must do it ourselves
EnterpriseDT.Net.Ftp.FTPFile[] FtpServerFileInfo = this.FtpServer.GetFileInfos();


foreach (var LocalFile in LocalFiles)
{
if (!FileExistsAtFtp(FtpServerFileInfo, LocalFile))
{
this.FtpServer.UploadFile(System.IO.Path.Combine(localFolder, LocalFile), LocalFile);
}
else
{
if (FileIsDifferentAtFtp(FtpServerFileInfo, LocalFile, localFolder))
{
this.FtpServer.DeleteFile(LocalFile);
this.FtpServer.UploadFile(System.IO.Path.Combine(localFolder, LocalFile), LocalFile);
}

}
}


if (!recursive) return;

//upload folders
string[] Folders = null;

string LocalTargetFolder = null;
string FtpTargetFolder = null;


Folders = System.IO.Directory.GetDirectories(localFolder);

// remove the local folder value, so we can work relative
for (int i = 0; i <= Folders.Length - 1; i++)
{
Folders[i] = Folders[i].Remove(0, localFolder.Length + 1);
}


foreach (var Folder in Folders)
{
//explicit set the folder back, because of recursive calls
this.FtpServer.ChangeWorkingDirectory(remoteFolder);


if (!FolderExistsAtFtp(FtpServerFileInfo, Folder))
{
this.FtpServer.CreateDirectory(Folder);
}

LocalTargetFolder = System.IO.Path.Combine(localFolder, Folder);
FtpTargetFolder = string.Format("{0}/{1}", remoteFolder, Folder);

UploadFolder(FtpTargetFolder, LocalTargetFolder, recursive);
}
}

public void DisConnect()
{
this.FtpServer.Close();
}


public bool IsConnected()
{
return this.FtpServer.IsConnected;
}


public string CurrentWorkingFolder()
{
return this.FtpServer.ServerDirectory;
}

private bool FileExistsAtFtp(EnterpriseDT.Net.Ftp.FTPFile[] ftpServerFileInfo, string localFileName)
{

bool Found = false;

foreach (EnterpriseDT.Net.Ftp.FTPFile CurrentFileOrDirectory in ftpServerFileInfo)
{
if (!CurrentFileOrDirectory.Dir && CurrentFileOrDirectory.Name.ToLower() == localFileName.ToLower())
{
Found = true;
}
}

return Found;
}

private bool FolderExistsAtFtp(EnterpriseDT.Net.Ftp.FTPFile[] ftpServerFileInfo, string localFileName)
{

bool Found = false;
string updatedFolderName = null;

foreach (EnterpriseDT.Net.Ftp.FTPFile CurrentFileOrDirectory in ftpServerFileInfo)
{
if (CurrentFileOrDirectory.Name.EndsWith("/"))
{
updatedFolderName = CurrentFileOrDirectory.Name.Remove(CurrentFileOrDirectory.Name.Length - 1, 1);
}
else
{
updatedFolderName = CurrentFileOrDirectory.Name;
}

if (CurrentFileOrDirectory.Dir && updatedFolderName.ToLower() == localFileName.ToLower())
{
Found = true;
}
}

return Found;
}

private bool FileIsDifferentAtFtp(EnterpriseDT.Net.Ftp.FTPFile[] ftpServerFileInfo, string localFile, string localFolder)
{
bool isDifferent = false;
System.IO.FileInfo fi = default(System.IO.FileInfo);


foreach (EnterpriseDT.Net.Ftp.FTPFile CurrentFileOrDirectory in ftpServerFileInfo)
{
if (!CurrentFileOrDirectory.Dir && CurrentFileOrDirectory.Name.ToLower() == localFile.ToLower())
{
fi = new System.IO.FileInfo(System.IO.Path.Combine(localFolder, localFile));

if (fi.Length != CurrentFileOrDirectory.Size || fi.LastWriteTime != CurrentFileOrDirectory.LastModified)
{
isDifferent = true;
}
}
}

return isDifferent;
}

private void HandleMessages(object sender, EnterpriseDT.Net.Ftp.FTPMessageEventArgs e)
{
Log.Info(e.Message);
}

void FtpServer_Uploaded(object sender, EnterpriseDT.Net.Ftp.FTPFileTransferEventArgs e)
{
string file;
if (!e.RemoteDirectory.EndsWith("/"))
file = string.Concat("Uploaded : ", e.RemoteDirectory, "/", e.RemoteFile);
else
file = string.Concat("Uploaded : ", e.RemoteDirectory, e.RemoteFile);

CallingTask.CurrentStatus.AddChild(new ThoughtWorks.CruiseControl.Remote.ItemStatus(file));
}

void FtpServer_Downloaded(object sender, EnterpriseDT.Net.Ftp.FTPFileTransferEventArgs e)
{
string file;
if (!e.RemoteDirectory.EndsWith("/"))
file = string.Concat("Downloaded : ", e.RemoteDirectory, "/", e.RemoteFile);
else
file = string.Concat("Downloaded : ", e.RemoteDirectory, e.RemoteFile);

CallingTask.CurrentStatus.AddChild(new ThoughtWorks.CruiseControl.Remote.ItemStatus(file));
}

}
}


Full code of ftpTask

using System;
using System.Collections.Generic;
using System.Text;
using Exortech.NetReflector;
using ThoughtWorks.CruiseControl.Core.Util;

namespace ThoughtWorks.CruiseControl.Core.Tasks
{
[ReflectorType("ftp")]
class FtpTask : TaskBase
{
public enum FtpAction
{
UploadFolder,
DownloadFolder
}

[ReflectorProperty("serverName", Required = true)]
public string ServerName = string.Empty;

[ReflectorProperty("userName", Required = true)]
public string UserName = string.Empty;

[ReflectorProperty("password", Required = true)]
public string Password = string.Empty;

[ReflectorProperty("useActiveConnectionMode", Required = false)]
public bool UseActiveConnectionMode = true;

[ReflectorProperty("action", Required = false)]
public FtpAction Action = FtpAction.DownloadFolder;

[ReflectorProperty("ftpFolderName", Required = true)]
public string FtpFolderName = string.Empty;

[ReflectorProperty("localFolderName", Required = true)]
public string LocalFolderName = string.Empty;

[ReflectorProperty("recursiveCopy", Required = true)]
public bool RecursiveCopy = true;

protected override bool Execute(IIntegrationResult result)
{
result.BuildProgressInformation.SignalStartRunTask(!string.IsNullOrEmpty(Description) ? Description : GetDescription());

string remoteFolder = FtpFolderName;
FtpLib ftp = new FtpLib(this);


try
{
ftp.LogIn(ServerName, UserName, Password, UseActiveConnectionMode);

if (!FtpFolderName.StartsWith("/"))
{
remoteFolder = ftp.CurrentWorkingFolder() + "/" + FtpFolderName;
}

if (Action == FtpAction.UploadFolder)
{
Log.Debug("Uploading {0} to {1}, recursive : {2}", LocalFolderName, remoteFolder, RecursiveCopy);
ftp.UploadFolder(remoteFolder, LocalFolderName, RecursiveCopy);
}

if (Action == FtpAction.DownloadFolder)
{
Log.Debug("Downloading {0} to {1}, recursive : {2}", remoteFolder, LocalFolderName, RecursiveCopy);
ftp.DownloadFolder(LocalFolderName, remoteFolder, RecursiveCopy);
}

}
catch (Exception ex)
{
// try to disconnect in a proper way on getting an error
if (ftp != null)
{
try
{ // swallow exception on disconnect to keep the original error
if (ftp.IsConnected()) ftp.DisConnect();
}
catch { }
}
throw ex;
}

return true;
}

private string GetDescription()
{
if (Action == FtpAction.DownloadFolder)
{
return string.Concat("Downloading ", FtpFolderName, " to ", LocalFolderName);
}

return string.Concat("Uploading ", LocalFolderName, " to ", FtpFolderName);
}
}
}

No comments:

Post a Comment