diff options
author | Lluis Sanchez Gual <lluis@novell.com> | 2011-04-07 16:14:02 +0400 |
---|---|---|
committer | Lluis Sanchez Gual <lluis@novell.com> | 2011-04-07 16:14:02 +0400 |
commit | a3bf1a090e4e0c2caea0e859b88f7c2c659322ea (patch) | |
tree | 026776292aa72d63155c7c95ee341d2f5d7ebbb8 | |
parent | 340763ff61088c80a7bff3778a7484f71c874758 (diff) |
Properly handle multiple installed instances of the same add-in version
Multiple installed instances of the same add-in version are now
tolerated, an won't cause add-in database inconsistencies anymore.
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/AddinDatabase.cs | 143 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/AddinScanResult.cs | 13 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/AddinScanner.cs | 6 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/FileDatabase.cs | 14 |
4 files changed, 134 insertions, 42 deletions
diff --git a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs index 3e57ea1..acff88a 100644 --- a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs +++ b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs @@ -229,13 +229,14 @@ namespace Mono.Addins.Database throw new InvalidOperationException ("LatestVersionsOnly flag not supported here"); if (allSetupInfos == null) { - List<Addin> alist = new List<Addin> (); + Dictionary<string,Addin> adict = new Dictionary<string, Addin> (); // Global add-ins are valid for any private domain if (domain != AddinDatabase.GlobalDomain) - FindInstalledAddins (alist, AddinDatabase.GlobalDomain, idFilter); + FindInstalledAddins (adict, AddinDatabase.GlobalDomain, idFilter); - FindInstalledAddins (alist, domain, idFilter); + FindInstalledAddins (adict, domain, idFilter); + List<Addin> alist = new List<Addin> (adict.Values); UpdateLastVersionFlags (alist); if (idFilter != null) return alist; @@ -271,7 +272,7 @@ namespace Mono.Addins.Database return addins.Where (a => Addin.GetIdName (a.Id) == id); } - void FindInstalledAddins (List<Addin> result, string domain, string idFilter) + void FindInstalledAddins (Dictionary<string,Addin> result, string domain, string idFilter) { if (idFilter == null) idFilter = "*"; @@ -279,7 +280,11 @@ namespace Mono.Addins.Database if (Directory.Exists (dir)) { foreach (string file in fileDatabase.GetDirectoryFiles (dir, idFilter + ",*.maddin")) { string id = Path.GetFileNameWithoutExtension (file); - result.Add (GetInstalledDomainAddin (domain, id, true, false, false)); + if (!result.ContainsKey (id)) { + var adesc = GetInstalledDomainAddin (domain, id, true, false, false); + if (adesc != null) + result.Add (id, adesc); + } } } } @@ -541,7 +546,7 @@ namespace Mono.Addins.Database Update (null, domain); } - void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, ArrayList addinsToUpdate, ArrayList addinsToUpdateRelations, ArrayList removedAddins) + void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, List<string> addinsToUpdate, List<string> addinsToUpdateRelations, List<string> removedAddins) { AddinUpdateData updateData = new AddinUpdateData (this, monitor); @@ -570,7 +575,10 @@ namespace Mono.Addins.Database if (monitor.LogLevel > 2) monitor.Log ("Doing a partial registry update.\nAdd-ins to be updated:"); // Get the files and ids of all add-ins that have to be updated - foreach (string sa in addinsToUpdate) { + // Include removed add-ins: if there are several instances of the same add-in, removing one of + // them will make other instances to show up. If there is a single instance, its files are + // already removed. + foreach (string sa in addinsToUpdate.Union (removedAddins)) { changedAddins [sa] = sa; if (monitor.LogLevel > 2) monitor.Log (" - " + sa); @@ -600,9 +608,6 @@ namespace Mono.Addins.Database } } } - - foreach (string s in removedAddins) - changedAddins [s] = s; } else { foreach (string dom in domains) @@ -1342,7 +1347,7 @@ namespace Mono.Addins.Database AddinFileInfo afi = finfo.GetAddinFileInfo (file); if (afi != null && afi.IsAddin) { AddinDescription adesc; - GetAddinDescription (progressStatus, afi.Domain, afi.AddinId, out adesc); + GetAddinDescription (progressStatus, afi.Domain, afi.AddinId, file, out adesc); if (adesc != null) adesc.Save (outFile); return; @@ -1406,36 +1411,102 @@ namespace Mono.Addins.Database return Path.Combine (AddinFolderCachePath, s + ".data"); } - internal void UninstallAddin (IProgressStatus monitor, string domain, string addinId, AddinScanResult scanResult) + internal void UninstallAddin (IProgressStatus monitor, string domain, string addinId, string addinFile, AddinScanResult scanResult) { - scanResult.AddRemovedAddin (addinId); - string file = GetDescriptionPath (domain, addinId); - if (!fileDatabase.Exists (file)) { + AddinDescription desc; + + if (!GetAddinDescription (monitor, domain, addinId, addinFile, out desc)) { + // If we can't get information about the old assembly, just regenerate all relation data + scanResult.RegenerateRelationData = true; return; } - // Add-in already existed. The dependencies of the old add-in need to be re-analized + // If the add-in didn't exist, there is nothing left to do + + if (desc == null) + return; + + // If the add-in already existed, the dependencies of the old add-in need to be re-analized + + Util.AddDependencies (desc, scanResult); + if (desc.IsRoot) + scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile); - AddinDescription desc; - if (ReadAddinDescription (monitor, file, out desc)) { - Util.AddDependencies (desc, scanResult); - if (desc.IsRoot) - scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile); - } else - // If we can't get information about the old assembly, just regenerate all relation data - scanResult.RegenerateRelationData = true; + RemoveAddinDescriptionFile (monitor, desc.FileName); + } + + public bool GetAddinDescription (IProgressStatus monitor, string domain, string addinId, string addinFile, out AddinDescription description) + { + // If the same add-in is installed in different folders (in the same domain) there will be several .maddin files for it, + // using the suffix "_X" where X is a number > 1 (for example: someAddin,1.0.maddin, someAddin,1.0.maddin_2, someAddin,1.0.maddin_3, ...) + // We need to return the .maddin whose AddinFile matches the one being requested + + addinFile = Path.GetFullPath (addinFile); + int altNum = 1; + string baseFile = GetDescriptionPath (domain, addinId); + string file = baseFile; + bool failed = false; + + do { + if (!ReadAddinDescription (monitor, file, out description)) { + // Remove the AddinDescription here since it is corrupted. + // Avoids creating alternate versions of corrupted files when later calling SaveDescription. + RemoveAddinDescriptionFile (monitor, file); + failed = true; + continue; + } + if (description == null) + break; + if (Path.GetFullPath (description.AddinFile) == addinFile) + return true; + file = baseFile + "_" + (++altNum); + } + while (fileDatabase.Exists (file)); + + // File not found. Return false only if there has been any read error. + description = null; + return failed; + } + + bool RemoveAddinDescriptionFile (IProgressStatus monitor, string file) + { + // Removes an add-in description and shifts up alternate instances of the description file + // (so xxx,1.0.maddin_2 will become xxx,1.0.maddin, xxx,1.0.maddin_3 -> xxx,1.0.maddin_2, etc) + + if (!SafeDelete (monitor, file)) + return false; + + int dversion; + if (file.EndsWith (".maddin")) + dversion = 2; + else { + int i = file.LastIndexOf ('_'); + dversion = 1 + int.Parse (file.Substring (i + 1)); + file = file.Substring (0, i); + } - SafeDelete (monitor, file); + while (fileDatabase.Exists (file + "_" + dversion)) { + string newFile = dversion == 2 ? file : file + "_" + (dversion-1); + try { + fileDatabase.Rename (file + "_" + dversion, newFile); + } catch (Exception ex) { + if (monitor.LogLevel > 1) { + monitor.Log ("Could not rename file '" + file + "_" + dversion + "' to '" + newFile + "'"); + monitor.Log (ex.ToString ()); + } + } + dversion++; + } string dir = Path.GetDirectoryName (file); if (fileDatabase.DirectoryIsEmpty (dir)) SafeDeleteDir (monitor, dir); - SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file))); - } - - public bool GetAddinDescription (IProgressStatus monitor, string domain, string addinId, out AddinDescription description) - { - string file = GetDescriptionPath (domain, addinId); - return ReadAddinDescription (monitor, file, out description); + + if (dversion == 2) { + // All versions of the add-in removed. + SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file))); + } + + return true; } public bool ReadAddinDescription (IProgressStatus monitor, string file, out AddinDescription description) @@ -1465,6 +1536,14 @@ namespace Mono.Addins.Database string dir = Path.GetDirectoryName (file); if (!fileDatabase.DirExists (dir)) fileDatabase.CreateDir (dir); + if (fileDatabase.Exists (file)) { + // Another AddinDescription already exists with the same name. + // Create an alternate AddinDescription file + int altNum = 2; + while (fileDatabase.Exists (file + "_" + altNum)) + altNum++; + file = file + "_" + altNum; + } desc.SaveBinary (fileDatabase, file); } return true; diff --git a/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs b/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs index f69bec8..4a3fbf5 100644 --- a/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs +++ b/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs @@ -28,24 +28,23 @@ using System; -using System.Reflection; -using System.IO; using System.Collections; -using System.Collections.Specialized; -using Mono.Addins.Description; +using System.Collections.Generic; +using System.IO; +using System.Reflection; namespace Mono.Addins.Database { internal class AddinScanResult: MarshalByRefObject, IAssemblyLocator { internal ArrayList AddinsToScan = new ArrayList (); - internal ArrayList AddinsToUpdateRelations = new ArrayList (); - internal ArrayList AddinsToUpdate = new ArrayList (); + internal List<string> AddinsToUpdateRelations = new List<string> (); + internal List<string> AddinsToUpdate = new List<string> (); internal ArrayList FilesToScan = new ArrayList (); internal ArrayList ModifiedFolderInfos = new ArrayList (); internal ArrayList FilesWithScanFailure = new ArrayList (); internal AddinHostIndex HostIndex; - internal ArrayList RemovedAddins = new ArrayList (); + internal List<string> RemovedAddins = new List<string> (); Hashtable visitedFolders = new Hashtable (); Hashtable assemblyLocations = new Hashtable (); diff --git a/Mono.Addins/Mono.Addins.Database/AddinScanner.cs b/Mono.Addins/Mono.Addins.Database/AddinScanner.cs index 613610b..2b5db01 100644 --- a/Mono.Addins/Mono.Addins.Database/AddinScanner.cs +++ b/Mono.Addins/Mono.Addins.Database/AddinScanner.cs @@ -179,7 +179,7 @@ namespace Mono.Addins.Database return; foreach (AddinFileInfo info in missing) { - database.UninstallAddin (monitor, info.Domain, info.AddinId, scanResult); + database.UninstallAddin (monitor, info.Domain, info.AddinId, info.File, scanResult); } } } @@ -288,7 +288,7 @@ namespace Mono.Addins.Database // Also, the dependencies of the old add-in need to be re-analized AddinDescription existingDescription = null; - bool res = database.GetAddinDescription (monitor, folderInfo.Domain, config.AddinId, out existingDescription); + bool res = database.GetAddinDescription (monitor, folderInfo.Domain, config.AddinId, config.AddinFile, out existingDescription); // If we can't get information about the old assembly, just regenerate all relation data if (!res) @@ -306,7 +306,7 @@ namespace Mono.Addins.Database // If the scanned file results in an add-in version different from the one obtained from // previous scans, the old add-in needs to be uninstalled. if (fi != null && fi.IsAddin && fi.AddinId != config.AddinId) { - database.UninstallAddin (monitor, folderInfo.Domain, fi.AddinId, scanResult); + database.UninstallAddin (monitor, folderInfo.Domain, fi.AddinId, fi.File, scanResult); // If the add-in version has changed, regenerate everything again since old data can't be reused if (Addin.GetIdName (fi.AddinId) == Addin.GetIdName (config.AddinId)) diff --git a/Mono.Addins/Mono.Addins.Database/FileDatabase.cs b/Mono.Addins/Mono.Addins.Database/FileDatabase.cs index ac4f626..b96f33d 100644 --- a/Mono.Addins/Mono.Addins.Database/FileDatabase.cs +++ b/Mono.Addins/Mono.Addins.Database/FileDatabase.cs @@ -153,6 +153,20 @@ namespace Mono.Addins.Database return File.Create (fileName); } + public void Rename (string fileName, string newName) + { + if (inTransaction) { + deletedFiles.Remove (newName); + deletedDirs.Remove (Path.GetDirectoryName (newName)); + foldersToUpdate [Path.GetDirectoryName (newName)] = null; + string s = File.Exists (fileName + ".new") ? fileName + ".new" : fileName; + File.Copy (s, newName + ".new"); + Delete (fileName); + } + else + File.Move (fileName, newName); + } + public Stream OpenRead (string fileName) { if (inTransaction) { |