diff --git a/WebsitePanel.Installer/Sources/Setup/Setup.vdproj b/WebsitePanel.Installer/Sources/Setup/Setup.vdproj index b8775a0e..836c2919 100644 --- a/WebsitePanel.Installer/Sources/Setup/Setup.vdproj +++ b/WebsitePanel.Installer/Sources/Setup/Setup.vdproj @@ -63,6 +63,24 @@ } "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" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -294,6 +312,37 @@ "IsDependency" = "11:FALSE" "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" { "SourcePath" = "8:Banner.bmp" @@ -407,15 +456,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:WebsitePanel Installer" - "ProductCode" = "8:{09BCDF68-1964-4FC6-9570-9AB3B8512CF6}" - "PackageCode" = "8:{D94FF2E6-1D7D-4E33-8528-C7B6BFADEC99}" + "ProductCode" = "8:{A22F374C-4AFC-4B5D-A509-7456A6107588}" + "PackageCode" = "8:{401F157D-6D55-4F66-A2C6-419F87BBF97D}" "UpgradeCode" = "8:{2950A907-11E7-436C-86CE-049C414AFD08}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE" "DetectNewerInstalledVersion" = "11:FALSE" "InstallAllUsers" = "11:TRUE" - "ProductVersion" = "8:1.2.0" + "ProductVersion" = "8:1.2.1" "Manufacturer" = "8:Outercurve Foundation" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" diff --git a/WebsitePanel.Installer/Sources/VersionInfo.cs b/WebsitePanel.Installer/Sources/VersionInfo.cs index d03067e7..f939546f 100644 --- a/WebsitePanel.Installer/Sources/VersionInfo.cs +++ b/WebsitePanel.Installer/Sources/VersionInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -35,7 +35,7 @@ using System.Reflection; // Revision // [assembly: AssemblyCompany("Outercurve Foundation")] -[assembly: AssemblyCopyright("Copyright © 2011 Outercurve Foundation.")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.38")] -[assembly: AssemblyInformationalVersion("1.2.0")] \ No newline at end of file +[assembly: AssemblyCopyright("Copyright © 2012 Outercurve Foundation.")] +[assembly: AssemblyVersion("1.2.1.0")] +[assembly: AssemblyFileVersion("1.2.1.0")] +[assembly: AssemblyInformationalVersion("1.2.1")] \ No newline at end of file diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/FileUtils.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/FileUtils.cs index 7ca351b9..fb93c644 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/FileUtils.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/FileUtils.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -34,43 +34,43 @@ using System.IO; namespace WebsitePanel.Installer.Common { - /// - /// File utils. - /// - public sealed class FileUtils - { - /// - /// Initializes a new instance of the class. - /// - private FileUtils() - { - } + /// + /// File utils. + /// + public sealed class FileUtils + { + /// + /// Initializes a new instance of the class. + /// + private FileUtils() + { + } - /// - /// Creates drectory with the specified directory. - /// - /// The directory path to create. - public static void CreateDirectory(string path) - { - string dir = Path.GetDirectoryName(path); - if(!Directory.Exists(dir)) - { - // create directory structure - Directory.CreateDirectory(dir); - } - } + /// + /// Creates drectory with the specified directory. + /// + /// The directory path to create. + public static void CreateDirectory(string path) + { + string dir = Path.GetDirectoryName(path); + if(!Directory.Exists(dir)) + { + // create directory structure + Directory.CreateDirectory(dir); + } + } - /// - /// Saves file content. - /// - /// File name. - /// The array of bytes to write. - public static void SaveFileContent(string fileName, byte[] content) - { - FileStream stream = new FileStream(fileName, FileMode.Create); - stream.Write(content, 0, content.Length); - stream.Close(); - } + /// + /// Saves file content. + /// + /// File name. + /// The array of bytes to write. + public static void SaveFileContent(string fileName, byte[] content) + { + FileStream stream = new FileStream(fileName, FileMode.Create); + stream.Write(content, 0, content.Length); + stream.Close(); + } /// /// Saves file content. @@ -84,188 +84,203 @@ namespace WebsitePanel.Installer.Common stream.Close(); } - /// - /// Deletes the specified file. - /// - /// The name of the file to be deleted. - public static void DeleteFile(string fileName) - { - int attempts = 0; - while (true) - { - try - { - DeleteFileInternal(fileName); - break; - } - catch (Exception) - { - if (attempts > 2) - throw; + /// + /// Deletes the specified file. + /// + /// The name of the file to be deleted. + public static void DeleteFile(string fileName) + { + int attempts = 0; + while (true) + { + try + { + DeleteFileInternal(fileName); + break; + } + catch (Exception) + { + if (attempts > 2) + throw; - attempts++; - System.Threading.Thread.Sleep(1000); - } - } - } + attempts++; + System.Threading.Thread.Sleep(1000); + } + } + } - /// - /// Deletes the specified file. - /// - /// The name of the file to be deleted. - private static void DeleteReadOnlyFile(string fileName) - { - FileInfo info = new FileInfo(fileName); - info.Attributes = FileAttributes.Normal; - info.Delete(); - } + /// + /// Deletes the specified file. + /// + /// The name of the file to be deleted. + private static void DeleteReadOnlyFile(string fileName) + { + FileInfo info = new FileInfo(fileName); + info.Attributes = FileAttributes.Normal; + info.Delete(); + } - /// - /// Deletes the specified file. - /// - /// The name of the file to be deleted. - private static void DeleteFileInternal(string fileName) - { - try - { - File.Delete(fileName); - } - catch (UnauthorizedAccessException) - { - DeleteReadOnlyFile(fileName); - } - } + /// + /// Deletes the specified file. + /// + /// The name of the file to be deleted. + private static void DeleteFileInternal(string fileName) + { + try + { + File.Delete(fileName); + } + catch (UnauthorizedAccessException) + { + DeleteReadOnlyFile(fileName); + } + } - /// - /// Deletes the specified directory. - /// - /// The name of the directory to be deleted. - public static void DeleteDirectory(string directory) - { - if (!Directory.Exists(directory)) - return; + /// + /// Deletes the specified directory. + /// + /// The name of the directory to be deleted. + public static void DeleteDirectory(string directory) + { + if (!Directory.Exists(directory)) + return; - // iterate through child folders - string[] dirs = Directory.GetDirectories(directory); - foreach (string dir in dirs) - { - DeleteDirectory(dir); - } + // iterate through child folders + string[] dirs = Directory.GetDirectories(directory); + foreach (string dir in dirs) + { + DeleteDirectory(dir); + } - // iterate through child files - string[] files = Directory.GetFiles(directory); - foreach (string file in files) - { - DeleteFile(file); - } + // iterate through child files + string[] files = Directory.GetFiles(directory); + foreach (string file in files) + { + DeleteFile(file); + } - //try to delete dir for 3 times - int attempts = 0; - while (true) - { - try - { - DeleteDirectoryInternal(directory); - break; - } - catch (Exception) - { - if (attempts > 2) - throw; + //try to delete dir for 3 times + int attempts = 0; + while (true) + { + try + { + DeleteDirectoryInternal(directory); + break; + } + catch (Exception) + { + if (attempts > 2) + throw; - attempts++; - System.Threading.Thread.Sleep(1000); - } - } - } + attempts++; + System.Threading.Thread.Sleep(1000); + } + } + } - /// - /// Deletes the specified directory. - /// - /// The name of the directory to be deleted. - private static void DeleteDirectoryInternal(string directory) - { - try - { - Directory.Delete(directory); - } - catch (IOException) - { - DeleteReadOnlyDirectory(directory); - } - } + /// + /// Deletes the specified directory. + /// + /// The name of the directory to be deleted. + private static void DeleteDirectoryInternal(string directory) + { + try + { + Directory.Delete(directory); + } + catch (IOException) + { + DeleteReadOnlyDirectory(directory); + } + } - /// - /// Deletes the specified directory. - /// - /// The name of the directory to be deleted. - private static void DeleteReadOnlyDirectory(string directory) - { - DirectoryInfo info = new DirectoryInfo(directory); - info.Attributes = FileAttributes.Normal; - info.Delete(); - } + /// + /// Deletes the specified directory. + /// + /// The name of the directory to be deleted. + private static void DeleteReadOnlyDirectory(string directory) + { + DirectoryInfo info = new DirectoryInfo(directory); + info.Attributes = FileAttributes.Normal; + info.Delete(); + } - /// - /// Determines whether the specified file exists. - /// - /// The path to check. - /// - public static bool FileExists(string fileName) - { - return File.Exists(fileName); - } + /// + /// Determines whether the specified file exists. + /// + /// The path to check. + /// + public static bool FileExists(string fileName) + { + return File.Exists(fileName); + } - /// - /// Determines whether the given path refers to an existing directory on disk. - /// - /// The path to test. - /// - public static bool DirectoryExists(string path) - { - return Directory.Exists(path); - } - - /// - /// Returns current application path. - /// - /// Curent application path. - public static string GetCurrentDirectory() - { - return AppDomain.CurrentDomain.BaseDirectory; - } + /// + /// Determines whether the given path refers to an existing directory on disk. + /// + /// The path to test. + /// + public static bool DirectoryExists(string path) + { + return Directory.Exists(path); + } + + /// + /// Returns current application path. + /// + /// Curent application path. + public static string GetCurrentDirectory() + { + return AppDomain.CurrentDomain.BaseDirectory; + } - /// - /// Returns application temp directory. - /// - /// Application temp directory. - public static string GetTempDirectory() - { - return Path.Combine(GetCurrentDirectory(), "Tmp"); - } + /// + /// Returns application temp directory. + /// + /// Application temp directory. + public static string GetTempDirectory() + { + return Path.Combine(GetCurrentDirectory(), "Tmp"); + } - /// - /// Returns application data directory. - /// - /// Application data directory. - public static string GetDataDirectory() - { - return Path.Combine(GetCurrentDirectory(), "Data"); - } + /// + /// Returns application data directory. + /// + /// Application data directory. + public static string GetDataDirectory() + { + return Path.Combine(GetCurrentDirectory(), "Data"); + } - /// - /// Deletes application temp directory. - /// - public static void DeleteTempDirectory() - { - try - { - DeleteDirectory(GetTempDirectory()); - } - catch (Exception ex) - { - Log.WriteError("IO Error", ex); - } - } - } + /// + /// Deletes application's Data folder. + /// + public static void DeleteDataDirectory() + { + try + { + DeleteDirectory(GetDataDirectory()); + } + catch (Exception ex) + { + Log.WriteError("IO Error", ex); + } + } + + /// + /// Deletes application Tmp directory. + /// + public static void DeleteTempDirectory() + { + try + { + DeleteDirectory(GetTempDirectory()); + } + catch (Exception ex) + { + Log.WriteError("IO Error", ex); + } + } + } } diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/Utils.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/Utils.cs index 68b4cc31..cd036dd1 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/Utils.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Common/Utils.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // 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.Configuration; using System.Xml; +using System.Data; namespace WebsitePanel.Installer.Common { @@ -389,5 +390,23 @@ namespace WebsitePanel.Installer.Common mutex = new Mutex(true, "WebsitePanel Installer", out 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(); + } } } diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs index cc38ca29..39fe1861 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -36,380 +36,495 @@ using System.Text; using Ionic.Zip; 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 { - public class LoaderEventArgs : EventArgs - { - public string StatusMessage { get; set; } - public T EventData { get; set; } - public bool Cancellable { get; set; } - } + public class LoaderEventArgs : EventArgs + { + public string StatusMessage { get; set; } + public T EventData { get; set; } + public bool Cancellable { get; set; } + } - /// - /// Loader form. - /// - public partial class Loader - { - public const string ConnectingRemotServiceMessage = "Connecting..."; - public const string DownloadingSetupFilesMessage = "Downloading setup files..."; - public const string CopyingSetupFilesMessage = "Copying setup files..."; - public const string PreparingSetupFilesMessage = "Please wait while Setup prepares the necessary files..."; - public const string DownloadProgressMessage = "{0} KB of {1} KB"; - public const string PrepareSetupProgressMessage = "{0}%"; + public static class LoaderFactory + { + /// + /// Instantiates either BitlyLoader or InstallerServiceLoader based on remote file format. + /// + /// + /// + public static Loader CreateFileLoader(string remoteFile) + { + Debug.Assert(!String.IsNullOrEmpty(remoteFile), "Remote file is empty"); - private const int ChunkSize = 262144; - private Thread thread; - private string localFile; - private string remoteFile; - private string componentCode; - private string version; + if (remoteFile.StartsWith("http://bit.ly/")) + { + return new BitlyLoader(remoteFile); + } + else + { + return new Loader(remoteFile); + } + } + } - public event EventHandler> StatusChanged; - public event EventHandler> OperationFailed; - public event EventHandler> ProgressChanged; - public event EventHandler OperationCompleted; + public class BitlyLoader : Loader + { + public const string WEB_PI_USER_AGENT_HEADER = "PI-Integrator/3.0.0.0({0})"; - public Loader(string remoteFile) - { - this.remoteFile = remoteFile; - } + private WebClient fileLoader; - public Loader(string localFile, string componentCode, string version) - { - this.localFile = localFile; - this.componentCode = componentCode; - this.version = version; - } + public BitlyLoader(string remoteFile) + : base(remoteFile) + { + InitFileLoader(); + } - public void LoadAppDistributive() - { - thread = new Thread(new ThreadStart(LoadAppDistributiveInternal)); - thread.Start(); - } + 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)); + } - private void RaiseOnStatusChangedEvent(string statusMessage) - { - RaiseOnStatusChangedEvent(statusMessage, String.Empty); - } + 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); - private void RaiseOnStatusChangedEvent(string statusMessage, string eventData) - { - RaiseOnStatusChangedEvent(statusMessage, eventData, true); - } + if (fileLoader.IsBusy.Equals(true)) + { + return; + } - private void RaiseOnStatusChangedEvent(string statusMessage, string eventData, bool cancellable) - { - if (StatusChanged == null) - { - return; - } - // No event data for status updates - StatusChanged(this, new LoaderEventArgs { - StatusMessage = statusMessage, - EventData = eventData, - Cancellable = cancellable - }); - } + ct.Register(() => + { + fileLoader.CancelAsync(); + }); - private void RaiseOnProgressChangedEvent(int eventData) - { - RaiseOnProgressChangedEvent(eventData, true); - } + 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; - private void RaiseOnProgressChangedEvent(int eventData, bool cancellable) - { - if (ProgressChanged == null) - { - return; - } - // - ProgressChanged(this, new LoaderEventArgs { - EventData = eventData, - Cancellable = cancellable - }); - } + RaiseOnProgressChangedEvent(e.ProgressPercentage); + RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, + String.Format(DownloadProgressMessage, e.BytesReceived / 1024, e.TotalBytesToReceive / 1024)); + }; - private void RaiseOnOperationFailedEvent(Exception ex) - { - if (OperationFailed == null) - { - return; - } - // - OperationFailed(this, new LoaderEventArgs { EventData = ex }); - } + fileLoader.DownloadFileCompleted += (obj, e) => + { + if (ct.IsCancellationRequested == false) + { + RaiseOnProgressChangedEvent(100); + RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%"); + } - private void RaiseOnOperationCompletedEvent() - { - if (OperationCompleted == null) - { - return; - } - // - OperationCompleted(this, EventArgs.Empty); - } + if (e.Cancelled) + { + CancelDownload(tmpFile); + } - /// - /// Displays process progress. - /// - private void LoadAppDistributiveInternal() - { - // - try - { - var service = ServiceProviderProxy.GetInstallerWebService(); - // - string dataFolder = FileUtils.GetDataDirectory(); - string tmpFolder = FileUtils.GetTempDirectory(); + autoEvent.Set(); + }; - if (!Directory.Exists(dataFolder)) - { - Directory.CreateDirectory(dataFolder); - Log.WriteInfo("Data directory created"); - } + fileLoader.DownloadFileAsync(new Uri(remoteFile), tmpFile); + RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage); + + autoEvent.WaitOne(); + } + }, ct); - if (Directory.Exists(tmpFolder)) - { - FileUtils.DeleteTempDirectory(); - } + return downloadFileTask; + } + } - if (!Directory.Exists(tmpFolder)) - { - Directory.CreateDirectory(tmpFolder); - Log.WriteInfo("Tmp directory created"); - } + /// + /// Loader form. + /// + public class Loader + { + public const string ConnectingRemotServiceMessage = "Connecting..."; + public const string DownloadingSetupFilesMessage = "Downloading setup files..."; + public const string CopyingSetupFilesMessage = "Copying setup files..."; + public const string PreparingSetupFilesMessage = "Please wait while Setup prepares the necessary files..."; + public const string DownloadProgressMessage = "{0} KB of {1} KB"; + public const string PrepareSetupProgressMessage = "{0}%"; - string fileToDownload = null; - if (!string.IsNullOrEmpty(localFile)) - { - fileToDownload = localFile; - } - else - { - fileToDownload = Path.GetFileName(remoteFile); - } + private const int ChunkSize = 262144; + private string remoteFile; + private CancellationTokenSource cts; - string destinationFile = Path.Combine(dataFolder, fileToDownload); - string tmpFile = Path.Combine(tmpFolder, fileToDownload); + public event EventHandler> StatusChanged; + public event EventHandler> OperationFailed; + public event EventHandler> ProgressChanged; + public event EventHandler OperationCompleted; - //check whether file already downloaded - if (!File.Exists(destinationFile)) - { - 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"); - } - } + public Loader(string remoteFile) + { + this.remoteFile = remoteFile; + } - // download file to tmp folder - RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage); - // - RaiseOnProgressChangedEvent(0); - // - DownloadFile(remoteFile, tmpFile); - // - RaiseOnProgressChangedEvent(100); + public void LoadAppDistributive() + { + ThreadPool.QueueUserWorkItem(q => LoadAppDistributiveInternal()); + } - // copy downloaded file to data folder - RaiseOnStatusChangedEvent(CopyingSetupFilesMessage); - // - RaiseOnProgressChangedEvent(0); + protected void RaiseOnStatusChangedEvent(string statusMessage) + { + RaiseOnStatusChangedEvent(statusMessage, String.Empty); + } - // Ensure that the target does not exist. - if (File.Exists(destinationFile)) - FileUtils.DeleteFile(destinationFile); - File.Move(tmpFile, destinationFile); - // - RaiseOnProgressChangedEvent(100); - } + protected void RaiseOnStatusChangedEvent(string statusMessage, string eventData) + { + RaiseOnStatusChangedEvent(statusMessage, eventData, true); + } - // unzip file - RaiseOnStatusChangedEvent(PreparingSetupFilesMessage); - // - RaiseOnProgressChangedEvent(0); - // - UnzipFile(destinationFile, tmpFolder); - // - RaiseOnProgressChangedEvent(100); - // - RaiseOnOperationCompletedEvent(); - } - catch (Exception ex) - { - if (Utils.IsThreadAbortException(ex)) - return; + protected void RaiseOnStatusChangedEvent(string statusMessage, string eventData, bool cancellable) + { + if (StatusChanged == null) + { + return; + } + // No event data for status updates + StatusChanged(this, new LoaderEventArgs + { + StatusMessage = statusMessage, + EventData = eventData, + Cancellable = cancellable + }); + } - Log.WriteError("Loader module error", ex); - // - RaiseOnOperationFailedEvent(ex); - } - } + protected void RaiseOnProgressChangedEvent(int eventData) + { + RaiseOnProgressChangedEvent(eventData, true); + } - private void DownloadFile(string sourceFile, string destinationFile) - { - try - { - var service = ServiceProviderProxy.GetInstallerWebService(); - // - Log.WriteStart("Downloading file"); - Log.WriteInfo(string.Format("Downloading file \"{0}\" to \"{1}\"", sourceFile, destinationFile)); + protected void RaiseOnProgressChangedEvent(int eventData, bool cancellable) + { + if (ProgressChanged == null) + { + return; + } + // + ProgressChanged(this, new LoaderEventArgs + { + EventData = eventData, + Cancellable = cancellable + }); + } - long downloaded = 0; - long fileSize = service.GetFileSize(sourceFile); - if (fileSize == 0) - { - throw new FileNotFoundException("Service returned empty file.", sourceFile); - } + protected void RaiseOnOperationFailedEvent(Exception ex) + { + if (OperationFailed == null) + { + return; + } + // + OperationFailed(this, new LoaderEventArgs { EventData = ex }); + } - byte[] content; + protected void RaiseOnOperationCompletedEvent() + { + if (OperationCompleted == null) + { + return; + } + // + OperationCompleted(this, EventArgs.Empty); + } - while (downloaded < fileSize) - { - content = service.GetFileChunk(sourceFile, (int)downloaded, ChunkSize); - if (content == null) - { - throw new FileNotFoundException("Service returned NULL file content.", sourceFile); - } - FileUtils.AppendFileContent(destinationFile, content); - downloaded += content.Length; - //update progress bar - RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, - string.Format(DownloadProgressMessage, downloaded / 1024, fileSize / 1024)); - // - RaiseOnProgressChangedEvent(Convert.ToInt32((downloaded * 100) / fileSize)); + /// + /// Executes a file download request asynchronously + /// + private void LoadAppDistributiveInternal() + { + try + { + string dataFolder; + string tmpFolder; + // Retrieve local storage configuration + GetLocalStorageInfo(out dataFolder, out tmpFolder); + // Initialize storage + InitializeLocalStorage(dataFolder, tmpFolder); - if (content.Length < ChunkSize) - break; - } - // - RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%"); - // - Log.WriteEnd(string.Format("Downloaded {0} bytes", downloaded)); - } - catch (Exception ex) - { - if (Utils.IsThreadAbortException(ex)) - return; + string fileToDownload = Path.GetFileName(remoteFile); - throw; - } - } + string destinationFile = Path.Combine(dataFolder, fileToDownload); + string tmpFile = Path.Combine(tmpFolder, fileToDownload); - private void UnzipFile(string zipFile, string destFolder) - { - try - { - int val = 0; - // Negative value means no progress made yet - int progress = -1; - // - Log.WriteStart("Unzipping file"); - Log.WriteInfo(string.Format("Unzipping file \"{0}\" to the folder \"{1}\"", zipFile, destFolder)); + cts = new CancellationTokenSource(); + CancellationToken token = cts.Token; - long zipSize = 0; - var zipInfo = ZipFile.Read(zipFile); - try - { - foreach (ZipEntry entry in zipInfo) - { - if (!entry.IsDirectory) - zipSize += entry.UncompressedSize; - } - } - finally - { - if (zipInfo != null) - { - zipInfo.Dispose(); - } - } + 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 + RaiseOnStatusChangedEvent(CopyingSetupFilesMessage); + // + RaiseOnProgressChangedEvent(0); - long unzipped = 0; - // - var zip = ZipFile.Read(zipFile); - // - try - { - foreach (ZipEntry entry in zip) - { - // - entry.Extract(destFolder, ExtractExistingFileAction.OverwriteSilently); - // - if (!entry.IsDirectory) - unzipped += entry.UncompressedSize; + // Ensure that the target does not exist. + if (File.Exists(destinationFile)) + FileUtils.DeleteFile(destinationFile); + File.Move(tmpFile, destinationFile); + // + RaiseOnProgressChangedEvent(100); + } + }, TaskContinuationOptions.NotOnCanceled); + // Unzip file downloaded + var unzipFileTask = moveFileTask.ContinueWith((t) => + { + if (File.Exists(destinationFile)) + { + RaiseOnStatusChangedEvent(PreparingSetupFilesMessage); + // + RaiseOnProgressChangedEvent(0); + // + UnzipFile(destinationFile, tmpFolder); + // + RaiseOnProgressChangedEvent(100); + } + }, token); + // + var notifyCompletionTask = unzipFileTask.ContinueWith((t) => + { + 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) + { + if (Utils.IsThreadAbortException(ex)) + return; - if (zipSize != 0) - { - val = Convert.ToInt32(unzipped * 100 / zipSize); - // Skip to raise the progress event change when calculated progress - // and the current progress value are even - if (val == progress) - { - continue; - } - // - RaiseOnStatusChangedEvent( - PreparingSetupFilesMessage, - String.Format(PrepareSetupProgressMessage, val), - false); - // - RaiseOnProgressChangedEvent(val, false); - } - } - // Notify client the operation can be cancelled at this time - RaiseOnProgressChangedEvent(100); - // - Log.WriteEnd("Unzipped file"); - } - finally - { - if (zip != null) - { - zip.Dispose(); - } - } - } - catch (Exception ex) - { - if (Utils.IsThreadAbortException(ex)) - return; - // - RaiseOnOperationFailedEvent(ex); - } - } + Log.WriteError("Loader module error", ex); + // + RaiseOnOperationFailedEvent(ex); + } + } - public void AbortOperation() - { - if (thread != null) - { - if (thread.IsAlive) - { - thread.Abort(); - } - thread.Join(); - } - } - } + protected virtual Task GetDownloadFileTask(string sourceFile, string tmpFile, CancellationToken ct) + { + var downloadFileTask = new Task(() => + { + if (!File.Exists(tmpFile)) + { + var service = ServiceProviderProxy.GetInstallerWebService(); + + RaiseOnProgressChangedEvent(0); + RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage); + + Log.WriteStart("Downloading file"); + Log.WriteInfo(string.Format("Downloading file \"{0}\" to \"{1}\"", sourceFile, tmpFile)); + + long downloaded = 0; + long fileSize = service.GetFileSize(sourceFile); + if (fileSize == 0) + { + throw new FileNotFoundException("Service returned empty file.", sourceFile); + } + + byte[] content; + + while (downloaded < fileSize) + { + // Throw OperationCancelledException if there is an incoming cancel request + ct.ThrowIfCancellationRequested(); + + content = service.GetFileChunk(sourceFile, (int)downloaded, ChunkSize); + if (content == null) + { + throw new FileNotFoundException("Service returned NULL file content.", sourceFile); + } + FileUtils.AppendFileContent(tmpFile, content); + downloaded += content.Length; + // Update download progress + RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, + string.Format(DownloadProgressMessage, downloaded / 1024, fileSize / 1024)); + + RaiseOnProgressChangedEvent(Convert.ToInt32((downloaded * 100) / fileSize)); + + if (content.Length < ChunkSize) + break; + } + + RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%"); + Log.WriteEnd(string.Format("Downloaded {0} bytes", downloaded)); + } + }, ct); + + 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) + { + try + { + int val = 0; + // Negative value means no progress made yet + int progress = -1; + // + Log.WriteStart("Unzipping file"); + Log.WriteInfo(string.Format("Unzipping file \"{0}\" to the folder \"{1}\"", zipFile, destFolder)); + + long zipSize = 0; + var zipInfo = ZipFile.Read(zipFile); + try + { + foreach (ZipEntry entry in zipInfo) + { + if (!entry.IsDirectory) + zipSize += entry.UncompressedSize; + } + } + finally + { + if (zipInfo != null) + { + zipInfo.Dispose(); + } + } + + long unzipped = 0; + // + var zip = ZipFile.Read(zipFile); + // + try + { + foreach (ZipEntry entry in zip) + { + // + entry.Extract(destFolder, ExtractExistingFileAction.OverwriteSilently); + // + if (!entry.IsDirectory) + unzipped += entry.UncompressedSize; + + if (zipSize != 0) + { + val = Convert.ToInt32(unzipped * 100 / zipSize); + // Skip to raise the progress event change when calculated progress + // and the current progress value are even + if (val == progress) + { + continue; + } + // + RaiseOnStatusChangedEvent( + PreparingSetupFilesMessage, + String.Format(PrepareSetupProgressMessage, val), + false); + // + RaiseOnProgressChangedEvent(val, false); + } + } + // Notify client the operation can be cancelled at this time + RaiseOnProgressChangedEvent(100); + // + Log.WriteEnd("Unzipped file"); + } + finally + { + if (zip != null) + { + zip.Dispose(); + } + } + } + catch (Exception ex) + { + if (Utils.IsThreadAbortException(ex)) + return; + // + RaiseOnOperationFailedEvent(ex); + } + } + + /// + /// Cleans up temporary file if the download process has been cancelled. + /// + /// Path to the temporary file to cleanup + protected virtual void CancelDownload(string tmpFile) + { + if (File.Exists(tmpFile)) + { + File.Delete(tmpFile); + } + } + + public void AbortOperation() + { + // Make sure we are in business + if (cts != null) + { + cts.Cancel(); + } + } + } } \ No newline at end of file diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/ServiceComponentNotFoundException.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/ServiceComponentNotFoundException.cs new file mode 100644 index 00000000..d0f2ab78 --- /dev/null +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/ServiceComponentNotFoundException.cs @@ -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) + { + } + } +} diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/WebsitePanel.Installer.Core.csproj b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/WebsitePanel.Installer.Core.csproj index 954d8f1c..c33c09e2 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/WebsitePanel.Installer.Core.csproj +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/WebsitePanel.Installer.Core.csproj @@ -35,6 +35,7 @@ ..\..\Lib\Ionic.Zip.Reduced.dll + @@ -80,6 +81,7 @@ True Settings.settings + Code diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentControl.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentControl.cs index 2420e91a..d24c5383 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentControl.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // 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)); //download installer - Loader form = new Loader(this.AppContext, fileName); + Loader form = new Loader(fileName, (e) => AppContext.AppForm.ShowError(e)); DialogResult result = form.ShowDialog(this); if (result == DialogResult.OK) { @@ -273,7 +273,7 @@ namespace WebsitePanel.Installer.Controls { Log.WriteInfo(string.Format("Uninstalling {0}", componentName)); //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); if (result == DialogResult.OK) { @@ -326,15 +326,15 @@ namespace WebsitePanel.Installer.Controls string path = element.GetStringSetting("InstallerPath"); string type = element.GetStringSetting("InstallerType"); string componentId = element.ID; - string componentCode = element.GetStringSetting("ComponentCode"); + string ccode = element.GetStringSetting("ComponentCode"); string componentName = element.GetStringSetting("ComponentName"); - string release = element.GetStringSetting("Release"); + string cversion = element.GetStringSetting("Release"); try { - Log.WriteInfo(string.Format("Setup {0} {1}", componentName, release)); - //download installer - Loader form = new Loader(this.AppContext, installer, componentCode, release); + Log.WriteInfo(string.Format("Setup {0} {1}", componentName, cversion)); + //download installer + Loader form = new Loader(installer, ccode, cversion, (e) => AppContext.AppForm.ShowError(e)); DialogResult result = form.ShowDialog(this); if (result == DialogResult.OK) { diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentsControl.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentsControl.cs index 2cfb8907..218d7520 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentsControl.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/ComponentsControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -45,273 +45,273 @@ using WebsitePanel.Installer.Configuration; namespace WebsitePanel.Installer.Controls { - /// - /// Components control - /// - internal partial class ComponentsControl : ResultViewControl - { - delegate void SetGridDataSourceCallback(object dataSource, string dataMember); + /// + /// Components control + /// + internal partial class ComponentsControl : ResultViewControl + { + delegate void SetGridDataSourceCallback(object dataSource, string dataMember); - private string componentCode = null; - private string componentVersion = null; - private string componentSettingsXml = null; + private string componentCode = null; + private string componentVersion = null; + private string componentSettingsXml = null; - public ComponentsControl() - { - InitializeComponent(); - grdComponents.AutoGenerateColumns = false; - } + public ComponentsControl() + { + InitializeComponent(); + grdComponents.AutoGenerateColumns = false; + } - /// - /// Action on click Install link - /// - /// - /// - private void OnInstallLinkClick(object sender, DataGridViewCellEventArgs e) - { - if (e.ColumnIndex == grdComponents.Columns.IndexOf(colLink)) - { - DataRowView row = grdComponents.Rows[e.RowIndex].DataBoundItem as DataRowView; - if (row != null) - { - StartInstaller(row); - StartLoadingComponents(); - } - } - } + /// + /// Action on click Install link + /// + /// + /// + private void OnInstallLinkClick(object sender, DataGridViewCellEventArgs e) + { + if (e.ColumnIndex == grdComponents.Columns.IndexOf(colLink)) + { + DataRowView row = grdComponents.Rows[e.RowIndex].DataBoundItem as DataRowView; + if (row != null) + { + StartInstaller(row); + StartLoadingComponents(); + } + } + else + { - private void StartInstaller(DataRowView row) - { - string applicationName = Utils.GetDbString(row[Global.Parameters.ApplicationName]); - string componentName = Utils.GetDbString(row[Global.Parameters.ComponentName]); - string componentCode = Utils.GetDbString(row[Global.Parameters.ComponentCode]); - string componentDescription = Utils.GetDbString(row[Global.Parameters.ComponentDescription]); - string component = Utils.GetDbString(row[Global.Parameters.Component]); - string version = Utils.GetDbString(row[Global.Parameters.Version]); - string fileName = row[Global.Parameters.FullFilePath].ToString(); - string installerPath = Utils.GetDbString(row[Global.Parameters.InstallerPath]); - string installerType = Utils.GetDbString(row[Global.Parameters.InstallerType]); + } + } - if (CheckForInstalledComponent(componentCode)) - { - AppContext.AppForm.ShowWarning(Global.Messages.ComponentIsAlreadyInstalled); - return; - } - try - { - // download installer - Loader form = new Loader(this.AppContext, fileName); - DialogResult result = form.ShowDialog(this); + private void StartInstaller(DataRowView row) + { + string applicationName = Utils.GetDbString(row[Global.Parameters.ApplicationName]); + string componentName = Utils.GetDbString(row[Global.Parameters.ComponentName]); + string componentCode = Utils.GetDbString(row[Global.Parameters.ComponentCode]); + string componentDescription = Utils.GetDbString(row[Global.Parameters.ComponentDescription]); + string component = Utils.GetDbString(row[Global.Parameters.Component]); + string version = Utils.GetDbString(row[Global.Parameters.Version]); + string fileName = row[Global.Parameters.FullFilePath].ToString(); + string installerPath = Utils.GetDbString(row[Global.Parameters.InstallerPath]); + string installerType = Utils.GetDbString(row[Global.Parameters.InstallerType]); - if (result == DialogResult.OK) - { - string tmpFolder = FileUtils.GetTempDirectory(); - string path = Path.Combine(tmpFolder, installerPath); - Update(); - string method = "Install"; - Log.WriteStart(string.Format("Running installer {0}.{1} from {2}", installerType, method, path)); + if (CheckForInstalledComponent(componentCode)) + { + AppContext.AppForm.ShowWarning(Global.Messages.ComponentIsAlreadyInstalled); + return; + } + try + { + // download installer + Loader form = new Loader(fileName, (e) => AppContext.AppForm.ShowError(e)); + DialogResult result = form.ShowDialog(this); - //prepare installer args - Hashtable args = new Hashtable(); + if (result == DialogResult.OK) + { + string tmpFolder = FileUtils.GetTempDirectory(); + string path = Path.Combine(tmpFolder, installerPath); + Update(); + string method = "Install"; + Log.WriteStart(string.Format("Running installer {0}.{1} from {2}", installerType, method, path)); - args[Global.Parameters.ComponentName] = componentName; - args[Global.Parameters.ApplicationName] = applicationName; - args[Global.Parameters.ComponentCode] = componentCode; - args[Global.Parameters.ComponentDescription] = componentDescription; - args[Global.Parameters.Version] = version; - args[Global.Parameters.InstallerFolder] = tmpFolder; - args[Global.Parameters.InstallerPath] = installerPath; - args[Global.Parameters.InstallerType] = installerType; - args[Global.Parameters.Installer] = Path.GetFileName(fileName); - args[Global.Parameters.ShellVersion] = AssemblyLoader.GetShellVersion(); - args[Global.Parameters.BaseDirectory] = FileUtils.GetCurrentDirectory(); - args[Global.Parameters.ShellMode] = Global.VisualInstallerShell; - args[Global.Parameters.IISVersion] = Global.IISVersion; - args[Global.Parameters.SetupXml] = this.componentSettingsXml; - args[Global.Parameters.ParentForm] = FindForm(); + //prepare installer args + Hashtable args = new Hashtable(); - //run installer - DialogResult res = (DialogResult)AssemblyLoader.Execute(path, installerType, method, new object[] { args }); - Log.WriteInfo(string.Format("Installer returned {0}", res)); - Log.WriteEnd("Installer finished"); - Update(); - if (res == DialogResult.OK) - { - AppContext.AppForm.ReloadApplication(); - } - FileUtils.DeleteTempDirectory(); - } - } - catch (Exception ex) - { - Log.WriteError("Installer error", ex); - AppContext.AppForm.ShowError(ex); - } - finally - { - this.componentSettingsXml = null; - this.componentCode = null; - } + args[Global.Parameters.ComponentName] = componentName; + args[Global.Parameters.ApplicationName] = applicationName; + args[Global.Parameters.ComponentCode] = componentCode; + args[Global.Parameters.ComponentDescription] = componentDescription; + args[Global.Parameters.Version] = version; + args[Global.Parameters.InstallerFolder] = tmpFolder; + args[Global.Parameters.InstallerPath] = installerPath; + args[Global.Parameters.InstallerType] = installerType; + args[Global.Parameters.Installer] = Path.GetFileName(fileName); + args[Global.Parameters.ShellVersion] = AssemblyLoader.GetShellVersion(); + args[Global.Parameters.BaseDirectory] = FileUtils.GetCurrentDirectory(); + args[Global.Parameters.ShellMode] = Global.VisualInstallerShell; + args[Global.Parameters.IISVersion] = Global.IISVersion; + args[Global.Parameters.SetupXml] = this.componentSettingsXml; + args[Global.Parameters.ParentForm] = FindForm(); - } + //run installer + DialogResult res = (DialogResult)AssemblyLoader.Execute(path, installerType, method, new object[] { args }); + Log.WriteInfo(string.Format("Installer returned {0}", res)); + Log.WriteEnd("Installer finished"); + Update(); + if (res == DialogResult.OK) + { + AppContext.AppForm.ReloadApplication(); + } + FileUtils.DeleteTempDirectory(); + } + } + catch (Exception ex) + { + Log.WriteError("Installer error", ex); + AppContext.AppForm.ShowError(ex); + } + finally + { + this.componentSettingsXml = null; + this.componentCode = null; + } - private bool CheckForInstalledComponent(string componentCode) - { - bool ret = false; - List installedComponents = new List(); - foreach (ComponentConfigElement componentConfig in AppConfigManager.AppConfiguration.Components) - { - string code = componentConfig.Settings["ComponentCode"].Value; - installedComponents.Add(code); - if (code == componentCode) - { - ret = true; - break; - } - } - if (componentCode == "standalone") - { - if (installedComponents.Contains("server") || - installedComponents.Contains("enterprise server") || - installedComponents.Contains("portal")) - ret = true; - } - return ret; - } + } - /// - /// Displays component description when entering grid row - /// - /// - /// - private void OnRowEnter(object sender, DataGridViewCellEventArgs e) - { - DataRowView row = grdComponents.Rows[e.RowIndex].DataBoundItem as DataRowView; - if (row != null) - { - lblDescription.Text = Utils.GetDbString(row["ComponentDescription"]); - } - } + private bool CheckForInstalledComponent(string componentCode) + { + bool ret = false; + List installedComponents = new List(); + foreach (ComponentConfigElement componentConfig in AppConfigManager.AppConfiguration.Components) + { + string code = componentConfig.Settings["ComponentCode"].Value; + installedComponents.Add(code); + if (code == componentCode) + { + ret = true; + break; + } + } + if (componentCode == "standalone") + { + if (installedComponents.Contains("server") || + installedComponents.Contains("enterprise server") || + installedComponents.Contains("portal")) + ret = true; + } + return ret; + } - /// - /// Start new thread to load components - /// - /// - /// - private void OnLoadComponentsClick(object sender, EventArgs e) - { - StartLoadingComponents(); - } + /// + /// Displays component description when entering grid row + /// + /// + /// + private void OnRowEnter(object sender, DataGridViewCellEventArgs e) + { + DataRowView row = grdComponents.Rows[e.RowIndex].DataBoundItem as DataRowView; + if (row != null) + { + lblDescription.Text = Utils.GetDbString(row["ComponentDescription"]); + } + } - private void StartLoadingComponents() - { - //load list of available components in the separate thread - AppContext.AppForm.StartAsyncProgress("Connecting...", true); - ThreadStart threadDelegate = new ThreadStart(LoadComponents); - Thread newThread = new Thread(threadDelegate); - newThread.Start(); - } + /// + /// Start new thread to load components + /// + /// + /// + private void OnLoadComponentsClick(object sender, EventArgs e) + { + StartLoadingComponents(); + } - /// - /// Loads list of available components via web service - /// - private void LoadComponents() - { - try - { - Log.WriteStart("Loading list of available components"); - lblDescription.Text = string.Empty; - //load components via web service - var webService = ServiceProviderProxy.GetInstallerWebService(); - DataSet dsComponents = webService.GetAvailableComponents(); - //remove already installed components - foreach (DataRow row in dsComponents.Tables[0].Rows) - { - string componentCode = Utils.GetDbString(row["ComponentCode"]); - if (CheckForInstalledComponent(componentCode)) - { - row.Delete(); - } - } - dsComponents.AcceptChanges(); - Log.WriteEnd("Available components loaded"); - SetGridDataSource(dsComponents, dsComponents.Tables[0].TableName); - AppContext.AppForm.FinishProgress(); - } - catch (Exception ex) - { - Log.WriteError("Web service error", ex); - AppContext.AppForm.FinishProgress(); - AppContext.AppForm.ShowServerError(); - } - } + private void StartLoadingComponents() + { + //load list of available components in the separate thread + AppContext.AppForm.StartAsyncProgress("Connecting...", true); + ThreadPool.QueueUserWorkItem(o => LoadComponents()); + } - /// - /// Thread safe grid binding. - /// - /// Data source - /// Data member - private void SetGridDataSource(object dataSource, string dataMember) - { - // InvokeRequired required compares the thread ID of the - // calling thread to the thread ID of the creating thread. - // If these threads are different, it returns true. - if (this.grdComponents.InvokeRequired) - { - SetGridDataSourceCallback callBack = new SetGridDataSourceCallback(SetGridDataSource); - this.grdComponents.Invoke(callBack, new object[] { dataSource, dataMember }); - } - else - { - this.grdComponents.DataSource = dataSource; - this.grdComponents.DataMember = dataMember; - } - } + /// + /// Loads list of available components via web service + /// + private void LoadComponents() + { + try + { + Log.WriteStart("Loading list of available components"); + lblDescription.Text = string.Empty; + //load components via web service + var webService = ServiceProviderProxy.GetInstallerWebService(); + DataSet dsComponents = webService.GetAvailableComponents(); + //remove already installed components + foreach (DataRow row in dsComponents.Tables[0].Rows) + { + string componentCode = Utils.GetDbString(row["ComponentCode"]); + if (CheckForInstalledComponent(componentCode)) + { + row.Delete(); + } + } + dsComponents.AcceptChanges(); + Log.WriteEnd("Available components loaded"); + SetGridDataSource(dsComponents, dsComponents.Tables[0].TableName); + AppContext.AppForm.FinishProgress(); + } + catch (Exception ex) + { + Log.WriteError("Web service error", ex); + AppContext.AppForm.FinishProgress(); + AppContext.AppForm.ShowServerError(); + } + } - /// - /// Installs component during unattended setup - /// - /// - internal void InstallComponent(string componentCode, string componentVersion, string settingsXml) - { - //load list of available components in the separate thread - this.componentCode = componentCode; - this.componentVersion = componentVersion; - this.componentSettingsXml = settingsXml; - AppContext.AppForm.StartAsyncProgress("Connecting...", true); - ThreadStart threadDelegate = new ThreadStart(Install); - Thread newThread = new Thread(threadDelegate); - newThread.Start(); - } + /// + /// Thread safe grid binding. + /// + /// Data source + /// Data member + private void SetGridDataSource(object dataSource, string dataMember) + { + // InvokeRequired required compares the thread ID of the + // calling thread to the thread ID of the creating thread. + // If these threads are different, it returns true. + if (this.grdComponents.InvokeRequired) + { + SetGridDataSourceCallback callBack = new SetGridDataSourceCallback(SetGridDataSource); + this.grdComponents.Invoke(callBack, new object[] { dataSource, dataMember }); + } + else + { + this.grdComponents.DataSource = dataSource; + this.grdComponents.DataMember = dataMember; + } + } - /// - /// Loads list of available components via web service and install specified component - /// during unattended setup - /// - private void Install() - { - LoadComponents(); - foreach (DataGridViewRow gridRow in grdComponents.Rows) - { - DataRowView row = gridRow.DataBoundItem as DataRowView; - if (row != null) - { - string code = Utils.GetDbString(row["ComponentCode"]); - string version = Utils.GetDbString(row["Version"]); - if (code == componentCode) - { - //check component version if specified - if (!string.IsNullOrEmpty(componentVersion)) - { - if (version != componentVersion) - continue; - } - StartInstaller(row); - AppContext.AppForm.ProceedUnattendedSetup(); - break; - } - } - } - } - } + /// + /// Installs component during unattended setup + /// + /// + internal void InstallComponent(string componentCode, string componentVersion, string settingsXml) + { + //load list of available components in the separate thread + this.componentCode = componentCode; + this.componentVersion = componentVersion; + this.componentSettingsXml = settingsXml; + AppContext.AppForm.StartAsyncProgress("Connecting...", true); + ThreadPool.QueueUserWorkItem(o => Install()); + } + + /// + /// Loads list of available components via web service and install specified component + /// during unattended setup + /// + private void Install() + { + LoadComponents(); + foreach (DataGridViewRow gridRow in grdComponents.Rows) + { + DataRowView row = gridRow.DataBoundItem as DataRowView; + if (row != null) + { + string code = Utils.GetDbString(row["ComponentCode"]); + string version = Utils.GetDbString(row["Version"]); + if (code == componentCode) + { + //check component version if specified + if (!string.IsNullOrEmpty(componentVersion)) + { + if (version != componentVersion) + continue; + } + StartInstaller(row); + AppContext.AppForm.ProceedUnattendedSetup(); + break; + } + } + } + } + } } diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/Loader.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/Loader.cs index 81eeab02..8fd6558b 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/Loader.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Controls/Loader.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -39,118 +39,156 @@ using Ionic.Zip; using WebsitePanel.Installer.Services; using WebsitePanel.Installer.Common; +using WebsitePanel.Installer.Core; namespace WebsitePanel.Installer.Controls { - public delegate void OperationProgressDelegate(int percentage); + public delegate void OperationProgressDelegate(int percentage); - /// - /// Loader form. - /// - internal partial class Loader : Form - { - private AppContext appContext; - private Core.Loader appLoader; + /// + /// Loader form. + /// + internal partial class Loader : Form + { + private Core.Loader appLoader; - public Loader() - { - InitializeComponent(); - DialogResult = DialogResult.Cancel; - } + public Loader() + { + InitializeComponent(); + DialogResult = DialogResult.Cancel; + } - public Loader(AppContext context, string remoteFile) - : this() - { - this.appContext = context; - // - appLoader = new Core.Loader(remoteFile); - // - Start(); - } + public Loader(string remoteFile, Action callback) + : this() + { + Start(remoteFile, callback); + } - public Loader(AppContext context, string localFile, string componentCode, string version) - : this() - { - this.appContext = context; - // - appLoader = new Core.Loader(localFile, componentCode, version); - // - Start(); - } + public Loader(string localFile, string componentCode, string version, Action callback) + : this() + { + Start(componentCode, version, callback); + } - private void Start() - { - // - appLoader.OperationFailed += new EventHandler>(appLoader_OperationFailed); - appLoader.ProgressChanged += new EventHandler>(appLoader_ProgressChanged); - appLoader.StatusChanged += new EventHandler>(appLoader_StatusChanged); - appLoader.OperationCompleted += new EventHandler(appLoader_OperationCompleted); - // - appLoader.LoadAppDistributive(); - } + /// + /// Resolves URL of the component's distributive and initiates download process. + /// + /// Component code to resolve + /// Component version to resolve + private void Start(string componentCode, string version, Action callback) + { + string remoteFile = Utils.GetDistributiveLocationInfo(componentCode, version); - void appLoader_OperationCompleted(object sender, EventArgs e) - { - DialogResult = DialogResult.OK; - Close(); - } + Start(remoteFile, callback); + } - void appLoader_StatusChanged(object sender, Core.LoaderEventArgs e) - { - lblProcess.Text = e.StatusMessage; - lblValue.Text = e.EventData; - // Adjust Cancel button availability for an operation being performed - if (btnCancel.Enabled != e.Cancellable) - { - btnCancel.Enabled = e.Cancellable; - } - // This check allows to avoid extra form redrawing operations - if (ControlBox != e.Cancellable) - { - ControlBox = e.Cancellable; - } - } + /// + /// Initializes and starts the app distributive download process. + /// + /// URL of the file to be downloaded + private void Start(string remoteFile, Action callback) + { + appLoader = Core.LoaderFactory.CreateFileLoader(remoteFile); - void appLoader_ProgressChanged(object sender, Core.LoaderEventArgs e) - { - progressBar.Value = e.EventData; - // Adjust Cancel button availability for an operation being performed - if (btnCancel.Enabled != e.Cancellable) - { - btnCancel.Enabled = e.Cancellable; - } - // This check allows to avoid extra form redrawing operations - if (ControlBox != e.Cancellable) - { - ControlBox = e.Cancellable; - } - } + appLoader.OperationFailed += new EventHandler>(appLoader_OperationFailed); + appLoader.OperationFailed += (object sender, Core.LoaderEventArgs e) => { + if (callback != null) + { + try + { + callback(e.EventData); + } + catch + { + // Just swallow the exception as we have no interest in it. + } + } + }; + appLoader.ProgressChanged += new EventHandler>(appLoader_ProgressChanged); + appLoader.StatusChanged += new EventHandler>(appLoader_StatusChanged); + appLoader.OperationCompleted += new EventHandler(appLoader_OperationCompleted); - void appLoader_OperationFailed(object sender, Core.LoaderEventArgs e) - { - appContext.AppForm.ShowError(e.EventData); - // - DialogResult = DialogResult.Abort; - Close(); - } + appLoader.LoadAppDistributive(); + } - private void btnCancel_Click(object sender, EventArgs e) - { - Log.WriteInfo("Execution was canceled by user"); - Close(); - } + void appLoader_OperationCompleted(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } - private void OnLoaderFormClosing(object sender, FormClosingEventArgs e) - { - if (this.DialogResult == DialogResult.Cancel) - { - appLoader.AbortOperation(); - } - // Remove event handlers - appLoader.OperationFailed -= new EventHandler>(appLoader_OperationFailed); - appLoader.ProgressChanged -= new EventHandler>(appLoader_ProgressChanged); - appLoader.StatusChanged -= new EventHandler>(appLoader_StatusChanged); - appLoader.OperationCompleted -= new EventHandler(appLoader_OperationCompleted); - } - } + void appLoader_StatusChanged(object sender, Core.LoaderEventArgs e) + { + lblProcess.Text = e.StatusMessage; + lblValue.Text = e.EventData; + // Adjust Cancel button availability for an operation being performed + if (btnCancel.Enabled != e.Cancellable) + { + btnCancel.Enabled = e.Cancellable; + } + // This check allows to avoid extra form redrawing operations + if (ControlBox != e.Cancellable) + { + ControlBox = e.Cancellable; + } + } + + void appLoader_ProgressChanged(object sender, Core.LoaderEventArgs e) + { + bool updateControl = (progressBar.Value != e.EventData); + progressBar.Value = e.EventData; + // Adjust Cancel button availability for an operation being performed + if (btnCancel.Enabled != e.Cancellable) + { + btnCancel.Enabled = e.Cancellable; + } + // This check allows to avoid extra form redrawing operations + if (ControlBox != e.Cancellable) + { + ControlBox = e.Cancellable; + } + // + if (updateControl) + { + progressBar.Update(); + } + } + + void appLoader_OperationFailed(object sender, Core.LoaderEventArgs e) + { + DialogResult = DialogResult.Abort; + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DetachEventHandlers(); + Log.WriteInfo("Execution was canceled by user"); + Close(); + } + + private void DetachEventHandlers() + { + // Detach event handlers + if (appLoader != null) + { + appLoader.OperationFailed -= new EventHandler>(appLoader_OperationFailed); + appLoader.ProgressChanged -= new EventHandler>(appLoader_ProgressChanged); + appLoader.StatusChanged -= new EventHandler>(appLoader_StatusChanged); + appLoader.OperationCompleted -= new EventHandler(appLoader_OperationCompleted); + } + } + + private void OnLoaderFormClosing(object sender, FormClosingEventArgs e) + { + if (this.DialogResult == DialogResult.Cancel) + { + if (appLoader != null) + { + appLoader.AbortOperation(); + appLoader = null; + } + } + } + } } \ No newline at end of file diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe index b3fb1974..92ddc226 100644 Binary files a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe and b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe differ diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Setup/Wizard/ExpressInstallPage.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Setup/Wizard/ExpressInstallPage.cs index 45447e4f..f101d6da 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Setup/Wizard/ExpressInstallPage.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Setup/Wizard/ExpressInstallPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Outercurve Foundation. +// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -1901,52 +1901,52 @@ namespace WebsitePanel.Setup } } - private void UpdateWebConfigNamespaces() - { - try - { - // find all .config files in the installation directory - string[] configFiles = Directory.GetFiles(Wizard.SetupVariables.InstallationFolder, - "*.config", SearchOption.TopDirectoryOnly); + private void UpdateWebConfigNamespaces() + { + try + { + // find all .config files in the installation directory + string[] configFiles = Directory.GetFiles(Wizard.SetupVariables.InstallationFolder, + "*.config", SearchOption.TopDirectoryOnly); - if (configFiles != null && configFiles.Length > 0) - { - foreach (string path in configFiles) - { - try - { - Log.WriteStart(String.Format("Updating '{0}' file", path)); + if (configFiles != null && configFiles.Length > 0) + { + foreach (string path in configFiles) + { + try + { + Log.WriteStart(String.Format("Updating '{0}' file", path)); - // load configuration file in memory - string content = File.ReadAllText(path); + // load configuration file in memory + string content = File.ReadAllText(path); - // replace DotNetPark. to empty strings - content = Regex.Replace(content, "dotnetpark\\.", "", RegexOptions.IgnoreCase); + // replace DotNetPark. to empty strings + content = Regex.Replace(content, "dotnetpark\\.", "", RegexOptions.IgnoreCase); - // save updated config - File.WriteAllText(path, content); + // save updated config + File.WriteAllText(path, content); - Log.WriteEnd(String.Format("Updated '{0}' file", path)); - InstallLog.AppendLine(String.Format("- Updated {0} file", path)); - } - catch (Exception ex) - { - if (Utils.IsThreadAbortException(ex)) - return; - Log.WriteError(String.Format("Error updating '{0}' file", path), ex); - throw; - } - } - } - } - catch (Exception ex) - { - if (Utils.IsThreadAbortException(ex)) - return; - Log.WriteError("Error listing *.config files", ex); - throw; - } - } + Log.WriteEnd(String.Format("Updated '{0}' file", path)); + InstallLog.AppendLine(String.Format("- Updated {0} file", path)); + } + catch (Exception ex) + { + if (Utils.IsThreadAbortException(ex)) + return; + Log.WriteError(String.Format("Error updating '{0}' file", path), ex); + throw; + } + } + } + } + catch (Exception ex) + { + if (Utils.IsThreadAbortException(ex)) + return; + Log.WriteError("Error listing *.config files", ex); + throw; + } + } private void UpdateEnterpriseServerUrl() {