Added bit.ly support for distiributives download; Updated year in copyright text;

This commit is contained in:
ptsurbeleu 2012-02-14 23:15:45 -08:00
parent 8b7b150da7
commit 2dc3ced5cf
12 changed files with 1210 additions and 955 deletions

View file

@ -63,6 +63,24 @@
} }
"Entry" "Entry"
{ {
"MsmKey" = "8:_5FD334A6C47943FA9A98232EA921C90B"
"OwnerKey" = "8:_05F59A142DD147798C90054A203C0EE9"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
{
"MsmKey" = "8:_5FD334A6C47943FA9A98232EA921C90B"
"OwnerKey" = "8:_1239E87E938248B1BAF9BF75C32D3EDC"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
{
"MsmKey" = "8:_5FD334A6C47943FA9A98232EA921C90B"
"OwnerKey" = "8:_CFB0AE8275767700870555C8CDA92C96"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
{
"MsmKey" = "8:_BD9DC4338DFD4472BE5D099C388608B6" "MsmKey" = "8:_BD9DC4338DFD4472BE5D099C388608B6"
"OwnerKey" = "8:_UNDEFINED" "OwnerKey" = "8:_UNDEFINED"
"MsmSig" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED"
@ -294,6 +312,37 @@
"IsDependency" = "11:FALSE" "IsDependency" = "11:FALSE"
"IsolateTo" = "8:" "IsolateTo" = "8:"
} }
"{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_5FD334A6C47943FA9A98232EA921C90B"
{
"AssemblyRegister" = "3:1"
"AssemblyIsInGAC" = "11:FALSE"
"AssemblyAsmDisplayName" = "8:Ionic.Zip.Reduced, Version=1.8.4.28, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL"
"ScatterAssemblies"
{
"_5FD334A6C47943FA9A98232EA921C90B"
{
"Name" = "8:Ionic.Zip.Reduced.dll"
"Attributes" = "3:512"
}
}
"SourcePath" = "8:Ionic.Zip.Reduced.dll"
"TargetName" = "8:"
"Tag" = "8:"
"Folder" = "8:_E742E59BFE4D43C59AA65A07792B89FB"
"Condition" = "8:"
"Transitive" = "11:FALSE"
"Vital" = "11:TRUE"
"ReadOnly" = "11:FALSE"
"Hidden" = "11:FALSE"
"System" = "11:FALSE"
"Permanent" = "11:FALSE"
"SharedLegacy" = "11:FALSE"
"PackageAs" = "3:1"
"Register" = "3:1"
"Exclude" = "11:FALSE"
"IsDependency" = "11:TRUE"
"IsolateTo" = "8:"
}
"{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BD9DC4338DFD4472BE5D099C388608B6" "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BD9DC4338DFD4472BE5D099C388608B6"
{ {
"SourcePath" = "8:Banner.bmp" "SourcePath" = "8:Banner.bmp"
@ -407,15 +456,15 @@
{ {
"Name" = "8:Microsoft Visual Studio" "Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:WebsitePanel Installer" "ProductName" = "8:WebsitePanel Installer"
"ProductCode" = "8:{09BCDF68-1964-4FC6-9570-9AB3B8512CF6}" "ProductCode" = "8:{A22F374C-4AFC-4B5D-A509-7456A6107588}"
"PackageCode" = "8:{D94FF2E6-1D7D-4E33-8528-C7B6BFADEC99}" "PackageCode" = "8:{401F157D-6D55-4F66-A2C6-419F87BBF97D}"
"UpgradeCode" = "8:{2950A907-11E7-436C-86CE-049C414AFD08}" "UpgradeCode" = "8:{2950A907-11E7-436C-86CE-049C414AFD08}"
"AspNetVersion" = "8:4.0.30319.0" "AspNetVersion" = "8:4.0.30319.0"
"RestartWWWService" = "11:FALSE" "RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE"
"DetectNewerInstalledVersion" = "11:FALSE" "DetectNewerInstalledVersion" = "11:FALSE"
"InstallAllUsers" = "11:TRUE" "InstallAllUsers" = "11:TRUE"
"ProductVersion" = "8:1.2.0" "ProductVersion" = "8:1.2.1"
"Manufacturer" = "8:Outercurve Foundation" "Manufacturer" = "8:Outercurve Foundation"
"ARPHELPTELEPHONE" = "8:" "ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:" "ARPHELPLINK" = "8:"

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -35,7 +35,7 @@ using System.Reflection;
// Revision // Revision
// //
[assembly: AssemblyCompany("Outercurve Foundation")] [assembly: AssemblyCompany("Outercurve Foundation")]
[assembly: AssemblyCopyright("Copyright © 2011 Outercurve Foundation.")] [assembly: AssemblyCopyright("Copyright © 2012 Outercurve Foundation.")]
[assembly: AssemblyVersion("1.2.0.0")] [assembly: AssemblyVersion("1.2.1.0")]
[assembly: AssemblyFileVersion("1.2.0.38")] [assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyInformationalVersion("1.2.1")]

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -254,7 +254,22 @@ namespace WebsitePanel.Installer.Common
} }
/// <summary> /// <summary>
/// Deletes application temp directory. /// Deletes application's Data folder.
/// </summary>
public static void DeleteDataDirectory()
{
try
{
DeleteDirectory(GetDataDirectory());
}
catch (Exception ex)
{
Log.WriteError("IO Error", ex);
}
}
/// <summary>
/// Deletes application Tmp directory.
/// </summary> /// </summary>
public static void DeleteTempDirectory() public static void DeleteTempDirectory()
{ {

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -45,6 +45,7 @@ using System.Threading;
using WebsitePanel.Installer.Core; using WebsitePanel.Installer.Core;
using WebsitePanel.Installer.Configuration; using WebsitePanel.Installer.Configuration;
using System.Xml; using System.Xml;
using System.Data;
namespace WebsitePanel.Installer.Common namespace WebsitePanel.Installer.Common
{ {
@ -389,5 +390,23 @@ namespace WebsitePanel.Installer.Common
mutex = new Mutex(true, "WebsitePanel Installer", out createdNew); mutex = new Mutex(true, "WebsitePanel Installer", out createdNew);
return createdNew; return createdNew;
} }
public static string GetDistributiveLocationInfo(string ccode, string cversion)
{
var service = ServiceProviderProxy.GetInstallerWebService();
//
DataSet ds = service.GetReleaseFileInfo(ccode, cversion);
//
if (ds == null || ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
{
Log.WriteInfo("Component code: {0}; Component version: {1};", ccode, cversion);
//
throw new ServiceComponentNotFoundException("Seems that the Service has no idea about the component requested.");
}
//
DataRow row = ds.Tables[0].Rows[0];
//
return row["FullFilePath"].ToString();
}
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -36,6 +36,12 @@ using System.Text;
using Ionic.Zip; using Ionic.Zip;
using WebsitePanel.Installer.Common; using WebsitePanel.Installer.Common;
using System.Net;
using System.Text.RegularExpressions;
using System.Collections;
using System.Threading.Tasks;
using System.Reflection;
using System.Diagnostics;
namespace WebsitePanel.Installer.Core namespace WebsitePanel.Installer.Core
{ {
@ -46,10 +52,112 @@ namespace WebsitePanel.Installer.Core
public bool Cancellable { get; set; } public bool Cancellable { get; set; }
} }
public static class LoaderFactory
{
/// <summary>
/// Instantiates either BitlyLoader or InstallerServiceLoader based on remote file format.
/// </summary>
/// <param name="remoteFile"></param>
/// <returns></returns>
public static Loader CreateFileLoader(string remoteFile)
{
Debug.Assert(!String.IsNullOrEmpty(remoteFile), "Remote file is empty");
if (remoteFile.StartsWith("http://bit.ly/"))
{
return new BitlyLoader(remoteFile);
}
else
{
return new Loader(remoteFile);
}
}
}
public class BitlyLoader : Loader
{
public const string WEB_PI_USER_AGENT_HEADER = "PI-Integrator/3.0.0.0({0})";
private WebClient fileLoader;
public BitlyLoader(string remoteFile)
: base(remoteFile)
{
InitFileLoader();
}
private void InitFileLoader()
{
fileLoader = new WebClient();
// Set HTTP header for Codeplex to allow direct downloads
fileLoader.Headers.Add("User-Agent", String.Format(WEB_PI_USER_AGENT_HEADER, Assembly.GetExecutingAssembly().FullName));
}
protected override Task GetDownloadFileTask(string remoteFile, string tmpFile, CancellationToken ct)
{
var downloadFileTask = new Task(() =>
{
if (!File.Exists(tmpFile))
{
// Mimic synchronous file download operation because we need to track the download progress
// and be able to cancel the operation in progress
AutoResetEvent autoEvent = new AutoResetEvent(false);
if (fileLoader.IsBusy.Equals(true))
{
return;
}
ct.Register(() =>
{
fileLoader.CancelAsync();
});
Log.WriteStart("Downloading file");
Log.WriteInfo("Downloading file \"{0}\" to \"{1}\"", remoteFile, tmpFile);
// Attach event handlers to track status of the download process
fileLoader.DownloadProgressChanged += (obj, e) =>
{
if (ct.IsCancellationRequested)
return;
RaiseOnProgressChangedEvent(e.ProgressPercentage);
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage,
String.Format(DownloadProgressMessage, e.BytesReceived / 1024, e.TotalBytesToReceive / 1024));
};
fileLoader.DownloadFileCompleted += (obj, e) =>
{
if (ct.IsCancellationRequested == false)
{
RaiseOnProgressChangedEvent(100);
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%");
}
if (e.Cancelled)
{
CancelDownload(tmpFile);
}
autoEvent.Set();
};
fileLoader.DownloadFileAsync(new Uri(remoteFile), tmpFile);
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage);
autoEvent.WaitOne();
}
}, ct);
return downloadFileTask;
}
}
/// <summary> /// <summary>
/// Loader form. /// Loader form.
/// </summary> /// </summary>
public partial class Loader public class Loader
{ {
public const string ConnectingRemotServiceMessage = "Connecting..."; public const string ConnectingRemotServiceMessage = "Connecting...";
public const string DownloadingSetupFilesMessage = "Downloading setup files..."; public const string DownloadingSetupFilesMessage = "Downloading setup files...";
@ -59,11 +167,8 @@ namespace WebsitePanel.Installer.Core
public const string PrepareSetupProgressMessage = "{0}%"; public const string PrepareSetupProgressMessage = "{0}%";
private const int ChunkSize = 262144; private const int ChunkSize = 262144;
private Thread thread;
private string localFile;
private string remoteFile; private string remoteFile;
private string componentCode; private CancellationTokenSource cts;
private string version;
public event EventHandler<LoaderEventArgs<String>> StatusChanged; public event EventHandler<LoaderEventArgs<String>> StatusChanged;
public event EventHandler<LoaderEventArgs<Exception>> OperationFailed; public event EventHandler<LoaderEventArgs<Exception>> OperationFailed;
@ -75,62 +180,56 @@ namespace WebsitePanel.Installer.Core
this.remoteFile = remoteFile; this.remoteFile = remoteFile;
} }
public Loader(string localFile, string componentCode, string version)
{
this.localFile = localFile;
this.componentCode = componentCode;
this.version = version;
}
public void LoadAppDistributive() public void LoadAppDistributive()
{ {
thread = new Thread(new ThreadStart(LoadAppDistributiveInternal)); ThreadPool.QueueUserWorkItem(q => LoadAppDistributiveInternal());
thread.Start();
} }
private void RaiseOnStatusChangedEvent(string statusMessage) protected void RaiseOnStatusChangedEvent(string statusMessage)
{ {
RaiseOnStatusChangedEvent(statusMessage, String.Empty); RaiseOnStatusChangedEvent(statusMessage, String.Empty);
} }
private void RaiseOnStatusChangedEvent(string statusMessage, string eventData) protected void RaiseOnStatusChangedEvent(string statusMessage, string eventData)
{ {
RaiseOnStatusChangedEvent(statusMessage, eventData, true); RaiseOnStatusChangedEvent(statusMessage, eventData, true);
} }
private void RaiseOnStatusChangedEvent(string statusMessage, string eventData, bool cancellable) protected void RaiseOnStatusChangedEvent(string statusMessage, string eventData, bool cancellable)
{ {
if (StatusChanged == null) if (StatusChanged == null)
{ {
return; return;
} }
// No event data for status updates // No event data for status updates
StatusChanged(this, new LoaderEventArgs<String> { StatusChanged(this, new LoaderEventArgs<String>
{
StatusMessage = statusMessage, StatusMessage = statusMessage,
EventData = eventData, EventData = eventData,
Cancellable = cancellable Cancellable = cancellable
}); });
} }
private void RaiseOnProgressChangedEvent(int eventData) protected void RaiseOnProgressChangedEvent(int eventData)
{ {
RaiseOnProgressChangedEvent(eventData, true); RaiseOnProgressChangedEvent(eventData, true);
} }
private void RaiseOnProgressChangedEvent(int eventData, bool cancellable) protected void RaiseOnProgressChangedEvent(int eventData, bool cancellable)
{ {
if (ProgressChanged == null) if (ProgressChanged == null)
{ {
return; return;
} }
// //
ProgressChanged(this, new LoaderEventArgs<int> { ProgressChanged(this, new LoaderEventArgs<int>
{
EventData = eventData, EventData = eventData,
Cancellable = cancellable Cancellable = cancellable
}); });
} }
private void RaiseOnOperationFailedEvent(Exception ex) protected void RaiseOnOperationFailedEvent(Exception ex)
{ {
if (OperationFailed == null) if (OperationFailed == null)
{ {
@ -140,7 +239,7 @@ namespace WebsitePanel.Installer.Core
OperationFailed(this, new LoaderEventArgs<Exception> { EventData = ex }); OperationFailed(this, new LoaderEventArgs<Exception> { EventData = ex });
} }
private void RaiseOnOperationCompletedEvent() protected void RaiseOnOperationCompletedEvent()
{ {
if (OperationCompleted == null) if (OperationCompleted == null)
{ {
@ -151,87 +250,36 @@ namespace WebsitePanel.Installer.Core
} }
/// <summary> /// <summary>
/// Displays process progress. /// Executes a file download request asynchronously
/// </summary> /// </summary>
private void LoadAppDistributiveInternal() private void LoadAppDistributiveInternal()
{ {
//
try try
{ {
var service = ServiceProviderProxy.GetInstallerWebService(); string dataFolder;
// string tmpFolder;
string dataFolder = FileUtils.GetDataDirectory(); // Retrieve local storage configuration
string tmpFolder = FileUtils.GetTempDirectory(); GetLocalStorageInfo(out dataFolder, out tmpFolder);
// Initialize storage
InitializeLocalStorage(dataFolder, tmpFolder);
if (!Directory.Exists(dataFolder)) string fileToDownload = Path.GetFileName(remoteFile);
{
Directory.CreateDirectory(dataFolder);
Log.WriteInfo("Data directory created");
}
if (Directory.Exists(tmpFolder))
{
FileUtils.DeleteTempDirectory();
}
if (!Directory.Exists(tmpFolder))
{
Directory.CreateDirectory(tmpFolder);
Log.WriteInfo("Tmp directory created");
}
string fileToDownload = null;
if (!string.IsNullOrEmpty(localFile))
{
fileToDownload = localFile;
}
else
{
fileToDownload = Path.GetFileName(remoteFile);
}
string destinationFile = Path.Combine(dataFolder, fileToDownload); string destinationFile = Path.Combine(dataFolder, fileToDownload);
string tmpFile = Path.Combine(tmpFolder, fileToDownload); string tmpFile = Path.Combine(tmpFolder, fileToDownload);
//check whether file already downloaded cts = new CancellationTokenSource();
if (!File.Exists(destinationFile)) CancellationToken token = cts.Token;
{
if (string.IsNullOrEmpty(remoteFile))
{
//need to get remote file name
RaiseOnStatusChangedEvent(ConnectingRemotServiceMessage);
//
RaiseOnProgressChangedEvent(0);
//
DataSet ds = service.GetReleaseFileInfo(componentCode, version);
//
RaiseOnProgressChangedEvent(100);
//
if (ds != null &&
ds.Tables.Count > 0 &&
ds.Tables[0].Rows.Count > 0)
{
DataRow row = ds.Tables[0].Rows[0];
remoteFile = row["FullFilePath"].ToString();
fileToDownload = Path.GetFileName(remoteFile);
destinationFile = Path.Combine(dataFolder, fileToDownload);
tmpFile = Path.Combine(tmpFolder, fileToDownload);
}
else
{
throw new Exception("Installer not found");
}
}
// download file to tmp folder
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage);
//
RaiseOnProgressChangedEvent(0);
//
DownloadFile(remoteFile, tmpFile);
//
RaiseOnProgressChangedEvent(100);
try
{
// Download the file requested
Task downloadFileTask = GetDownloadFileTask(remoteFile, tmpFile, token);
// Move the file downloaded from temporary location to Data folder
var moveFileTask = downloadFileTask.ContinueWith((t) =>
{
if (File.Exists(tmpFile))
{
// copy downloaded file to data folder // copy downloaded file to data folder
RaiseOnStatusChangedEvent(CopyingSetupFilesMessage); RaiseOnStatusChangedEvent(CopyingSetupFilesMessage);
// //
@ -244,8 +292,12 @@ namespace WebsitePanel.Installer.Core
// //
RaiseOnProgressChangedEvent(100); RaiseOnProgressChangedEvent(100);
} }
}, TaskContinuationOptions.NotOnCanceled);
// unzip file // Unzip file downloaded
var unzipFileTask = moveFileTask.ContinueWith((t) =>
{
if (File.Exists(destinationFile))
{
RaiseOnStatusChangedEvent(PreparingSetupFilesMessage); RaiseOnStatusChangedEvent(PreparingSetupFilesMessage);
// //
RaiseOnProgressChangedEvent(0); RaiseOnProgressChangedEvent(0);
@ -253,8 +305,33 @@ namespace WebsitePanel.Installer.Core
UnzipFile(destinationFile, tmpFolder); UnzipFile(destinationFile, tmpFolder);
// //
RaiseOnProgressChangedEvent(100); RaiseOnProgressChangedEvent(100);
}
}, token);
// //
var notifyCompletionTask = unzipFileTask.ContinueWith((t) =>
{
RaiseOnOperationCompletedEvent(); RaiseOnOperationCompletedEvent();
}, token);
downloadFileTask.Start();
downloadFileTask.Wait();
}
catch (AggregateException ae)
{
ae.Handle((e) =>
{
// We handle cancellation requests
if (e is OperationCanceledException)
{
CancelDownload(tmpFile);
Log.WriteInfo("Download has been cancelled by the user");
return true;
}
// But other issues just being logged
Log.WriteError("Could not download the file", e);
return false;
});
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -267,14 +344,19 @@ namespace WebsitePanel.Installer.Core
} }
} }
private void DownloadFile(string sourceFile, string destinationFile) protected virtual Task GetDownloadFileTask(string sourceFile, string tmpFile, CancellationToken ct)
{ {
try var downloadFileTask = new Task(() =>
{
if (!File.Exists(tmpFile))
{ {
var service = ServiceProviderProxy.GetInstallerWebService(); var service = ServiceProviderProxy.GetInstallerWebService();
//
RaiseOnProgressChangedEvent(0);
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage);
Log.WriteStart("Downloading file"); Log.WriteStart("Downloading file");
Log.WriteInfo(string.Format("Downloading file \"{0}\" to \"{1}\"", sourceFile, destinationFile)); Log.WriteInfo(string.Format("Downloading file \"{0}\" to \"{1}\"", sourceFile, tmpFile));
long downloaded = 0; long downloaded = 0;
long fileSize = service.GetFileSize(sourceFile); long fileSize = service.GetFileSize(sourceFile);
@ -287,34 +369,58 @@ namespace WebsitePanel.Installer.Core
while (downloaded < fileSize) while (downloaded < fileSize)
{ {
// Throw OperationCancelledException if there is an incoming cancel request
ct.ThrowIfCancellationRequested();
content = service.GetFileChunk(sourceFile, (int)downloaded, ChunkSize); content = service.GetFileChunk(sourceFile, (int)downloaded, ChunkSize);
if (content == null) if (content == null)
{ {
throw new FileNotFoundException("Service returned NULL file content.", sourceFile); throw new FileNotFoundException("Service returned NULL file content.", sourceFile);
} }
FileUtils.AppendFileContent(destinationFile, content); FileUtils.AppendFileContent(tmpFile, content);
downloaded += content.Length; downloaded += content.Length;
//update progress bar // Update download progress
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage,
string.Format(DownloadProgressMessage, downloaded / 1024, fileSize / 1024)); string.Format(DownloadProgressMessage, downloaded / 1024, fileSize / 1024));
//
RaiseOnProgressChangedEvent(Convert.ToInt32((downloaded * 100) / fileSize)); RaiseOnProgressChangedEvent(Convert.ToInt32((downloaded * 100) / fileSize));
if (content.Length < ChunkSize) if (content.Length < ChunkSize)
break; break;
} }
//
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%"); RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%");
//
Log.WriteEnd(string.Format("Downloaded {0} bytes", downloaded)); Log.WriteEnd(string.Format("Downloaded {0} bytes", downloaded));
} }
catch (Exception ex) }, ct);
{
if (Utils.IsThreadAbortException(ex))
return;
throw; return downloadFileTask;
} }
private static void InitializeLocalStorage(string dataFolder, string tmpFolder)
{
if (!Directory.Exists(dataFolder))
{
Directory.CreateDirectory(dataFolder);
Log.WriteInfo("Data directory created");
}
if (Directory.Exists(tmpFolder))
{
Directory.Delete(tmpFolder, true);
}
if (!Directory.Exists(tmpFolder))
{
Directory.CreateDirectory(tmpFolder);
Log.WriteInfo("Tmp directory created");
}
}
private static void GetLocalStorageInfo(out string dataFolder, out string tmpFolder)
{
dataFolder = FileUtils.GetDataDirectory();
tmpFolder = FileUtils.GetTempDirectory();
} }
private void UnzipFile(string zipFile, string destFolder) private void UnzipFile(string zipFile, string destFolder)
@ -400,15 +506,24 @@ namespace WebsitePanel.Installer.Core
} }
} }
/// <summary>
/// Cleans up temporary file if the download process has been cancelled.
/// </summary>
/// <param name="tmpFile">Path to the temporary file to cleanup</param>
protected virtual void CancelDownload(string tmpFile)
{
if (File.Exists(tmpFile))
{
File.Delete(tmpFile);
}
}
public void AbortOperation() public void AbortOperation()
{ {
if (thread != null) // Make sure we are in business
if (cts != null)
{ {
if (thread.IsAlive) cts.Cancel();
{
thread.Abort();
}
thread.Join();
} }
} }
} }

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebsitePanel.Installer.Core
{
class ServiceComponentNotFoundException : Exception
{
private string p;
public ServiceComponentNotFoundException(string p)
: base(p)
{
}
}
}

View file

@ -35,6 +35,7 @@
<Reference Include="Ionic.Zip.Reduced"> <Reference Include="Ionic.Zip.Reduced">
<HintPath>..\..\Lib\Ionic.Zip.Reduced.dll</HintPath> <HintPath>..\..\Lib\Ionic.Zip.Reduced.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.configuration" /> <Reference Include="System.configuration" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
@ -80,6 +81,7 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput> <DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon> <DependentUpon>Settings.settings</DependentUpon>
</Compile> </Compile>
<Compile Include="ServiceComponentNotFoundException.cs" />
<Compile Include="ServiceProviderProxy.cs"> <Compile Include="ServiceProviderProxy.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -211,7 +211,7 @@ namespace WebsitePanel.Installer.Controls
{ {
Log.WriteInfo(string.Format("Updating {0}", componentName)); Log.WriteInfo(string.Format("Updating {0}", componentName));
//download installer //download installer
Loader form = new Loader(this.AppContext, fileName); Loader form = new Loader(fileName, (e) => AppContext.AppForm.ShowError(e));
DialogResult result = form.ShowDialog(this); DialogResult result = form.ShowDialog(this);
if (result == DialogResult.OK) if (result == DialogResult.OK)
{ {
@ -273,7 +273,7 @@ namespace WebsitePanel.Installer.Controls
{ {
Log.WriteInfo(string.Format("Uninstalling {0}", componentName)); Log.WriteInfo(string.Format("Uninstalling {0}", componentName));
//download installer //download installer
Loader form = new Loader(this.AppContext, installer, componentCode, release); Loader form = new Loader(installer, componentCode, release, (e) => AppContext.AppForm.ShowError(e));
DialogResult result = form.ShowDialog(this); DialogResult result = form.ShowDialog(this);
if (result == DialogResult.OK) if (result == DialogResult.OK)
{ {
@ -326,15 +326,15 @@ namespace WebsitePanel.Installer.Controls
string path = element.GetStringSetting("InstallerPath"); string path = element.GetStringSetting("InstallerPath");
string type = element.GetStringSetting("InstallerType"); string type = element.GetStringSetting("InstallerType");
string componentId = element.ID; string componentId = element.ID;
string componentCode = element.GetStringSetting("ComponentCode"); string ccode = element.GetStringSetting("ComponentCode");
string componentName = element.GetStringSetting("ComponentName"); string componentName = element.GetStringSetting("ComponentName");
string release = element.GetStringSetting("Release"); string cversion = element.GetStringSetting("Release");
try try
{ {
Log.WriteInfo(string.Format("Setup {0} {1}", componentName, release)); Log.WriteInfo(string.Format("Setup {0} {1}", componentName, cversion));
//download installer //download installer
Loader form = new Loader(this.AppContext, installer, componentCode, release); Loader form = new Loader(installer, ccode, cversion, (e) => AppContext.AppForm.ShowError(e));
DialogResult result = form.ShowDialog(this); DialogResult result = form.ShowDialog(this);
if (result == DialogResult.OK) if (result == DialogResult.OK)
{ {

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -78,6 +78,10 @@ namespace WebsitePanel.Installer.Controls
StartLoadingComponents(); StartLoadingComponents();
} }
} }
else
{
}
} }
private void StartInstaller(DataRowView row) private void StartInstaller(DataRowView row)
@ -100,7 +104,7 @@ namespace WebsitePanel.Installer.Controls
try try
{ {
// download installer // download installer
Loader form = new Loader(this.AppContext, fileName); Loader form = new Loader(fileName, (e) => AppContext.AppForm.ShowError(e));
DialogResult result = form.ShowDialog(this); DialogResult result = form.ShowDialog(this);
if (result == DialogResult.OK) if (result == DialogResult.OK)
@ -207,9 +211,7 @@ namespace WebsitePanel.Installer.Controls
{ {
//load list of available components in the separate thread //load list of available components in the separate thread
AppContext.AppForm.StartAsyncProgress("Connecting...", true); AppContext.AppForm.StartAsyncProgress("Connecting...", true);
ThreadStart threadDelegate = new ThreadStart(LoadComponents); ThreadPool.QueueUserWorkItem(o => LoadComponents());
Thread newThread = new Thread(threadDelegate);
newThread.Start();
} }
/// <summary> /// <summary>
@ -279,9 +281,7 @@ namespace WebsitePanel.Installer.Controls
this.componentVersion = componentVersion; this.componentVersion = componentVersion;
this.componentSettingsXml = settingsXml; this.componentSettingsXml = settingsXml;
AppContext.AppForm.StartAsyncProgress("Connecting...", true); AppContext.AppForm.StartAsyncProgress("Connecting...", true);
ThreadStart threadDelegate = new ThreadStart(Install); ThreadPool.QueueUserWorkItem(o => Install());
Thread newThread = new Thread(threadDelegate);
newThread.Start();
} }
/// <summary> /// <summary>

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
@ -39,6 +39,7 @@ using Ionic.Zip;
using WebsitePanel.Installer.Services; using WebsitePanel.Installer.Services;
using WebsitePanel.Installer.Common; using WebsitePanel.Installer.Common;
using WebsitePanel.Installer.Core;
namespace WebsitePanel.Installer.Controls namespace WebsitePanel.Installer.Controls
{ {
@ -49,7 +50,6 @@ namespace WebsitePanel.Installer.Controls
/// </summary> /// </summary>
internal partial class Loader : Form internal partial class Loader : Form
{ {
private AppContext appContext;
private Core.Loader appLoader; private Core.Loader appLoader;
public Loader() public Loader()
@ -58,34 +58,56 @@ namespace WebsitePanel.Installer.Controls
DialogResult = DialogResult.Cancel; DialogResult = DialogResult.Cancel;
} }
public Loader(AppContext context, string remoteFile) public Loader(string remoteFile, Action<Exception> callback)
: this() : this()
{ {
this.appContext = context; Start(remoteFile, callback);
//
appLoader = new Core.Loader(remoteFile);
//
Start();
} }
public Loader(AppContext context, string localFile, string componentCode, string version) public Loader(string localFile, string componentCode, string version, Action<Exception> callback)
: this() : this()
{ {
this.appContext = context; Start(componentCode, version, callback);
//
appLoader = new Core.Loader(localFile, componentCode, version);
//
Start();
} }
private void Start() /// <summary>
/// Resolves URL of the component's distributive and initiates download process.
/// </summary>
/// <param name="componentCode">Component code to resolve</param>
/// <param name="version">Component version to resolve</param>
private void Start(string componentCode, string version, Action<Exception> callback)
{ {
// string remoteFile = Utils.GetDistributiveLocationInfo(componentCode, version);
Start(remoteFile, callback);
}
/// <summary>
/// Initializes and starts the app distributive download process.
/// </summary>
/// <param name="remoteFile">URL of the file to be downloaded</param>
private void Start(string remoteFile, Action<Exception> callback)
{
appLoader = Core.LoaderFactory.CreateFileLoader(remoteFile);
appLoader.OperationFailed += new EventHandler<Core.LoaderEventArgs<Exception>>(appLoader_OperationFailed); appLoader.OperationFailed += new EventHandler<Core.LoaderEventArgs<Exception>>(appLoader_OperationFailed);
appLoader.OperationFailed += (object sender, Core.LoaderEventArgs<Exception> e) => {
if (callback != null)
{
try
{
callback(e.EventData);
}
catch
{
// Just swallow the exception as we have no interest in it.
}
}
};
appLoader.ProgressChanged += new EventHandler<Core.LoaderEventArgs<Int32>>(appLoader_ProgressChanged); appLoader.ProgressChanged += new EventHandler<Core.LoaderEventArgs<Int32>>(appLoader_ProgressChanged);
appLoader.StatusChanged += new EventHandler<Core.LoaderEventArgs<String>>(appLoader_StatusChanged); appLoader.StatusChanged += new EventHandler<Core.LoaderEventArgs<String>>(appLoader_StatusChanged);
appLoader.OperationCompleted += new EventHandler<EventArgs>(appLoader_OperationCompleted); appLoader.OperationCompleted += new EventHandler<EventArgs>(appLoader_OperationCompleted);
//
appLoader.LoadAppDistributive(); appLoader.LoadAppDistributive();
} }
@ -113,6 +135,7 @@ namespace WebsitePanel.Installer.Controls
void appLoader_ProgressChanged(object sender, Core.LoaderEventArgs<Int32> e) void appLoader_ProgressChanged(object sender, Core.LoaderEventArgs<Int32> e)
{ {
bool updateControl = (progressBar.Value != e.EventData);
progressBar.Value = e.EventData; progressBar.Value = e.EventData;
// Adjust Cancel button availability for an operation being performed // Adjust Cancel button availability for an operation being performed
if (btnCancel.Enabled != e.Cancellable) if (btnCancel.Enabled != e.Cancellable)
@ -124,33 +147,48 @@ namespace WebsitePanel.Installer.Controls
{ {
ControlBox = e.Cancellable; ControlBox = e.Cancellable;
} }
//
if (updateControl)
{
progressBar.Update();
}
} }
void appLoader_OperationFailed(object sender, Core.LoaderEventArgs<Exception> e) void appLoader_OperationFailed(object sender, Core.LoaderEventArgs<Exception> e)
{ {
appContext.AppForm.ShowError(e.EventData);
//
DialogResult = DialogResult.Abort; DialogResult = DialogResult.Abort;
Close(); Close();
} }
private void btnCancel_Click(object sender, EventArgs e) private void btnCancel_Click(object sender, EventArgs e)
{ {
DetachEventHandlers();
Log.WriteInfo("Execution was canceled by user"); Log.WriteInfo("Execution was canceled by user");
Close(); Close();
} }
private void OnLoaderFormClosing(object sender, FormClosingEventArgs e) private void DetachEventHandlers()
{ {
if (this.DialogResult == DialogResult.Cancel) // Detach event handlers
if (appLoader != null)
{ {
appLoader.AbortOperation();
}
// Remove event handlers
appLoader.OperationFailed -= new EventHandler<Core.LoaderEventArgs<Exception>>(appLoader_OperationFailed); appLoader.OperationFailed -= new EventHandler<Core.LoaderEventArgs<Exception>>(appLoader_OperationFailed);
appLoader.ProgressChanged -= new EventHandler<Core.LoaderEventArgs<Int32>>(appLoader_ProgressChanged); appLoader.ProgressChanged -= new EventHandler<Core.LoaderEventArgs<Int32>>(appLoader_ProgressChanged);
appLoader.StatusChanged -= new EventHandler<Core.LoaderEventArgs<String>>(appLoader_StatusChanged); appLoader.StatusChanged -= new EventHandler<Core.LoaderEventArgs<String>>(appLoader_StatusChanged);
appLoader.OperationCompleted -= new EventHandler<EventArgs>(appLoader_OperationCompleted); appLoader.OperationCompleted -= new EventHandler<EventArgs>(appLoader_OperationCompleted);
} }
} }
private void OnLoaderFormClosing(object sender, FormClosingEventArgs e)
{
if (this.DialogResult == DialogResult.Cancel)
{
if (appLoader != null)
{
appLoader.AbortOperation();
appLoader = null;
}
}
}
}
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Outercurve Foundation. // Copyright (c) 2012, Outercurve Foundation.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,