#At file:///D:/Work/MySQL/installer/ based on revid:mike.lischke@stripped
377 Mike Lischke 2011-03-22
- ConfigurationController: avoid duplicate initialization.
- Package: improved log output if a package is not yet installed and hence no feature for it found.
- Product: improved check for related products (better version number parsing). Removed error message if a product with the same version number is already installed.
- ProductManager: reordered product initialization. First initialize its controller then the package itself to have certain values already set when the package and its features are processed.
added:
WexInstaller.Core/Utilities.cs
modified:
StandardPlugins/Server/ConfigurationController.cs
StandardPlugins/Server/ServerConfigPanel2.cs
WexInstaller.Core/Package.cs
WexInstaller.Core/PluginManager.cs
WexInstaller.Core/Product.cs
WexInstaller.Core/ProductManager.cs
WexInstaller.Core/WexInstaller.Core.csproj
=== modified file 'StandardPlugins/Server/ConfigurationController.cs'
=== modified file 'StandardPlugins/Server/ConfigurationController.cs'
--- a/StandardPlugins/Server/ConfigurationController.cs 2011-03-17 15:51:58 +0000
+++ b/StandardPlugins/Server/ConfigurationController.cs 2011-03-22 11:40:09 +0000
@@ -43,7 +43,7 @@
{
CurrentState = ConfigState.ConfigurationRequired;
Logger.LogInformation("Product configuration controller created.");
- Initialize();
+ //Initialize(); Triggered by the product manager.
processedTemplate = false;
processedService = false;
=== modified file 'StandardPlugins/Server/ServerConfigPanel2.cs'
--- a/StandardPlugins/Server/ServerConfigPanel2.cs 2011-03-18 10:29:04 +0000
+++ b/StandardPlugins/Server/ServerConfigPanel2.cs 2011-03-22 11:40:09 +0000
@@ -55,7 +55,8 @@
// test active listeners
IPEndPoint[] endPoints = ipGlobalProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in endPoints)
- if (endPoint.Port == port) return false;
+ if (endPoint.Port == port)
+ return false;
return true;
}
=== modified file 'WexInstaller.Core/Package.cs'
--- a/WexInstaller.Core/Package.cs 2011-03-21 10:34:56 +0000
+++ b/WexInstaller.Core/Package.cs 2011-03-22 11:40:09 +0000
@@ -138,7 +138,7 @@
{
bool success = false;
- if (Id != String.Empty)
+ if (!string.IsNullOrEmpty(Id))
{
DisplayName = GetMSIProductInfo("ProductName");
Publisher = GetMSIProductInfo("Publisher");
@@ -148,7 +148,7 @@
if (FileName == null)
FileName = GetMSIProductInfo("PackageName");
- if (DisplayName != String.Empty && Publisher != String.Empty)
+ if (!string.IsNullOrEmpty(DisplayName) && !string.IsNullOrEmpty(Publisher))
{
if (RegistryEntries.Count > 0)
RegistryEntries.Clear();
@@ -311,7 +311,7 @@
public void PostInitialize()
{
- if (String.IsNullOrEmpty(Id) == false) // Valid Package.
+ if (!String.IsNullOrEmpty(Id)) // Valid Package.
{
// Synchronize Installed Feature state.
StringBuilder FeatureCode = new StringBuilder(256);
@@ -365,7 +365,8 @@
if (rc != MSIEnumError.NoMoreItems)
{
// Record the real error.
- Logger.LogError(String.Format("Found error: {0}", rc.ToString()));
+ Logger.LogWarning(String.Format("Package initialization for {0} returned:\n\t{1} (" +
+ "expected if the product is not installed yet).", packageCode, rc.ToString()));
}
}
}
=== modified file 'WexInstaller.Core/PluginManager.cs'
--- a/WexInstaller.Core/PluginManager.cs 2011-02-09 13:49:30 +0000
+++ b/WexInstaller.Core/PluginManager.cs 2011-03-22 11:40:09 +0000
@@ -1,146 +1,146 @@
using System;
using System.Collections.Generic;
-using System.Text;
-using WexInstaller.Core;
+using System.IO;
using System.Reflection;
-using System.IO;
namespace WexInstaller.Core
{
- public class PluginManager
- {
- private bool loadedPlugins;
- private Dictionary<string, ControllerType> controllerTypes =
- new Dictionary<string, ControllerType>();
- private Dictionary<string, ProductConfigurationController> controllerCache =
- new Dictionary<string,ProductConfigurationController>();
-
- public static PluginManager Instance = new PluginManager();
-
- /// <summary>
- /// Retrieves the latest controller plugin (if any) associated with the given product name
- /// </summary>
- /// <param name="productName"></param>
- /// <returns></returns>
- public static ProductConfigurationController GetController(string productName)
- {
- return Instance.GetControllerInternal(productName);
- }
-
- private ProductConfigurationController GetControllerInternal(string productName)
- {
- if (!loadedPlugins)
- LoadPlugins();
-
- // first look to see if we have created this controller before
- if (controllerCache.ContainsKey(productName))
- return controllerCache[productName];
-
- // now find the best match between the product name and our controller product names
- string longestKey = String.Empty;
- foreach (string key in controllerTypes.Keys)
- if (productName.StartsWith(key) && key.Length > longestKey.Length)
- longestKey = key;
- if (!String.IsNullOrEmpty(longestKey))
- {
- ControllerType cType = controllerTypes[longestKey];
- ProductConfigurationController c = (ProductConfigurationController)
- cType.HostingAssembly.CreateInstance(cType.TypeName, false);
- if (c != null)
- {
- controllerCache[productName] = c;
- return c;
- }
- }
-
- return null;
- }
-
- private void LoadControllersFromAssembly(Assembly assembly)
- {
- Type interfaceType = typeof(ProductConfigurationController);
- Type[] types = assembly.GetTypes();
-
- Logger.LogInformation(String.Format("Loading controllers from {0}", assembly.Location));
-
- foreach (Type t in types)
- {
- if (!t.IsAbstract && interfaceType.IsAssignableFrom(t))
- {
- object[] attr = t.GetCustomAttributes(typeof(ProductConfigurationAttribute), false);
-
- // if the type doesn't have the right attribute, we ignore it
- if (attr == null || attr.Length != 1) continue;
-
- ProductConfigurationAttribute a = (ProductConfigurationAttribute)attr[0];
-
- // if this is the first controller for this product name, then add it to our
- // dictionary
- if (!controllerTypes.ContainsKey(a.ProductName))
- controllerTypes[a.ProductName] = new ControllerType(assembly, t.FullName, a.Version);
- else
- {
- ControllerType existingType = controllerTypes[a.ProductName];
- if (a.Version > existingType.Version)
- controllerTypes[a.ProductName] = new ControllerType(assembly, t.FullName, a.Version);
- }
- }
- }
- }
-
- /// <summary>
- /// Loads all the plugins from the executing assembly and all assemblies in the same folder
- /// that end in -plugin.dll
- /// </summary>
- private void LoadPlugins()
- {
- if (loadedPlugins) return;
-
- // first load the plugins that are baked in
- LoadControllersFromAssembly(Assembly.GetExecutingAssembly());
-
- string pluginsDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- string[] plugins = Directory.GetFiles(pluginsDir, "*Plugins.dll");
- foreach (string plugin in plugins)
- {
- Assembly a = Assembly.LoadFile(plugin);
- LoadControllersFromAssembly(a);
- }
-
- loadedPlugins = true;
- }
-
- /// <summary>
- /// Returns a list of server controllers, possibly running
- /// </summary>
- /// <param name="running">If true only return the controllers whose servers are running</param>
- /// <returns></returns>
- public List<ServerProductConfigurationController> GetServerControllers(bool running)
- {
- List<ServerProductConfigurationController> servers = new List<ServerProductConfigurationController>();
-
- foreach (ProductConfigurationController controller in controllerCache.Values)
- {
- if (!(controller is ServerProductConfigurationController)) continue;
- ServerProductConfigurationController s = controller as ServerProductConfigurationController;
- if (running && !s.IsRunning) continue;
- servers.Add(s);
- }
- return servers;
- }
- }
-
- struct ControllerType
- {
- public Assembly HostingAssembly;
- public string TypeName;
- public int Version;
-
- public ControllerType(Assembly a, string type, int version)
- {
- HostingAssembly = a;
- TypeName = type;
- Version = version;
- }
- }
+ public class PluginManager
+ {
+ private bool loadedPlugins;
+ private Dictionary<string, ControllerType> controllerTypes =
+ new Dictionary<string, ControllerType>();
+ private Dictionary<string, ProductConfigurationController> controllerCache =
+ new Dictionary<string,ProductConfigurationController>();
+
+ public static PluginManager Instance = new PluginManager();
+
+ /// <summary>
+ /// Retrieves the latest controller plugin (if any) associated with the given product name
+ /// </summary>
+ public static ProductConfigurationController GetController(string productName)
+ {
+ return Instance.GetControllerInternal(productName);
+ }
+
+ private ProductConfigurationController GetControllerInternal(string productName)
+ {
+ if (!loadedPlugins)
+ LoadPlugins();
+
+ // first look to see if we have created this controller before
+ if (controllerCache.ContainsKey(productName))
+ return controllerCache[productName];
+
+ // now find the best match between the product name and our controller product names
+ string longestKey = String.Empty;
+ foreach (string key in controllerTypes.Keys)
+ if (productName.StartsWith(key) && key.Length > longestKey.Length)
+ longestKey = key;
+ if (!String.IsNullOrEmpty(longestKey))
+ {
+ Logger.LogInformation(String.Format("Creating configuration controller for: {0}.", productName));
+ ControllerType cType = controllerTypes[longestKey];
+ ProductConfigurationController c = (ProductConfigurationController)
+ cType.HostingAssembly.CreateInstance(cType.TypeName, false);
+ if (c != null)
+ {
+ controllerCache[productName] = c;
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ private void LoadControllersFromAssembly(Assembly assembly)
+ {
+ Type interfaceType = typeof(ProductConfigurationController);
+ Type[] types = assembly.GetTypes();
+
+ Logger.LogInformation(String.Format("Loading controllers from {0}", assembly.Location));
+
+ foreach (Type t in types)
+ {
+ if (!t.IsAbstract && interfaceType.IsAssignableFrom(t))
+ {
+ object[] attr = t.GetCustomAttributes(typeof(ProductConfigurationAttribute), false);
+
+ // if the type doesn't have the right attribute, we ignore it
+ if (attr == null || attr.Length != 1)
+ continue;
+
+ ProductConfigurationAttribute a = (ProductConfigurationAttribute)attr[0];
+
+ // if this is the first controller for this product name, then add it to our
+ // dictionary
+ if (!controllerTypes.ContainsKey(a.ProductName))
+ controllerTypes[a.ProductName] = new ControllerType(assembly, t.FullName, a.Version);
+ else
+ {
+ ControllerType existingType = controllerTypes[a.ProductName];
+ if (a.Version > existingType.Version)
+ controllerTypes[a.ProductName] = new ControllerType(assembly, t.FullName, a.Version);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Loads all the plugins from the executing assembly and all assemblies in the same folder
+ /// that end in -plugin.dll
+ /// </summary>
+ private void LoadPlugins()
+ {
+ if (loadedPlugins)
+ return;
+
+ // first load the plugins that are baked in
+ LoadControllersFromAssembly(Assembly.GetExecutingAssembly());
+
+ string pluginsDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string[] plugins = Directory.GetFiles(pluginsDir, "*Plugins.dll");
+ foreach (string plugin in plugins)
+ {
+ Assembly a = Assembly.LoadFile(plugin);
+ LoadControllersFromAssembly(a);
+ }
+
+ loadedPlugins = true;
+ }
+
+ /// <summary>
+ /// Returns a list of server controllers, possibly running
+ /// </summary>
+ /// <param name="running">If true only return the controllers whose servers are running</param>
+ public List<ServerProductConfigurationController> GetServerControllers(bool running)
+ {
+ List<ServerProductConfigurationController> servers = new List<ServerProductConfigurationController>();
+
+ foreach (ProductConfigurationController controller in controllerCache.Values)
+ {
+ if (!(controller is ServerProductConfigurationController))
+ continue;
+ ServerProductConfigurationController s = controller as ServerProductConfigurationController;
+ if (running && !s.IsRunning)
+ continue;
+ servers.Add(s);
+ }
+ return servers;
+ }
+ }
+
+ struct ControllerType
+ {
+ public Assembly HostingAssembly;
+ public string TypeName;
+ public int Version;
+
+ public ControllerType(Assembly a, string type, int version)
+ {
+ HostingAssembly = a;
+ TypeName = type;
+ Version = version;
+ }
+ }
}
=== modified file 'WexInstaller.Core/Product.cs'
--- a/WexInstaller.Core/Product.cs 2011-03-21 12:59:48 +0000
+++ b/WexInstaller.Core/Product.cs 2011-03-22 11:40:09 +0000
@@ -130,10 +130,10 @@
{
Package p = GetPackage();
if (initalizePackage)
- p.PostInitialize();
+ p.PostInitialize();
p.UrlBase = UrlBase;
- if (String.IsNullOrEmpty(p.Id) == false) // Valid Package.
+ if (!String.IsNullOrEmpty(p.Id)) // Valid Package.
{
// Is the package in our cache?
CurrentState = (p.FoundLocal) ? ProductState.FoundLocal : ProductState.WebRemote;
@@ -511,7 +511,8 @@
else
type = ChangeType.Removal;
else
- type = ChangeType.Installation;
+ if (ProposedInstalled)
+ type = ChangeType.Installation;
}
// If there was no change for the product as such go through the features to find changes.
@@ -528,7 +529,10 @@
else
type = ChangeType.Removal;
else
- type = ChangeType.Installation;
+ if (ProposedInstalled)
+ type = ChangeType.Installation;
+
+ break;
}
}
@@ -683,77 +687,115 @@
private bool FindReleatedInstalledProducts()
{
- bool FoundUpgradeableProducts = false;
-
- if (UpgradeId.Length > 0)
+ bool foundUpgradeableProducts = false;
+
+ if (UpgradeId.Length > 0)
+ {
+ StringBuilder productCode = new StringBuilder(39);
+ uint currentIndex = 0;
+ MSIEnumError rc = MSIEnumError.Success;
+
+ do
{
- StringBuilder ProductCode = new StringBuilder(39);
- uint CurrentIndex = 0;
- MSIEnumError rc = MSIEnumError.Success;
-
- do
+ if (productCode.Length > 0) // else this.Installed == true;
+ {
+ Package p = GetPackage();
+ string relatedProductCode = productCode.ToString();
+ if (relatedProductCode != p.Id)
{
- if (ProductCode.Length > 0) // else this.Installed == true;
+ if (Packages.Count == 2)
+ {
+ // No need to search again because there can be only 2 packages.
+ foundUpgradeableProducts = true;
+ }
+ else
+ {
+ Package possibleOlderPackage = new Package();
+ possibleOlderPackage.Id = relatedProductCode;
+
+ if (p.ThisVersion != possibleOlderPackage.ThisVersion)
{
- Package p = GetPackage();
- string relatedProductCode = ProductCode.ToString();
- if (relatedProductCode != p.Id)
+ // Compare only products with same major and minor number.
+ Regex regex = new Regex(@"(?<main>(\d+\.){2})(?<build>\d+[a-z]?)(?<revision>\.\d+)?");
+ Match thisMatch = regex.Match(p.ThisVersion);
+ Match otherMatch = regex.Match(possibleOlderPackage.ThisVersion);
+ if (thisMatch.Success && otherMatch.Success)
+ {
+ if (thisMatch.Groups["main"].Value == otherMatch.Groups["main"].Value)
{
- if (Packages.Count == 2)
- {
- // No need to search again because there can be only 2 packages.
- FoundUpgradeableProducts = true;
- }
- else
- {
- Package possibleOlderVersion = new Package();
- possibleOlderVersion.Id = relatedProductCode;
-
- if (p.ThisVersion.Substring(0, 3) == possibleOlderVersion.ThisVersion.Substring(0, 3)) // Same MAJOR.MINOR family
- {
- int olderMinor = Convert.ToInt32(possibleOlderVersion.ThisVersion.Substring(possibleOlderVersion.ThisVersion.LastIndexOf('.') + 1));
- int thisMinor = Convert.ToInt32(p.ThisVersion.Substring(p.ThisVersion.LastIndexOf('.') + 1));
-
- if (thisMinor > olderMinor)
- {
- FoundUpgradeableProducts = PopulateRelatePackageFeatures(possibleOlderVersion);
- if (FoundUpgradeableProducts == true)
- {
- possibleOlderVersion.PostInitialize();
- possibleOlderVersion.UrlBase = p.UrlBase;
- Packages.Add(possibleOlderVersion);
- }
- }
- else
- {
- // The install will fail because a newer version of the MSI is available.
- // Maybe, the current package should be replaced with a from memory version since everything is INSTALLED?
- Logger.LogError("A newer product has been found but we're not sure what to do with it yet so hold this place.");
- }
- }
- }
+ // Unfortunately we have to do string comparisons instead just using numbers here
+ // as it can happen there are additional letters in a number (<sic>).
+ string thisBuild = thisMatch.Groups["build"].Value;
+ string otherBuild = otherMatch.Groups["build"].Value;
+
+ int comparison = Utilities.CompareAsNumbers(thisBuild, otherBuild);
+ bool haveNewer = false;
+ if (comparison > 0)
+ haveNewer = true;
+ else
+ if (comparison == 0)
+ {
+ // Same build numbers too. So compare finally the release numbers.
+ // This also works if one or both values are missing.
+ // If both are missing then they are considered equal, otherwise the missing
+ // one represents the older version.
+ string thisRelease = thisMatch.Groups["build"].Value;
+ string otherRelease = otherMatch.Groups["build"].Value;
+
+ if (Utilities.CompareAsNumbers(thisRelease, otherRelease) > 0)
+ haveNewer = true;
+ }
+
+ if (haveNewer)
+ {
+ foundUpgradeableProducts = PopulateRelatePackageFeatures(possibleOlderPackage);
+ if (foundUpgradeableProducts)
+ {
+ possibleOlderPackage.PostInitialize();
+ possibleOlderPackage.UrlBase = p.UrlBase;
+ Packages.Add(possibleOlderPackage);
+ }
+ }
+ else
+ {
+ // The install will fail because a newer version of the product is already installed.
+ // Maybe, the current package should be replaced with a from memory version since everything is INSTALLED?
+ // TODO: warn the user that this product is already installed and won't be installed again by this installer.
+ Logger.LogError("A newer product is already installed but we're not sure what to do with it yet so hold this place.");
+ }
}
- ProductCode.Remove(0, ProductCode.Length);
- }
- try
- {
- rc = (MSIEnumError)MsiInterop.MsiEnumRelatedProducts(UpgradeId, 0, CurrentIndex++, ProductCode);
- }
- catch (Exception e)
- {
- InstallActionLog.AppendLine(e.Message);
- }
- }
- while (rc == MSIEnumError.Success);
-
- if (rc != MSIEnumError.NoMoreItems)
- {
- // Record the real error.
- InstallActionLog.AppendLine(String.Format("Found error: {0}", rc.ToString()));
- }
- }
-
- return FoundUpgradeableProducts;
+ }
+ else
+ {
+ if (!thisMatch.Success)
+ Logger.LogError(string.Format("Version of this package ({0}) is invalid.", p.ThisVersion));
+ if (!otherMatch.Success)
+ Logger.LogError(string.Format("Version of related package ({0}) is invalid.", possibleOlderPackage.ThisVersion));
+ }
+ }
+ }
+ }
+ productCode.Remove(0, productCode.Length);
+ }
+ try
+ {
+ rc = (MSIEnumError)MsiInterop.MsiEnumRelatedProducts(UpgradeId, 0, currentIndex++, productCode);
+ }
+ catch (Exception e)
+ {
+ InstallActionLog.AppendLine(e.Message);
+ }
+ }
+ while (rc == MSIEnumError.Success);
+
+ if (rc != MSIEnumError.NoMoreItems)
+ {
+ // Record the real error.
+ InstallActionLog.AppendLine(String.Format("Found error: {0}", rc.ToString()));
+ }
+ }
+
+ return foundUpgradeableProducts;
}
// Download
=== modified file 'WexInstaller.Core/ProductManager.cs'
--- a/WexInstaller.Core/ProductManager.cs 2011-03-21 15:29:28 +0000
+++ b/WexInstaller.Core/ProductManager.cs 2011-03-22 11:40:09 +0000
@@ -80,14 +80,14 @@
pc.Type = (ProductCategoryType)Enum.Parse(typeof(ProductCategoryType), pc.Name);
foreach (Product p in pc.Products)
{
+ // Initialize the controller if there is any.
+ if (p.Controller != null)
+ p.Controller.Initialize();
+
p.PostInitialize(true);
p.SetParent(pc);
foreach (ProductFeature feature in p.GetProductFeatures())
feature.SetParent(p);
-
- // Initialize the controller if there are any
- if (p.Controller != null)
- p.Controller.Initialize();
}
}
=== added file 'WexInstaller.Core/Utilities.cs'
--- a/WexInstaller.Core/Utilities.cs 1970-01-01 00:00:00 +0000
+++ b/WexInstaller.Core/Utilities.cs 2011-03-22 11:40:09 +0000
@@ -0,0 +1,21 @@
+
+namespace WexInstaller.Core
+{
+ class Utilities
+ {
+ /// <summary>
+ /// Compares two strings containing numbers and optionally additional content lexically.
+ /// </summary>
+ /// <returns>Same as string.CompareTo.</returns>
+ public static int CompareAsNumbers(string s1, string s2)
+ {
+ // Make strings comparable as numbers (they must have the same length to work properly).
+ while (s1.Length < s2.Length)
+ s1.Insert(0, "0");
+ while (s2.Length < s1.Length)
+ s2.Insert(0, "0");
+
+ return s1.CompareTo(s2);
+ }
+ }
+}
=== modified file 'WexInstaller.Core/WexInstaller.Core.csproj'
--- a/WexInstaller.Core/WexInstaller.Core.csproj 2011-03-18 10:29:04 +0000
+++ b/WexInstaller.Core/WexInstaller.Core.csproj 2011-03-22 11:40:09 +0000
@@ -79,6 +79,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
+ <Compile Include="Utilities.cs" />
<Compile Include="Win32.cs" />
</ItemGroup>
<ItemGroup>
Attachment: [text/bzr-bundle] bzr/mike.lischke@oracle.com-20110322114009-7ca4p2imyu0nemxk.bundle
| Thread |
|---|
| • bzr commit into wex-installer-1.0 branch (mike.lischke:377) | Mike Lischke | 22 Mar |